summaryrefslogtreecommitdiff
path: root/indra/llwindow
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llwindow
Print done when done.
Diffstat (limited to 'indra/llwindow')
-rw-r--r--indra/llwindow/lldxhardware.cpp508
-rw-r--r--indra/llwindow/lldxhardware.h90
-rw-r--r--indra/llwindow/llkeyboard.cpp387
-rw-r--r--indra/llwindow/llkeyboard.h124
-rw-r--r--indra/llwindow/llkeyboardmacosx.cpp309
-rw-r--r--indra/llwindow/llkeyboardmacosx.h36
-rw-r--r--indra/llwindow/llkeyboardsdl.cpp324
-rw-r--r--indra/llwindow/llkeyboardsdl.h37
-rw-r--r--indra/llwindow/llkeyboardwin32.cpp372
-rw-r--r--indra/llwindow/llkeyboardwin32.h40
-rw-r--r--indra/llwindow/llmousehandler.h39
-rw-r--r--indra/llwindow/llwindow.cpp399
-rw-r--r--indra/llwindow/llwindow.h328
-rw-r--r--indra/llwindow/llwindowheadless.cpp32
-rw-r--r--indra/llwindow/llwindowheadless.h98
-rw-r--r--indra/llwindow/llwindowmacosx-objc.h19
-rw-r--r--indra/llwindow/llwindowmacosx-objc.mm87
-rw-r--r--indra/llwindow/llwindowmacosx.cpp2904
-rw-r--r--indra/llwindow/llwindowmacosx.h189
-rw-r--r--indra/llwindow/llwindowmesaheadless.cpp64
-rw-r--r--indra/llwindow/llwindowmesaheadless.h104
-rw-r--r--indra/llwindow/llwindowsdl.cpp2487
-rw-r--r--indra/llwindow/llwindowsdl.h198
-rw-r--r--indra/llwindow/llwindowwin32.cpp3247
-rw-r--r--indra/llwindow/llwindowwin32.h187
25 files changed, 12609 insertions, 0 deletions
diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp
new file mode 100644
index 0000000000..a972a29aa4
--- /dev/null
+++ b/indra/llwindow/lldxhardware.cpp
@@ -0,0 +1,508 @@
+/**
+ * @file lldxhardware.cpp
+ * @brief LLDXHardware implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifdef LL_WINDOWS
+
+// Culled from some Microsoft sample code
+
+#include "linden_common.h"
+
+#include <assert.h>
+#include <dxdiag.h>
+
+#include <boost/tokenizer.hpp>
+
+#include "lldxhardware.h"
+#include "llerror.h"
+
+#include "llstring.h"
+#include "llstl.h"
+
+void (*gWriteDebug)(const char* msg) = NULL;
+LLDXHardware gDXHardware;
+
+//-----------------------------------------------------------------------------
+// Defines, and constants
+//-----------------------------------------------------------------------------
+#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
+#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
+#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
+
+std::string get_string(IDxDiagContainer *containerp, WCHAR *wszPropName)
+{
+ HRESULT hr;
+ VARIANT var;
+ WCHAR wszPropValue[256];
+
+ VariantInit( &var );
+ hr = containerp->GetProp(wszPropName, &var );
+ if( SUCCEEDED(hr) )
+ {
+ // Switch off the type. There's 4 different types:
+ switch( var.vt )
+ {
+ case VT_UI4:
+ swprintf( wszPropValue, L"%d", var.ulVal );
+ break;
+ case VT_I4:
+ swprintf( wszPropValue, L"%d", var.lVal );
+ break;
+ case VT_BOOL:
+ wcscpy( wszPropValue, (var.boolVal) ? L"true" : L"false" );
+ break;
+ case VT_BSTR:
+ wcsncpy( wszPropValue, var.bstrVal, 255 );
+ wszPropValue[255] = 0;
+ break;
+ }
+ }
+ // Clear the variant (this is needed to free BSTR memory)
+ VariantClear( &var );
+
+ return utf16str_to_utf8str(wszPropValue);
+}
+
+
+LLVersion::LLVersion()
+{
+ mValid = FALSE;
+ S32 i;
+ for (i = 0; i < 4; i++)
+ {
+ mFields[i] = 0;
+ }
+}
+
+BOOL LLVersion::set(const std::string &version_string)
+{
+ S32 i;
+ for (i = 0; i < 4; i++)
+ {
+ mFields[i] = 0;
+ }
+ // Split the version string.
+ std::string str(version_string);
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".", "", boost::keep_empty_tokens);
+ tokenizer tokens(str, sep);
+
+ tokenizer::iterator iter = tokens.begin();
+ S32 count = 0;
+ for (;(iter != tokens.end()) && (count < 4);++iter)
+ {
+ mFields[count] = atoi(iter->c_str());
+ count++;
+ }
+ if (count < 4)
+ {
+ //llwarns << "Potentially bogus version string!" << version_string << llendl;
+ for (i = 0; i < 4; i++)
+ {
+ mFields[i] = 0;
+ }
+ mValid = FALSE;
+ }
+ else
+ {
+ mValid = TRUE;
+ }
+ return mValid;
+}
+
+S32 LLVersion::getField(const S32 field_num)
+{
+ if (!mValid)
+ {
+ return -1;
+ }
+ else
+ {
+ return mFields[field_num];
+ }
+}
+
+LLString LLDXDriverFile::dump()
+{
+ if (gWriteDebug)
+ {
+ gWriteDebug("Filename:");
+ gWriteDebug(mName.c_str());
+ gWriteDebug("\n");
+ gWriteDebug("Ver:");
+ gWriteDebug(mVersionString.c_str());
+ gWriteDebug("\n");
+ gWriteDebug("Date:");
+ gWriteDebug(mDateString.c_str());
+ gWriteDebug("\n");
+ }
+ llinfos << mFilepath << llendl;
+ llinfos << mName << llendl;
+ llinfos << mVersionString << llendl;
+ llinfos << mDateString << llendl;
+
+ return "";
+}
+
+LLDXDevice::~LLDXDevice()
+{
+ for_each(mDriverFiles.begin(), mDriverFiles.end(), DeletePairedPointer());
+}
+
+std::string LLDXDevice::dump()
+{
+ if (gWriteDebug)
+ {
+ gWriteDebug("StartDevice\n");
+ gWriteDebug("DeviceName:");
+ gWriteDebug(mName.c_str());
+ gWriteDebug("\n");
+ gWriteDebug("PCIString:");
+ gWriteDebug(mPCIString.c_str());
+ gWriteDebug("\n");
+ }
+ llinfos << llendl;
+ llinfos << "DeviceName:" << mName << llendl;
+ llinfos << "PCIString:" << mPCIString << llendl;
+ llinfos << "Drivers" << llendl;
+ llinfos << "-------" << llendl;
+ for (driver_file_map_t::iterator iter = mDriverFiles.begin(),
+ end = mDriverFiles.end();
+ iter != end; iter++)
+ {
+ LLDXDriverFile *filep = iter->second;
+ filep->dump();
+ }
+ if (gWriteDebug)
+ {
+ gWriteDebug("EndDevice\n");
+ }
+
+ return "";
+}
+
+LLDXDriverFile *LLDXDevice::findDriver(const std::string &driver)
+{
+ for (driver_file_map_t::iterator iter = mDriverFiles.begin(),
+ end = mDriverFiles.end();
+ iter != end; iter++)
+ {
+ LLDXDriverFile *filep = iter->second;
+ if (!utf8str_compare_insensitive(filep->mName,driver))
+ {
+ return filep;
+ }
+ }
+
+ return NULL;
+}
+
+LLDXHardware::LLDXHardware()
+{
+ mVRAM = 0;
+ gWriteDebug = NULL;
+}
+
+void LLDXHardware::cleanup()
+{
+ for_each(mDevices.begin(), mDevices.end(), DeletePairedPointer());
+}
+
+LLString LLDXHardware::dumpDevices()
+{
+ if (gWriteDebug)
+ {
+ gWriteDebug("\n");
+ gWriteDebug("StartAllDevices\n");
+ }
+ for (device_map_t::iterator iter = mDevices.begin(),
+ end = mDevices.end();
+ iter != end; iter++)
+ {
+ LLDXDevice *devicep = iter->second;
+ devicep->dump();
+ }
+ if (gWriteDebug)
+ {
+ gWriteDebug("EndAllDevices\n\n");
+ }
+ return "";
+}
+
+LLDXDevice *LLDXHardware::findDevice(const std::string &vendor, const std::string &devices)
+{
+ // Iterate through different devices tokenized in devices string
+ std::string str(devices);
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
+ tokenizer tokens(str, sep);
+
+ tokenizer::iterator iter = tokens.begin();
+ for (;iter != tokens.end();++iter)
+ {
+ std::string dev_str = *iter;
+ for (device_map_t::iterator iter = mDevices.begin(),
+ end = mDevices.end();
+ iter != end; iter++)
+ {
+ LLDXDevice *devicep = iter->second;
+ if ((devicep->mVendorID == vendor)
+ && (devicep->mDeviceID == dev_str))
+ {
+ return devicep;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+BOOL LLDXHardware::getInfo(BOOL vram_only)
+{
+ LLTimer hw_timer;
+ BOOL ok = FALSE;
+ HRESULT hr;
+
+ CoInitialize(NULL);
+
+ IDxDiagProvider *dx_diag_providerp = NULL;
+ IDxDiagContainer *dx_diag_rootp = NULL;
+ IDxDiagContainer *devices_containerp = NULL;
+ IDxDiagContainer *system_device_containerp= NULL;
+ IDxDiagContainer *device_containerp = NULL;
+ IDxDiagContainer *file_containerp = NULL;
+ IDxDiagContainer *driver_containerp = NULL;
+
+ // CoCreate a IDxDiagProvider*
+ llinfos << "CoCreateInstance IID_IDxDiagProvider" << llendl;
+ hr = CoCreateInstance(CLSID_DxDiagProvider,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IDxDiagProvider,
+ (LPVOID*) &dx_diag_providerp);
+
+ if (FAILED(hr))
+ {
+ llwarns << "No DXDiag provider found! DirectX 9 not installed!" << llendl;
+ gWriteDebug("No DXDiag provider found! DirectX 9 not installed!\n");
+ goto LCleanup;
+ }
+ if (SUCCEEDED(hr)) // if FAILED(hr) then dx9 is not installed
+ {
+ // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize
+ // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are
+ // digital signed as logo'd by WHQL which may connect via internet to update
+ // WHQL certificates.
+ DXDIAG_INIT_PARAMS dx_diag_init_params;
+ ZeroMemory(&dx_diag_init_params, sizeof(DXDIAG_INIT_PARAMS));
+
+ dx_diag_init_params.dwSize = sizeof(DXDIAG_INIT_PARAMS);
+ dx_diag_init_params.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION;
+ dx_diag_init_params.bAllowWHQLChecks = TRUE;
+ dx_diag_init_params.pReserved = NULL;
+
+ llinfos << "dx_diag_providerp->Initialize" << llendl;
+ hr = dx_diag_providerp->Initialize(&dx_diag_init_params);
+ if(FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ llinfos << "dx_diag_providerp->GetRootContainer" << llendl;
+ hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp );
+ if(FAILED(hr) || !dx_diag_rootp)
+ {
+ goto LCleanup;
+ }
+
+ HRESULT hr;
+
+ // Get display driver information
+ llinfos << "dx_diag_rootp->GetChildContainer" << llendl;
+ hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp);
+ if(FAILED(hr) || !devices_containerp)
+ {
+ goto LCleanup;
+ }
+
+ // Get device 0
+ llinfos << "devices_containerp->GetChildContainer" << llendl;
+ hr = devices_containerp->GetChildContainer(L"0", &device_containerp);
+ if(FAILED(hr) || !device_containerp)
+ {
+ goto LCleanup;
+ }
+
+ // Get the English VRAM string
+ std::string ram_str = get_string(device_containerp, L"szDisplayMemoryEnglish");
+
+ // We don't need the device any more
+ SAFE_RELEASE(device_containerp);
+
+ // Dump the string as an int into the structure
+ char *stopstring;
+ mVRAM = strtol(ram_str.c_str(), &stopstring, 10);
+ llinfos << "VRAM Detected: " << mVRAM << " DX9 string: " << ram_str << llendl;
+
+ if (vram_only)
+ {
+ ok = TRUE;
+ goto LCleanup;
+ }
+
+ // Now let's get device and driver information
+ // Get the IDxDiagContainer object called "DxDiag_SystemDevices".
+ // This call may take some time while dxdiag gathers the info.
+ DWORD num_devices = 0;
+ WCHAR wszContainer[256];
+ llinfos << "dx_diag_rootp->GetChildContainer DxDiag_SystemDevices" << llendl;
+ hr = dx_diag_rootp->GetChildContainer(L"DxDiag_SystemDevices", &system_device_containerp);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ hr = system_device_containerp->GetNumberOfChildContainers(&num_devices);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ llinfos << "DX9 iterating over devices" << llendl;
+ S32 device_num = 0;
+ for (device_num = 0; device_num < (S32)num_devices; device_num++)
+ {
+ hr = system_device_containerp->EnumChildContainerNames(device_num, wszContainer, 256);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ hr = system_device_containerp->GetChildContainer(wszContainer, &device_containerp);
+ if (FAILED(hr) || device_containerp == NULL)
+ {
+ goto LCleanup;
+ }
+
+ std::string device_name = get_string(device_containerp, L"szDescription");
+ std::string device_id = get_string(device_containerp, L"szDeviceID");
+
+ LLDXDevice *dxdevicep = new LLDXDevice;
+ dxdevicep->mName = device_name;
+ dxdevicep->mPCIString = device_id;
+ mDevices[dxdevicep->mPCIString] = dxdevicep;
+
+ // Split the PCI string based on vendor, device, subsys, rev.
+ std::string str(device_id);
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("&\\", "", boost::keep_empty_tokens);
+ tokenizer tokens(str, sep);
+
+ tokenizer::iterator iter = tokens.begin();
+ S32 count = 0;
+ BOOL valid = TRUE;
+ for (;(iter != tokens.end()) && (count < 3);++iter)
+ {
+ switch (count)
+ {
+ case 0:
+ if (strcmp(iter->c_str(), "PCI"))
+ {
+ valid = FALSE;
+ }
+ break;
+ case 1:
+ dxdevicep->mVendorID = iter->c_str();
+ break;
+ case 2:
+ dxdevicep->mDeviceID = iter->c_str();
+ break;
+ default:
+ // Ignore it
+ break;
+ }
+ count++;
+ }
+
+
+ // Now, iterate through the related drivers
+ hr = device_containerp->GetChildContainer(L"Drivers", &driver_containerp);
+ if (FAILED(hr) || !driver_containerp)
+ {
+ goto LCleanup;
+ }
+
+ DWORD num_files = 0;
+ hr = driver_containerp->GetNumberOfChildContainers(&num_files);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ S32 file_num = 0;
+ for (file_num = 0; file_num < (S32)num_files; file_num++ )
+ {
+ hr = driver_containerp->EnumChildContainerNames(file_num, wszContainer, 256);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ hr = driver_containerp->GetChildContainer(wszContainer, &file_containerp);
+ if (FAILED(hr) || file_containerp == NULL)
+ {
+ goto LCleanup;
+ }
+
+ std::string driver_path = get_string(file_containerp, L"szPath");
+ std::string driver_name = get_string(file_containerp, L"szName");
+ std::string driver_version = get_string(file_containerp, L"szVersion");
+ std::string driver_date = get_string(file_containerp, L"szDatestampEnglish");
+
+ LLDXDriverFile *dxdriverfilep = new LLDXDriverFile;
+ dxdriverfilep->mName = driver_name;
+ dxdriverfilep->mFilepath= driver_path;
+ dxdriverfilep->mVersionString = driver_version;
+ dxdriverfilep->mVersion.set(driver_version);
+ dxdriverfilep->mDateString = driver_date;
+
+ dxdevicep->mDriverFiles[driver_name] = dxdriverfilep;
+
+ SAFE_RELEASE(file_containerp);
+ }
+ SAFE_RELEASE(device_containerp);
+ }
+ }
+
+ dumpDevices();
+ ok = TRUE;
+
+LCleanup:
+ if (!ok)
+ {
+ llwarns << "DX9 probe failed" << llendl;
+ gWriteDebug("DX9 probe failed\n");
+ }
+
+ SAFE_RELEASE(file_containerp);
+ SAFE_RELEASE(driver_containerp);
+ SAFE_RELEASE(device_containerp);
+ SAFE_RELEASE(devices_containerp);
+ SAFE_RELEASE(dx_diag_rootp);
+ SAFE_RELEASE(dx_diag_providerp);
+
+ CoUninitialize();
+
+ return ok;
+}
+
+void LLDXHardware::setWriteDebugFunc(void (*func)(const char*))
+{
+ gWriteDebug = func;
+}
+
+#endif
diff --git a/indra/llwindow/lldxhardware.h b/indra/llwindow/lldxhardware.h
new file mode 100644
index 0000000000..00ae525372
--- /dev/null
+++ b/indra/llwindow/lldxhardware.h
@@ -0,0 +1,90 @@
+/**
+ * @file lldxhardware.h
+ * @brief LLDXHardware definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDXHARDWARE_H
+#define LL_LLDXHARDWARE_H
+
+#include <map>
+
+#include "stdtypes.h"
+#include "llstring.h"
+
+class LLVersion
+{
+public:
+ LLVersion();
+ BOOL set(const std::string &version_string);
+ S32 getField(const S32 field_num);
+protected:
+ std::string mVersionString;
+ S32 mFields[4];
+ BOOL mValid;
+};
+
+class LLDXDriverFile
+{
+public:
+ LLString dump();
+
+public:
+ std::string mFilepath;
+ std::string mName;
+ std::string mVersionString;
+ LLVersion mVersion;
+ std::string mDateString;
+};
+
+class LLDXDevice
+{
+public:
+ ~LLDXDevice();
+ std::string dump();
+
+ LLDXDriverFile *findDriver(const std::string &driver);
+public:
+ std::string mName;
+ std::string mPCIString;
+ std::string mVendorID;
+ std::string mDeviceID;
+
+ typedef std::map<std::string, LLDXDriverFile *> driver_file_map_t;
+ driver_file_map_t mDriverFiles;
+};
+
+
+class LLDXHardware
+{
+public:
+ LLDXHardware();
+ void setWriteDebugFunc(void (*func)(const char*));
+ void cleanup();
+
+ // Returns TRUE on success.
+ // vram_only TRUE does a "light" probe.
+ BOOL getInfo(BOOL vram_only);
+
+ S32 getVRAM() const { return mVRAM; }
+
+ // Find a particular device that matches the following specs.
+ // Empty strings indicate that you don't care.
+ // You can separate multiple devices with '|' chars to indicate you want
+ // ANY of them to match and return.
+ LLDXDevice *findDevice(const std::string &vendor, const std::string &devices);
+
+ LLString dumpDevices();
+public:
+ typedef std::map<std::string, LLDXDevice *> device_map_t;
+ device_map_t mDevices;
+protected:
+ S32 mVRAM;
+};
+
+extern void (*gWriteDebug)(const char* msg);
+extern LLDXHardware gDXHardware;
+
+#endif // LL_LLDXHARDWARE_H
diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp
new file mode 100644
index 0000000000..ee42f53571
--- /dev/null
+++ b/indra/llwindow/llkeyboard.cpp
@@ -0,0 +1,387 @@
+/**
+ * @file llkeyboard.cpp
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "indra_constants.h"
+#include "llkeyboard.h"
+
+#include "llwindow.h"
+
+
+//
+// Globals
+//
+
+LLKeyboard *gKeyboard = NULL;
+
+//static
+std::map<KEY,LLString> LLKeyboard::sKeysToNames;
+std::map<LLString,KEY> LLKeyboard::sNamesToKeys;
+
+//
+// Class Implementation
+//
+
+LLKeyboard::LLKeyboard() : mCallbacks(NULL), mNumpadDistinct(ND_NUMLOCK_OFF)
+{
+ S32 i;
+
+ // Constructor for LLTimer inits each timer. We want them to
+ // be constructed without being initialized, so we shut them down here.
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyLevelFrameCount[i] = 0;
+ mKeyLevel[i] = FALSE;
+ mKeyUp[i] = FALSE;
+ mKeyDown[i] = FALSE;
+ mKeyRepeated[i] = FALSE;
+ }
+
+ mInsertMode = LL_KIM_INSERT;
+ mCurTranslatedKey = KEY_NONE;
+
+ addKeyName(' ', "Space" );
+ addKeyName(KEY_RETURN, "Enter" );
+ addKeyName(KEY_LEFT, "Left" );
+ addKeyName(KEY_RIGHT, "Right" );
+ addKeyName(KEY_UP, "Up" );
+ addKeyName(KEY_DOWN, "Down" );
+ addKeyName(KEY_ESCAPE, "Esc" );
+ addKeyName(KEY_HOME, "Home" );
+ addKeyName(KEY_END, "End" );
+ addKeyName(KEY_PAGE_UP, "PgUp" );
+ addKeyName(KEY_PAGE_DOWN, "PgDn" );
+ addKeyName(KEY_F1, "F1" );
+ addKeyName(KEY_F2, "F2" );
+ addKeyName(KEY_F3, "F3" );
+ addKeyName(KEY_F4, "F4" );
+ addKeyName(KEY_F5, "F5" );
+ addKeyName(KEY_F6, "F6" );
+ addKeyName(KEY_F7, "F7" );
+ addKeyName(KEY_F8, "F8" );
+ addKeyName(KEY_F9, "F9" );
+ addKeyName(KEY_F10, "F10" );
+ addKeyName(KEY_F11, "F11" );
+ addKeyName(KEY_F12, "F12" );
+ addKeyName(KEY_TAB, "Tab" );
+ addKeyName(KEY_ADD, "Add" );
+ addKeyName(KEY_SUBTRACT, "Subtract" );
+ addKeyName(KEY_MULTIPLY, "Multiply" );
+ addKeyName(KEY_DIVIDE, "Divide" );
+ addKeyName(KEY_PAD_LEFT, "PAD_LEFT" );
+ addKeyName(KEY_PAD_RIGHT, "PAD_RIGHT" );
+ addKeyName(KEY_PAD_DOWN, "PAD_DOWN" );
+ addKeyName(KEY_PAD_UP, "PAD_UP" );
+ addKeyName(KEY_PAD_HOME, "PAD_HOME" );
+ addKeyName(KEY_PAD_END, "PAD_END" );
+ addKeyName(KEY_PAD_PGUP, "PAD_PGUP" );
+ addKeyName(KEY_PAD_PGDN, "PAD_PGDN" );
+ addKeyName(KEY_PAD_CENTER, "PAD_CENTER" );
+ addKeyName(KEY_PAD_INS, "PAD_INS" );
+ addKeyName(KEY_PAD_DEL, "PAD_DEL" );
+ addKeyName(KEY_PAD_RETURN, "PAD_Enter" );
+ addKeyName(KEY_BUTTON0, "PAD_BUTTON0" );
+ addKeyName(KEY_BUTTON1, "PAD_BUTTON1" );
+ addKeyName(KEY_BUTTON2, "PAD_BUTTON2" );
+ addKeyName(KEY_BUTTON3, "PAD_BUTTON3" );
+ addKeyName(KEY_BUTTON4, "PAD_BUTTON4" );
+ addKeyName(KEY_BUTTON5, "PAD_BUTTON5" );
+ addKeyName(KEY_BUTTON6, "PAD_BUTTON6" );
+ addKeyName(KEY_BUTTON7, "PAD_BUTTON7" );
+ addKeyName(KEY_BUTTON8, "PAD_BUTTON8" );
+ addKeyName(KEY_BUTTON9, "PAD_BUTTON9" );
+ addKeyName(KEY_BUTTON10, "PAD_BUTTON10" );
+ addKeyName(KEY_BUTTON11, "PAD_BUTTON11" );
+ addKeyName(KEY_BUTTON12, "PAD_BUTTON12" );
+ addKeyName(KEY_BUTTON13, "PAD_BUTTON13" );
+ addKeyName(KEY_BUTTON14, "PAD_BUTTON14" );
+ addKeyName(KEY_BUTTON15, "PAD_BUTTON15" );
+
+ addKeyName(KEY_BACKSPACE, "Backsp" );
+ addKeyName(KEY_DELETE, "Del" );
+ addKeyName(KEY_SHIFT, "Shift" );
+ addKeyName(KEY_CONTROL, "Ctrl" );
+ addKeyName(KEY_ALT, "Alt" );
+ addKeyName(KEY_HYPHEN, "-" );
+ addKeyName(KEY_EQUALS, "=" );
+ addKeyName(KEY_INSERT, "Ins" );
+ addKeyName(KEY_CAPSLOCK, "CapsLock" );
+}
+
+
+LLKeyboard::~LLKeyboard()
+{
+ // nothing
+}
+
+void LLKeyboard::addKeyName(KEY key, const LLString& name)
+{
+ sKeysToNames[key] = name;
+ LLString nameuc = name;
+ LLString::toUpper(nameuc);
+ sNamesToKeys[nameuc] = key;
+}
+
+// BUG this has to be called when an OS dialog is shown, otherwise modifier key state
+// is wrong because the keyup event is never received by the main window. JC
+void LLKeyboard::resetKeys()
+{
+ S32 i;
+
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ if( mKeyLevel[i] )
+ {
+ mKeyLevel[i] = FALSE;
+ mKeyLevelFrameCount[i] = 0;
+ }
+ }
+
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyUp[i] = FALSE;
+ }
+
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyDown[i] = FALSE;
+ }
+
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyRepeated[i] = FALSE;
+ }
+}
+
+
+BOOL LLKeyboard::translateKey(const U16 os_key, KEY *out_key)
+{
+ std::map<U16, KEY>::iterator iter;
+
+ // Only translate keys in the map, ignore all other keys for now
+ iter = mTranslateKeyMap.find(os_key);
+ if (iter == mTranslateKeyMap.end())
+ {
+ //llwarns << "Unknown virtual key " << os_key << llendl;
+ *out_key = 0;
+ return FALSE;
+ }
+ else
+ {
+ *out_key = iter->second;
+ return TRUE;
+ }
+}
+
+
+U16 LLKeyboard::inverseTranslateKey(const KEY translated_key)
+{
+ std::map<KEY, U16>::iterator iter;
+ iter = mInvTranslateKeyMap.find(translated_key);
+ if (iter == mInvTranslateKeyMap.end())
+ {
+ return 0;
+ }
+ else
+ {
+ return iter->second;
+ }
+}
+
+
+BOOL LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask)
+{
+ BOOL handled = FALSE;
+ BOOL repeated = FALSE;
+
+ // is this the first time the key went down?
+ // if so, generate "character" message
+ if( !mKeyLevel[translated_key] )
+ {
+ mKeyLevel[translated_key] = TRUE;
+ mKeyLevelTimer[translated_key].reset();
+ }
+ else
+ {
+ // Level is already down, assume it's repeated.
+ repeated = TRUE;
+ mKeyRepeated[translated_key] = TRUE;
+ }
+
+ mKeyDown[translated_key] = TRUE;
+ mCurTranslatedKey = (KEY)translated_key;
+ handled = mCallbacks->handleTranslatedKeyDown(translated_key, translated_mask, repeated);
+ return handled;
+}
+
+
+BOOL LLKeyboard::handleTranslatedKeyUp(KEY translated_key, U32 translated_mask)
+{
+ BOOL handled = FALSE;
+ if( mKeyLevel[translated_key] )
+ {
+ mKeyLevel[translated_key] = FALSE;
+ mKeyLevelFrameCount[translated_key] = 0;
+
+ // Only generate key up events if the key is thought to
+ // be down. This allows you to call resetKeys() in the
+ // middle of a frame and ignore subsequent KEY_UP
+ // messages in the same frame. This was causing the
+ // sequence W<return> in chat to move agents forward. JC
+ mKeyUp[translated_key] = TRUE;
+ mKeyRepeated[translated_key] = FALSE;
+ handled = mCallbacks->handleTranslatedKeyUp(translated_key, translated_mask);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "keyup -" << translated_key << "-" << llendl;
+
+ return handled;
+}
+
+
+void LLKeyboard::toggleInsertMode()
+{
+ if (LL_KIM_INSERT == mInsertMode)
+ {
+ mInsertMode = LL_KIM_OVERWRITE;
+ }
+ else
+ {
+ mInsertMode = LL_KIM_INSERT;
+ }
+}
+
+
+// Returns time in seconds since key was pressed.
+F32 LLKeyboard::getKeyElapsedTime(KEY key)
+{
+ if( mKeyLevel[key] )
+ {
+ return mKeyLevelTimer[key].getElapsedTimeF32();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+// Returns time in frames since key was pressed.
+S32 LLKeyboard::getKeyElapsedFrameCount(KEY key)
+{
+ if( mKeyLevel[key] )
+ {
+ return mKeyLevelFrameCount[key];
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// static
+BOOL LLKeyboard::keyFromString(const LLString& str, KEY *key)
+{
+ LLString instring(str);
+ size_t length = instring.size();
+
+ if (length < 1)
+ {
+ return FALSE;
+ }
+ if (length == 1)
+ {
+ char ch = toupper(instring[0]);
+ if (('0' <= ch && ch <= '9') ||
+ ('A' <= ch && ch <= 'Z') ||
+ ('!' <= ch && ch <= '/') || // !"#$%&'()*+,-./
+ (':' <= ch && ch <= '@') || // :;<=>?@
+ ('[' <= ch && ch <= '`') || // [\]^_`
+ ('{' <= ch && ch <= '~')) // {|}~
+ {
+ *key = ch;
+ return TRUE;
+ }
+ }
+
+ LLString::toUpper(instring);
+ KEY res = get_if_there(sNamesToKeys, instring, (KEY)0);
+ if (res != 0)
+ {
+ *key = res;
+ return TRUE;
+ }
+ llwarns << "keyFromString failed: " << str << llendl;
+ return FALSE;
+}
+
+
+// static
+LLString LLKeyboard::stringFromKey(KEY key)
+{
+ LLString res = get_if_there(sKeysToNames, key, LLString::null);
+ if (res.empty())
+ {
+ char buffer[2];
+ buffer[0] = key;
+ buffer[1] = '\0';
+ res = LLString(buffer);
+ }
+ return res;
+}
+
+
+
+//static
+BOOL LLKeyboard::maskFromString(const LLString& str, MASK *mask)
+{
+ LLString instring(str);
+ if (instring == "NONE")
+ {
+ *mask = MASK_NONE;
+ return TRUE;
+ }
+ else if (instring == "SHIFT")
+ {
+ *mask = MASK_SHIFT;
+ return TRUE;
+ }
+ else if (instring == "CTL")
+ {
+ *mask = MASK_CONTROL;
+ return TRUE;
+ }
+ else if (instring == "ALT")
+ {
+ *mask = MASK_ALT;
+ return TRUE;
+ }
+ else if (instring == "CTL_SHIFT")
+ {
+ *mask = MASK_CONTROL | MASK_SHIFT;
+ return TRUE;
+ }
+ else if (instring == "ALT_SHIFT")
+ {
+ *mask = MASK_ALT | MASK_SHIFT;
+ return TRUE;
+ }
+ else if (instring == "CTL_ALT")
+ {
+ *mask = MASK_CONTROL | MASK_ALT;
+ return TRUE;
+ }
+ else if (instring == "CTL_ALT_SHIFT")
+ {
+ *mask = MASK_CONTROL | MASK_ALT | MASK_SHIFT;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h
new file mode 100644
index 0000000000..0c47d117d0
--- /dev/null
+++ b/indra/llwindow/llkeyboard.h
@@ -0,0 +1,124 @@
+/**
+ * @file llkeyboard.h
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYBOARD_H
+#define LL_LLKEYBOARD_H
+
+#include <map>
+
+#include "string_table.h"
+#include "lltimer.h"
+#include "indra_constants.h"
+
+enum EKeystate
+{
+ KEYSTATE_DOWN,
+ KEYSTATE_LEVEL,
+ KEYSTATE_UP
+};
+
+typedef void (*LLKeyFunc)(EKeystate keystate);
+
+enum EKeyboardInsertMode
+{
+ LL_KIM_INSERT,
+ LL_KIM_OVERWRITE
+};
+
+class LLKeyBinding
+{
+public:
+ KEY mKey;
+ MASK mMask;
+// const char *mName; // unused
+ LLKeyFunc mFunction;
+};
+
+class LLWindowCallbacks;
+
+class LLKeyboard
+{
+public:
+ typedef enum e_numpad_distinct
+ {
+ ND_NEVER,
+ ND_NUMLOCK_OFF,
+ ND_NUMLOCK_ON
+ } ENumpadDistinct;
+
+public:
+ LLKeyboard();
+ virtual ~LLKeyboard();
+
+ void resetKeys();
+
+
+ F32 getCurKeyElapsedTime() { return getKeyElapsedTime( mCurScanKey ); }
+ F32 getCurKeyElapsedFrameCount() { return (F32)getKeyElapsedFrameCount( mCurScanKey ); }
+ BOOL getKeyDown(const KEY key) { return mKeyLevel[key]; }
+ BOOL getKeyRepeated(const KEY key) { return mKeyRepeated[key]; }
+
+ BOOL translateKey(const U16 os_key, KEY *translated_key);
+ U16 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
+
+
+ virtual BOOL handleKeyUp(const U16 key, MASK mask) = 0;
+ virtual BOOL handleKeyDown(const U16 key, MASK mask) = 0;
+
+ // Asynchronously poll the control, alt, and shift keys and set the
+ // appropriate internal key masks.
+ virtual void resetMaskKeys() = 0;
+ virtual void scanKeyboard() = 0; // scans keyboard, calls functions as necessary
+ // Mac must differentiate between Command = Control for keyboard events
+ // and Command != Control for mouse events.
+ virtual MASK currentMask(BOOL for_mouse_event) = 0;
+ virtual KEY currentKey() { return mCurTranslatedKey; }
+
+ EKeyboardInsertMode getInsertMode() { return mInsertMode; }
+ void toggleInsertMode();
+
+ static BOOL maskFromString(const LLString& str, MASK *mask); // False on failure
+ static BOOL keyFromString(const LLString& str, KEY *key); // False on failure
+ static LLString stringFromKey(KEY key);
+
+ e_numpad_distinct getNumpadDistinct() { return mNumpadDistinct; }
+ void setNumpadDistinct(e_numpad_distinct val) { mNumpadDistinct = val; }
+
+ void setCallbacks(LLWindowCallbacks *cbs) { mCallbacks = cbs; }
+protected:
+ F32 getKeyElapsedTime( KEY key ); // Returns time in seconds since key was pressed.
+ S32 getKeyElapsedFrameCount( KEY key ); // Returns time in frames since key was pressed.
+ void addKeyName(KEY key, const LLString& name);
+
+protected:
+ std::map<U16, KEY> mTranslateKeyMap; // Map of translations from OS keys to Linden KEYs
+ std::map<KEY, U16> mInvTranslateKeyMap; // Map of translations from Linden KEYs to OS keys
+ LLWindowCallbacks *mCallbacks;
+
+ LLTimer mKeyLevelTimer[KEY_COUNT]; // Time since level was set
+ S32 mKeyLevelFrameCount[KEY_COUNT]; // Frames since level was set
+ BOOL mKeyLevel[KEY_COUNT]; // Levels
+ BOOL mKeyRepeated[KEY_COUNT]; // Key was repeated
+ BOOL mKeyUp[KEY_COUNT]; // Up edge
+ BOOL mKeyDown[KEY_COUNT]; // Down edge
+ KEY mCurTranslatedKey;
+ KEY mCurScanKey; // Used during the scanKeyboard()
+
+ e_numpad_distinct mNumpadDistinct;
+
+ EKeyboardInsertMode mInsertMode;
+
+ static std::map<KEY,LLString> sKeysToNames;
+ static std::map<LLString,KEY> sNamesToKeys;
+};
+
+extern LLKeyboard *gKeyboard;
+
+#endif
diff --git a/indra/llwindow/llkeyboardmacosx.cpp b/indra/llwindow/llkeyboardmacosx.cpp
new file mode 100644
index 0000000000..8ce3b1fb2d
--- /dev/null
+++ b/indra/llwindow/llkeyboardmacosx.cpp
@@ -0,0 +1,309 @@
+/**
+ * @file llkeyboardmacosx.cpp
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_DARWIN
+
+#include "linden_common.h"
+#include "llkeyboardmacosx.h"
+#include "llwindow.h"
+
+#include <Carbon/Carbon.h>
+
+LLKeyboardMacOSX::LLKeyboardMacOSX()
+{
+ // Virtual keycode mapping table. Yes, this was as annoying to generate as it looks.
+ mTranslateKeyMap[0x00] = 'A';
+ mTranslateKeyMap[0x01] = 'S';
+ mTranslateKeyMap[0x02] = 'D';
+ mTranslateKeyMap[0x03] = 'F';
+ mTranslateKeyMap[0x04] = 'H';
+ mTranslateKeyMap[0x05] = 'G';
+ mTranslateKeyMap[0x06] = 'Z';
+ mTranslateKeyMap[0x07] = 'X';
+ mTranslateKeyMap[0x08] = 'C';
+ mTranslateKeyMap[0x09] = 'V';
+ mTranslateKeyMap[0x0b] = 'B';
+ mTranslateKeyMap[0x0c] = 'Q';
+ mTranslateKeyMap[0x0d] = 'W';
+ mTranslateKeyMap[0x0e] = 'E';
+ mTranslateKeyMap[0x0f] = 'R';
+ mTranslateKeyMap[0x10] = 'Y';
+ mTranslateKeyMap[0x11] = 'T';
+ mTranslateKeyMap[0x12] = '1';
+ mTranslateKeyMap[0x13] = '2';
+ mTranslateKeyMap[0x14] = '3';
+ mTranslateKeyMap[0x15] = '4';
+ mTranslateKeyMap[0x16] = '6';
+ mTranslateKeyMap[0x17] = '5';
+ mTranslateKeyMap[0x18] = '='; // KEY_EQUALS
+ mTranslateKeyMap[0x19] = '9';
+ mTranslateKeyMap[0x1a] = '7';
+ mTranslateKeyMap[0x1b] = '-'; // KEY_HYPHEN
+ mTranslateKeyMap[0x1c] = '8';
+ mTranslateKeyMap[0x1d] = '0';
+ mTranslateKeyMap[0x1e] = ']';
+ mTranslateKeyMap[0x1f] = 'O';
+ mTranslateKeyMap[0x20] = 'U';
+ mTranslateKeyMap[0x21] = '[';
+ mTranslateKeyMap[0x22] = 'I';
+ mTranslateKeyMap[0x23] = 'P';
+ mTranslateKeyMap[0x24] = KEY_RETURN,
+ mTranslateKeyMap[0x25] = 'L';
+ mTranslateKeyMap[0x26] = 'J';
+ mTranslateKeyMap[0x27] = '\'';
+ mTranslateKeyMap[0x28] = 'K';
+ mTranslateKeyMap[0x29] = ';';
+ mTranslateKeyMap[0x2a] = '\\';
+ mTranslateKeyMap[0x2b] = ',';
+ mTranslateKeyMap[0x2c] = '/';
+ mTranslateKeyMap[0x2d] = 'N';
+ mTranslateKeyMap[0x2e] = 'M';
+ mTranslateKeyMap[0x2f] = '.';
+ mTranslateKeyMap[0x30] = KEY_TAB;
+ mTranslateKeyMap[0x31] = ' '; // space!
+ mTranslateKeyMap[0x32] = '`';
+ mTranslateKeyMap[0x33] = KEY_BACKSPACE;
+ mTranslateKeyMap[0x35] = KEY_ESCAPE;
+ //mTranslateKeyMap[0x37] = 0; // Command key. (not used yet)
+ mTranslateKeyMap[0x38] = KEY_SHIFT;
+ mTranslateKeyMap[0x39] = KEY_CAPSLOCK;
+ mTranslateKeyMap[0x3a] = KEY_ALT;
+ mTranslateKeyMap[0x3b] = KEY_CONTROL;
+ mTranslateKeyMap[0x41] = '.'; // keypad
+ mTranslateKeyMap[0x43] = '*'; // keypad
+ mTranslateKeyMap[0x45] = '+'; // keypad
+ mTranslateKeyMap[0x4b] = '/'; // keypad
+ mTranslateKeyMap[0x4c] = KEY_RETURN; // keypad enter
+ mTranslateKeyMap[0x4e] = '-'; // keypad
+ mTranslateKeyMap[0x51] = '='; // keypad
+ mTranslateKeyMap[0x52] = '0'; // keypad
+ mTranslateKeyMap[0x53] = '1'; // keypad
+ mTranslateKeyMap[0x54] = '2'; // keypad
+ mTranslateKeyMap[0x55] = '3'; // keypad
+ mTranslateKeyMap[0x56] = '4'; // keypad
+ mTranslateKeyMap[0x57] = '5'; // keypad
+ mTranslateKeyMap[0x58] = '6'; // keypad
+ mTranslateKeyMap[0x59] = '7'; // keypad
+ mTranslateKeyMap[0x5b] = '8'; // keypad
+ mTranslateKeyMap[0x5c] = '9'; // keypad
+ mTranslateKeyMap[0x60] = KEY_F5;
+ mTranslateKeyMap[0x61] = KEY_F6;
+ mTranslateKeyMap[0x62] = KEY_F7;
+ mTranslateKeyMap[0x63] = KEY_F3;
+ mTranslateKeyMap[0x64] = KEY_F8;
+ mTranslateKeyMap[0x65] = KEY_F9;
+ mTranslateKeyMap[0x67] = KEY_F11;
+ mTranslateKeyMap[0x6d] = KEY_F10;
+ mTranslateKeyMap[0x6f] = KEY_F12;
+ mTranslateKeyMap[0x72] = KEY_INSERT;
+ mTranslateKeyMap[0x73] = KEY_HOME;
+ mTranslateKeyMap[0x74] = KEY_PAGE_UP;
+ mTranslateKeyMap[0x75] = KEY_DELETE;
+ mTranslateKeyMap[0x76] = KEY_F4;
+ mTranslateKeyMap[0x77] = KEY_END;
+ mTranslateKeyMap[0x78] = KEY_F2;
+ mTranslateKeyMap[0x79] = KEY_PAGE_DOWN;
+ mTranslateKeyMap[0x7a] = KEY_F1;
+ mTranslateKeyMap[0x7b] = KEY_LEFT;
+ mTranslateKeyMap[0x7c] = KEY_RIGHT;
+ mTranslateKeyMap[0x7d] = KEY_DOWN;
+ mTranslateKeyMap[0x7e] = KEY_UP;
+
+ // Build inverse map
+ std::map<U16, KEY>::iterator iter;
+ for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++)
+ {
+ mInvTranslateKeyMap[iter->second] = iter->first;
+ }
+
+ // build numpad maps
+ mTranslateNumpadMap[0x52] = KEY_PAD_INS; // keypad 0
+ mTranslateNumpadMap[0x53] = KEY_PAD_END; // keypad 1
+ mTranslateNumpadMap[0x54] = KEY_PAD_DOWN; // keypad 2
+ mTranslateNumpadMap[0x55] = KEY_PAD_PGDN; // keypad 3
+ mTranslateNumpadMap[0x56] = KEY_PAD_LEFT; // keypad 4
+ mTranslateNumpadMap[0x57] = KEY_PAD_CENTER; // keypad 5
+ mTranslateNumpadMap[0x58] = KEY_PAD_RIGHT; // keypad 6
+ mTranslateNumpadMap[0x59] = KEY_PAD_HOME; // keypad 7
+ mTranslateNumpadMap[0x5b] = KEY_PAD_UP; // keypad 8
+ mTranslateNumpadMap[0x5c] = KEY_PAD_PGUP; // keypad 9
+ mTranslateNumpadMap[0x41] = KEY_PAD_DEL; // keypad .
+ mTranslateNumpadMap[0x4c] = KEY_PAD_RETURN; // keypad enter
+
+ // Build inverse numpad map
+ for (iter = mTranslateNumpadMap.begin(); iter != mTranslateNumpadMap.end(); iter++)
+ {
+ mInvTranslateNumpadMap[iter->second] = iter->first;
+ }
+}
+
+void LLKeyboardMacOSX::resetMaskKeys()
+{
+ U32 mask = GetCurrentEventKeyModifiers();
+
+ // 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.
+ // Is this the way it's supposed to work?
+
+ if(mask & shiftKey)
+ {
+ mKeyLevel[KEY_SHIFT] = TRUE;
+ }
+
+ if(mask & (controlKey))
+ {
+ mKeyLevel[KEY_CONTROL] = TRUE;
+ }
+
+ if(mask & optionKey)
+ {
+ mKeyLevel[KEY_ALT] = TRUE;
+ }
+}
+
+/*
+static BOOL translateKeyMac(const U16 key, const U32 mask, KEY &outKey, U32 &outMask)
+{
+ // Translate the virtual keycode into the keycodes the keyboard system expects.
+ U16 virtualKey = (mask >> 24) & 0x0000007F;
+ outKey = macKeyTransArray[virtualKey];
+
+
+ return(outKey != 0);
+}
+*/
+
+MASK LLKeyboardMacOSX::updateModifiers(const U32 mask)
+{
+ // translate the mask
+ MASK out_mask = 0;
+
+ if(mask & shiftKey)
+ {
+ out_mask |= MASK_SHIFT;
+ }
+
+ if(mask & (controlKey | cmdKey))
+ {
+ out_mask |= MASK_CONTROL;
+ }
+
+ if(mask & optionKey)
+ {
+ out_mask |= MASK_ALT;
+ }
+
+ return out_mask;
+}
+
+BOOL LLKeyboardMacOSX::handleKeyDown(const U16 key, const U32 mask)
+{
+ KEY translated_key = 0;
+ U32 translated_mask = 0;
+ BOOL handled = FALSE;
+
+ translated_mask = updateModifiers(mask);
+
+ if(translateNumpadKey(key, &translated_key))
+ {
+ handled = handleTranslatedKeyDown(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+
+BOOL LLKeyboardMacOSX::handleKeyUp(const U16 key, const U32 mask)
+{
+ KEY translated_key = 0;
+ U32 translated_mask = 0;
+ BOOL handled = FALSE;
+
+ translated_mask = updateModifiers(mask);
+
+ if(translateNumpadKey(key, &translated_key))
+ {
+ handled = handleTranslatedKeyUp(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+MASK LLKeyboardMacOSX::currentMask(BOOL for_mouse_event)
+{
+ MASK result = MASK_NONE;
+ U32 mask = GetCurrentEventKeyModifiers();
+
+ if (mask & shiftKey) result |= MASK_SHIFT;
+ if (mask & controlKey) result |= MASK_CONTROL;
+ if (mask & optionKey) result |= MASK_ALT;
+
+ // For keyboard events, consider Command equivalent to Control
+ if (!for_mouse_event)
+ {
+ if (mask & cmdKey) result |= MASK_CONTROL;
+ }
+
+ return result;
+}
+
+void LLKeyboardMacOSX::scanKeyboard()
+{
+ S32 key;
+ for (key = 0; key < KEY_COUNT; key++)
+ {
+ // Generate callback if any event has occurred on this key this frame.
+ // Can't just test mKeyLevel, because this could be a slow frame and
+ // key might have gone down then up. JC
+ if (mKeyLevel[key] || mKeyDown[key] || mKeyUp[key])
+ {
+ mCurScanKey = key;
+ mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
+ }
+ }
+
+ // Reset edges for next frame
+ for (key = 0; key < KEY_COUNT; key++)
+ {
+ mKeyUp[key] = FALSE;
+ mKeyDown[key] = FALSE;
+ if (mKeyLevel[key])
+ {
+ mKeyLevelFrameCount[key]++;
+ }
+ }
+}
+
+BOOL LLKeyboardMacOSX::translateNumpadKey( const U16 os_key, KEY *translated_key )
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<U16, KEY>::iterator iter= mTranslateNumpadMap.find(os_key);
+ if(iter != mTranslateNumpadMap.end())
+ {
+ *translated_key = iter->second;
+ return TRUE;
+ }
+ }
+ return translateKey(os_key, translated_key);
+}
+
+U16 LLKeyboardMacOSX::inverseTranslateNumpadKey(const KEY translated_key)
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<KEY, U16>::iterator iter= mInvTranslateNumpadMap.find(translated_key);
+ if(iter != mInvTranslateNumpadMap.end())
+ {
+ return iter->second;
+ }
+ }
+ return inverseTranslateKey(translated_key);
+}
+
+#endif // LL_DARWIN
diff --git a/indra/llwindow/llkeyboardmacosx.h b/indra/llwindow/llkeyboardmacosx.h
new file mode 100644
index 0000000000..d9b7de4049
--- /dev/null
+++ b/indra/llwindow/llkeyboardmacosx.h
@@ -0,0 +1,36 @@
+/**
+ * @file llkeyboardmacosx.h
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYBOARDMACOSX_H
+#define LL_LLKEYBOARDMACOSX_H
+
+#include "llkeyboard.h"
+
+class LLKeyboardMacOSX : public LLKeyboard
+{
+public:
+ LLKeyboardMacOSX();
+ /*virtual*/ ~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();
+
+protected:
+ MASK updateModifiers(const U32 mask);
+ void setModifierKeyLevel( KEY key, BOOL new_state );
+ BOOL translateNumpadKey( const U16 os_key, KEY *translated_key );
+ U16 inverseTranslateNumpadKey(const KEY translated_key);
+private:
+ std::map<U16, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys
+ std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above
+};
+
+#endif
diff --git a/indra/llwindow/llkeyboardsdl.cpp b/indra/llwindow/llkeyboardsdl.cpp
new file mode 100644
index 0000000000..436a31b5cd
--- /dev/null
+++ b/indra/llwindow/llkeyboardsdl.cpp
@@ -0,0 +1,324 @@
+/**
+ * @file llkeyboardsdl.cpp
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_SDL
+
+#include "linden_common.h"
+#include "llkeyboardsdl.h"
+#include "llwindow.h"
+#include "SDL/SDL.h"
+
+LLKeyboardSDL::LLKeyboardSDL()
+{
+ // Set up key mapping for SDL - eventually can read this from a file?
+ // Anything not in the key map gets dropped
+ // Add default A-Z
+
+ // Virtual key mappings from SDL_keysym.h ...
+
+ // SDL maps the letter keys to the ASCII you'd expect, but it's lowercase...
+ U16 cur_char;
+ for (cur_char = 'A'; cur_char <= 'Z'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = cur_char;
+ }
+ for (cur_char = 'a'; cur_char <= 'z'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = (cur_char - 'a') + 'A';
+ }
+
+ for (cur_char = '0'; cur_char <= '9'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = cur_char;
+ }
+
+ // These ones are translated manually upon keydown/keyup because
+ // SDL doesn't handle their numlock transition.
+ //mTranslateKeyMap[SDLK_KP4] = KEY_PAD_LEFT;
+ //mTranslateKeyMap[SDLK_KP6] = KEY_PAD_RIGHT;
+ //mTranslateKeyMap[SDLK_KP8] = KEY_PAD_UP;
+ //mTranslateKeyMap[SDLK_KP2] = KEY_PAD_DOWN;
+ //mTranslateKeyMap[SDLK_KP_PERIOD] = KEY_DELETE;
+ //mTranslateKeyMap[SDLK_KP7] = KEY_HOME;
+ //mTranslateKeyMap[SDLK_KP1] = KEY_END;
+ //mTranslateKeyMap[SDLK_KP9] = KEY_PAGE_UP;
+ //mTranslateKeyMap[SDLK_KP3] = KEY_PAGE_DOWN;
+ //mTranslateKeyMap[SDLK_KP0] = KEY_INSERT;
+
+ mTranslateKeyMap[SDLK_SPACE] = ' ';
+ mTranslateKeyMap[SDLK_RETURN] = KEY_RETURN;
+ mTranslateKeyMap[SDLK_LEFT] = KEY_LEFT;
+ mTranslateKeyMap[SDLK_RIGHT] = KEY_RIGHT;
+ mTranslateKeyMap[SDLK_UP] = KEY_UP;
+ mTranslateKeyMap[SDLK_DOWN] = KEY_DOWN;
+ mTranslateKeyMap[SDLK_ESCAPE] = KEY_ESCAPE;
+ mTranslateKeyMap[SDLK_KP_ENTER] = KEY_RETURN;
+ mTranslateKeyMap[SDLK_ESCAPE] = KEY_ESCAPE;
+ mTranslateKeyMap[SDLK_BACKSPACE] = KEY_BACKSPACE;
+ mTranslateKeyMap[SDLK_DELETE] = KEY_DELETE;
+ mTranslateKeyMap[SDLK_LSHIFT] = KEY_SHIFT;
+ mTranslateKeyMap[SDLK_RSHIFT] = KEY_SHIFT;
+ mTranslateKeyMap[SDLK_LCTRL] = KEY_CONTROL;
+ mTranslateKeyMap[SDLK_RCTRL] = KEY_CONTROL;
+ mTranslateKeyMap[SDLK_LALT] = KEY_ALT;
+ mTranslateKeyMap[SDLK_RALT] = KEY_ALT;
+ mTranslateKeyMap[SDLK_HOME] = KEY_HOME;
+ mTranslateKeyMap[SDLK_END] = KEY_END;
+ mTranslateKeyMap[SDLK_PAGEUP] = KEY_PAGE_UP;
+ mTranslateKeyMap[SDLK_PAGEDOWN] = KEY_PAGE_DOWN;
+ mTranslateKeyMap[SDLK_MINUS] = KEY_HYPHEN;
+ mTranslateKeyMap[SDLK_EQUALS] = KEY_EQUALS;
+ mTranslateKeyMap[SDLK_KP_EQUALS] = KEY_EQUALS;
+ mTranslateKeyMap[SDLK_INSERT] = KEY_INSERT;
+ mTranslateKeyMap[SDLK_CAPSLOCK] = KEY_CAPSLOCK;
+ mTranslateKeyMap[SDLK_TAB] = KEY_TAB;
+ mTranslateKeyMap[SDLK_KP_PLUS] = KEY_ADD;
+ mTranslateKeyMap[SDLK_KP_MINUS] = KEY_SUBTRACT;
+ mTranslateKeyMap[SDLK_KP_MULTIPLY] = KEY_MULTIPLY;
+ mTranslateKeyMap[SDLK_KP_DIVIDE] = KEY_DIVIDE;
+ mTranslateKeyMap[SDLK_F1] = KEY_F1;
+ mTranslateKeyMap[SDLK_F2] = KEY_F2;
+ mTranslateKeyMap[SDLK_F3] = KEY_F3;
+ mTranslateKeyMap[SDLK_F4] = KEY_F4;
+ mTranslateKeyMap[SDLK_F5] = KEY_F5;
+ mTranslateKeyMap[SDLK_F6] = KEY_F6;
+ mTranslateKeyMap[SDLK_F7] = KEY_F7;
+ mTranslateKeyMap[SDLK_F8] = KEY_F8;
+ mTranslateKeyMap[SDLK_F9] = KEY_F9;
+ mTranslateKeyMap[SDLK_F10] = KEY_F10;
+ mTranslateKeyMap[SDLK_F11] = KEY_F11;
+ mTranslateKeyMap[SDLK_F12] = KEY_F12;
+ mTranslateKeyMap[SDLK_PLUS] = '=';
+ mTranslateKeyMap[SDLK_COMMA] = ',';
+ mTranslateKeyMap[SDLK_MINUS] = '-';
+ mTranslateKeyMap[SDLK_PERIOD] = '.';
+ mTranslateKeyMap[SDLK_BACKQUOTE] = '`';
+ mTranslateKeyMap[SDLK_SLASH] = '/';
+ mTranslateKeyMap[SDLK_SEMICOLON] = ';';
+ mTranslateKeyMap[SDLK_LEFTBRACKET] = '[';
+ mTranslateKeyMap[SDLK_BACKSLASH] = '\\';
+ mTranslateKeyMap[SDLK_RIGHTBRACKET] = ']';
+ mTranslateKeyMap[SDLK_QUOTE] = '\'';
+
+ // Build inverse map
+ std::map<U16, KEY>::iterator iter;
+ for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++)
+ {
+ mInvTranslateKeyMap[iter->second] = iter->first;
+ }
+
+ // numpad map
+ mTranslateNumpadMap[SDLK_KP0] = KEY_PAD_INS;
+ mTranslateNumpadMap[SDLK_KP1] = KEY_PAD_END;
+ mTranslateNumpadMap[SDLK_KP2] = KEY_PAD_DOWN;
+ mTranslateNumpadMap[SDLK_KP3] = KEY_PAD_PGDN;
+ mTranslateNumpadMap[SDLK_KP4] = KEY_PAD_LEFT;
+ mTranslateNumpadMap[SDLK_KP5] = KEY_PAD_CENTER;
+ mTranslateNumpadMap[SDLK_KP6] = KEY_PAD_RIGHT;
+ mTranslateNumpadMap[SDLK_KP7] = KEY_PAD_HOME;
+ mTranslateNumpadMap[SDLK_KP8] = KEY_PAD_UP;
+ mTranslateNumpadMap[SDLK_KP9] = KEY_PAD_PGUP;
+ mTranslateNumpadMap[SDLK_KP_PERIOD] = KEY_PAD_DEL;
+
+ // build inverse numpad map
+ for (iter = mTranslateNumpadMap.begin();
+ iter != mTranslateNumpadMap.end();
+ iter++)
+ {
+ mInvTranslateNumpadMap[iter->second] = iter->first;
+ }
+}
+
+void LLKeyboardSDL::resetMaskKeys()
+{
+ SDLMod mask = SDL_GetModState();
+
+ // 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.
+ // Is this the way it's supposed to work?
+
+ if(mask & KMOD_SHIFT)
+ {
+ mKeyLevel[KEY_SHIFT] = TRUE;
+ }
+
+ if(mask & KMOD_CTRL)
+ {
+ mKeyLevel[KEY_CONTROL] = TRUE;
+ }
+
+ if(mask & KMOD_ALT)
+ {
+ mKeyLevel[KEY_ALT] = TRUE;
+ }
+}
+
+
+MASK LLKeyboardSDL::updateModifiers(const U32 mask)
+{
+ // translate the mask
+ MASK out_mask = MASK_NONE;
+
+ if(mask & KMOD_SHIFT)
+ {
+ out_mask |= MASK_SHIFT;
+ }
+
+ if(mask & KMOD_CTRL)
+ {
+ out_mask |= MASK_CONTROL;
+ }
+
+ if(mask & KMOD_ALT)
+ {
+ out_mask |= MASK_ALT;
+ }
+
+ return out_mask;
+}
+
+
+static U16 adjustNativekeyFromUnhandledMask(const U16 key, const U32 mask)
+{
+ // SDL doesn't automatically adjust the keysym according to
+ // whether NUMLOCK is engaged, so we massage the keysym manually.
+ U16 rtn = key;
+ if (!(mask & KMOD_NUM))
+ {
+ switch (key)
+ {
+ case SDLK_KP_PERIOD: rtn = SDLK_DELETE; break;
+ case SDLK_KP0: rtn = SDLK_INSERT; break;
+ case SDLK_KP1: rtn = SDLK_END; break;
+ case SDLK_KP2: rtn = SDLK_DOWN; break;
+ case SDLK_KP3: rtn = SDLK_PAGEDOWN; break;
+ case SDLK_KP4: rtn = SDLK_LEFT; break;
+ case SDLK_KP6: rtn = SDLK_RIGHT; break;
+ case SDLK_KP7: rtn = SDLK_HOME; break;
+ case SDLK_KP8: rtn = SDLK_UP; break;
+ case SDLK_KP9: rtn = SDLK_PAGEUP; break;
+ }
+ }
+ return rtn;
+}
+
+
+BOOL LLKeyboardSDL::handleKeyDown(const U16 key, const U32 mask)
+{
+ U16 adjusted_nativekey;
+ KEY translated_key = 0;
+ U32 translated_mask = MASK_NONE;
+ BOOL handled = FALSE;
+
+ adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask);
+
+ translated_mask = updateModifiers(mask);
+
+ if(translateNumpadKey(adjusted_nativekey, &translated_key))
+ {
+ handled = handleTranslatedKeyDown(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+
+BOOL LLKeyboardSDL::handleKeyUp(const U16 key, const U32 mask)
+{
+ U16 adjusted_nativekey;
+ KEY translated_key = 0;
+ U32 translated_mask = MASK_NONE;
+ BOOL handled = FALSE;
+
+ adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask);
+
+ translated_mask = updateModifiers(mask);
+
+ if(translateNumpadKey(adjusted_nativekey, &translated_key))
+ {
+ handled = handleTranslatedKeyUp(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+MASK LLKeyboardSDL::currentMask(BOOL for_mouse_event)
+{
+ MASK result = MASK_NONE;
+ SDLMod mask = SDL_GetModState();
+
+ if (mask & KMOD_SHIFT) result |= MASK_SHIFT;
+ if (mask & KMOD_CTRL) result |= MASK_CONTROL;
+ if (mask & KMOD_ALT) result |= MASK_ALT;
+
+ // For keyboard events, consider Meta keys equivalent to Control
+ if (!for_mouse_event)
+ {
+ if (mask & KMOD_META) result |= MASK_CONTROL;
+ }
+
+ return result;
+}
+
+void LLKeyboardSDL::scanKeyboard()
+{
+ for (S32 key = 0; key < KEY_COUNT; key++)
+ {
+ // Generate callback if any event has occurred on this key this frame.
+ // Can't just test mKeyLevel, because this could be a slow frame and
+ // key might have gone down then up. JC
+ if (mKeyLevel[key] || mKeyDown[key] || mKeyUp[key])
+ {
+ mCurScanKey = key;
+ mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
+ }
+ }
+
+ // Reset edges for next frame
+ for (S32 key = 0; key < KEY_COUNT; key++)
+ {
+ mKeyUp[key] = FALSE;
+ mKeyDown[key] = FALSE;
+ if (mKeyLevel[key])
+ {
+ mKeyLevelFrameCount[key]++;
+ }
+ }
+}
+
+
+BOOL LLKeyboardSDL::translateNumpadKey( const U16 os_key, KEY *translated_key)
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<U16, KEY>::iterator iter= mTranslateNumpadMap.find(os_key);
+ if(iter != mTranslateNumpadMap.end())
+ {
+ *translated_key = iter->second;
+ return TRUE;
+ }
+ }
+ BOOL success = translateKey(os_key, translated_key);
+ return success;
+}
+
+U16 LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key)
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<KEY, U16>::iterator iter= mInvTranslateNumpadMap.find(translated_key);
+ if(iter != mInvTranslateNumpadMap.end())
+ {
+ return iter->second;
+ }
+ }
+ return inverseTranslateKey(translated_key);
+}
+
+#endif
+
diff --git a/indra/llwindow/llkeyboardsdl.h b/indra/llwindow/llkeyboardsdl.h
new file mode 100644
index 0000000000..83701d37bf
--- /dev/null
+++ b/indra/llwindow/llkeyboardsdl.h
@@ -0,0 +1,37 @@
+/**
+ * @file llkeyboardsdl.h
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYBOARDSDL_H
+#define LL_LLKEYBOARDSDL_H
+
+#include "llkeyboard.h"
+#include "SDL/SDL.h"
+
+class LLKeyboardSDL : public LLKeyboard
+{
+public:
+ LLKeyboardSDL();
+ /*virtual*/ ~LLKeyboardSDL() {};
+
+ /*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();
+
+protected:
+ MASK updateModifiers(const U32 mask);
+ void setModifierKeyLevel( KEY key, BOOL new_state );
+ BOOL translateNumpadKey( const U16 os_key, KEY *translated_key );
+ U16 inverseTranslateNumpadKey(const KEY translated_key);
+private:
+ std::map<U16, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys
+ std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above
+};
+
+#endif
diff --git a/indra/llwindow/llkeyboardwin32.cpp b/indra/llwindow/llkeyboardwin32.cpp
new file mode 100644
index 0000000000..37eb967e27
--- /dev/null
+++ b/indra/llwindow/llkeyboardwin32.cpp
@@ -0,0 +1,372 @@
+/**
+ * @file llkeyboardwin32.cpp
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_WINDOWS
+
+#include "linden_common.h"
+#include "llkeyboardwin32.h"
+#include "llwindow.h"
+
+LLKeyboardWin32::LLKeyboardWin32()
+{
+ // Set up key mapping for windows - eventually can read this from a file?
+ // Anything not in the key map gets dropped
+ // Add default A-Z
+
+ // Virtual key mappings from WinUser.h
+
+ KEY cur_char;
+ for (cur_char = 'A'; cur_char <= 'Z'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = (KEY)cur_char;
+ }
+
+ for (cur_char = '0'; cur_char <= '9'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = (KEY)cur_char;
+ }
+ // numpad number keys
+ for (cur_char = 0x60; cur_char <= 0x69; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = (KEY)('0' + (0x60 - cur_char));
+ }
+
+
+ mTranslateKeyMap[VK_SPACE] = ' ';
+ mTranslateKeyMap[VK_OEM_1] = ';';
+ // When the user hits, for example, Ctrl-= as a keyboard shortcut,
+ // Windows generates VK_OEM_PLUS. This is true on both QWERTY and DVORAK
+ // keyboards in the US. Numeric keypad '+' generates VK_ADD below.
+ // Thus we translate it as '='.
+ // Potential bug: This may not be true on international keyboards. JC
+ mTranslateKeyMap[VK_OEM_PLUS] = '=';
+ mTranslateKeyMap[VK_OEM_COMMA] = ',';
+ mTranslateKeyMap[VK_OEM_MINUS] = '-';
+ mTranslateKeyMap[VK_OEM_PERIOD] = '.';
+ mTranslateKeyMap[VK_OEM_2] = '/';
+ mTranslateKeyMap[VK_OEM_3] = '`';
+ mTranslateKeyMap[VK_OEM_4] = '[';
+ mTranslateKeyMap[VK_OEM_5] = '\\';
+ mTranslateKeyMap[VK_OEM_6] = ']';
+ mTranslateKeyMap[VK_OEM_7] = '\'';
+ mTranslateKeyMap[VK_ESCAPE] = KEY_ESCAPE;
+ mTranslateKeyMap[VK_RETURN] = KEY_RETURN;
+ mTranslateKeyMap[VK_LEFT] = KEY_LEFT;
+ mTranslateKeyMap[VK_RIGHT] = KEY_RIGHT;
+ mTranslateKeyMap[VK_UP] = KEY_UP;
+ mTranslateKeyMap[VK_DOWN] = KEY_DOWN;
+ mTranslateKeyMap[VK_BACK] = KEY_BACKSPACE;
+ mTranslateKeyMap[VK_INSERT] = KEY_INSERT;
+ mTranslateKeyMap[VK_DELETE] = KEY_DELETE;
+ mTranslateKeyMap[VK_SHIFT] = KEY_SHIFT;
+ mTranslateKeyMap[VK_CONTROL] = KEY_CONTROL;
+ mTranslateKeyMap[VK_MENU] = KEY_ALT;
+ mTranslateKeyMap[VK_CAPITAL] = KEY_CAPSLOCK;
+ mTranslateKeyMap[VK_HOME] = KEY_HOME;
+ mTranslateKeyMap[VK_END] = KEY_END;
+ mTranslateKeyMap[VK_PRIOR] = KEY_PAGE_UP;
+ mTranslateKeyMap[VK_NEXT] = KEY_PAGE_DOWN;
+ mTranslateKeyMap[VK_TAB] = KEY_TAB;
+ mTranslateKeyMap[VK_ADD] = KEY_ADD;
+ mTranslateKeyMap[VK_SUBTRACT] = KEY_SUBTRACT;
+ mTranslateKeyMap[VK_MULTIPLY] = KEY_MULTIPLY;
+ mTranslateKeyMap[VK_DIVIDE] = KEY_DIVIDE;
+ mTranslateKeyMap[VK_F1] = KEY_F1;
+ mTranslateKeyMap[VK_F2] = KEY_F2;
+ mTranslateKeyMap[VK_F3] = KEY_F3;
+ mTranslateKeyMap[VK_F4] = KEY_F4;
+ mTranslateKeyMap[VK_F5] = KEY_F5;
+ mTranslateKeyMap[VK_F6] = KEY_F6;
+ mTranslateKeyMap[VK_F7] = KEY_F7;
+ mTranslateKeyMap[VK_F8] = KEY_F8;
+ mTranslateKeyMap[VK_F9] = KEY_F9;
+ mTranslateKeyMap[VK_F10] = KEY_F10;
+ mTranslateKeyMap[VK_F11] = KEY_F11;
+ mTranslateKeyMap[VK_F12] = KEY_F12;
+ mTranslateKeyMap[VK_CLEAR] = KEY_PAD_CENTER;
+
+ // Build inverse map
+ std::map<U16, KEY>::iterator iter;
+ for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++)
+ {
+ mInvTranslateKeyMap[iter->second] = iter->first;
+ }
+
+ // numpad map
+ mTranslateNumpadMap[0x60] = KEY_PAD_INS; // keypad 0
+ mTranslateNumpadMap[0x61] = KEY_PAD_END; // keypad 1
+ mTranslateNumpadMap[0x62] = KEY_PAD_DOWN; // keypad 2
+ mTranslateNumpadMap[0x63] = KEY_PAD_PGDN; // keypad 3
+ mTranslateNumpadMap[0x64] = KEY_PAD_LEFT; // keypad 4
+ mTranslateNumpadMap[0x65] = KEY_PAD_CENTER; // keypad 5
+ mTranslateNumpadMap[0x66] = KEY_PAD_RIGHT; // keypad 6
+ mTranslateNumpadMap[0x67] = KEY_PAD_HOME; // keypad 7
+ mTranslateNumpadMap[0x68] = KEY_PAD_UP; // keypad 8
+ mTranslateNumpadMap[0x69] = KEY_PAD_PGUP; // keypad 9
+ mTranslateNumpadMap[0x6E] = KEY_PAD_DEL; // keypad .
+
+ for (iter = mTranslateNumpadMap.begin(); iter != mTranslateNumpadMap.end(); iter++)
+ {
+ mInvTranslateNumpadMap[iter->second] = iter->first;
+ }
+}
+
+// Asynchronously poll the control, alt and shift keys and set the
+// appropriate states.
+// Note: this does not generate edges.
+void LLKeyboardWin32::resetMaskKeys()
+{
+ // GetAsyncKeyState returns a short and uses the most significant
+ // bit to indicate that the key is down.
+ if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
+ {
+ mKeyLevel[KEY_SHIFT] = TRUE;
+ }
+
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ {
+ mKeyLevel[KEY_CONTROL] = TRUE;
+ }
+
+ if (GetAsyncKeyState(VK_MENU) & 0x8000)
+ {
+ mKeyLevel[KEY_ALT] = TRUE;
+ }
+}
+
+
+void LLKeyboardWin32::setModifierKeyLevel( KEY key, BOOL new_state )
+{
+ if( mKeyLevel[key] != new_state )
+ {
+ mKeyLevelFrameCount[key] = 0;
+
+ if( new_state )
+ {
+ mKeyLevelTimer[key].reset();
+ }
+ mKeyLevel[key] = new_state;
+ }
+}
+
+
+MASK LLKeyboardWin32::updateModifiers()
+{
+ // Scan the modifier keys as of the last Windows key message
+ // (keydown encoded in high order bit of short)
+ setModifierKeyLevel( KEY_SHIFT, GetKeyState(VK_SHIFT) & 0x8000 );
+ setModifierKeyLevel( KEY_CONTROL, GetKeyState(VK_CONTROL) & 0x8000 );
+ setModifierKeyLevel( KEY_ALT, GetKeyState(VK_MENU) & 0x8000 );
+ setModifierKeyLevel( KEY_CAPSLOCK, GetKeyState(VK_CAPITAL) & 0x0001); // Low order bit carries the toggle state.
+ // Get mask for keyboard events
+ MASK mask = currentMask(FALSE);
+ return mask;
+}
+
+
+// mask is ignored, except for extended flag -- we poll the modifier keys for the other flags
+BOOL LLKeyboardWin32::handleKeyDown(const U16 key, MASK mask)
+{
+ KEY translated_key;
+ U32 translated_mask;
+ BOOL handled = FALSE;
+
+ translated_mask = updateModifiers();
+
+ if (translateExtendedKey(key, mask, &translated_key))
+ {
+ handled = handleTranslatedKeyDown(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+
+// mask is ignored, except for extended flag -- we poll the modifier keys for the other flags
+BOOL LLKeyboardWin32::handleKeyUp(const U16 key, MASK mask)
+{
+ KEY translated_key;
+ U32 translated_mask;
+ BOOL handled = FALSE;
+
+ translated_mask = updateModifiers();
+
+ if (translateExtendedKey(key, mask, &translated_key))
+ {
+ handled = handleTranslatedKeyUp(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+
+MASK LLKeyboardWin32::currentMask(BOOL)
+{
+ MASK mask = MASK_NONE;
+
+ if (mKeyLevel[KEY_SHIFT]) mask |= MASK_SHIFT;
+ if (mKeyLevel[KEY_CONTROL]) mask |= MASK_CONTROL;
+ if (mKeyLevel[KEY_ALT]) mask |= MASK_ALT;
+
+ return mask;
+}
+
+
+void LLKeyboardWin32::scanKeyboard()
+{
+ S32 key;
+ for (key = 0; key < KEY_COUNT; key++)
+ {
+ // On Windows, verify key down state. JC
+ if (mKeyLevel[key])
+ {
+ // FIXME: I KNOW there must be a better way of interrogating the key state than this, using async
+ // key state can cause ALL kinds of bugs - Doug
+ if (key < KEY_BUTTON0)
+ {
+ // ...under windows make sure the key actually still is down.
+ // ...translate back to windows key
+ U16 virtual_key = inverseTranslateExtendedKey(key);
+ // keydown in highest bit
+ if (!(GetAsyncKeyState(virtual_key) & 0x8000))
+ {
+ //llinfos << "Key up event missed, resetting" << llendl;
+ mKeyLevel[key] = FALSE;
+ mKeyLevelFrameCount[key] = 0;
+ }
+ }
+ }
+
+ // Generate callback if any event has occurred on this key this frame.
+ // Can't just test mKeyLevel, because this could be a slow frame and
+ // key might have gone down then up. JC
+ if (mKeyLevel[key] || mKeyDown[key] || mKeyUp[key])
+ {
+ mCurScanKey = key;
+ mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
+ }
+ }
+
+ // Reset edges for next frame
+ for (key = 0; key < KEY_COUNT; key++)
+ {
+ mKeyUp[key] = FALSE;
+ mKeyDown[key] = FALSE;
+ if (mKeyLevel[key])
+ {
+ mKeyLevelFrameCount[key]++;
+ }
+ }
+}
+
+BOOL LLKeyboardWin32::translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key)
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<U16, KEY>::iterator iter = mTranslateNumpadMap.find(os_key);
+ if (iter != mTranslateNumpadMap.end())
+ {
+ *translated_key = iter->second;
+ return TRUE;
+ }
+ }
+
+ BOOL success = translateKey(os_key, translated_key);
+ if(mNumpadDistinct != ND_NEVER) {
+ if(!success) return success;
+ if(mask & MASK_EXTENDED)
+ {
+ // this is where we'd create new keycodes for extended keys
+ // the set of extended keys includes the 'normal' arrow keys and
+ // the pgup/dn/insert/home/end/delete cluster above the arrow keys
+ // see http://windowssdk.msdn.microsoft.com/en-us/library/ms646280.aspx
+
+ // only process the return key if numlock is off
+ if(((mNumpadDistinct == ND_NUMLOCK_OFF &&
+ !(GetKeyState(VK_NUMLOCK) & 1))
+ || mNumpadDistinct == ND_NUMLOCK_ON) &&
+ *translated_key == KEY_RETURN) {
+ *translated_key = KEY_PAD_RETURN;
+ }
+ }
+ else
+ {
+ // the non-extended keys, those are in the numpad
+ switch (*translated_key)
+ {
+ case KEY_LEFT:
+ *translated_key = KEY_PAD_LEFT; break;
+ case KEY_RIGHT:
+ *translated_key = KEY_PAD_RIGHT; break;
+ case KEY_UP:
+ *translated_key = KEY_PAD_UP; break;
+ case KEY_DOWN:
+ *translated_key = KEY_PAD_DOWN; break;
+ case KEY_HOME:
+ *translated_key = KEY_PAD_HOME; break;
+ case KEY_END:
+ *translated_key = KEY_PAD_END; break;
+ case KEY_PAGE_UP:
+ *translated_key = KEY_PAD_PGUP; break;
+ case KEY_PAGE_DOWN:
+ *translated_key = KEY_PAD_PGDN; break;
+ case KEY_INSERT:
+ *translated_key = KEY_PAD_INS; break;
+ case KEY_DELETE:
+ *translated_key = KEY_PAD_DEL; break;
+ }
+ }
+ }
+ return success;
+}
+
+U16 LLKeyboardWin32::inverseTranslateExtendedKey(const KEY translated_key)
+{
+ // if numlock is on, then we need to translate KEY_PAD_FOO to the corresponding number pad number
+ if((mNumpadDistinct == ND_NUMLOCK_ON) && (GetKeyState(VK_NUMLOCK) & 1))
+ {
+ std::map<KEY, U16>::iterator iter = mInvTranslateNumpadMap.find(translated_key);
+ if (iter != mInvTranslateNumpadMap.end())
+ {
+ return iter->second;
+ }
+ }
+
+ // if numlock is off or we're not converting numbers to arrows, we map our keypad arrows
+ // to regular arrows since Windows doesn't distinguish between them
+ KEY converted_key = translated_key;
+ switch (converted_key)
+ {
+ case KEY_PAD_LEFT:
+ converted_key = KEY_LEFT; break;
+ case KEY_PAD_RIGHT:
+ converted_key = KEY_RIGHT; break;
+ case KEY_PAD_UP:
+ converted_key = KEY_UP; break;
+ case KEY_PAD_DOWN:
+ converted_key = KEY_DOWN; break;
+ case KEY_PAD_HOME:
+ converted_key = KEY_HOME; break;
+ case KEY_PAD_END:
+ converted_key = KEY_END; break;
+ case KEY_PAD_PGUP:
+ converted_key = KEY_PAGE_UP; break;
+ case KEY_PAD_PGDN:
+ converted_key = KEY_PAGE_DOWN; break;
+ case KEY_PAD_INS:
+ converted_key = KEY_INSERT; break;
+ case KEY_PAD_DEL:
+ converted_key = KEY_DELETE; break;
+ case KEY_PAD_RETURN:
+ converted_key = KEY_RETURN; break;
+ }
+ // convert our virtual keys to OS keys
+ return inverseTranslateKey(converted_key);
+}
+
+#endif
diff --git a/indra/llwindow/llkeyboardwin32.h b/indra/llwindow/llkeyboardwin32.h
new file mode 100644
index 0000000000..e7eb4b9c1e
--- /dev/null
+++ b/indra/llwindow/llkeyboardwin32.h
@@ -0,0 +1,40 @@
+/**
+ * @file llkeyboardwin32.h
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYBOARDWIN32_H
+#define LL_LLKEYBOARDWIN32_H
+
+#include "llkeyboard.h"
+
+// this mask distinguishes extended keys, which include non-numpad arrow keys
+// (and, curiously, the num lock and numpad '/')
+const MASK MASK_EXTENDED = 0x0100;
+
+class LLKeyboardWin32 : public LLKeyboard
+{
+public:
+ LLKeyboardWin32();
+ /*virtual*/ ~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);
+
+protected:
+ MASK updateModifiers();
+ void setModifierKeyLevel( KEY key, BOOL new_state );
+private:
+ std::map<U16, KEY> mTranslateNumpadMap;
+ std::map<KEY, U16> mInvTranslateNumpadMap;
+};
+
+#endif
diff --git a/indra/llwindow/llmousehandler.h b/indra/llwindow/llmousehandler.h
new file mode 100644
index 0000000000..bd163535a0
--- /dev/null
+++ b/indra/llwindow/llmousehandler.h
@@ -0,0 +1,39 @@
+/**
+ * @file llmousehandler.h
+ * @brief LLMouseHandler class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MOUSEHANDLER_H
+#define LL_MOUSEHANDLER_H
+
+// Abstract interface.
+// Intended for use via multiple inheritance.
+// A class may have as many interfaces as it likes, but never needs to inherit one more than once.
+
+class LLMouseHandler
+{
+public:
+ LLMouseHandler() {}
+ virtual ~LLMouseHandler() {}
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) = 0;
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) = 0;
+ virtual const LLString& getName() const = 0;
+
+ // Hack to support LLFocusMgr
+ virtual BOOL isView() = 0;
+
+ virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const = 0;
+ virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const = 0;
+};
+
+#endif
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp
new file mode 100644
index 0000000000..435ac9d0cc
--- /dev/null
+++ b/indra/llwindow/llwindow.cpp
@@ -0,0 +1,399 @@
+/**
+ * @file llwindow.cpp
+ * @brief Basic graphical window class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llwindowheadless.h"
+
+#if LL_MESA_HEADLESS
+#include "llwindowmesaheadless.h"
+#elif LL_SDL
+#include "llwindowsdl.h"
+#elif LL_WINDOWS
+#include "llwindowwin32.h"
+#elif LL_DARWIN
+#include "llwindowmacosx.h"
+#elif LL_LINUX
+#include "llwindowlinux.h" // currently just a dummy wrapper
+#endif
+
+#include "llerror.h"
+#include "llkeyboard.h"
+
+//static instance for default callbacks
+LLWindowCallbacks LLWindow::sDefaultCallbacks;
+
+//
+// LLWindowCallbacks
+//
+
+LLSplashScreen *gSplashScreenp = NULL;
+BOOL gDebugClicks = FALSE;
+BOOL gDebugWindowProc = FALSE;
+
+const S32 gURLProtocolWhitelistCount = 3;
+const char* gURLProtocolWhitelist[] = { "file", "http", "https" };
+
+// CP: added a handler list - this is what's used to open the protocol and is based on registry entry
+// only meaningful difference currently is that file: protocols are opened using http:
+// since no protocol handler exists in registry for file:
+// Important - these lists should match - protocol to handler
+const char* gURLProtocolWhitelistHandler[] = { "http", "http", "https" };
+
+BOOL LLWindowCallbacks::handleTranslatedKeyDown(const KEY key, const MASK mask, BOOL repeated)
+{
+ return FALSE;
+}
+
+
+BOOL LLWindowCallbacks::handleTranslatedKeyUp(const KEY key, const MASK mask)
+{
+ return FALSE;
+}
+
+void LLWindowCallbacks::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
+{
+}
+
+BOOL LLWindowCallbacks::handleUnicodeChar(llwchar uni_char, MASK mask)
+{
+ return FALSE;
+}
+
+
+BOOL LLWindowCallbacks::handleMouseDown(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+BOOL LLWindowCallbacks::handleMouseUp(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+void LLWindowCallbacks::handleMouseLeave(LLWindow *window)
+{
+ return;
+}
+
+BOOL LLWindowCallbacks::handleCloseRequest(LLWindow *window)
+{
+ //allow the window to close
+ return TRUE;
+}
+
+void LLWindowCallbacks::handleQuit(LLWindow *window)
+{
+ if(LLWindowManager::destroyWindow(window) == FALSE)
+ {
+ llerrs << "LLWindowCallbacks::handleQuit() : Couldn't destroy window" << llendl;
+ }
+}
+
+BOOL LLWindowCallbacks::handleRightMouseDown(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+BOOL LLWindowCallbacks::handleRightMouseUp(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+BOOL LLWindowCallbacks::handleActivate(LLWindow *window, BOOL activated)
+{
+ return FALSE;
+}
+
+void LLWindowCallbacks::handleMouseMove(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+}
+
+void LLWindowCallbacks::handleScrollWheel(LLWindow *window, S32 clicks)
+{
+}
+
+void LLWindowCallbacks::handleResize(LLWindow *window, const S32 width, const S32 height)
+{
+}
+
+void LLWindowCallbacks::handleFocus(LLWindow *window)
+{
+}
+
+void LLWindowCallbacks::handleFocusLost(LLWindow *window)
+{
+}
+
+void LLWindowCallbacks::handleMenuSelect(LLWindow *window, const S32 menu_item)
+{
+}
+
+BOOL LLWindowCallbacks::handlePaint(LLWindow *window, const S32 x, const S32 y,
+ const S32 width, const S32 height)
+{
+ return FALSE;
+}
+
+BOOL LLWindowCallbacks::handleDoubleClick(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+void LLWindowCallbacks::handleWindowBlock(LLWindow *window)
+{
+}
+
+void LLWindowCallbacks::handleWindowUnblock(LLWindow *window)
+{
+}
+
+void LLWindowCallbacks::handleDataCopy(LLWindow *window, S32 data_type, void *data)
+{
+}
+
+
+S32 OSMessageBox(const char* text, const char* caption, U32 type)
+{
+ // Properly hide the splash screen when displaying the message box
+ BOOL was_visible = FALSE;
+ if (LLSplashScreen::isVisible())
+ {
+ was_visible = TRUE;
+ LLSplashScreen::hide();
+ }
+
+ S32 result = 0;
+#if LL_MESA_HEADLESS // !!! FIXME
+ llwarns << "OSMessageBox: " << text << llendl;
+ return OSBTN_OK;
+#elif LL_WINDOWS
+ result = OSMessageBoxWin32(text, caption, type);
+#elif LL_DARWIN
+ result = OSMessageBoxMacOSX(text, caption, type);
+#elif LL_SDL
+ result = OSMessageBoxSDL(text, caption, type);
+#else
+#error("OSMessageBox not implemented for this platform!")
+#endif
+
+ if (was_visible)
+ {
+ LLSplashScreen::show();
+ }
+
+ return result;
+}
+
+
+//
+// LLWindow
+//
+
+LLWindow::LLWindow(BOOL fullscreen, U32 flags)
+ : mCallbacks(&sDefaultCallbacks),
+ mPostQuit(TRUE),
+ mFullscreen(fullscreen),
+ mFullscreenWidth(0),
+ mFullscreenHeight(0),
+ mFullscreenBits(0),
+ mFullscreenRefresh(0),
+ mSupportedResolutions(NULL),
+ mNumSupportedResolutions(0),
+ mCurrentCursor(UI_CURSOR_ARROW),
+ mCursorHidden(FALSE),
+ mBusyCount(0),
+ mIsMouseClipping(FALSE),
+ mSwapMethod(SWAP_METHOD_UNDEFINED),
+ mHideCursorPermanent(FALSE),
+ mFlags(flags)
+{
+}
+
+// virtual
+void LLWindow::incBusyCount()
+{
+ ++mBusyCount;
+}
+
+// virtual
+void LLWindow::decBusyCount()
+{
+ if (mBusyCount > 0)
+ {
+ --mBusyCount;
+ }
+}
+
+void LLWindow::setCallbacks(LLWindowCallbacks *callbacks)
+{
+ mCallbacks = callbacks;
+ if (gKeyboard)
+ {
+ gKeyboard->setCallbacks(callbacks);
+ }
+}
+
+//
+// LLSplashScreen
+//
+
+// static
+bool LLSplashScreen::isVisible()
+{
+ return gSplashScreenp ? true: false;
+}
+
+// static
+LLSplashScreen *LLSplashScreen::create()
+{
+#if LL_MESA_HEADLESS || LL_SDL // !!! FIXME
+ return 0;
+#elif LL_WINDOWS
+ return new LLSplashScreenWin32;
+#elif LL_DARWIN
+ return new LLSplashScreenMacOSX;
+#else
+#error("LLSplashScreen not implemented on this platform!")
+#endif
+}
+
+
+//static
+void LLSplashScreen::show()
+{
+ if (!gSplashScreenp)
+ {
+#if LL_WINDOWS && !LL_MESA_HEADLESS
+ gSplashScreenp = new LLSplashScreenWin32;
+#elif LL_DARWIN
+ gSplashScreenp = new LLSplashScreenMacOSX;
+#endif
+ if (gSplashScreenp)
+ {
+ gSplashScreenp->showImpl();
+ }
+ }
+}
+
+//static
+void LLSplashScreen::update(const char* str)
+{
+ LLSplashScreen::show();
+ if (gSplashScreenp)
+ {
+ gSplashScreenp->updateImpl(str);
+ }
+}
+
+//static
+void LLSplashScreen::hide()
+{
+ if (gSplashScreenp)
+ {
+ gSplashScreenp->hideImpl();
+ }
+ delete gSplashScreenp;
+ gSplashScreenp = NULL;
+}
+
+//
+// LLWindowManager
+//
+
+LLLinkedList<LLWindow> LLWindowManager::sWindowList;
+
+LLWindow* LLWindowManager::createWindow(
+ char *title,
+ char *name,
+ LLCoordScreen upper_left,
+ LLCoordScreen size,
+ U32 flags,
+ BOOL fullscreen,
+ BOOL clearBg,
+ BOOL disable_vsync,
+ BOOL use_gl,
+ BOOL ignore_pixel_depth)
+{
+ return createWindow(
+ title, name, upper_left.mX, upper_left.mY, size.mX, size.mY, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+}
+
+LLWindow* LLWindowManager::createWindow(
+ char *title, char *name, S32 x, S32 y, S32 width, S32 height, U32 flags,
+ BOOL fullscreen,
+ BOOL clearBg,
+ BOOL disable_vsync,
+ BOOL use_gl,
+ BOOL ignore_pixel_depth)
+{
+ LLWindow* new_window;
+
+ if (use_gl)
+ {
+#if LL_MESA_HEADLESS
+ new_window = new LLWindowMesaHeadless(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#elif LL_SDL
+ new_window = new LLWindowSDL(
+ title, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#elif LL_WINDOWS
+ new_window = new LLWindowWin32(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#elif LL_DARWIN
+ new_window = new LLWindowMacOSX(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#elif LL_LINUX
+ new_window = new LLWindowLinux(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#endif
+ }
+ else
+ {
+ new_window = new LLWindowHeadless(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+ }
+
+ if (FALSE == new_window->isValid())
+ {
+ delete new_window;
+ llwarns << "LLWindowManager::create() : Error creating window." << llendl;
+ return NULL;
+ }
+ sWindowList.addDataAtEnd(new_window);
+ return new_window;
+}
+
+BOOL LLWindowManager::destroyWindow(LLWindow* window)
+{
+ if (!sWindowList.checkData(window))
+ {
+ llerrs << "LLWindowManager::destroyWindow() : Window pointer not valid, this window doesn't exist!"
+ << llendl;
+ return FALSE;
+ }
+
+ window->close();
+
+ sWindowList.removeData(window);
+
+ delete window;
+
+ return TRUE;
+}
+
+BOOL LLWindowManager::isWindowValid(LLWindow *window)
+{
+ return sWindowList.checkData(window);
+}
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
new file mode 100644
index 0000000000..703eee32d0
--- /dev/null
+++ b/indra/llwindow/llwindow.h
@@ -0,0 +1,328 @@
+/**
+ * @file llwindow.h
+ * @brief Basic graphical window class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOW_H
+#define LL_LLWINDOW_H
+
+#include <sys/stat.h>
+
+#include "llrect.h"
+#include "linked_lists.h"
+#include "llcoord.h"
+#include "llstring.h"
+
+
+enum ECursorType {
+ UI_CURSOR_ARROW,
+ UI_CURSOR_WAIT,
+ UI_CURSOR_HAND,
+ UI_CURSOR_IBEAM,
+ UI_CURSOR_CROSS,
+ UI_CURSOR_SIZENWSE,
+ UI_CURSOR_SIZENESW,
+ UI_CURSOR_SIZEWE,
+ UI_CURSOR_SIZENS,
+ UI_CURSOR_NO,
+ UI_CURSOR_WORKING,
+ UI_CURSOR_TOOLGRAB,
+ UI_CURSOR_TOOLLAND,
+ UI_CURSOR_TOOLFOCUS,
+ UI_CURSOR_TOOLCREATE,
+ UI_CURSOR_ARROWDRAG,
+ UI_CURSOR_ARROWCOPY, // drag with copy
+ UI_CURSOR_ARROWDRAGMULTI,
+ UI_CURSOR_ARROWCOPYMULTI, // drag with copy
+ UI_CURSOR_NOLOCKED,
+ UI_CURSOR_ARROWLOCKED,
+ UI_CURSOR_GRABLOCKED,
+ UI_CURSOR_TOOLTRANSLATE,
+ UI_CURSOR_TOOLROTATE,
+ UI_CURSOR_TOOLSCALE,
+ UI_CURSOR_TOOLCAMERA,
+ UI_CURSOR_TOOLPAN,
+ UI_CURSOR_TOOLZOOMIN,
+ UI_CURSOR_TOOLPICKOBJECT3,
+ UI_CURSOR_TOOLSIT,
+ UI_CURSOR_TOOLBUY,
+ UI_CURSOR_TOOLPAY,
+ UI_CURSOR_TOOLOPEN,
+ UI_CURSOR_PIPETTE,
+ UI_CURSOR_COUNT // Number of elements in this enum (NOT a cursor)
+};
+
+class LLSplashScreen;
+
+class LLWindow;
+
+class LLWindowCallbacks
+{
+public:
+ virtual ~LLWindowCallbacks() {}
+ virtual BOOL handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated);
+ virtual BOOL handleTranslatedKeyUp(KEY key, MASK mask);
+ virtual void handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level);
+ virtual BOOL handleUnicodeChar(llwchar uni_char, MASK mask);
+
+ virtual BOOL handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual BOOL handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual void handleMouseLeave(LLWindow *window);
+ // return TRUE to allow window to close, which will then cause handleQuit to be called
+ virtual BOOL handleCloseRequest(LLWindow *window);
+ // window is about to be destroyed, clean up your business
+ virtual void handleQuit(LLWindow *window);
+ virtual BOOL handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual BOOL handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual BOOL handleActivate(LLWindow *window, BOOL activated);
+ virtual void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual void handleScrollWheel(LLWindow *window, S32 clicks);
+ virtual void handleResize(LLWindow *window, S32 width, S32 height);
+ virtual void handleFocus(LLWindow *window);
+ virtual void handleFocusLost(LLWindow *window);
+ virtual void handleMenuSelect(LLWindow *window, S32 menu_item);
+ virtual BOOL handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height);
+ virtual BOOL handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask); // double-click of left mouse button
+ virtual void handleWindowBlock(LLWindow *window); // window is taking over CPU for a while
+ virtual void handleWindowUnblock(LLWindow *window); // window coming back after taking over CPU for a while
+ virtual void handleDataCopy(LLWindow *window, S32 data_type, void *data);
+};
+
+// Refer to llwindow_test in test/common/llwindow for usage example
+
+class LLWindow
+{
+public:
+ struct LLWindowResolution
+ {
+ S32 mWidth;
+ S32 mHeight;
+ };
+ enum ESwapMethod
+ {
+ SWAP_METHOD_UNDEFINED,
+ SWAP_METHOD_EXCHANGE,
+ SWAP_METHOD_COPY
+ };
+ enum EFlags
+ {
+ // currently unused
+ };
+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 maximize() = 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 setPosition(LLCoordScreen position) = 0;
+ virtual BOOL setSize(LLCoordScreen size) = 0;
+ virtual BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync) = 0;
+ virtual BOOL setCursorPosition(LLCoordWindow position) = 0;
+ virtual BOOL getCursorPosition(LLCoordWindow *position) = 0;
+ virtual void showCursor() = 0;
+ virtual void hideCursor() = 0;
+ virtual BOOL isCursorHidden() = 0;
+ virtual void showCursorFromMouseMove() = 0;
+ virtual void hideCursorUntilMouseMove() = 0;
+
+ // These two functions create a way to make a busy cursor instead
+ // of an arrow when someone's busy doing something. Draw an
+ // arrow/hour if busycount > 0.
+ virtual void incBusyCount();
+ virtual void decBusyCount();
+ virtual void resetBusyCount() { mBusyCount = 0; }
+ virtual S32 getBusyCount() const { return mBusyCount; }
+
+ // Sets cursor, may set to arrow+hourglass
+ virtual void setCursor(ECursorType cursor) = 0;
+ virtual ECursorType getCursor() const { return mCurrentCursor; }
+
+ virtual void captureMouse() = 0;
+ virtual void releaseMouse() = 0;
+ virtual void setMouseClipping( BOOL b ) = 0;
+ virtual BOOL isClipboardTextAvailable() = 0;
+ virtual BOOL pasteTextFromClipboard(LLWString &dst) = 0;
+ virtual BOOL copyTextToClipboard(const LLWString &src) = 0;
+ virtual void flashIcon(F32 seconds) = 0;
+ virtual F32 getGamma() = 0;
+ virtual BOOL setGamma(const F32 gamma) = 0; // Set the gamma
+ virtual BOOL restoreGamma() = 0; // Restore original gamma table (before updating gamma)
+ virtual ESwapMethod getSwapMethod() { return mSwapMethod; }
+ virtual void gatherInput() = 0;
+ virtual void delayInputProcessing() = 0;
+ virtual void swapBuffers() = 0;
+ virtual void bringToFront() = 0;
+ virtual void focusClient() { }; // this may not have meaning or be required on other platforms, therefore, it's not abstract
+
+ virtual S32 stat( const char* file_name, struct stat* stat_info ) = 0;
+ virtual BOOL sendEmail(const char* address,const char* subject,const char* body_text, const char* attachment=NULL, const char* attachment_displayed_name=NULL ) = 0;
+
+
+ // 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;
+
+ // query supported resolutions
+ virtual LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) = 0;
+ virtual F32 getNativeAspectRatio() = 0;
+ virtual F32 getPixelAspectRatio() = 0;
+ virtual void setNativeAspectRatio(F32 aspect) = 0;
+
+ void setCallbacks(LLWindowCallbacks *callbacks);
+
+ virtual void beforeDialog() {}; // prepare to put up an OS dialog (if special measures are required, such as in fullscreen mode)
+ virtual void afterDialog() {}; // undo whatever was done in beforeDialog()
+
+// opens system default color picker
+ virtual BOOL dialog_color_picker (F32 *r, F32 *g, F32 *b) { return FALSE; };
+
+// return a platform-specific window reference (HWND on Windows, WindowRef on the Mac)
+ virtual void *getPlatformWindow() = 0;
+
+protected:
+ LLWindow(BOOL fullscreen, U32 flags);
+ virtual ~LLWindow() {}
+ virtual BOOL isValid() {return TRUE;}
+ virtual BOOL canDelete() {return TRUE;}
+protected:
+ static LLWindowCallbacks sDefaultCallbacks;
+
+protected:
+ LLWindowCallbacks* mCallbacks;
+
+ BOOL mPostQuit; // should this window post a quit message when destroyed?
+ BOOL mFullscreen;
+ S32 mFullscreenWidth;
+ S32 mFullscreenHeight;
+ S32 mFullscreenBits;
+ S32 mFullscreenRefresh;
+ LLWindowResolution* mSupportedResolutions;
+ S32 mNumSupportedResolutions;
+ ECursorType mCurrentCursor;
+ BOOL mCursorHidden;
+ S32 mBusyCount; // how deep is the "cursor busy" stack?
+ BOOL mIsMouseClipping; // Is this window currently clipping the mouse
+ ESwapMethod mSwapMethod;
+ BOOL mHideCursorPermanent;
+ U32 mFlags;
+
+ friend class LLWindowManager;
+};
+
+
+// LLSplashScreen
+// A simple, OS-specific splash screen that we can display
+// while initializing the application and before creating a GL
+// window
+
+
+class LLSplashScreen
+{
+public:
+ LLSplashScreen() { };
+ virtual ~LLSplashScreen() { };
+
+
+ // Call to display the window.
+ static LLSplashScreen * create();
+ static void show();
+ static void hide();
+ static void update(const char* string);
+
+ static bool isVisible();
+protected:
+ // These are overridden by the platform implementation
+ virtual void showImpl() = 0;
+ virtual void updateImpl(const char* string) = 0;
+ virtual void hideImpl() = 0;
+
+ static BOOL sVisible;
+
+};
+
+// Platform-neutral for accessing the platform specific message box
+S32 OSMessageBox(const char* text, const char* caption, U32 type);
+const U32 OSMB_OK = 0;
+const U32 OSMB_OKCANCEL = 1;
+const U32 OSMB_YESNO = 2;
+
+const S32 OSBTN_YES = 0;
+const S32 OSBTN_NO = 1;
+const S32 OSBTN_OK = 2;
+const S32 OSBTN_CANCEL = 3;
+
+//
+// LLWindowManager
+// Manages window creation and error checking
+
+class LLWindowManager
+{
+private:
+ static LLLinkedList<LLWindow> sWindowList;
+
+public:
+ static LLWindow* createWindow(
+ char *title,
+ char *name,
+ LLCoordScreen upper_left = LLCoordScreen(10, 10),
+ LLCoordScreen size = LLCoordScreen(320, 240),
+ U32 flags = 0,
+ BOOL fullscreen = FALSE,
+ BOOL clearBg = FALSE,
+ BOOL disable_vsync = TRUE,
+ BOOL use_gl = TRUE,
+ BOOL ignore_pixel_depth = FALSE);
+ static LLWindow *createWindow(
+ char* title, char* name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags = 0,
+ BOOL fullscreen = FALSE,
+ BOOL clearBg = FALSE,
+ BOOL disable_vsync = TRUE,
+ BOOL use_gl = TRUE,
+ BOOL ignore_pixel_depth = FALSE);
+ static BOOL destroyWindow(LLWindow* window);
+ static BOOL isWindowValid(LLWindow *window);
+};
+
+//
+// helper funcs
+//
+
+// Protocols, like "http" and "https" we support in URLs
+extern const S32 gURLProtocolWhitelistCount;
+extern const char* gURLProtocolWhitelist[];
+extern const char* gURLProtocolWhitelistHandler[];
+
+// Loads a URL with the user's default browser
+void spawn_web_browser(const char* escaped_url);
+
+// Opens a file with ShellExecute. Security risk!
+void shell_open(const char* file_path);
+
+void simpleEscapeString ( std::string& stringIn );
+
+
+#if LL_WINDOWS
+ // return Win32 specific window handle
+ HWND llwindow_get_hwnd(LLWindow *window);
+
+ // backdoor for special case handling of Win32 messages
+ void llwindow_install_wndproc(LLWindow *window, WNDPROC wnd_proc);
+#endif
+
+#endif // _LL_window_h_
diff --git a/indra/llwindow/llwindowheadless.cpp b/indra/llwindow/llwindowheadless.cpp
new file mode 100644
index 0000000000..1251ed1bb7
--- /dev/null
+++ b/indra/llwindow/llwindowheadless.cpp
@@ -0,0 +1,32 @@
+/**
+ * @file llwindowheadless.cpp
+ * @brief Headless implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "indra_constants.h"
+
+#include "llwindowheadless.h"
+
+//
+// LLWindowHeadless
+//
+LLWindowHeadless::LLWindowHeadless(char *title, char *name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags, BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags)
+{
+}
+
+
+LLWindowHeadless::~LLWindowHeadless()
+{
+}
+
+void LLWindowHeadless::swapBuffers()
+{
+}
+
diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h
new file mode 100644
index 0000000000..7c9b2e2d77
--- /dev/null
+++ b/indra/llwindow/llwindowheadless.h
@@ -0,0 +1,98 @@
+/**
+ * @file llwindowheadless.h
+ * @brief Headless definition of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWHEADLESS_H
+#define LL_LLWINDOWHEADLESS_H
+
+#include "llwindow.h"
+
+class LLWindowHeadless : 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*/ 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 setSize(LLCoordScreen size) {return FALSE;};
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync) {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 setCursor(ECursorType cursor) {};
+ //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 ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput() {};
+ /*virtual*/ void delayInputProcessing() {};
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName() {return LLString(""); };
+ /*virtual*/ void deleteFile( const char* file_name ) {};
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info ) {return 0; };
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL) { return FALSE; };
+
+
+ // 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; };
+
+ /*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) {}
+
+ /*virtual*/ void *getPlatformWindow() { return 0; };
+ /*virtual*/ void bringToFront() {};
+
+ LLWindowHeadless(char *title, char *name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags, BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth);
+ virtual ~LLWindowHeadless();
+
+private:
+};
+
+class LLSplashScreenHeadless : public LLSplashScreen
+{
+public:
+ LLSplashScreenHeadless() {};
+ virtual ~LLSplashScreenHeadless() {};
+
+ /*virtual*/ void showImpl() {};
+ /*virtual*/ void updateImpl(const char* mesg) {};
+ /*virtual*/ void hideImpl() {};
+
+};
+
+#endif //LL_LLWINDOWHEADLESS_H
+
diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h
new file mode 100644
index 0000000000..86ac74ff1f
--- /dev/null
+++ b/indra/llwindow/llwindowmacosx-objc.h
@@ -0,0 +1,19 @@
+/**
+ * @file llwindowmacosx-objc.h
+ * @brief Prototypes for functions shared between llwindowmacosx.cpp
+ * and llwindowmacosx-objc.mm.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+// This will actually hold an NSCursor*, but that type is only available in objective C.
+typedef void *CursorRef;
+
+/* Defined in llwindowmacosx-objc.mm: */
+void setupCocoa();
+CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY);
+OSErr releaseImageCursor(CursorRef ref);
+OSErr setImageCursor(CursorRef ref);
+
diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm
new file mode 100644
index 0000000000..82d2babab5
--- /dev/null
+++ b/indra/llwindow/llwindowmacosx-objc.mm
@@ -0,0 +1,87 @@
+/**
+ * @file llwindowmacosx-objc.mm
+ * @brief Definition of functions shared between llwindowmacosx.cpp
+ * and llwindowmacosx-objc.mm.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <AppKit/AppKit.h>
+
+/*
+ * These functions are broken out into a separate file because the
+ * objective-C typedef for 'BOOL' conflicts with the one in
+ * llcommon/stdtypes.h. This makes it impossible to use the standard
+ * linden headers with any objective-C++ source.
+ */
+
+#include "llwindowmacosx-objc.h"
+
+void setupCocoa()
+{
+ static bool inited = false;
+
+ if(!inited)
+ {
+ // This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor":
+ // http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html
+
+ // Needed for Carbon based applications which call into Cocoa
+ NSApplicationLoad();
+
+ // Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image
+ [[[NSWindow alloc] init] release];
+ }
+}
+
+CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // extra retain on the NSCursor since we want it to live for the lifetime of the app.
+ NSCursor *cursor =
+ [[[NSCursor alloc]
+ initWithImage:
+ [[[NSImage alloc] initWithContentsOfFile:
+ [NSString stringWithFormat:@"%s", fullpath]
+ ]autorelease]
+ hotSpot:NSMakePoint(hotspotX, hotspotY)
+ ]retain];
+
+ [pool release];
+
+ return (CursorRef)cursor;
+}
+
+// This is currently unused, since we want all our cursors to persist for the life of the app, but I've included it for completeness.
+OSErr releaseImageCursor(CursorRef ref)
+{
+ if( ref != NULL )
+ {
+ NSCursor *cursor = (NSCursor*)ref;
+ [cursor release];
+ }
+ else
+ {
+ return paramErr;
+ }
+
+ return noErr;
+}
+
+OSErr setImageCursor(CursorRef ref)
+{
+ if( ref != NULL )
+ {
+ NSCursor *cursor = (NSCursor*)ref;
+ [cursor set];
+ }
+ else
+ {
+ return paramErr;
+ }
+
+ return noErr;
+}
+
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
new file mode 100644
index 0000000000..4d75a30a8e
--- /dev/null
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -0,0 +1,2904 @@
+/**
+ * @file llwindowmacosx.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_DARWIN
+
+#include "linden_common.h"
+
+#include <Carbon/Carbon.h>
+
+#include "llwindowmacosx.h"
+#include "llkeyboardmacosx.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llstring.h"
+#include "lldir.h"
+
+#include "llglheaders.h"
+
+#include "indra_constants.h"
+
+#include "llwindowmacosx-objc.h"
+
+extern BOOL gDebugWindowProc;
+
+// culled from winuser.h
+//const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
+// On the Mac, the scroll wheel reports a delta of 1 for each detent.
+// There's also acceleration for faster scrolling, based on a slider in the system preferences.
+const S32 WHEEL_DELTA = 1; /* Value for rolling one detent */
+const S32 BITS_PER_PIXEL = 32;
+const S32 MAX_NUM_RESOLUTIONS = 32;
+
+
+//
+// LLWindowMacOSX
+//
+
+// Cross-platform bits:
+
+void show_window_creation_error(const char* title)
+{
+ llwarns << title << llendl;
+ shell_open( "help/window_creation_error.html");
+ /*
+ OSMessageBox(
+ "Second Life is unable to run because it can't set up your display.\n"
+ "We need to be able to make a 32-bit color window at 1024x768, with\n"
+ "an 8 bit alpha channel.\n"
+ "\n"
+ "First, be sure your monitor is set to True Color (32-bit) in\n"
+ "Start -> Control Panels -> Display -> Settings.\n"
+ "\n"
+ "Otherwise, this may be due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "ATI drivers are available at http://www.ati.com/\n"
+ "nVidia drivers are available at http://www.nvidia.com/\n"
+ "\n"
+ "If you continue to receive this message, contact customer service.",
+ title,
+ OSMB_OK);
+ */
+}
+
+BOOL check_for_card(const char* RENDERER, const char* bad_card)
+{
+ if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
+ {
+ char buffer[1024];
+ sprintf(buffer,
+ "Your video card appears to be a %s, which Second Life does not support.\n"
+ "\n"
+ "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
+ "multitexture support. We explicitly support nVidia GeForce 2 or better, \n"
+ "and ATI Radeon 8500 or better.\n"
+ "\n"
+ "If you own a supported card and continue to receive this message, try \n"
+ "updating to the latest video card drivers. Otherwise look in the\n"
+ "secondlife.com support section or e-mail technical support\n"
+ "\n"
+ "You can try to run Second Life, but it will probably crash or run\n"
+ "very slowly. Try anyway?",
+ bad_card);
+ S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
+ if (OSBTN_YES == button)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+// Switch to determine whether we capture all displays, or just the main one.
+// We may want to base this on the setting of _DEBUG...
+
+#define CAPTURE_ALL_DISPLAYS 0
+static double getDictDouble (CFDictionaryRef refDict, CFStringRef key);
+static long getDictLong (CFDictionaryRef refDict, CFStringRef key);
+
+
+
+
+// CarbonEvents we're interested in.
+static EventTypeSpec WindowHandlerEventList[] =
+{
+ // Window-related events
+ // { kEventClassWindow, kEventWindowCollapsing },
+ // { kEventClassWindow, kEventWindowCollapsed },
+ // { kEventClassWindow, kEventWindowShown },
+ { kEventClassWindow, kEventWindowActivated },
+ { kEventClassWindow, kEventWindowDeactivated },
+ { kEventClassWindow, kEventWindowShown },
+ { kEventClassWindow, kEventWindowHidden },
+ { kEventClassWindow, kEventWindowCollapsed },
+ { kEventClassWindow, kEventWindowExpanded },
+ { kEventClassWindow, kEventWindowGetClickActivation },
+ { kEventClassWindow, kEventWindowClose },
+ { kEventClassWindow, kEventWindowBoundsChanging },
+ { kEventClassWindow, kEventWindowBoundsChanged },
+ // { kEventClassWindow, kEventWindowZoomed },
+ // { kEventClassWindow, kEventWindowDrawContent },
+
+ // Mouse events
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseDragged },
+ { kEventClassMouse, kEventMouseWheelMoved },
+ { kEventClassMouse, kEventMouseMoved },
+
+ // Keyboard events
+ // No longer handle raw key down events directly.
+ // When text input events come in, extract the raw key events from them and process at that point.
+ // This allows input methods to eat keystrokes the way they're supposed to.
+// { kEventClassKeyboard, kEventRawKeyDown },
+// { kEventClassKeyboard, kEventRawKeyRepeat },
+ { kEventClassKeyboard, kEventRawKeyUp },
+ { kEventClassKeyboard, kEventRawKeyModifiersChanged },
+
+ // Text input events
+ { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
+
+};
+
+static EventTypeSpec GlobalHandlerEventList[] =
+{
+ // Mouse events
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseDragged },
+ { kEventClassMouse, kEventMouseWheelMoved },
+ { kEventClassMouse, kEventMouseMoved },
+
+ // Keyboard events
+ // No longer handle raw key down events directly.
+ // When text input events come in, extract the raw key events from them and process at that point.
+ // This allows input methods to eat keystrokes the way they're supposed to.
+// { kEventClassKeyboard, kEventRawKeyDown },
+// { kEventClassKeyboard, kEventRawKeyRepeat },
+ { kEventClassKeyboard, kEventRawKeyUp },
+ { kEventClassKeyboard, kEventRawKeyModifiersChanged },
+
+ // Text input events
+ { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
+};
+
+static EventTypeSpec CommandHandlerEventList[] =
+{
+ { kEventClassCommand, kEventCommandProcess }
+};
+
+// MBW -- HACK ALERT
+// On the Mac, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
+// The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
+// require a pointer to the LLWindowMacOSX object. Stash it here and maintain in the constructor and destructor.
+// This assumes that there will be only one object of this class at any time. Hopefully this is true.
+static LLWindowMacOSX *gWindowImplementation = NULL;
+
+
+
+LLWindowMacOSX::LLWindowMacOSX(char *title, char *name, S32 x, S32 y, S32 width,
+ S32 height, U32 flags,
+ BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags)
+{
+ // Voodoo for calling cocoa from carbon (see llwindowmacosx-objc.mm).
+ setupCocoa();
+
+ // Initialize the keyboard
+ gKeyboard = new LLKeyboardMacOSX();
+
+ // Ignore use_gl for now, only used for drones on PC
+ mWindow = NULL;
+ mContext = NULL;
+ mPixelFormat = NULL;
+ mDisplay = CGMainDisplayID();
+ mOldDisplayMode = NULL;
+ mTimer = NULL;
+ mSimulatedRightClick = FALSE;
+ mLastModifiers = 0;
+ mHandsOffEvents = FALSE;
+ mCursorDecoupled = FALSE;
+ mCursorLastEventDeltaX = 0;
+ mCursorLastEventDeltaY = 0;
+ mCursorIgnoreNextDelta = FALSE;
+ mNeedsResize = FALSE;
+ mOverrideAspectRatio = 0.f;
+ mMinimized = FALSE;
+
+ // For reasons that aren't clear to me, LLTimers seem to be created in the "started" state.
+ // Since the started state of this one is used to track whether the NMRec has been installed, it wants to start out in the "stopped" state.
+ mBounceTimer.stop();
+
+ // Get the original aspect ratio of the main device.
+ mOriginalAspectRatio = (double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
+
+ // Stash the window title
+ strcpy((char*)mWindowTitle + 1, title);
+ mWindowTitle[0] = strlen(title);
+
+ mEventHandlerUPP = NewEventHandlerUPP(staticEventHandler);
+ mGlobalHandlerRef = NULL;
+ mWindowHandlerRef = NULL;
+
+ // We're not clipping yet
+ SetRect( &mOldMouseClip, 0, 0, 0, 0 );
+
+ // Set up global event handlers (the fullscreen case needs this)
+ InstallStandardEventHandler(GetApplicationEventTarget());
+
+ // Stash an object pointer for OSMessageBox()
+ gWindowImplementation = this;
+
+ // Create the GL context and set it up for windowed or fullscreen, as appropriate.
+ if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
+ {
+ if(mWindow != NULL)
+ {
+ // MBW -- XXX -- I think we can now do this here?
+ // Constrain the window to the screen it's mostly on, resizing if necessary.
+ ConstrainWindowToScreen(
+ mWindow,
+ kWindowStructureRgn,
+ kWindowConstrainMayResize |
+ // kWindowConstrainStandardOptions |
+ 0,
+ NULL,
+ NULL);
+
+ MacShowWindow(mWindow);
+ BringToFront(mWindow);
+ }
+
+ if (!gGLManager.initGL())
+ {
+ setupFailure(
+ "Second Life is unable to run because your video card drivers\n"
+ "are out of date or unsupported. Please make sure you have\n"
+ "the latest video card drivers installed.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+
+ stop_glerror();
+}
+
+BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
+{
+ OSStatus err;
+ BOOL glNeedsInit = FALSE;
+
+ if(mGlobalHandlerRef == NULL)
+ {
+ InstallApplicationEventHandler(mEventHandlerUPP, GetEventTypeCount (CommandHandlerEventList), CommandHandlerEventList, (void*)this, &mGlobalHandlerRef);
+ }
+
+ mFullscreen = fullscreen;
+
+ if (mFullscreen && (mOldDisplayMode == NULL))
+ {
+ llinfos << "createContext: setting up fullscreen " << width << "x" << height << llendl;
+
+ // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays. Plan accordingly.
+ double refresh = getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate);
+
+ // If the requested width or height is 0, find the best default for the monitor.
+ if((width == 0) || (height == 0))
+ {
+ // Scan through the list of modes, looking for one which has:
+ // height between 700 and 800
+ // aspect ratio closest to the user's original mode
+ S32 resolutionCount = 0;
+ LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
+
+ if(resolutionList != NULL)
+ {
+ F32 closestAspect = 0;
+ U32 closestHeight = 0;
+ U32 closestWidth = 0;
+ int i;
+
+ llinfos << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << llendl;
+
+ for(i=0; i < resolutionCount; i++)
+ {
+ F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
+
+ llinfos << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << llendl;
+
+ if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
+ (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
+ {
+ llinfos << " (new closest mode) " << llendl;
+
+ // This is the closest mode we've seen yet.
+ closestWidth = resolutionList[i].mWidth;
+ closestHeight = resolutionList[i].mHeight;
+ closestAspect = aspect;
+ }
+ }
+
+ width = closestWidth;
+ height = closestHeight;
+ }
+ }
+
+ if((width == 0) || (height == 0))
+ {
+ // Mode search failed for some reason. Use the old-school default.
+ width = 1024;
+ height = 768;
+ }
+
+ if (true)
+ {
+ // Fullscreen support
+ CFDictionaryRef refDisplayMode = 0;
+ boolean_t exactMatch = false;
+
+#if CAPTURE_ALL_DISPLAYS
+ // Capture all displays (may want to do this for final build)
+ CGCaptureAllDisplays ();
+#else
+ // Capture only the main display (useful for debugging)
+ CGDisplayCapture (mDisplay);
+#endif
+
+ // Switch the display to the desired resolution and refresh
+ refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
+ mDisplay,
+ BITS_PER_PIXEL,
+ width,
+ height,
+ refresh,
+ &exactMatch);
+
+ if (refDisplayMode)
+ {
+ llinfos << "createContext: switching display resolution" << llendl;
+ mOldDisplayMode = CGDisplayCurrentMode (mDisplay);
+ CGDisplaySwitchToMode (mDisplay, refDisplayMode);
+ // CFRelease(refDisplayMode);
+
+ AddEventTypesToHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
+ }
+
+
+ mFullscreen = TRUE;
+ mFullscreenWidth = CGDisplayPixelsWide(mDisplay);
+ mFullscreenHeight = CGDisplayPixelsHigh(mDisplay);
+ mFullscreenBits = CGDisplayBitsPerPixel(mDisplay);
+ mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate));
+
+ llinfos << "Running at " << mFullscreenWidth
+ << "x" << mFullscreenHeight
+ << "x" << mFullscreenBits
+ << " @ " << mFullscreenRefresh
+ << llendl;
+ }
+ else
+ {
+ // No fullscreen support
+ mFullscreen = FALSE;
+ mFullscreenWidth = -1;
+ mFullscreenHeight = -1;
+ mFullscreenBits = -1;
+ mFullscreenRefresh = -1;
+
+ char error[256];
+ sprintf(error, "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
+ OSMessageBox(error, "Error", OSMB_OK);
+ }
+ }
+
+ if(!mFullscreen && (mWindow == NULL))
+ {
+ Rect window_rect;
+ //int displayWidth = CGDisplayPixelsWide(mDisplay);
+ //int displayHeight = CGDisplayPixelsHigh(mDisplay);
+ //const int menuBarPlusTitleBar = 44; // Ugly magic number.
+
+ llinfos << "createContext: creating window" << llendl;
+
+ window_rect.left = (long) x;
+ window_rect.right = (long) x + width;
+ window_rect.top = (long) y;
+ window_rect.bottom = (long) y + height;
+
+ //-----------------------------------------------------------------------
+ // Create the window
+ //-----------------------------------------------------------------------
+ mWindow = NewCWindow(
+ NULL,
+ &window_rect,
+ mWindowTitle,
+ false, // Create the window invisible. Whoever calls createContext() should show it after any moving/resizing.
+ // noGrowDocProc, // Window with no grow box and no zoom box
+ zoomDocProc, // Window with a grow box and a zoom box
+ // zoomNoGrow, // Window with a zoom box but no grow box
+ kFirstWindowOfClass,
+ true,
+ (long)this);
+
+
+ if (!mWindow)
+ {
+ setupFailure("Window creation error", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ // Turn on live resize.
+ // For this to work correctly, we need to be able to call LLViewerWindow::draw from
+ // the event handler for kEventWindowBoundsChanged. It's not clear that we have access from here.
+ // err = ChangeWindowAttributes(mWindow, kWindowLiveResizeAttribute, 0);
+
+ // Set up window event handlers (some window-related events ONLY go to window handlers.)
+ InstallStandardEventHandler(GetWindowEventTarget(mWindow));
+ InstallWindowEventHandler (mWindow, mEventHandlerUPP, GetEventTypeCount (WindowHandlerEventList), WindowHandlerEventList, (void*)this, &mWindowHandlerRef); // add event handler
+
+ }
+
+ if(mContext == NULL)
+ {
+ AGLRendererInfo rendererInfo = NULL;
+
+ //-----------------------------------------------------------------------
+ // Create GL drawing context
+ //-----------------------------------------------------------------------
+
+ if(mPixelFormat == NULL)
+ {
+ if(mFullscreen)
+ {
+ GLint fullscreenAttrib[] =
+ {
+ AGL_RGBA,
+ AGL_FULLSCREEN,
+ // AGL_NO_RECOVERY, // MBW -- XXX -- Not sure if we want this attribute
+ AGL_DOUBLEBUFFER,
+ AGL_CLOSEST_POLICY,
+ AGL_ACCELERATED,
+ AGL_RED_SIZE, 8,
+ AGL_GREEN_SIZE, 8,
+ AGL_BLUE_SIZE, 8,
+ AGL_ALPHA_SIZE, 8,
+ AGL_DEPTH_SIZE, 24,
+ AGL_STENCIL_SIZE, 8,
+ AGL_NONE
+ };
+
+ llinfos << "createContext: creating fullscreen pixelformat" << llendl;
+
+ GDHandle gdhDisplay = NULL;
+ err = DMGetGDeviceByDisplayID ((DisplayIDType)mDisplay, &gdhDisplay, false);
+
+ mPixelFormat = aglChoosePixelFormat(&gdhDisplay, 1, fullscreenAttrib);
+ rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
+ }
+ else
+ {
+ GLint windowedAttrib[] =
+ {
+ AGL_RGBA,
+ AGL_DOUBLEBUFFER,
+ AGL_CLOSEST_POLICY,
+ AGL_ACCELERATED,
+ AGL_RED_SIZE, 8,
+ AGL_GREEN_SIZE, 8,
+ AGL_BLUE_SIZE, 8,
+ AGL_ALPHA_SIZE, 8,
+ AGL_DEPTH_SIZE, 24,
+ AGL_STENCIL_SIZE, 8,
+ AGL_NONE
+ };
+
+ llinfos << "createContext: creating windowed pixelformat" << llendl;
+
+ mPixelFormat = aglChoosePixelFormat(NULL, 0, windowedAttrib);
+
+ GDHandle gdhDisplay = GetMainDevice();
+ rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
+ }
+
+ // May want to get the real error text like this:
+ // (char *) aglErrorString(aglGetError());
+
+ if(aglGetError() != AGL_NO_ERROR)
+ {
+ setupFailure("Can't find suitable pixel format", "Error", OSMB_OK);
+ return FALSE;
+ }
+ }
+
+ if(mPixelFormat)
+ {
+ llinfos << "createContext: creating GL context" << llendl;
+ mContext = aglCreateContext(mPixelFormat, NULL);
+ }
+
+ if(mContext == NULL)
+ {
+ setupFailure("Can't make GL context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ gGLManager.mVRAM = 0;
+
+ if(rendererInfo != NULL)
+ {
+ GLint result;
+
+ if(aglDescribeRenderer(rendererInfo, AGL_VIDEO_MEMORY, &result))
+ {
+ // llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) returned " << result << llendl;
+ gGLManager.mVRAM = result / (1024 * 1024);
+ }
+ else
+ {
+ // llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) failed." << llendl;
+ }
+
+ // This could be useful at some point, if it takes into account the memory already used by screen buffers, etc...
+ if(aglDescribeRenderer(rendererInfo, AGL_TEXTURE_MEMORY, &result))
+ {
+ // llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) returned " << result << llendl;
+ }
+ else
+ {
+ // llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) failed." << llendl;
+ }
+
+ aglDestroyRendererInfo(rendererInfo);
+ }
+
+ // Since we just created the context, it needs to be set up.
+ glNeedsInit = TRUE;
+ }
+
+ // Hook up the context to a drawable
+ if (mFullscreen && (mOldDisplayMode != NULL))
+ {
+ // We successfully captured the display. Use a fullscreen drawable
+
+ llinfos << "createContext: attaching fullscreen drawable" << llendl;
+
+#if CAPTURE_ALL_DISPLAYS
+ // Capture all displays (may want to do this for final build)
+ aglDisable (mContext, AGL_FS_CAPTURE_SINGLE);
+#else
+ // Capture only the main display (useful for debugging)
+ aglEnable (mContext, AGL_FS_CAPTURE_SINGLE);
+#endif
+
+ if (!aglSetFullScreen (mContext, 0, 0, 0, 0))
+ {
+ setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
+ return FALSE;
+ }
+ }
+ else if(!mFullscreen && (mWindow != NULL))
+ {
+ llinfos << "createContext: attaching windowed drawable" << llendl;
+
+ // We created a window. Use it as the drawable.
+ if(!aglSetDrawable(mContext, GetWindowPort (mWindow)))
+ {
+ setupFailure("Can't set GL drawable", "Error", OSMB_OK);
+ return FALSE;
+ }
+ }
+ else
+ {
+ setupFailure("Can't get fullscreen or windowed drawable.", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if(mContext != NULL)
+ {
+ llinfos << "createContext: setting current context" << llendl;
+
+ if (!aglSetCurrentContext(mContext))
+ {
+ setupFailure("Can't activate GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+ }
+
+ if(glNeedsInit)
+ {
+ // Check for some explicitly unsupported cards.
+ const char* RENDERER = (const char*) glGetString(GL_RENDERER);
+
+ const char* CARD_LIST[] =
+ { "RAGE 128",
+ "RIVA TNT2",
+ "Intel 810",
+ "3Dfx/Voodoo3",
+ "Radeon 7000",
+ "Radeon 7200",
+ "Radeon 7500",
+ "Radeon DDR",
+ "Radeon VE",
+ "GDI Generic" };
+ const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
+
+ // Future candidates:
+ // ProSavage/Twister
+ // SuperSavage
+
+ S32 i;
+ for (i = 0; i < CARD_COUNT; i++)
+ {
+ if (check_for_card(RENDERER, CARD_LIST[i]))
+ {
+ close();
+ shell_open( "help/unsupported_card.html" );
+ return FALSE;
+ }
+ }
+ }
+
+ GLint colorBits, alphaBits, depthBits, stencilBits;
+
+ if( !aglDescribePixelFormat(mPixelFormat, AGL_BUFFER_SIZE, &colorBits) ||
+ !aglDescribePixelFormat(mPixelFormat, AGL_ALPHA_SIZE, &alphaBits) ||
+ !aglDescribePixelFormat(mPixelFormat, AGL_DEPTH_SIZE, &depthBits) ||
+ !aglDescribePixelFormat(mPixelFormat, AGL_STENCIL_SIZE, &stencilBits))
+ {
+ close();
+ setupFailure("Can't get pixel format description", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ llinfos << "GL buffer: Color Bits " << S32(colorBits)
+ << " Alpha Bits " << S32(alphaBits)
+ << " Depth Bits " << S32(depthBits)
+ << " Stencil Bits" << S32(stencilBits)
+ << llendl;
+
+ if (colorBits < 32)
+ {
+ close();
+ setupFailure(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (alphaBits < 8)
+ {
+ close();
+ setupFailure(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ // Disable vertical sync for swap
+ GLint frames_per_swap = 0;
+ if (disable_vsync)
+ {
+ llinfos << "Disabling vertical sync" << llendl;
+ frames_per_swap = 0;
+ }
+ else
+ {
+ llinfos << "Keeping vertical sync" << llendl;
+ frames_per_swap = 1;
+ }
+ aglSetInteger(mContext, AGL_SWAP_INTERVAL, &frames_per_swap);
+
+ // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
+ return TRUE;
+}
+
+
+// changing fullscreen resolution, or switching between windowed and fullscreen mode.
+BOOL LLWindowMacOSX::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
+{
+ BOOL needsRebuild = FALSE;
+ BOOL result = true;
+
+ if(fullscreen)
+ {
+ if(mFullscreen)
+ {
+ // Switching resolutions in fullscreen mode. Don't need to rebuild for this.
+ // Fullscreen support
+ CFDictionaryRef refDisplayMode = 0;
+ boolean_t exactMatch = false;
+
+ // Switch the display to the desired resolution and refresh
+ refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
+ mDisplay,
+ BITS_PER_PIXEL,
+ size.mX,
+ size.mY,
+ getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate),
+ &exactMatch);
+
+ if (refDisplayMode)
+ {
+ CGDisplaySwitchToMode (mDisplay, refDisplayMode);
+ // CFRelease(refDisplayMode);
+ }
+
+ mFullscreenWidth = CGDisplayPixelsWide(mDisplay);
+ mFullscreenHeight = CGDisplayPixelsHigh(mDisplay);
+ mFullscreenBits = CGDisplayBitsPerPixel(mDisplay);
+ mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate));
+
+ llinfos << "Switched resolution to " << mFullscreenWidth
+ << "x" << mFullscreenHeight
+ << "x" << mFullscreenBits
+ << " @ " << mFullscreenRefresh
+ << llendl;
+
+ // Update the GL context to the new screen size
+ if (!aglUpdateContext(mContext))
+ {
+ setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
+ result = FALSE;
+ }
+ }
+ else
+ {
+ // Switching from windowed to fullscreen
+ needsRebuild = TRUE;
+ }
+ }
+ else
+ {
+ if(mFullscreen)
+ {
+ // Switching from fullscreen to windowed
+ needsRebuild = TRUE;
+ }
+ else
+ {
+ // Windowed to windowed -- not sure why we would be called like this. Just change the window size.
+ // The bounds changed event handler will do the rest.
+ if(mWindow != NULL)
+ {
+ ::SizeWindow(mWindow, size.mX, size.mY, true);
+ }
+ }
+ }
+
+ stop_glerror();
+ if(needsRebuild)
+ {
+ destroyContext();
+ result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
+ if (result)
+ {
+ if(mWindow != NULL)
+ {
+ MacShowWindow(mWindow);
+ BringToFront(mWindow);
+ }
+
+ llverify(gGLManager.initGL());
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+ }
+
+ stop_glerror();
+
+ return result;
+}
+
+void LLWindowMacOSX::destroyContext()
+{
+ if (!mContext)
+ {
+ // We don't have a context
+ return;
+ }
+ // Unhook the GL context from any drawable it may have
+ if(mContext != NULL)
+ {
+ llinfos << "destroyContext: unhooking drawable " << llendl;
+
+ aglSetCurrentContext (NULL);
+ aglSetDrawable(mContext, NULL);
+ }
+
+ // Make sure the display resolution gets restored
+ if(mOldDisplayMode != NULL)
+ {
+ llinfos << "destroyContext: restoring display resolution " << llendl;
+
+ CGDisplaySwitchToMode (mDisplay, mOldDisplayMode);
+
+#if CAPTURE_ALL_DISPLAYS
+ // Uncapture all displays (may want to do this for final build)
+ CGReleaseAllDisplays ();
+#else
+ // Uncapture only the main display (useful for debugging)
+ CGDisplayRelease (mDisplay);
+#endif
+
+ // CFRelease(mOldDisplayMode);
+
+ mOldDisplayMode = NULL;
+
+ // Remove the global event handlers the fullscreen case needed
+ RemoveEventTypesFromHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
+ }
+
+ // Clean up remaining GL state before blowing away window
+ gGLManager.shutdownGL();
+
+ // Clean up the pixel format
+ if(mPixelFormat != NULL)
+ {
+ llinfos << "destroyContext: destroying pixel format " << llendl;
+ aglDestroyPixelFormat(mPixelFormat);
+ mPixelFormat = NULL;
+ }
+
+ // Remove any Carbon Event handlers we installed
+ if(mGlobalHandlerRef != NULL)
+ {
+ llinfos << "destroyContext: removing global event handler" << llendl;
+ RemoveEventHandler(mGlobalHandlerRef);
+ mGlobalHandlerRef = NULL;
+ }
+
+ if(mWindowHandlerRef != NULL)
+ {
+ llinfos << "destroyContext: removing window event handler" << llendl;
+ RemoveEventHandler(mWindowHandlerRef);
+ mWindowHandlerRef = NULL;
+ }
+
+ // Close the window
+ if(mWindow != NULL)
+ {
+ llinfos << "destroyContext: disposing window" << llendl;
+ DisposeWindow(mWindow);
+ mWindow = NULL;
+ }
+
+ // Clean up the GL context
+ if(mContext != NULL)
+ {
+ llinfos << "destroyContext: destroying GL context" << llendl;
+ aglDestroyContext(mContext);
+ mContext = NULL;
+ }
+
+}
+
+LLWindowMacOSX::~LLWindowMacOSX()
+{
+ destroyContext();
+
+ if(mSupportedResolutions != NULL)
+ {
+ delete []mSupportedResolutions;
+ }
+
+ gWindowImplementation = NULL;
+
+}
+
+
+void LLWindowMacOSX::show()
+{
+ if(IsWindowCollapsed(mWindow))
+ CollapseWindow(mWindow, false);
+
+ MacShowWindow(mWindow);
+ BringToFront(mWindow);
+}
+
+void LLWindowMacOSX::hide()
+{
+ setMouseClipping(FALSE);
+ HideWindow(mWindow);
+}
+
+void LLWindowMacOSX::minimize()
+{
+ setMouseClipping(FALSE);
+ showCursor();
+ CollapseWindow(mWindow, true);
+}
+
+void LLWindowMacOSX::restore()
+{
+ show();
+}
+
+
+// close() destroys all OS-specific code associated with a window.
+// Usually called from LLWindowManager::destroyWindow()
+void LLWindowMacOSX::close()
+{
+ // Is window is already closed?
+ // if (!mWindow)
+ // {
+ // return;
+ // }
+
+ // Make sure cursor is visible and we haven't mangled the clipping state.
+ setMouseClipping(FALSE);
+ showCursor();
+
+ destroyContext();
+}
+
+BOOL LLWindowMacOSX::isValid()
+{
+ if(mFullscreen)
+ {
+ return(TRUE);
+ }
+
+ return (mWindow != NULL);
+}
+
+BOOL LLWindowMacOSX::getVisible()
+{
+ BOOL result = FALSE;
+
+ if(mFullscreen)
+ {
+ result = TRUE;
+ }if (mWindow)
+ {
+ if(MacIsWindowVisible(mWindow))
+ result = TRUE;
+ }
+
+ return(result);
+}
+
+BOOL LLWindowMacOSX::getMinimized()
+{
+ BOOL result = FALSE;
+
+ // Since the set of states where we want to act "minimized" is non-trivial, it's easier to
+ // track things locally than to try and retrieve the state from the window manager.
+ result = mMinimized;
+
+ return(result);
+}
+
+BOOL LLWindowMacOSX::getMaximized()
+{
+ BOOL result = FALSE;
+
+ if (mWindow)
+ {
+ // TODO
+ }
+
+ return(result);
+}
+
+BOOL LLWindowMacOSX::maximize()
+{
+ // TODO
+ return FALSE;
+}
+
+BOOL LLWindowMacOSX::getFullscreen()
+{
+ return mFullscreen;
+}
+
+void LLWindowMacOSX::gatherInput()
+{
+ // stop bouncing icon after fixed period of time
+ if (mBounceTimer.getStarted() && mBounceTimer.getElapsedTimeF32() > mBounceTime)
+ {
+ stopDockTileBounce();
+ }
+
+ // Use the old-school version so we get AppleEvent handler dispatch and menuselect handling.
+ // Anything that has an event handler will get processed inside WaitNextEvent, so we only need to handle
+ // the odd stuff here.
+ EventRecord evt;
+ while(WaitNextEvent(everyEvent, &evt, 0, NULL))
+ {
+ // printf("WaitNextEvent returned true, event is %d.\n", evt.what);
+ switch(evt.what)
+ {
+ case mouseDown:
+ {
+ short part;
+ WindowRef window;
+ long selectResult;
+ part = FindWindow(evt.where, &window);
+ switch ( part )
+ {
+ case inMenuBar:
+ selectResult = MenuSelect(evt.where);
+
+ HiliteMenu(0);
+ break;
+ }
+ }
+ break;
+
+ case kHighLevelEvent:
+ AEProcessAppleEvent (&evt);
+ break;
+
+ case updateEvt:
+ // We shouldn't be getting these regularly (since our window will be buffered), but we need to handle them correctly...
+ BeginUpdate((WindowRef)evt.message);
+ EndUpdate((WindowRef)evt.message);
+ break;
+
+ }
+ }
+}
+
+BOOL LLWindowMacOSX::getPosition(LLCoordScreen *position)
+{
+ Rect window_rect;
+ OSStatus err = -1;
+
+ if(mFullscreen)
+ {
+ position->mX = 0;
+ position->mY = 0;
+ err = noErr;
+ }
+ else if(mWindow)
+ {
+ err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
+
+ position->mX = window_rect.left;
+ position->mY = window_rect.top;
+ }
+ else
+ {
+ llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
+ }
+
+ return (err == noErr);
+}
+
+BOOL LLWindowMacOSX::getSize(LLCoordScreen *size)
+{
+ Rect window_rect;
+ OSStatus err = -1;
+
+ if(mFullscreen)
+ {
+ size->mX = mFullscreenWidth;
+ size->mY = mFullscreenHeight;
+ err = noErr;
+ }
+ else if(mWindow)
+ {
+ err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
+
+ size->mX = window_rect.right - window_rect.left;
+ size->mY = window_rect.bottom - window_rect.top;
+ }
+ else
+ {
+ llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
+ }
+
+ return (err == noErr);
+}
+
+BOOL LLWindowMacOSX::getSize(LLCoordWindow *size)
+{
+ Rect window_rect;
+ OSStatus err = -1;
+
+ if(mFullscreen)
+ {
+ size->mX = mFullscreenWidth;
+ size->mY = mFullscreenHeight;
+ err = noErr;
+ }
+ else if(mWindow)
+ {
+ err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
+
+ size->mX = window_rect.right - window_rect.left;
+ size->mY = window_rect.bottom - window_rect.top;
+ }
+ else
+ {
+ llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
+ }
+
+ return (err == noErr);
+}
+
+BOOL LLWindowMacOSX::setPosition(const LLCoordScreen position)
+{
+ if(mWindow)
+ {
+ MacMoveWindow(mWindow, position.mX, position.mY, false);
+ }
+
+ return TRUE;
+}
+
+BOOL LLWindowMacOSX::setSize(const LLCoordScreen size)
+{
+ if(mWindow)
+ {
+ SizeWindow(mWindow, size.mX, size.mY, true);
+ }
+
+ return TRUE;
+}
+
+void LLWindowMacOSX::swapBuffers()
+{
+ aglSwapBuffers(mContext);
+}
+
+F32 LLWindowMacOSX::getGamma()
+{
+ F32 result = 1.8; // Default to something sane
+
+ CGGammaValue redMin;
+ CGGammaValue redMax;
+ CGGammaValue redGamma;
+ CGGammaValue greenMin;
+ CGGammaValue greenMax;
+ CGGammaValue greenGamma;
+ CGGammaValue blueMin;
+ CGGammaValue blueMax;
+ CGGammaValue blueGamma;
+
+ if(CGGetDisplayTransferByFormula(
+ mDisplay,
+ &redMin,
+ &redMax,
+ &redGamma,
+ &greenMin,
+ &greenMax,
+ &greenGamma,
+ &blueMin,
+ &blueMax,
+ &blueGamma) == noErr)
+ {
+ // So many choices...
+ // Let's just return the green channel gamma for now.
+ result = greenGamma;
+ }
+
+ return result;
+}
+
+BOOL LLWindowMacOSX::restoreGamma()
+{
+ CGDisplayRestoreColorSyncSettings();
+ return true;
+}
+
+BOOL LLWindowMacOSX::setGamma(const F32 gamma)
+{
+ CGGammaValue redMin;
+ CGGammaValue redMax;
+ CGGammaValue redGamma;
+ CGGammaValue greenMin;
+ CGGammaValue greenMax;
+ CGGammaValue greenGamma;
+ CGGammaValue blueMin;
+ CGGammaValue blueMax;
+ CGGammaValue blueGamma;
+
+ // MBW -- XXX -- Should we allow this in windowed mode?
+
+ if(CGGetDisplayTransferByFormula(
+ mDisplay,
+ &redMin,
+ &redMax,
+ &redGamma,
+ &greenMin,
+ &greenMax,
+ &greenGamma,
+ &blueMin,
+ &blueMax,
+ &blueGamma) != noErr)
+ {
+ return false;
+ }
+
+ if(CGSetDisplayTransferByFormula(
+ mDisplay,
+ redMin,
+ redMax,
+ gamma,
+ greenMin,
+ greenMax,
+ gamma,
+ blueMin,
+ blueMax,
+ gamma) != noErr)
+ {
+ return false;
+ }
+
+
+ return true;
+}
+
+BOOL LLWindowMacOSX::isCursorHidden()
+{
+ return mCursorHidden;
+}
+
+
+
+// Constrains the mouse to the window.
+void LLWindowMacOSX::setMouseClipping( BOOL b )
+{
+ // Just stash the requested state. We'll simulate this when the cursor is hidden by decoupling.
+ mIsMouseClipping = b;
+
+ if(b)
+ {
+ // llinfos << "setMouseClipping(TRUE)" << llendl
+ }
+ else
+ {
+ // llinfos << "setMouseClipping(FALSE)" << llendl
+ }
+
+ adjustCursorDecouple();
+}
+
+BOOL LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
+{
+ BOOL result = FALSE;
+ LLCoordScreen screen_pos;
+
+ if (!convertCoords(position, &screen_pos))
+ {
+ return FALSE;
+ }
+
+ CGPoint newPosition;
+
+ // llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl
+
+ newPosition.x = screen_pos.mX;
+ newPosition.y = screen_pos.mY;
+
+ CGSetLocalEventsSuppressionInterval(0.0);
+ if(CGWarpMouseCursorPosition(newPosition) == noErr)
+ {
+ result = TRUE;
+ }
+
+ // Under certain circumstances, this will trigger us to decouple the cursor.
+ adjustCursorDecouple(true);
+
+ return result;
+}
+
+static void fixOrigin(void)
+{
+ GrafPtr port;
+ Rect portrect;
+
+ ::GetPort(&port);
+ ::GetPortBounds(port, &portrect);
+ if((portrect.left != 0) || (portrect.top != 0))
+ {
+ // Mozilla sometimes changes our port origin. Fuckers.
+ ::SetOrigin(0,0);
+ }
+}
+
+BOOL LLWindowMacOSX::getCursorPosition(LLCoordWindow *position)
+{
+ Point cursor_point;
+ LLCoordScreen screen_pos;
+ GrafPtr save;
+
+ if(mWindow == NULL)
+ return FALSE;
+
+ ::GetPort(&save);
+ ::SetPort(GetWindowPort(mWindow));
+ fixOrigin();
+
+ // gets the mouse location in local coordinates
+ ::GetMouse(&cursor_point);
+
+// lldebugs << "getCursorPosition(): cursor is at " << cursor_point.h << ", " << cursor_point.v << " port origin: " << portrect.left << ", " << portrect.top << llendl;
+
+ ::SetPort(save);
+
+ if(mCursorDecoupled)
+ {
+ // CGMouseDelta x, y;
+
+ // If the cursor's decoupled, we need to read the latest movement delta as well.
+ // CGGetLastMouseDelta( &x, &y );
+ // cursor_point.h += x;
+ // cursor_point.v += y;
+
+ // CGGetLastMouseDelta may behave strangely when the cursor's first captured.
+ // Stash in the event handler instead.
+ cursor_point.h += mCursorLastEventDeltaX;
+ cursor_point.v += mCursorLastEventDeltaY;
+ }
+
+ position->mX = cursor_point.h;
+ position->mY = cursor_point.v;
+
+ return TRUE;
+}
+
+void LLWindowMacOSX::adjustCursorDecouple(bool warpingMouse)
+{
+ if(mIsMouseClipping && mCursorHidden)
+ {
+ if(warpingMouse)
+ {
+ // The cursor should be decoupled. Make sure it is.
+ if(!mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
+ CGAssociateMouseAndMouseCursorPosition(false);
+ mCursorDecoupled = true;
+ mCursorIgnoreNextDelta = TRUE;
+ }
+ }
+ }
+ else
+ {
+ // The cursor should not be decoupled. Make sure it isn't.
+ if(mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
+ CGAssociateMouseAndMouseCursorPosition(true);
+ mCursorDecoupled = false;
+ }
+ }
+}
+
+F32 LLWindowMacOSX::getNativeAspectRatio()
+{
+ if (mFullscreen)
+ {
+ return (F32)mFullscreenWidth / (F32)mFullscreenHeight;
+ }
+ else
+ {
+ // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
+ // switching, and stashes it in mOriginalAspectRatio. Here, we just return it.
+
+ if (mOverrideAspectRatio > 0.f)
+ {
+ return mOverrideAspectRatio;
+ }
+
+ return mOriginalAspectRatio;
+ }
+}
+
+F32 LLWindowMacOSX::getPixelAspectRatio()
+{
+ //OS X always enforces a 1:1 pixel aspect ratio, regardless of video mode
+ return 1.f;
+}
+
+//static SInt32 oldWindowLevel;
+
+// MBW -- XXX -- There's got to be a better way than this. Find it, please...
+
+void LLWindowMacOSX::beforeDialog()
+{
+ if(mFullscreen)
+ {
+
+#if CAPTURE_ALL_DISPLAYS
+ // Uncapture all displays (may want to do this for final build)
+ CGReleaseAllDisplays ();
+#else
+ // Uncapture only the main display (useful for debugging)
+ CGDisplayRelease (mDisplay);
+#endif
+ // kDocumentWindowClass
+ // kMovableModalWindowClass
+ // kAllWindowClasses
+
+ // GLint order = 0;
+ // aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
+ aglSetDrawable(mContext, NULL);
+ // GetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), &oldWindowLevel);
+ // SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), CGShieldingWindowLevel());
+
+ mHandsOffEvents = TRUE;
+
+ }
+}
+
+void LLWindowMacOSX::afterDialog()
+{
+ if(mFullscreen)
+ {
+ mHandsOffEvents = FALSE;
+
+ // SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), oldWindowLevel);
+ aglSetFullScreen(mContext, 0, 0, 0, 0);
+ // GLint order = 1;
+ // aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
+
+#if CAPTURE_ALL_DISPLAYS
+ // Capture all displays (may want to do this for final build)
+ CGCaptureAllDisplays ();
+#else
+ // Capture only the main display (useful for debugging)
+ CGDisplayCapture (mDisplay);
+#endif
+ }
+}
+
+
+S32 LLWindowMacOSX::stat(const char* file_name, struct stat* stat_info)
+{
+ return ::stat( file_name, stat_info );
+}
+
+void LLWindowMacOSX::flashIcon(F32 seconds)
+{
+ // Don't do this if we're already started, since this would try to install the NMRec twice.
+ if(!mBounceTimer.getStarted())
+ {
+ OSErr err;
+
+ mBounceTime = seconds;
+ memset(&mBounceRec, sizeof(mBounceRec), 0);
+ mBounceRec.qType = nmType;
+ mBounceRec.nmMark = 1;
+ err = NMInstall(&mBounceRec);
+ if(err == noErr)
+ {
+ mBounceTimer.start();
+ }
+ else
+ {
+ // This is very not-fatal (only problem is the icon will not bounce), but we'd like to find out about it somehow...
+ llinfos << "NMInstall failed with error code " << err << llendl;
+ }
+ }
+}
+
+BOOL LLWindowMacOSX::isClipboardTextAvailable()
+{
+ OSStatus err;
+ ScrapRef scrap;
+ ScrapFlavorFlags flags;
+ BOOL result = false;
+
+ err = GetCurrentScrap(&scrap);
+
+ if(err == noErr)
+ {
+ err = GetScrapFlavorFlags(scrap, kScrapFlavorTypeUnicode, &flags);
+ }
+
+ if(err == noErr)
+ result = true;
+
+ return result;
+}
+
+BOOL LLWindowMacOSX::pasteTextFromClipboard(LLWString &dst)
+{
+ OSStatus err;
+ ScrapRef scrap;
+ Size len;
+ BOOL result = false;
+
+ err = GetCurrentScrap(&scrap);
+
+ if(err == noErr)
+ {
+ err = GetScrapFlavorSize(scrap, kScrapFlavorTypeUnicode, &len);
+ }
+
+ if((err == noErr) && (len > 0))
+ {
+ int u16len = len / sizeof(U16);
+ U16 *temp = new U16[u16len + 1];
+ if (temp)
+ {
+ memset(temp, 0, (u16len + 1) * sizeof(temp[0]));
+ err = GetScrapFlavorData(scrap, kScrapFlavorTypeUnicode, &len, temp);
+ if (err == noErr)
+ {
+ // convert \r\n to \n and \r to \n in the incoming text.
+ U16 *s, *d;
+ for(s = d = temp; s[0] != '\0'; s++, d++)
+ {
+ if(s[0] == '\r')
+ {
+ if(s[1] == '\n')
+ {
+ // CRLF, a.k.a. DOS newline. Collapse to a single '\n'.
+ s++;
+ }
+
+ d[0] = '\n';
+ }
+ else
+ {
+ d[0] = s[0];
+ }
+ }
+
+ d[0] = '\0';
+
+ dst = utf16str_to_wstring(temp);
+
+ result = true;
+ }
+ delete[] temp;
+ }
+ }
+
+ return result;
+}
+
+BOOL LLWindowMacOSX::copyTextToClipboard(const LLWString &s)
+{
+ OSStatus err;
+ ScrapRef scrap;
+ //Size len;
+ //char *temp;
+ BOOL result = false;
+
+ if (!s.empty())
+ {
+ err = GetCurrentScrap(&scrap);
+ if (err == noErr)
+ err = ClearScrap(&scrap);
+
+ if (err == noErr)
+ {
+ llutf16string utf16str = wstring_to_utf16str(s);
+ size_t u16len = utf16str.length() * sizeof(U16);
+ err = PutScrapFlavor(scrap, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone, u16len, utf16str.data());
+ if (err == noErr)
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+
+BOOL LLWindowMacOSX::sendEmail(const char* address, const char* subject, const char* body_text,
+ const char* attachment, const char* attachment_displayed_name )
+{
+ // MBW -- XXX -- Um... yeah. I'll get to this later.
+
+ return false;
+}
+
+
+// protected
+BOOL LLWindowMacOSX::resetDisplayResolution()
+{
+ // This is only called from elsewhere in this class, and it's not used by the Mac implementation.
+ return true;
+}
+
+
+LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_resolutions)
+{
+ if (!mSupportedResolutions)
+ {
+ CFArrayRef modes = CGDisplayAvailableModes(mDisplay);
+
+ if(modes != NULL)
+ {
+ CFIndex index, cnt;
+
+ mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
+ mNumSupportedResolutions = 0;
+
+ // Examine each mode
+ cnt = CFArrayGetCount( modes );
+
+ for ( index = 0; (index < cnt) && (mNumSupportedResolutions < MAX_NUM_RESOLUTIONS); index++ )
+ {
+ // Pull the mode dictionary out of the CFArray
+ CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex( modes, index );
+ long width = getDictLong(mode, kCGDisplayWidth);
+ long height = getDictLong(mode, kCGDisplayHeight);
+ long bits = getDictLong(mode, kCGDisplayBitsPerPixel);
+
+ if(bits == BITS_PER_PIXEL && width >= 800 && height >= 600)
+ {
+ BOOL resolution_exists = FALSE;
+ for(S32 i = 0; i < mNumSupportedResolutions; i++)
+ {
+ if (mSupportedResolutions[i].mWidth == width &&
+ mSupportedResolutions[i].mHeight == height)
+ {
+ resolution_exists = TRUE;
+ }
+ }
+ if (!resolution_exists)
+ {
+ mSupportedResolutions[mNumSupportedResolutions].mWidth = width;
+ mSupportedResolutions[mNumSupportedResolutions].mHeight = height;
+ mNumSupportedResolutions++;
+ }
+ }
+ }
+ }
+ }
+
+ num_resolutions = mNumSupportedResolutions;
+ return mSupportedResolutions;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to)
+{
+ S32 client_height;
+ Rect client_rect;
+
+ if(mFullscreen)
+ {
+ // In the fullscreen case, the "window" is the entire screen.
+ client_rect.left = 0;
+ client_rect.top = 0;
+ client_rect.right = mFullscreenWidth;
+ client_rect.bottom = mFullscreenHeight;
+ }
+ else if (!mWindow ||
+ (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to)
+{
+ S32 client_height;
+ Rect client_rect;
+
+ if(mFullscreen)
+ {
+ // In the fullscreen case, the "window" is the entire screen.
+ client_rect.left = 0;
+ client_rect.top = 0;
+ client_rect.right = mFullscreenWidth;
+ client_rect.bottom = mFullscreenHeight;
+ }
+ else if (!mWindow ||
+ (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+{
+ if(mFullscreen)
+ {
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return TRUE;
+ }
+ else if(mWindow)
+ {
+ GrafPtr save;
+ Point mouse_point;
+
+ mouse_point.h = from.mX;
+ mouse_point.v = from.mY;
+
+ ::GetPort(&save);
+ ::SetPort(GetWindowPort(mWindow));
+ fixOrigin();
+
+ ::GlobalToLocal(&mouse_point);
+
+ to->mX = mouse_point.h;
+ to->mY = mouse_point.v;
+
+ ::SetPort(save);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+{
+ if(mFullscreen)
+ {
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return TRUE;
+ }
+ else if(mWindow)
+ {
+ GrafPtr save;
+ Point mouse_point;
+
+ mouse_point.h = from.mX;
+ mouse_point.v = from.mY;
+ ::GetPort(&save);
+ ::SetPort(GetWindowPort(mWindow));
+ fixOrigin();
+
+ LocalToGlobal(&mouse_point);
+
+ to->mX = mouse_point.h;
+ to->mY = mouse_point.v;
+
+ ::SetPort(save);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+
+
+
+void LLWindowMacOSX::setupFailure(const char* text, const char* caption, U32 type)
+{
+ destroyContext();
+
+ OSMessageBox(text, caption, type);
+}
+
+pascal OSStatus LLWindowMacOSX::staticEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData)
+{
+ LLWindowMacOSX *self = (LLWindowMacOSX*)userData;
+
+ return(self->eventHandler(myHandler, event));
+}
+
+OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef event)
+{
+ OSStatus result = eventNotHandledErr;
+ UInt32 evtClass = GetEventClass (event);
+ UInt32 evtKind = GetEventKind (event);
+
+ // Always handle command events, even in hands-off mode.
+ if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
+ {
+ HICommand command;
+ GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command);
+
+ switch(command.commandID)
+ {
+ case kHICommandQuit:
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ result = noErr;
+ break;
+
+ default:
+ // MBW -- XXX -- Should we handle other events here?
+ break;
+ }
+ }
+
+ if(mHandsOffEvents)
+ {
+ return(result);
+ }
+
+ switch (evtClass)
+ {
+ case kEventClassTextInput:
+ {
+ switch (evtKind)
+ {
+ case kEventTextInputUnicodeForKeyEvent:
+ {
+ UInt32 modifiers = 0;
+
+ // First, process the raw event.
+ {
+ EventRef rawEvent;
+
+ // Get the original event and extract the modifier keys, so we can ignore command-key events.
+ if (GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(rawEvent), NULL, &rawEvent) == noErr)
+ {
+ // Grab the modifiers for later use in this function...
+ GetEventParameter (rawEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
+
+ // and call this function recursively to handle the raw key event.
+ eventHandler (myHandler, rawEvent);
+ }
+ }
+
+ OSStatus err = noErr;
+ EventParamType actualType = typeUnicodeText;
+ UInt32 actualSize = 0;
+ size_t actualCount = 0;
+ U16 *buffer = NULL;
+
+ // Get the size of the unicode data
+ err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, 0, &actualSize, NULL);
+ if(err == noErr)
+ {
+ // allocate a buffer and get the actual data.
+ actualCount = actualSize / sizeof(U16);
+ buffer = new U16[actualCount];
+ err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, actualSize, &actualSize, buffer);
+ }
+
+ if(err == noErr)
+ {
+ if(modifiers & (cmdKey | controlKey))
+ {
+ // This was a menu key equivalent. Ignore it.
+ }
+ else
+ {
+ MASK mask = 0;
+ if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
+ if(modifiers & (cmdKey | controlKey)) { mask |= MASK_CONTROL; }
+ if(modifiers & optionKey) { mask |= MASK_ALT; }
+
+ llassert( actualType == typeUnicodeText );
+
+ // The result is a UTF16 buffer. Pass the characters in turn to handleUnicodeChar.
+
+ // Convert to UTF32 and go character-by-character.
+ llutf16string utf16(buffer, actualCount);
+ LLWString utf32 = utf16str_to_wstring(utf16);
+ LLWString::iterator iter;
+
+ for(iter = utf32.begin(); iter != utf32.end(); iter++)
+ {
+ mCallbacks->handleUnicodeChar(*iter, mask);
+ }
+ }
+ }
+
+ if(buffer != NULL)
+ {
+ delete[] buffer;
+ }
+
+ result = err;
+ }
+ break;
+ }
+ }
+ break;
+
+ case kEventClassKeyboard:
+ {
+ UInt32 keyCode = 0;
+ char charCode = 0;
+ UInt32 modifiers = 0;
+
+ // Some of these may fail for some event types. That's fine.
+ GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
+ GetEventParameter (event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
+
+ // printf("key event, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08x\n", keyCode, charCode, (char)charCode, modifiers);
+ // fflush(stdout);
+
+ switch (evtKind)
+ {
+ case kEventRawKeyDown:
+ case kEventRawKeyRepeat:
+ if (gDebugWindowProc)
+ {
+ printf("key down, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08x\n",
+ (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
+ fflush(stdout);
+ }
+ gKeyboard->handleKeyDown(keyCode, modifiers);
+ result = eventNotHandledErr;
+ break;
+
+ case kEventRawKeyUp:
+ if (gDebugWindowProc)
+ {
+ printf("key up, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08x\n",
+ (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
+ fflush(stdout);
+ }
+ gKeyboard->handleKeyUp(keyCode, modifiers);
+ result = eventNotHandledErr;
+ break;
+
+ case kEventRawKeyModifiersChanged:
+ // The keyboard input system wants key up/down events for modifier keys.
+ // Mac OS doesn't supply these directly, but can supply events when the collective modifier state changes.
+ // Use these events to generate up/down events for the modifiers.
+
+ if((modifiers & shiftKey) && !(mLastModifiers & shiftKey))
+ {
+ if (gDebugWindowProc) printf("Shift key down event\n");
+ gKeyboard->handleKeyDown(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
+ }
+ else if(!(modifiers & shiftKey) && (mLastModifiers & shiftKey))
+ {
+ if (gDebugWindowProc) printf("Shift key up event\n");
+ gKeyboard->handleKeyUp(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
+ }
+
+ if((modifiers & alphaLock) && !(mLastModifiers & alphaLock))
+ {
+ if (gDebugWindowProc) printf("Caps lock down event\n");
+ gKeyboard->handleKeyDown(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
+ }
+ else if(!(modifiers & alphaLock) && (mLastModifiers & alphaLock))
+ {
+ if (gDebugWindowProc) printf("Caps lock up event\n");
+ gKeyboard->handleKeyUp(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
+ }
+
+ if((modifiers & controlKey) && !(mLastModifiers & controlKey))
+ {
+ if (gDebugWindowProc) printf("Control key down event\n");
+ gKeyboard->handleKeyDown(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
+ }
+ else if(!(modifiers & controlKey) && (mLastModifiers & controlKey))
+ {
+ if (gDebugWindowProc) printf("Control key up event\n");
+ gKeyboard->handleKeyUp(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
+ }
+
+ if((modifiers & optionKey) && !(mLastModifiers & optionKey))
+ {
+ if (gDebugWindowProc) printf("Option key down event\n");
+ gKeyboard->handleKeyDown(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
+ }
+ else if(!(modifiers & optionKey) && (mLastModifiers & optionKey))
+ {
+ if (gDebugWindowProc) printf("Option key up event\n");
+ gKeyboard->handleKeyUp(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
+ }
+
+ // When the state of the 'Fn' key (the one that changes some of the mappings on a powerbook/macbook keyboard
+ // to an embedded keypad) changes, it may subsequently cause a key up event to be lost, which may lead to
+ // a movement key getting "stuck" down. This is bad.
+ // This is an OS bug -- even the GetKeys() API doesn't tell you the key has been released.
+ // This workaround causes all held-down keys to be reset whenever the state of the Fn key changes. This isn't
+ // exactly what we want, but it does avoid the case where you get stuck running forward.
+ if((modifiers & kEventKeyModifierFnMask) != (mLastModifiers & kEventKeyModifierFnMask))
+ {
+ if (gDebugWindowProc) printf("Fn key state change event\n");
+ gKeyboard->resetKeys();
+ }
+
+ if (gDebugWindowProc) fflush(stdout);
+
+ mLastModifiers = modifiers;
+ result = eventNotHandledErr;
+ break;
+ }
+ }
+ break;
+
+ case kEventClassMouse:
+ {
+ result = CallNextEventHandler(myHandler, event);
+ if (eventNotHandledErr == result)
+ { // only handle events not already handled (prevents wierd resize interaction)
+ EventMouseButton button = kEventMouseButtonPrimary;
+ HIPoint location = {0.0f, 0.0f};
+ UInt32 modifiers = 0;
+ UInt32 clickCount = 1;
+ long wheelDelta = 0;
+ LLCoordScreen inCoords;
+ LLCoordGL outCoords;
+ MASK mask = 0;
+
+ GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
+ GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(location), NULL, &location);
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
+ GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(wheelDelta), NULL, &wheelDelta);
+ GetEventParameter(event, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount);
+
+ inCoords.mX = llround(location.x);
+ inCoords.mY = llround(location.y);
+
+ if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
+ if(modifiers & controlKey) { mask |= MASK_CONTROL; }
+ if(modifiers & optionKey) { mask |= MASK_ALT; }
+
+ if(mCursorDecoupled)
+ {
+ CGMouseDelta x, y;
+
+ // If the cursor's decoupled, we need to read the latest movement delta as well.
+ CGGetLastMouseDelta( &x, &y );
+ mCursorLastEventDeltaX = x;
+ mCursorLastEventDeltaY = y;
+
+ if(mCursorIgnoreNextDelta)
+ {
+ mCursorLastEventDeltaX = 0;
+ mCursorLastEventDeltaY = 0;
+ mCursorIgnoreNextDelta = FALSE;
+ }
+ }
+ else
+ {
+ mCursorLastEventDeltaX = 0;
+ mCursorLastEventDeltaY = 0;
+ }
+
+ inCoords.mX += mCursorLastEventDeltaX;
+ inCoords.mY += mCursorLastEventDeltaY;
+
+ convertCoords(inCoords, &outCoords);
+
+ // printf("coords in: %d, %d; coords out: %d, %d\n", inCoords.mX, inCoords.mY, outCoords.mX, outCoords.mY);
+ // fflush(stdout);
+
+
+ switch (evtKind)
+ {
+ case kEventMouseDown:
+ switch(button)
+ {
+ case kEventMouseButtonPrimary:
+ if(modifiers & cmdKey)
+ {
+ // Simulate a right click
+ mSimulatedRightClick = true;
+ mCallbacks->handleRightMouseDown(this, outCoords, mask);
+ }
+ else if(clickCount == 2)
+ {
+ // Windows double-click events replace the second mousedown event in a double-click.
+ mCallbacks->handleDoubleClick(this, outCoords, mask);
+ }
+ else
+ {
+ mCallbacks->handleMouseDown(this, outCoords, mask);
+ }
+ break;
+ case kEventMouseButtonSecondary:
+ mCallbacks->handleRightMouseDown(this, outCoords, mask);
+ break;
+ }
+ result = noErr;
+ break;
+ case kEventMouseUp:
+
+ switch(button)
+ {
+ case kEventMouseButtonPrimary:
+ if(mSimulatedRightClick)
+ {
+ // End of simulated right click
+ mSimulatedRightClick = false;
+ mCallbacks->handleRightMouseUp(this, outCoords, mask);
+ }
+ else
+ {
+ mCallbacks->handleMouseUp(this, outCoords, mask);
+ }
+ break;
+ case kEventMouseButtonSecondary:
+ mCallbacks->handleRightMouseUp(this, outCoords, mask);
+ break;
+ }
+ result = noErr;
+ break;
+
+ case kEventMouseWheelMoved:
+ {
+ static S32 z_delta = 0;
+
+ z_delta += wheelDelta;
+
+ if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta)
+ {
+ mCallbacks->handleScrollWheel(this, -z_delta / WHEEL_DELTA);
+ z_delta = 0;
+ }
+ }
+ result = noErr;
+ break;
+
+ case kEventMouseDragged:
+ case kEventMouseMoved:
+ mCallbacks->handleMouseMove(this, outCoords, mask);
+ result = noErr;
+ break;
+
+ }
+ }
+ }
+ break;
+
+ case kEventClassWindow:
+ switch(evtKind)
+ {
+ case kEventWindowBoundsChanging:
+ {
+ Rect currentBounds;
+ Rect previousBounds;
+
+ GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &currentBounds);
+ GetEventParameter(event, kEventParamPreviousBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &previousBounds);
+
+ // This is where we would constrain move/resize to a particular screen
+ if(0)
+ {
+ SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), &currentBounds);
+ }
+ }
+ break;
+
+ case kEventWindowBoundsChanged:
+ {
+ Rect newBounds;
+
+ GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &newBounds);
+ aglUpdateContext(mContext);
+ mCallbacks->handleResize(this, newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
+
+
+ }
+ break;
+
+ case kEventWindowClose:
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ result = noErr;
+ break;
+
+ case kEventWindowHidden:
+ // llinfos << "LLWindowMacOSX: Deactivating on hide" << llendl;
+ mMinimized = TRUE;
+ mCallbacks->handleActivate(this, false);
+ // result = noErr;
+ break;
+
+ case kEventWindowShown:
+ // llinfos << "LLWindowMacOSX: Activating on show" << llendl;
+ mMinimized = FALSE;
+ mCallbacks->handleActivate(this, true);
+ // result = noErr;
+ break;
+
+ case kEventWindowCollapsed:
+ // llinfos << "LLWindowMacOSX: Deactivating on collapse" << llendl;
+ mMinimized = TRUE;
+ mCallbacks->handleActivate(this, false);
+ // result = noErr;
+ break;
+
+ case kEventWindowExpanded:
+ // llinfos << "LLWindowMacOSX: Activating on expand" << llendl;
+ mMinimized = FALSE;
+ mCallbacks->handleActivate(this, true);
+ // result = noErr;
+ break;
+
+ case kEventWindowGetClickActivation:
+ // BringToFront(mWindow);
+ // result = noErr;
+ break;
+ }
+ break;
+ }
+ return result;
+}
+
+const char* cursorIDToName(int id)
+{
+ switch (id)
+ {
+ case UI_CURSOR_ARROW: return "UI_CURSOR_ARROW";
+ case UI_CURSOR_WAIT: return "UI_CURSOR_WAIT";
+ case UI_CURSOR_HAND: return "UI_CURSOR_HAND";
+ case UI_CURSOR_IBEAM: return "UI_CURSOR_IBEAM";
+ case UI_CURSOR_CROSS: return "UI_CURSOR_CROSS";
+ case UI_CURSOR_SIZENWSE: return "UI_CURSOR_SIZENWSE";
+ case UI_CURSOR_SIZENESW: return "UI_CURSOR_SIZENESW";
+ case UI_CURSOR_SIZEWE: return "UI_CURSOR_SIZEWE";
+ case UI_CURSOR_SIZENS: return "UI_CURSOR_SIZENS";
+ case UI_CURSOR_NO: return "UI_CURSOR_NO";
+ case UI_CURSOR_WORKING: return "UI_CURSOR_WORKING";
+ case UI_CURSOR_TOOLGRAB: return "UI_CURSOR_TOOLGRAB";
+ case UI_CURSOR_TOOLLAND: return "UI_CURSOR_TOOLLAND";
+ case UI_CURSOR_TOOLFOCUS: return "UI_CURSOR_TOOLFOCUS";
+ case UI_CURSOR_TOOLCREATE: return "UI_CURSOR_TOOLCREATE";
+ case UI_CURSOR_ARROWDRAG: return "UI_CURSOR_ARROWDRAG";
+ case UI_CURSOR_ARROWCOPY: return "UI_CURSOR_ARROWCOPY";
+ case UI_CURSOR_ARROWDRAGMULTI: return "UI_CURSOR_ARROWDRAGMULTI";
+ case UI_CURSOR_ARROWCOPYMULTI: return "UI_CURSOR_ARROWCOPYMULTI";
+ case UI_CURSOR_NOLOCKED: return "UI_CURSOR_NOLOCKED";
+ case UI_CURSOR_ARROWLOCKED: return "UI_CURSOR_ARROWLOCKED";
+ case UI_CURSOR_GRABLOCKED: return "UI_CURSOR_GRABLOCKED";
+ case UI_CURSOR_TOOLTRANSLATE: return "UI_CURSOR_TOOLTRANSLATE";
+ case UI_CURSOR_TOOLROTATE: return "UI_CURSOR_TOOLROTATE";
+ case UI_CURSOR_TOOLSCALE: return "UI_CURSOR_TOOLSCALE";
+ case UI_CURSOR_TOOLCAMERA: return "UI_CURSOR_TOOLCAMERA";
+ case UI_CURSOR_TOOLPAN: return "UI_CURSOR_TOOLPAN";
+ case UI_CURSOR_TOOLZOOMIN: return "UI_CURSOR_TOOLZOOMIN";
+ case UI_CURSOR_TOOLPICKOBJECT3: return "UI_CURSOR_TOOLPICKOBJECT3";
+ case UI_CURSOR_TOOLSIT: return "UI_CURSOR_TOOLSIT";
+ case UI_CURSOR_TOOLBUY: return "UI_CURSOR_TOOLBUY";
+ case UI_CURSOR_TOOLPAY: return "UI_CURSOR_TOOLPAY";
+ case UI_CURSOR_TOOLOPEN: return "UI_CURSOR_TOOLOPEN";
+ case UI_CURSOR_PIPETTE: return "UI_CURSOR_PIPETTE";
+ }
+
+ llerrs << "cursorIDToName: unknown cursor id" << id << llendl;
+
+ return "UI_CURSOR_ARROW";
+}
+
+static CursorRef gCursors[UI_CURSOR_COUNT];
+
+
+static void initPixmapCursor(int cursorid, int hotspotX, int hotspotY)
+{
+ // cursors are in <Application Bundle>/Contents/Resources/cursors_mac/UI_CURSOR_FOO.tif
+ std::string fullpath = gDirUtilp->getAppRODataDir();
+ fullpath += gDirUtilp->getDirDelimiter();
+ fullpath += "cursors_mac";
+ fullpath += gDirUtilp->getDirDelimiter();
+ fullpath += cursorIDToName(cursorid);
+ fullpath += ".tif";
+
+ gCursors[cursorid] = createImageCursor(fullpath.c_str(), hotspotX, hotspotY);
+}
+
+void LLWindowMacOSX::setCursor(ECursorType cursor)
+{
+ OSStatus result = noErr;
+
+ if (cursor == UI_CURSOR_ARROW
+ && mBusyCount > 0)
+ {
+ cursor = UI_CURSOR_WORKING;
+ }
+
+ if(mCurrentCursor == cursor)
+ return;
+
+ // RN: replace multi-drag cursors with single versions
+ if (cursor == UI_CURSOR_ARROWDRAGMULTI)
+ {
+ cursor = UI_CURSOR_ARROWDRAG;
+ }
+ else if (cursor == UI_CURSOR_ARROWCOPYMULTI)
+ {
+ cursor = UI_CURSOR_ARROWCOPY;
+ }
+
+ switch(cursor)
+ {
+ default:
+ case UI_CURSOR_ARROW:
+ InitCursor();
+ if(mCursorHidden)
+ {
+ // Since InitCursor resets the hide level, correct for it here.
+ ::HideCursor();
+ }
+ break;
+
+ // MBW -- XXX -- Some of the standard Windows cursors have no standard Mac equivalents.
+ // Find out what they look like and replicate them.
+
+ // These are essentially correct
+ case UI_CURSOR_WAIT: SetThemeCursor(kThemeWatchCursor); break;
+ case UI_CURSOR_IBEAM: SetThemeCursor(kThemeIBeamCursor); break;
+ case UI_CURSOR_CROSS: SetThemeCursor(kThemeCrossCursor); break;
+ case UI_CURSOR_HAND: SetThemeCursor(kThemePointingHandCursor); break;
+ // case UI_CURSOR_NO: SetThemeCursor(kThemeNotAllowedCursor); break;
+ case UI_CURSOR_ARROWCOPY: SetThemeCursor(kThemeCopyArrowCursor); break;
+
+ // Double-check these
+ case UI_CURSOR_NO:
+ case UI_CURSOR_SIZEWE:
+ case UI_CURSOR_SIZENS:
+ case UI_CURSOR_SIZENWSE:
+ case UI_CURSOR_SIZENESW:
+ case UI_CURSOR_WORKING:
+ case UI_CURSOR_TOOLGRAB:
+ case UI_CURSOR_TOOLLAND:
+ case UI_CURSOR_TOOLFOCUS:
+ case UI_CURSOR_TOOLCREATE:
+ case UI_CURSOR_ARROWDRAG:
+ case UI_CURSOR_NOLOCKED:
+ case UI_CURSOR_ARROWLOCKED:
+ case UI_CURSOR_GRABLOCKED:
+ case UI_CURSOR_TOOLTRANSLATE:
+ case UI_CURSOR_TOOLROTATE:
+ case UI_CURSOR_TOOLSCALE:
+ case UI_CURSOR_TOOLCAMERA:
+ case UI_CURSOR_TOOLPAN:
+ case UI_CURSOR_TOOLZOOMIN:
+ case UI_CURSOR_TOOLPICKOBJECT3:
+ case UI_CURSOR_TOOLSIT:
+ case UI_CURSOR_TOOLBUY:
+ case UI_CURSOR_TOOLPAY:
+ case UI_CURSOR_TOOLOPEN:
+ result = setImageCursor(gCursors[cursor]);
+ break;
+
+ }
+
+ if(result != noErr)
+ {
+ InitCursor();
+ }
+
+ mCurrentCursor = cursor;
+}
+
+ECursorType LLWindowMacOSX::getCursor()
+{
+ return mCurrentCursor;
+}
+
+void LLWindowMacOSX::initCursors()
+{
+ initPixmapCursor(UI_CURSOR_NO, 8, 8);
+ initPixmapCursor(UI_CURSOR_WORKING, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLGRAB, 2, 14);
+ initPixmapCursor(UI_CURSOR_TOOLLAND, 13, 8);
+ initPixmapCursor(UI_CURSOR_TOOLFOCUS, 7, 6);
+ initPixmapCursor(UI_CURSOR_TOOLCREATE, 7, 7);
+ initPixmapCursor(UI_CURSOR_ARROWDRAG, 1, 1);
+ initPixmapCursor(UI_CURSOR_ARROWCOPY, 1, 1);
+ initPixmapCursor(UI_CURSOR_NOLOCKED, 8, 8);
+ initPixmapCursor(UI_CURSOR_ARROWLOCKED, 1, 1);
+ initPixmapCursor(UI_CURSOR_GRABLOCKED, 2, 14);
+ initPixmapCursor(UI_CURSOR_TOOLTRANSLATE, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLROTATE, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLSCALE, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLCAMERA, 7, 6);
+ initPixmapCursor(UI_CURSOR_TOOLPAN, 7, 6);
+ initPixmapCursor(UI_CURSOR_TOOLZOOMIN, 7, 6);
+ initPixmapCursor(UI_CURSOR_TOOLPICKOBJECT3, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLSIT, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLBUY, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLPAY, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLOPEN, 1, 1);
+
+ initPixmapCursor(UI_CURSOR_SIZENWSE, 10, 10);
+ initPixmapCursor(UI_CURSOR_SIZENESW, 10, 10);
+ initPixmapCursor(UI_CURSOR_SIZEWE, 10, 10);
+ initPixmapCursor(UI_CURSOR_SIZENS, 10, 10);
+
+}
+
+void LLWindowMacOSX::captureMouse()
+{
+ // By registering a global CarbonEvent handler for mouse move events, we ensure that
+ // mouse events are always processed. Thus, capture and release are unnecessary.
+}
+
+void LLWindowMacOSX::releaseMouse()
+{
+ // By registering a global CarbonEvent handler for mouse move events, we ensure that
+ // mouse events are always processed. Thus, capture and release are unnecessary.
+}
+
+void LLWindowMacOSX::hideCursor()
+{
+ if(!mCursorHidden)
+ {
+ // llinfos << "hideCursor: hiding" << llendl;
+ mCursorHidden = TRUE;
+ mHideCursorPermanent = TRUE;
+ ::HideCursor();
+ }
+ else
+ {
+ // llinfos << "hideCursor: already hidden" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowMacOSX::showCursor()
+{
+ if(mCursorHidden)
+ {
+ // llinfos << "showCursor: showing" << llendl;
+ mCursorHidden = FALSE;
+ mHideCursorPermanent = FALSE;
+ ::ShowCursor();
+ }
+ else
+ {
+ // llinfos << "showCursor: already visible" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowMacOSX::showCursorFromMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ showCursor();
+ }
+}
+
+void LLWindowMacOSX::hideCursorUntilMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ hideCursor();
+ mHideCursorPermanent = FALSE;
+ }
+}
+
+
+
+//
+// LLSplashScreenMacOSX
+//
+LLSplashScreenMacOSX::LLSplashScreenMacOSX()
+{
+ mWindow = NULL;
+}
+
+LLSplashScreenMacOSX::~LLSplashScreenMacOSX()
+{
+}
+
+void LLSplashScreenMacOSX::showImpl()
+{
+ // This code _could_ be used to display a spash screen...
+#if 0
+ IBNibRef nib = NULL;
+ OSStatus err;
+
+ err = CreateNibReference(CFSTR("SecondLife"), &nib);
+
+ if(err == noErr)
+ {
+ CreateWindowFromNib(nib, CFSTR("Splash Screen"), &mWindow);
+
+ DisposeNibReference(nib);
+ }
+
+ if(mWindow != NULL)
+ {
+ ShowWindow(mWindow);
+ }
+#endif
+}
+
+void LLSplashScreenMacOSX::updateImpl(const char* mesg)
+{
+ if(mWindow != NULL)
+ {
+ CFStringRef string = NULL;
+
+ if(mesg != NULL)
+ {
+ string = CFStringCreateWithCString(NULL, mesg, kCFStringEncodingUTF8);
+ }
+ else
+ {
+ string = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
+ }
+
+ if(string != NULL)
+ {
+ ControlRef progressText = NULL;
+ ControlID id;
+ OSStatus err;
+
+ id.signature = 'what';
+ id.id = 0;
+
+ err = GetControlByID(mWindow, &id, &progressText);
+ if(err == noErr)
+ {
+ err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&string);
+ Draw1Control(progressText);
+ }
+
+ CFRelease(string);
+ }
+ }
+}
+
+
+void LLSplashScreenMacOSX::hideImpl()
+{
+ if(mWindow != NULL)
+ {
+ DisposeWindow(mWindow);
+ mWindow = NULL;
+ }
+}
+
+
+
+S32 OSMessageBoxMacOSX(const char* text, const char* caption, U32 type)
+{
+ S32 result = OSBTN_CANCEL;
+ SInt16 retval_mac = 1;
+ AlertStdCFStringAlertParamRec params;
+ CFStringRef errorString = NULL;
+ CFStringRef explanationString = NULL;
+ DialogRef alert = NULL;
+ AlertType alertType = kAlertCautionAlert;
+ OSStatus err;
+
+ if(text != NULL)
+ {
+ explanationString = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
+ }
+ else
+ {
+ explanationString = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
+ }
+
+ if(caption != NULL)
+ {
+ errorString = CFStringCreateWithCString(NULL, caption, kCFStringEncodingUTF8);
+ }
+ else
+ {
+ errorString = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
+ }
+
+ params.version = kStdCFStringAlertVersionOne;
+ params.movable = false;
+ params.helpButton = false;
+ params.defaultText = (CFStringRef)kAlertDefaultOKText;
+ params.cancelText = 0;
+ params.otherText = 0;
+ params.defaultButton = 1;
+ params.cancelButton = 0;
+ params.position = kWindowDefaultPosition;
+ params.flags = 0;
+
+ switch(type)
+ {
+ case OSMB_OK:
+ default:
+ break;
+ case OSMB_OKCANCEL:
+ params.cancelText = (CFStringRef)kAlertDefaultCancelText;
+ params.cancelButton = 2;
+ break;
+ case OSMB_YESNO:
+ alertType = kAlertNoteAlert;
+ params.defaultText = CFSTR("Yes");
+ params.cancelText = CFSTR("No");
+ params.cancelButton = 2;
+ break;
+ }
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->beforeDialog();
+
+ err = CreateStandardAlert(
+ alertType,
+ errorString,
+ explanationString,
+ &params,
+ &alert);
+
+ if(err == noErr)
+ {
+ err = RunStandardAlert(
+ alert,
+ NULL,
+ &retval_mac);
+ }
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->afterDialog();
+
+ switch(type)
+ {
+ case OSMB_OK:
+ case OSMB_OKCANCEL:
+ default:
+ if(retval_mac == 1)
+ result = OSBTN_OK;
+ else
+ result = OSBTN_CANCEL;
+ break;
+ case OSMB_YESNO:
+ if(retval_mac == 1)
+ result = OSBTN_YES;
+ else
+ result = OSBTN_NO;
+ break;
+ }
+
+ if(errorString != NULL)
+ {
+ CFRelease(errorString);
+ }
+
+ if(explanationString != NULL)
+ {
+ CFRelease(explanationString);
+ }
+
+ return result;
+}
+
+// Open a URL with the user's default web browser.
+// Must begin with protocol identifier.
+void spawn_web_browser(const char* escaped_url)
+{
+ bool found = false;
+ S32 i;
+ for (i = 0; i < gURLProtocolWhitelistCount; i++)
+ {
+ S32 len = strlen(gURLProtocolWhitelist[i]);
+ if (!strncmp(escaped_url, gURLProtocolWhitelist[i], len)
+ && escaped_url[len] == ':')
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ llwarns << "spawn_web_browser() called for url with protocol not on whitelist: " << escaped_url << llendl;
+ return;
+ }
+
+ OSStatus result = noErr;
+ CFURLRef urlRef = NULL;
+
+ llinfos << "Opening URL " << escaped_url << llendl;
+
+ CFStringRef stringRef = CFStringCreateWithCString(NULL, escaped_url, kCFStringEncodingUTF8);
+ if (stringRef)
+ {
+ // This will succeed if the string is a full URL, including the http://
+ // Note that URLs specified this way need to be properly percent-escaped.
+ urlRef = CFURLCreateWithString(NULL, stringRef, NULL);
+
+ // Don't use CRURLCreateWithFileSystemPath -- only want valid URLs
+
+ CFRelease(stringRef);
+ }
+
+ if (urlRef)
+ {
+ result = LSOpenCFURLRef(urlRef, NULL);
+
+ if (result != noErr)
+ {
+ llinfos << "Error " << result << " on open." << llendl;
+ }
+
+ CFRelease(urlRef);
+ }
+ else
+ {
+ llinfos << "Error: couldn't create URL." << llendl;
+ }
+}
+
+void shell_open( const char* file_path )
+{
+ OSStatus result = noErr;
+
+ llinfos << "Opening " << file_path << llendl;
+ CFURLRef urlRef = NULL;
+
+ CFStringRef stringRef = CFStringCreateWithCString(NULL, file_path, kCFStringEncodingUTF8);
+ if (stringRef)
+ {
+ // This will succeed if the string is a full URL, including the http://
+ // Note that URLs specified this way need to be properly percent-escaped.
+ urlRef = CFURLCreateWithString(NULL, stringRef, NULL);
+
+ if(urlRef == NULL)
+ {
+ // This will succeed if the string is a full or partial posix path.
+ // This will work even if the path contains characters that would need to be percent-escaped
+ // in the URL (such as spaces).
+ urlRef = CFURLCreateWithFileSystemPath(NULL, stringRef, kCFURLPOSIXPathStyle, false);
+ }
+
+ CFRelease(stringRef);
+ }
+
+ if (urlRef)
+ {
+ result = LSOpenCFURLRef(urlRef, NULL);
+
+ if (result != noErr)
+ {
+ llinfos << "Error " << result << " on open." << llendl;
+ }
+ CFRelease(urlRef);
+ }
+ else
+ {
+ llinfos << "Error: couldn't create URL." << llendl;
+ }
+}
+
+BOOL LLWindowMacOSX::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+{
+ BOOL retval = FALSE;
+ OSErr error = noErr;
+ NColorPickerInfo info;
+
+ memset(&info, 0, sizeof(info));
+ info.theColor.color.rgb.red = (UInt16)(*r * 65535.f);
+ info.theColor.color.rgb.green = (UInt16)(*g * 65535.f);
+ info.theColor.color.rgb.blue = (UInt16)(*b * 65535.f);
+ info.placeWhere = kCenterOnMainScreen;
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->beforeDialog();
+
+ error = NPickColor(&info);
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->afterDialog();
+
+ if (error == noErr)
+ {
+ retval = info.newColorChosen;
+ if (info.newColorChosen)
+ {
+ *r = ((float) info.theColor.color.rgb.red) / 65535.0;
+ *g = ((float) info.theColor.color.rgb.green) / 65535.0;
+ *b = ((float) info.theColor.color.rgb.blue) / 65535.0;
+ }
+ }
+ return (retval);
+}
+
+static WindowRef dummywindowref = NULL;
+
+void *LLWindowMacOSX::getPlatformWindow()
+{
+ if(mWindow != NULL)
+ return (void*)mWindow;
+
+ // If we're in fullscreen mode, there's no window pointer available.
+ // Since Mozilla needs one to function, create a dummy window here.
+ // Note that we will never destroy it, but since only one will be created per run of the application, that's okay.
+
+ if(dummywindowref == NULL)
+ {
+ Rect window_rect = {100, 100, 200, 200};
+
+ dummywindowref = NewCWindow(
+ NULL,
+ &window_rect,
+ "\p",
+ false, // Create the window invisible.
+ zoomDocProc, // Window with a grow box and a zoom box
+ kLastWindowOfClass, // create it behind other windows
+ false, // no close box
+ 0);
+ }
+
+ return (void*)dummywindowref;
+}
+
+void LLWindowMacOSX::stopDockTileBounce()
+{
+ NMRemove(&mBounceRec);
+ mBounceTimer.stop();
+}
+
+// get a double value from a dictionary
+static double getDictDouble (CFDictionaryRef refDict, CFStringRef key)
+{
+ double double_value;
+ CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
+ if (!number_value) // if can't get a number for the dictionary
+ return -1; // fail
+ if (!CFNumberGetValue(number_value, kCFNumberDoubleType, &double_value)) // or if cant convert it
+ return -1; // fail
+ return double_value; // otherwise return the long value
+}
+
+// get a long value from a dictionary
+static long getDictLong (CFDictionaryRef refDict, CFStringRef key)
+{
+ long int_value;
+ CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
+ if (!number_value) // if can't get a number for the dictionary
+ return -1; // fail
+ if (!CFNumberGetValue(number_value, kCFNumberLongType, &int_value)) // or if cant convert it
+ return -1; // fail
+ return int_value; // otherwise return the long value
+}
+
+#endif // LL_DARWIN
diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h
new file mode 100644
index 0000000000..1927f9bf31
--- /dev/null
+++ b/indra/llwindow/llwindowmacosx.h
@@ -0,0 +1,189 @@
+/**
+ * @file llwindowmacosx.h
+ * @brief Mac implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWMACOSX_H
+#define LL_LLWINDOWMACOSX_H
+
+#include "llwindow.h"
+
+#include <Carbon/Carbon.h>
+#include <AGL/agl.h>
+
+// AssertMacros.h does bad things.
+#undef verify
+#undef check
+#undef require
+
+
+class LLWindowMacOSX : 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*/ BOOL getFullscreen();
+ /*virtual*/ BOOL getPosition(LLCoordScreen *position);
+ /*virtual*/ BOOL getSize(LLCoordScreen *size);
+ /*virtual*/ BOOL getSize(LLCoordWindow *size);
+ /*virtual*/ BOOL setPosition(LLCoordScreen position);
+ /*virtual*/ BOOL setSize(LLCoordScreen size);
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync);
+ /*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
+ /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
+ /*virtual*/ void showCursor();
+ /*virtual*/ void hideCursor();
+ /*virtual*/ void showCursorFromMouseMove();
+ /*virtual*/ void hideCursorUntilMouseMove();
+ /*virtual*/ BOOL isCursorHidden();
+ /*virtual*/ void setCursor(ECursorType cursor);
+ /*virtual*/ ECursorType getCursor();
+ /*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*/ BOOL restoreGamma(); // Restore original gamma table (before updating gamma)
+ /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput();
+ /*virtual*/ void delayInputProcessing() {};
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName();
+ /*virtual*/ void deleteFile( const char* file_name );
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info );
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL);
+
+
+ // 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*/ void beforeDialog();
+ /*virtual*/ void afterDialog();
+
+ /*virtual*/ BOOL dialog_color_picker(F32 *r, F32 *g, F32 *b);
+
+ /*virtual*/ void *getPlatformWindow();
+ /*virtual*/ void bringToFront() {};
+
+protected:
+ LLWindowMacOSX(
+ char *title, char *name, int x, int y, int width, int height, U32 flags,
+ BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth);
+ ~LLWindowMacOSX();
+
+ void initCursors();
+ BOOL isValid();
+ void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
+
+
+ // Changes display resolution. Returns true if successful
+ BOOL setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
+
+ // Go back to last fullscreen display resolution.
+ BOOL setFullscreenResolution();
+
+ // Restore the display resolution to its value before we ran the app.
+ BOOL resetDisplayResolution();
+
+ void minimize();
+ void restore();
+
+ BOOL shouldPostQuit() { return mPostQuit; }
+
+
+protected:
+ //
+ // Platform specific methods
+ //
+
+ // create or re-create the GL context/window. Called from the constructor and switchContext().
+ BOOL createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync);
+ void destroyContext();
+ void setupFailure(const char* text, const char* caption, U32 type);
+ static pascal OSStatus staticEventHandler (EventHandlerCallRef myHandler, EventRef event, void* userData);
+ OSStatus eventHandler (EventHandlerCallRef myHandler, EventRef event);
+ void adjustCursorDecouple(bool warpingMouse = false);
+ void fixWindowSize(void);
+ void stopDockTileBounce();
+
+
+ //
+ // Platform specific variables
+ //
+ WindowRef mWindow;
+ AGLContext mContext;
+ AGLPixelFormat mPixelFormat;
+ CGDirectDisplayID mDisplay;
+ CFDictionaryRef mOldDisplayMode;
+ EventLoopTimerRef mTimer;
+ EventHandlerUPP mEventHandlerUPP;
+ EventHandlerRef mGlobalHandlerRef;
+ EventHandlerRef mWindowHandlerRef;
+ Rect mOldMouseClip; // Screen rect to which the mouse cursor was globally constrained before we changed it in clipMouse()
+ Str255 mWindowTitle;
+ double mOriginalAspectRatio;
+ BOOL mSimulatedRightClick;
+ UInt32 mLastModifiers;
+ BOOL mHandsOffEvents; // When true, temporarially disable CarbonEvent processing.
+ // Used to allow event processing when putting up dialogs in fullscreen mode.
+ BOOL mCursorDecoupled;
+ S32 mCursorLastEventDeltaX;
+ S32 mCursorLastEventDeltaY;
+ BOOL mCursorIgnoreNextDelta;
+ BOOL mNeedsResize; // Constructor figured out the window is too big, it needs a resize.
+ LLCoordScreen mNeedsResizeSize;
+ F32 mOverrideAspectRatio;
+ BOOL mMinimized;
+
+ F32 mBounceTime;
+ NMRec mBounceRec;
+ LLTimer mBounceTimer;
+
+ friend class LLWindowManager;
+};
+
+
+class LLSplashScreenMacOSX : public LLSplashScreen
+{
+public:
+ LLSplashScreenMacOSX();
+ virtual ~LLSplashScreenMacOSX();
+
+ /*virtual*/ void showImpl();
+ /*virtual*/ void updateImpl(const char* mesg);
+ /*virtual*/ void hideImpl();
+
+private:
+ WindowRef mWindow;
+};
+
+S32 OSMessageBoxMacOSX(const char* text, const char* caption, U32 type);
+
+void load_url_external(const char* url);
+void shell_open( const char* file_path );
+
+#endif //LL_LLWINDOWMACOSX_H
diff --git a/indra/llwindow/llwindowmesaheadless.cpp b/indra/llwindow/llwindowmesaheadless.cpp
new file mode 100644
index 0000000000..c924bb1efa
--- /dev/null
+++ b/indra/llwindow/llwindowmesaheadless.cpp
@@ -0,0 +1,64 @@
+/**
+ * @file llwindowmesaheadless.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_MESA_HEADLESS
+
+#include "linden_common.h"
+#include "indra_constants.h"
+
+#include "llwindowmesaheadless.h"
+#include "llgl.h"
+#include "llglheaders.h"
+
+#define MESA_CHANNEL_TYPE GL_UNSIGNED_SHORT
+#define MESA_CHANNEL_SIZE 2
+
+U16 *gMesaBuffer = NULL;
+
+//
+// LLWindowMesaHeadless
+//
+LLWindowMesaHeadless::LLWindowMesaHeadless(char *title, char *name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags, BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags)
+{
+ if (use_gl)
+ {
+ llinfos << "MESA Init" << llendl;
+ mMesaContext = OSMesaCreateContextExt( GL_RGBA, 32, 0, 0, NULL );
+
+ /* Allocate the image buffer */
+ mMesaBuffer = new unsigned char [width * height * 4 * MESA_CHANNEL_SIZE];
+ llassert(mMesaBuffer);
+
+ gMesaBuffer = (U16*)mMesaBuffer;
+
+ /* Bind the buffer to the context and make it current */
+ if (!OSMesaMakeCurrent( mMesaContext, mMesaBuffer, MESA_CHANNEL_TYPE, width, height ))
+ {
+ llerrs << "MESA: OSMesaMakeCurrent failed!" << llendl;
+ }
+
+ llverify(gGLManager.initGL());
+ }
+}
+
+
+LLWindowMesaHeadless::~LLWindowMesaHeadless()
+{
+ delete mMesaBuffer;
+ OSMesaDestroyContext( mMesaContext );
+}
+
+void LLWindowMesaHeadless::swapBuffers()
+{
+ glFinish();
+}
+
+#endif
diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h
new file mode 100644
index 0000000000..550a61d37a
--- /dev/null
+++ b/indra/llwindow/llwindowmesaheadless.h
@@ -0,0 +1,104 @@
+/**
+ * @file llwindowmesaheadless.h
+ * @brief Windows implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWMESAHEADLESS_H
+#define LL_LLWINDOWMESAHEADLESS_H
+
+#if LL_MESA_HEADLESS
+
+#include "llwindow.h"
+#include "GL/osmesa.h"
+
+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*/ 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 setSize(LLCoordScreen size) {return FALSE;};
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync) {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 setCursor(ECursorType cursor) {};
+ //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 ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput() {};
+ /*virtual*/ void delayInputProcessing() {};
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName() {return LLString(""); };
+ /*virtual*/ void deleteFile( const char* file_name ) {};
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info ) {return 0; };
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL) { return FALSE; };
+
+
+ // 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; };
+
+ /*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) {}
+
+ /*virtual*/ void *getPlatformWindow() { return 0; };
+ /*virtual*/ void bringToFront() {};
+
+ LLWindowMesaHeadless(char *title, char *name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags, BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth);
+ ~LLWindowMesaHeadless();
+
+private:
+ OSMesaContext mMesaContext;
+ unsigned char * mMesaBuffer;
+};
+
+class LLSplashScreenMesaHeadless : public LLSplashScreen
+{
+public:
+ LLSplashScreenMesaHeadless() {};
+ virtual ~LLSplashScreenMesaHeadless() {};
+
+ /*virtual*/ void showImpl() {};
+ /*virtual*/ void updateImpl(const char* mesg) {};
+ /*virtual*/ void hideImpl() {};
+
+};
+
+#endif
+
+#endif //LL_LLWINDOWMESAHEADLESS_H
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
new file mode 100644
index 0000000000..75793eb739
--- /dev/null
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -0,0 +1,2487 @@
+/**
+ * @file llwindowsdl.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_SDL
+
+#include "linden_common.h"
+
+#include "llwindowsdl.h"
+#include "llkeyboardsdl.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llstring.h"
+#include "lldir.h"
+
+#include "llglheaders.h"
+
+#include "indra_constants.h"
+
+#if LL_GTK
+# include "gtk/gtk.h"
+#endif // LL_GTK
+
+#if LL_LINUX
+// not necessarily available on random SDL platforms, so #if LL_LINUX
+// for execv(), waitpid(), fork()
+# include <unistd.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+#endif // LL_LINUX
+
+extern BOOL gDebugWindowProc;
+
+// culled from winuser.h
+//const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
+// On the Mac, the scroll wheel reports a delta of 1 for each detent.
+// There's also acceleration for faster scrolling, based on a slider in the system preferences.
+const S32 WHEEL_DELTA = 1; /* Value for rolling one detent */
+const S32 BITS_PER_PIXEL = 32;
+const S32 MAX_NUM_RESOLUTIONS = 32;
+
+//
+// LLWindowSDL
+//
+
+#if LL_X11
+# include <X11/Xutil.h>
+// A global! Well, SDL isn't really designed for communicating
+// with multiple physical X11 displays. Heck, it's not really
+// designed for multiple X11 windows.
+// So, we need this for the SDL/X11 event filter callback (which
+// doesnt have a userdata parameter) and more.
+static Display *SDL_Display = NULL;
+static Window SDL_XWindowID = None;
+#endif //LL_X11
+
+// TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for the same reasons)
+// For SDL, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
+// The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
+// require a pointer to the LLWindowMacSDL object. Stash it here and maintain in the constructor and destructor.
+// This assumes that there will be only one object of this class at any time. Hopefully this is true.
+static LLWindowSDL *gWindowImplementation = NULL;
+
+static BOOL was_fullscreen = FALSE;
+
+// Cross-platform bits:
+
+void show_window_creation_error(const char* title)
+{
+ llwarns << title << llendl;
+ shell_open( "help/window_creation_error.html");
+ /*
+ OSMessageBox(
+ "Second Life is unable to run because it can't set up your display.\n"
+ "We need to be able to make a 32-bit color window at 1024x768, with\n"
+ "an 8 bit alpha channel.\n"
+ "\n"
+ "First, be sure your monitor is set to True Color (32-bit) in\n"
+ "Start -> Control Panels -> Display -> Settings.\n"
+ "\n"
+ "Otherwise, this may be due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "ATI drivers are available at http://www.ati.com/\n"
+ "nVidia drivers are available at http://www.nvidia.com/\n"
+ "\n"
+ "If you continue to receive this message, contact customer service.",
+ title,
+ OSMB_OK);
+ */
+}
+
+
+#if LL_GTK
+// Check the runtime GTK version for goodness.
+static BOOL maybe_do_gtk_diagnostics(void)
+{
+ static BOOL done_gtk_diag = FALSE;
+ static BOOL is_good = TRUE;
+ gtk_disable_setlocale();
+ if ((!done_gtk_diag) && gtk_init_check(NULL, NULL))
+ {
+ llinfos << "GTK Initialized." << llendl;
+ llinfos << "- Compiled against GTK version "
+ << GTK_MAJOR_VERSION << "."
+ << GTK_MINOR_VERSION << "."
+ << GTK_MICRO_VERSION << llendl;
+ llinfos << "- Running against GTK version "
+ << gtk_major_version << "."
+ << gtk_minor_version << "."
+ << gtk_micro_version << llendl;
+ gchar *gtk_warning;
+ gtk_warning = gtk_check_version(GTK_MAJOR_VERSION,
+ GTK_MINOR_VERSION,
+ GTK_MICRO_VERSION);
+ if (gtk_warning)
+ {
+ llwarns << "- GTK COMPATIBILITY WARNING: " <<
+ gtk_warning << llendl;
+ is_good = FALSE;
+ }
+
+ done_gtk_diag = TRUE;
+ }
+ return is_good;
+}
+#endif // LL_GTK
+
+
+BOOL check_for_card(const char* RENDERER, const char* bad_card)
+{
+ if (!strncasecmp(RENDERER, bad_card, strlen(bad_card)))
+ {
+ char buffer[1024];
+ sprintf(buffer,
+ "Your video card appears to be a %s, which Second Life does not support.\n"
+ "\n"
+ "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
+ "multitexture support. We explicitly support nVidia GeForce 2 or better, \n"
+ "and ATI Radeon 8500 or better.\n"
+ "\n"
+ "If you own a supported card and continue to receive this message, try \n"
+ "updating to the latest video card drivers. Otherwise look in the\n"
+ "secondlife.com support section or e-mail technical support\n"
+ "\n"
+ "You can try to run Second Life, but it will probably crash or run\n"
+ "very slowly. Try anyway?",
+ bad_card);
+ S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
+ if (OSBTN_YES == button)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+
+LLWindowSDL::LLWindowSDL(char *title, S32 x, S32 y, S32 width,
+ S32 height, U32 flags,
+ BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags), mGamma(1.0f)
+{
+ // Initialize the keyboard
+ gKeyboard = new LLKeyboardSDL();
+ // Note that we can't set up key-repeat until after SDL has init'd video
+
+ // Ignore use_gl for now, only used for drones on PC
+ mWindow = NULL;
+ mCursorDecoupled = FALSE;
+ mCursorLastEventDeltaX = 0;
+ mCursorLastEventDeltaY = 0;
+ mCursorIgnoreNextDelta = FALSE;
+ mNeedsResize = FALSE;
+ mOverrideAspectRatio = 0.f;
+ mGrabbyKeyFlags = 0;
+ mReallyCapturedCount = 0;
+ mHaveInputFocus = -1;
+ mIsMinimized = -1;
+
+ // Get the original aspect ratio of the main device.
+ mOriginalAspectRatio = 1024.0 / 768.0; // !!! FIXME //(double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
+
+ if (!title)
+ title = "SDL Window"; // !!! FIXME
+
+ // Stash the window title
+ mWindowTitle = new char[strlen(title) + 1];
+ strcpy(mWindowTitle, title);
+
+ // Create the GL context and set it up for windowed or fullscreen, as appropriate.
+ if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
+ {
+ gGLManager.initGL();
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+
+ stop_glerror();
+
+ // Stash an object pointer for OSMessageBox()
+ gWindowImplementation = this;
+
+#if LL_X11
+ mFlashing = FALSE;
+#endif // LL_X11
+}
+
+static SDL_Surface *Load_BMP_Resource(const char *basename)
+{
+ const int PATH_BUFFER_SIZE=1000;
+ char path_buffer[PATH_BUFFER_SIZE];
+
+ // Figure out where our BMP is living on the disk
+ snprintf(path_buffer, PATH_BUFFER_SIZE-1, "%s%sres-sdl%s%s",
+ gDirUtilp->getAppRODataDir().c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ basename);
+ path_buffer[PATH_BUFFER_SIZE-1] = '\0';
+
+ return SDL_LoadBMP(path_buffer);
+}
+
+BOOL LLWindowSDL::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
+{
+ //bool glneedsinit = false;
+// const char *gllibname = null; // !!! fixme
+
+ llinfos << "createContext, fullscreen=" << fullscreen <<
+ " size=" << width << "x" << height << llendl;
+
+ // captures don't survive contexts
+ mGrabbyKeyFlags = 0;
+ mReallyCapturedCount = 0;
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ // !!! fixme: stderr?
+ llinfos << "sdl_init() failed! " << SDL_GetError() << llendl;
+ setupFailure("window creation error", "error", OSMB_OK);
+ return false;
+ }
+
+ SDL_version c_sdl_version;
+ SDL_VERSION(&c_sdl_version);
+ llinfos << "Compiled against SDL "
+ << int(c_sdl_version.major) << "."
+ << int(c_sdl_version.minor) << "."
+ << int(c_sdl_version.patch) << llendl;
+ const SDL_version *r_sdl_version;
+ r_sdl_version = SDL_Linked_Version();
+ llinfos << " Running against SDL "
+ << int(r_sdl_version->major) << "."
+ << int(r_sdl_version->minor) << "."
+ << int(r_sdl_version->patch) << llendl;
+
+ const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo( );
+ if (!videoInfo)
+ {
+ llinfos << "SDL_GetVideoInfo() failed! " << SDL_GetError() << llendl;
+ setupFailure("Window creation error", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ SDL_EnableUNICODE(1);
+ SDL_WM_SetCaption(mWindowTitle, mWindowTitle);
+
+ // Set the application icon.
+ SDL_Surface *bmpsurface;
+ bmpsurface = Load_BMP_Resource("ll_icon.BMP");
+ if (bmpsurface)
+ {
+ // This attempts to give a black-keyed mask to the icon.
+ SDL_SetColorKey(bmpsurface,
+ SDL_SRCCOLORKEY,
+ SDL_MapRGB(bmpsurface->format, 0,0,0) );
+ SDL_WM_SetIcon(bmpsurface, NULL);
+ // The SDL examples cheerfully avoid freeing the icon
+ // surface, but I'm betting that's leaky.
+ SDL_FreeSurface(bmpsurface);
+ bmpsurface = NULL;
+ }
+
+ // note: these SetAttributes make Tom's 9600-on-AMD64 fail to
+ // get a visual, but it's broken anyway when it does, and without
+ // these SetAttributes we might easily get an avoidable substandard
+ // visual to work with on most other machines.
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, (bits <= 16) ? 16 : 24);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8);
+
+ // !!! FIXME: try to toggle vsync here?
+
+ mFullscreen = fullscreen;
+ was_fullscreen = fullscreen;
+
+ int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT;
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ mSDLFlags = sdlflags;
+
+ if (mFullscreen)
+ {
+ llinfos << "createContext: setting up fullscreen " << width << "x" << height << llendl;
+
+ // If the requested width or height is 0, find the best default for the monitor.
+ if((width == 0) || (height == 0))
+ {
+ // Scan through the list of modes, looking for one which has:
+ // height between 700 and 800
+ // aspect ratio closest to the user's original mode
+ S32 resolutionCount = 0;
+ LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
+
+ if(resolutionList != NULL)
+ {
+ F32 closestAspect = 0;
+ U32 closestHeight = 0;
+ U32 closestWidth = 0;
+ int i;
+
+ llinfos << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << llendl;
+
+ for(i=0; i < resolutionCount; i++)
+ {
+ F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
+
+ llinfos << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << llendl;
+
+ if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
+ (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
+ {
+ llinfos << " (new closest mode) " << llendl;
+
+ // This is the closest mode we've seen yet.
+ closestWidth = resolutionList[i].mWidth;
+ closestHeight = resolutionList[i].mHeight;
+ closestAspect = aspect;
+ }
+ }
+
+ width = closestWidth;
+ height = closestHeight;
+ }
+ }
+
+ if((width == 0) || (height == 0))
+ {
+ // Mode search failed for some reason. Use the old-school default.
+ width = 1024;
+ height = 768;
+ }
+
+ mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
+
+ if (mWindow)
+ {
+ mFullscreen = TRUE;
+ was_fullscreen = TRUE;
+ mFullscreenWidth = mWindow->w;
+ mFullscreenHeight = mWindow->h;
+ mFullscreenBits = mWindow->format->BitsPerPixel;
+ mFullscreenRefresh = -1;
+
+ llinfos << "Running at " << mFullscreenWidth
+ << "x" << mFullscreenHeight
+ << "x" << mFullscreenBits
+ << " @ " << mFullscreenRefresh
+ << llendl;
+ }
+ else
+ {
+ llwarns << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << llendl;
+ // No fullscreen support
+ mFullscreen = FALSE;
+ was_fullscreen = FALSE;
+ mFullscreenWidth = -1;
+ mFullscreenHeight = -1;
+ mFullscreenBits = -1;
+ mFullscreenRefresh = -1;
+
+ char error[256];
+ sprintf(error, "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
+ OSMessageBox(error, "Error", OSMB_OK);
+ }
+ }
+
+ if(!mFullscreen && (mWindow == NULL))
+ {
+ if (width == 0)
+ width = 1024;
+ if (height == 0)
+ width = 768;
+
+ llinfos << "createContext: creating window " << width << "x" << height << "x" << bits << llendl;
+ mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
+
+ if (!mWindow)
+ {
+ llwarns << "createContext: window creation failure. SDL: " << SDL_GetError() << llendl;
+ setupFailure("Window creation error", "Error", OSMB_OK);
+ return FALSE;
+ }
+ } else if (!mFullscreen && (mWindow != NULL))
+ {
+ llinfos << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << llendl;
+ }
+
+ /*if (!load_all_glsyms(gllibname))
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return FALSE;
+ }*/
+
+ gGLManager.mVRAM = videoInfo->video_mem / 1024;
+ if (gGLManager.mVRAM != 0)
+ {
+ llinfos << "Detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
+ }
+ // If VRAM is not detected, that is handled later
+
+#if 0 // !!! FIXME: all video cards suck under Linux. :)
+ // Since we just created the context, it needs to be set up.
+ glNeedsInit = TRUE;
+ if(glNeedsInit)
+ {
+ // Check for some explicitly unsupported cards.
+ const char* RENDERER = (const char*) glGetString(GL_RENDERER);
+
+ const char* CARD_LIST[] =
+ { "RAGE 128",
+ "RIVA TNT2",
+ "Intel 810",
+ "3Dfx/Voodoo3",
+ "Radeon 7000",
+ "Radeon 7200",
+ "Radeon 7500",
+ "Radeon DDR",
+ "Radeon VE",
+ "GDI Generic" };
+ const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
+
+ // Future candidates:
+ // ProSavage/Twister
+ // SuperSavage
+
+ S32 i;
+ for (i = 0; i < CARD_COUNT; i++)
+ {
+ if (check_for_card(RENDERER, CARD_LIST[i]))
+ {
+ close();
+ shell_open( "help/unsupported_card.html" );
+ return FALSE;
+ }
+ }
+ }
+#endif
+
+ GLint depthBits, stencilBits, redBits, greenBits, blueBits, alphaBits;
+
+ glGetIntegerv(GL_RED_BITS, &redBits);
+ glGetIntegerv(GL_GREEN_BITS, &greenBits);
+ glGetIntegerv(GL_BLUE_BITS, &blueBits);
+ glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
+ glGetIntegerv(GL_DEPTH_BITS, &depthBits);
+ glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
+
+ llinfos << "GL buffer:" << llendl
+ llinfos << " Red Bits " << S32(redBits) << llendl
+ llinfos << " Green Bits " << S32(greenBits) << llendl
+ llinfos << " Blue Bits " << S32(blueBits) << llendl
+ llinfos << " Alpha Bits " << S32(alphaBits) << llendl
+ llinfos << " Depth Bits " << S32(depthBits) << llendl
+ llinfos << " Stencil Bits " << S32(stencilBits) << llendl;
+
+ GLint colorBits = redBits + greenBits + blueBits + alphaBits;
+ // fixme: actually, it's REALLY important for picking that we get at
+ // least 8 bits each of red,green,blue. Alpha we can be a bit more
+ // relaxed about if we have to.
+ if (colorBits < 32)
+ {
+ close();
+ setupFailure(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+#if 0 // !!! FIXME: we're going to brave it for now...
+ if (alphaBits < 8)
+ {
+ close();
+ setupFailure(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+#endif
+
+#if LL_X11
+ init_x11clipboard();
+#endif // LL_X11
+
+ // We need to do this here, once video is init'd
+ if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
+ SDL_DEFAULT_REPEAT_INTERVAL))
+ llwarns << "Couldn't enable key-repeat: " << SDL_GetError() <<llendl;
+
+ // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
+ return TRUE;
+}
+
+
+// changing fullscreen resolution, or switching between windowed and fullscreen mode.
+BOOL LLWindowSDL::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
+{
+ const BOOL needsRebuild = TRUE; // Just nuke the context and start over.
+ BOOL result = true;
+
+ llinfos << "switchContext, fullscreen=" << fullscreen << llendl;
+ stop_glerror();
+ if(needsRebuild)
+ {
+ destroyContext();
+ result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
+ if (result)
+ {
+ gGLManager.initGL();
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+ }
+
+ stop_glerror();
+
+ return result;
+}
+
+void LLWindowSDL::destroyContext()
+{
+ llinfos << "destroyContext begins" << llendl;
+#if LL_X11
+ quit_x11clipboard();
+#endif // LL_X11
+
+ // Clean up remaining GL state before blowing away window
+ llinfos << "shutdownGL begins" << llendl;
+ gGLManager.shutdownGL();
+ llinfos << "SDL_QuitSS/VID begins" << llendl;
+ SDL_QuitSubSystem(SDL_INIT_VIDEO); // !!! !!! FIXME: this might be risky...
+ //unload_all_glsyms();
+
+ mWindow = NULL;
+}
+
+LLWindowSDL::~LLWindowSDL()
+{
+ quitCursors();
+ destroyContext();
+
+ if(mSupportedResolutions != NULL)
+ {
+ delete []mSupportedResolutions;
+ }
+
+ delete[] mWindowTitle;
+
+ gWindowImplementation = NULL;
+}
+
+
+void LLWindowSDL::show()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+void LLWindowSDL::hide()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+void LLWindowSDL::minimize()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+void LLWindowSDL::restore()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+
+// close() destroys all OS-specific code associated with a window.
+// Usually called from LLWindowManager::destroyWindow()
+void LLWindowSDL::close()
+{
+ // Is window is already closed?
+ // if (!mWindow)
+ // {
+ // return;
+ // }
+
+ // Make sure cursor is visible and we haven't mangled the clipping state.
+ setMouseClipping(FALSE);
+ showCursor();
+
+ destroyContext();
+}
+
+BOOL LLWindowSDL::isValid()
+{
+ return (mWindow != NULL);
+}
+
+BOOL LLWindowSDL::getVisible()
+{
+ BOOL result = FALSE;
+
+ // !!! FIXME: This isn't really right...
+ if (mWindow)
+ {
+ result = TRUE;
+ }
+
+ return(result);
+}
+
+BOOL LLWindowSDL::getMinimized()
+{
+ BOOL result = FALSE;
+
+ if (mWindow && (1 == mIsMinimized))
+ {
+ result = TRUE;
+ }
+ return(result);
+}
+
+BOOL LLWindowSDL::getMaximized()
+{
+ BOOL result = FALSE;
+
+ if (mWindow)
+ {
+ // TODO
+ }
+
+ return(result);
+}
+
+BOOL LLWindowSDL::maximize()
+{
+ // TODO
+ return FALSE;
+}
+
+BOOL LLWindowSDL::getFullscreen()
+{
+ return mFullscreen;
+}
+
+BOOL LLWindowSDL::getPosition(LLCoordScreen *position)
+{
+ // !!! FIXME: can anything be done with this?
+ position->mX = 0;
+ position->mY = 0;
+ return TRUE;
+}
+
+BOOL LLWindowSDL::getSize(LLCoordScreen *size)
+{
+ if (mWindow)
+ {
+ size->mX = mWindow->w;
+ size->mY = mWindow->h;
+ return (TRUE);
+ }
+
+ llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
+ return (FALSE);
+}
+
+BOOL LLWindowSDL::getSize(LLCoordWindow *size)
+{
+ if (mWindow)
+ {
+ size->mX = mWindow->w;
+ size->mY = mWindow->h;
+ return (TRUE);
+ }
+
+ llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
+ return (FALSE);
+}
+
+BOOL LLWindowSDL::setPosition(const LLCoordScreen position)
+{
+ if(mWindow)
+ {
+ // !!! FIXME...
+ //MacMoveWindow(mWindow, position.mX, position.mY, false);
+ }
+
+ return TRUE;
+}
+
+BOOL LLWindowSDL::setSize(const LLCoordScreen size)
+{
+ if(mWindow)
+ {
+ // !!! FIXME...
+ //SizeWindow(mWindow, size.mX, size.mY, true);
+ }
+
+ return TRUE;
+}
+
+void LLWindowSDL::swapBuffers()
+{
+ if (mWindow)
+ SDL_GL_SwapBuffers();
+}
+
+F32 LLWindowSDL::getGamma()
+{
+ return 1/mGamma;
+}
+
+BOOL LLWindowSDL::restoreGamma()
+{
+ //CGDisplayRestoreColorSyncSettings();
+ SDL_SetGamma(1.0f, 1.0f, 1.0f);
+ return true;
+}
+
+BOOL LLWindowSDL::setGamma(const F32 gamma)
+{
+ mGamma = gamma;
+ if (mGamma == 0) mGamma = 0.1f;
+ mGamma = 1/mGamma;
+ SDL_SetGamma(mGamma, mGamma, mGamma);
+ return true;
+}
+
+BOOL LLWindowSDL::isCursorHidden()
+{
+ return mCursorHidden;
+}
+
+
+
+// Constrains the mouse to the window.
+void LLWindowSDL::setMouseClipping( BOOL b )
+{
+ //llinfos << "LLWindowSDL::setMouseClipping " << b << llendl;
+ // Just stash the requested state. We'll simulate this when the cursor is hidden by decoupling.
+ mIsMouseClipping = b;
+ //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
+ adjustCursorDecouple();
+}
+
+BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position)
+{
+ BOOL result = TRUE;
+ LLCoordScreen screen_pos;
+
+ if (!convertCoords(position, &screen_pos))
+ {
+ return FALSE;
+ }
+
+ //llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
+
+ SDL_WarpMouse(screen_pos.mX, screen_pos.mY);
+
+ // Under certain circumstances, this will trigger us to decouple the cursor.
+ adjustCursorDecouple(true);
+
+ return result;
+}
+
+BOOL LLWindowSDL::getCursorPosition(LLCoordWindow *position)
+{
+ //Point cursor_point;
+ LLCoordScreen screen_pos;
+
+ //GetMouse(&cursor_point);
+ int x, y;
+ SDL_GetMouseState(&x, &y);
+
+ screen_pos.mX = x;
+ screen_pos.mY = y;
+
+ return convertCoords(screen_pos, position);
+}
+
+void LLWindowSDL::adjustCursorDecouple(bool warpingMouse)
+{
+ if(mIsMouseClipping && mCursorHidden)
+ {
+ if(warpingMouse)
+ {
+ // The cursor should be decoupled. Make sure it is.
+ if(!mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
+ //CGAssociateMouseAndMouseCursorPosition(false);
+ mCursorDecoupled = true;
+ mCursorIgnoreNextDelta = TRUE;
+ }
+ }
+ }
+ else
+ {
+ // The cursor should not be decoupled. Make sure it isn't.
+ if(mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
+ //CGAssociateMouseAndMouseCursorPosition(true);
+ mCursorDecoupled = false;
+ }
+ }
+}
+
+F32 LLWindowSDL::getNativeAspectRatio()
+{
+#if 0
+ // RN: this hack presumes that the largest supported resolution is monitor-limited
+ // and that pixels in that mode are square, therefore defining the native aspect ratio
+ // of the monitor...this seems to work to a close approximation for most CRTs/LCDs
+ S32 num_resolutions;
+ LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
+
+
+ return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
+ //rn: AC
+#endif
+
+ // MBW -- there are a couple of bad assumptions here. One is that the display list won't include
+ // ridiculous resolutions nobody would ever use. The other is that the list is in order.
+
+ // New assumptions:
+ // - pixels are square (the only reasonable choice, really)
+ // - The user runs their display at a native resolution, so the resolution of the display
+ // when the app is launched has an aspect ratio that matches the monitor.
+
+ //RN: actually, the assumption that there are no ridiculous resolutions (above the display's native capabilities) has
+ // been born out in my experience.
+ // Pixels are often not square (just ask the people who run their LCDs at 1024x768 or 800x600 when running fullscreen, like me)
+ // The ordering of display list is a blind assumption though, so we should check for max values
+ // Things might be different on the Mac though, so I'll defer to MBW
+
+ // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
+ // switching, and stashes it in mOriginalAspectRatio. Here, we just return it.
+
+ if (mOverrideAspectRatio > 0.f)
+ {
+ return mOverrideAspectRatio;
+ }
+
+ return mOriginalAspectRatio;
+}
+
+F32 LLWindowSDL::getPixelAspectRatio()
+{
+ F32 pixel_aspect = 1.f;
+ if (getFullscreen())
+ {
+ LLCoordScreen screen_size;
+ getSize(&screen_size);
+ pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
+ }
+
+ return pixel_aspect;
+}
+
+
+// some of this stuff is to support 'temporarily windowed' mode so that
+// dialogs are still usable in fullscreen. HOWEVER! - it's not enabled/working
+// yet.
+static LLCoordScreen old_size;
+static BOOL old_fullscreen;
+void LLWindowSDL::beforeDialog()
+{
+ llinfos << "LLWindowSDL::beforeDialog()" << llendl;
+
+ if (SDLReallyCaptureInput(FALSE) // must ungrab input so popup works!
+ && getSize(&old_size))
+ {
+ old_fullscreen = was_fullscreen;
+
+ if (old_fullscreen)
+ {
+ // NOT YET WORKING
+ //switchContext(FALSE, old_size, TRUE);
+ }
+ }
+
+#if LL_X11
+ if (SDL_Display)
+ {
+ // Everything that we/SDL asked for should happen before we
+ // potentially hand control over to GTK.
+ XSync(SDL_Display, False);
+ }
+#endif // LL_X11
+
+#if LL_GTK
+ // this is a good time to grab some GTK version information for
+ // diagnostics
+ maybe_do_gtk_diagnostics();
+#endif // LL_GTK
+}
+
+void LLWindowSDL::afterDialog()
+{
+ llinfos << "LLWindowSDL::afterDialog()" << llendl;
+ if (old_fullscreen && !was_fullscreen)
+ {
+ // NOT YET WORKING (see below)
+ //switchContext(TRUE, old_size, TRUE);
+ }
+ // !!! FIXME - we need to restore the GL context using
+ // LLViewerWindow::restoreGL() - but how??
+}
+
+
+S32 LLWindowSDL::stat(const char* file_name, struct stat* stat_info)
+{
+ return ::stat( file_name, stat_info );
+}
+
+#if LL_X11
+// set/reset the XWMHints flag for 'urgency' that usually makes the icon flash
+void LLWindowSDL::x11_set_urgent(BOOL urgent)
+{
+ if (SDL_Display && !mFullscreen)
+ {
+ XWMHints *wm_hints;
+
+ llinfos << "X11 hint for urgency, " << urgent << llendl;
+
+ wm_hints = XGetWMHints(SDL_Display, mSDL_XWindowID);
+ if (!wm_hints)
+ wm_hints = XAllocWMHints();
+
+ if (urgent)
+ wm_hints->flags |= XUrgencyHint;
+ else
+ wm_hints->flags &= ~XUrgencyHint;
+
+ XSetWMHints(SDL_Display, mSDL_XWindowID, wm_hints);
+ XFree(wm_hints);
+ XSync(SDL_Display, False);
+ }
+}
+#endif // LL_X11
+
+void LLWindowSDL::flashIcon(F32 seconds)
+{
+#if !LL_X11
+ llinfos << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
+#else
+ llinfos << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
+
+ 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
+}
+
+#if LL_X11
+/* Lots of low-level X11 stuff to handle X11 copy-and-paste */
+
+/* Our X11 clipboard support is a bit bizarre in various
+ organically-grown ways. Ideally it should be fixed to do
+ real string-type negotiation (this would make pasting to
+ xterm faster and pasting to UTF-8 emacs work properly), but
+ right now it has the rare and desirable trait of being
+ generally stable and working. */
+
+/* PRIMARY and CLIPBOARD are the two main kinds of
+ X11 clipboard. A third are the CUT_BUFFERs which an
+ obsolete holdover from X10 days and use a quite orthogonal
+ mechanism. CLIPBOARD is the type whose design most
+ closely matches SL's own win32-alike explicit copy-and-paste
+ paradigm.
+
+ Pragmatically we support all three to varying degrees. When
+ we paste into SL, it is strictly from CLIPBOARD. When we copy,
+ we support (to as full an extent as the clipboard content type
+ allows) CLIPBOARD, PRIMARY, and CUT_BUFFER0.
+ */
+#define SL_READWRITE_XCLIPBOARD_TYPE XInternAtom(SDL_Display, "CLIPBOARD", False)
+#define SL_WRITE_XCLIPBOARD_TYPE XA_PRIMARY
+
+/* This is where our own private cutbuffer goes - we don't use
+ a regular cutbuffer (XA_CUT_BUFFER0 etc) for intermediate
+ storage because their use isn't really defined for holding UTF8. */
+#define SL_CUTBUFFER_TYPE XInternAtom(SDL_Display, "SECONDLIFE_CUTBUFFER", False)
+
+/* These defines, and convert_data/convert_x11clipboard,
+ mostly exist to support non-text or unusually-encoded
+ clipboard data, which we don't really have a need for at
+ the moment. */
+#define SDLCLIPTYPE(A, B, C, D) (int)((A<<24)|(B<<16)|(C<<8)|(D<<0))
+#define FORMAT_PREFIX "SECONDLIFE_x11clipboard_0x"
+
+typedef Atom x11clipboard_type;
+
+static
+x11clipboard_type convert_format(int type)
+{
+ switch (type)
+ {
+ case SDLCLIPTYPE('T', 'E', 'X', 'T'):
+ // old-style X11 clipboard, strictly only ISO 8859-1 encoding
+ return XA_STRING;
+ case SDLCLIPTYPE('U', 'T', 'F', '8'):
+ // newer de-facto UTF8 clipboard atom
+ return XInternAtom(SDL_Display, "UTF8_STRING", False);
+ default:
+ {
+ /* completely arbitrary clipboard types... we don't actually use
+ these right now, and support is skeletal. */
+ char format[sizeof(FORMAT_PREFIX)+8+1];
+
+ sprintf(format, "%s%08lx", FORMAT_PREFIX, (unsigned long)type);
+ return XInternAtom(SDL_Display, format, False);
+ }
+ }
+}
+
+/* convert platform string to x11 clipboard format. for our
+ purposes this is pretty trivial right now. */
+static int
+convert_data(int type, char *dst, const char *src, int srclen)
+{
+ int dstlen;
+
+ dstlen = 0;
+ switch (type)
+ {
+ case SDLCLIPTYPE('T', 'E', 'X', 'T'):
+ case SDLCLIPTYPE('U', 'T', 'F', '8'):
+ if ( srclen == 0 )
+ srclen = strlen(src);
+
+ dstlen = srclen + 1;
+
+ if ( dst ) // assume caller made it big enough by asking us
+ {
+ memcpy(dst, src, srclen);
+ dst[srclen] = '\0';
+ }
+ break;
+
+ default:
+ llwarns << "convert_data: Unknown medium type" << llendl;
+ break;
+ }
+ return(dstlen);
+}
+
+/* Convert x11clipboard data to platform string. This too is
+ pretty trivial for our needs right now, and just about identical
+ to above. */
+static int
+convert_x11clipboard(int type, char *dst, const char *src, int srclen)
+{
+ int dstlen;
+
+ dstlen = 0;
+ switch (type)
+ {
+ case SDLCLIPTYPE('U', 'T', 'F', '8'):
+ case SDLCLIPTYPE('T', 'E', 'X', 'T'):
+ if ( srclen == 0 )
+ srclen = strlen(src);
+
+ dstlen = srclen + 1;
+
+ if ( dst ) // assume caller made it big enough by asking us
+ {
+ memcpy(dst, src, srclen);
+ dst[srclen] = '\0';
+ }
+ break;
+
+ default:
+ llwarns << "convert_x11clipboard: Unknown medium type" << llendl;
+ break;
+ }
+ return dstlen;
+}
+
+int
+LLWindowSDL::is_empty_x11clipboard(void)
+{
+ int retval;
+
+ Lock_Display();
+ retval = ( XGetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE) == None );
+ Unlock_Display();
+
+ return(retval);
+}
+
+void
+LLWindowSDL::put_x11clipboard(int type, int srclen, const char *src)
+{
+ x11clipboard_type format;
+ int dstlen;
+ char *dst;
+
+ format = convert_format(type);
+ dstlen = convert_data(type, NULL, src, srclen);
+
+ dst = (char *)malloc(dstlen);
+ if ( dst != NULL )
+ {
+ Window root = DefaultRootWindow(SDL_Display);
+ Lock_Display();
+ convert_data(type, dst, src, srclen);
+ // Cutbuffers are only allowed to have STRING atom types,
+ // but Emacs puts UTF8 inside them anyway. We cautiously
+ // don't.
+ if (type == SDLCLIPTYPE('T','E','X','T'))
+ {
+ // dstlen-1 so we don't include the trailing \0
+ llinfos << "X11: Populating cutbuffer." <<llendl;
+ XChangeProperty(SDL_Display, root,
+ XA_CUT_BUFFER0, XA_STRING, 8, PropModeReplace,
+ (unsigned char*)dst, dstlen-1);
+ } else {
+ // Should we clear the cutbuffer if we can't put the selection in
+ // it because it's a UTF8 selection? Eh, no great reason I think.
+ //XDeleteProperty(SDL_Display, root, XA_CUT_BUFFER0);
+ }
+ // Private cutbuffer of an appropriate type.
+ XChangeProperty(SDL_Display, root,
+ SL_CUTBUFFER_TYPE, format, 8, PropModeReplace,
+ (unsigned char*)dst, dstlen-1);
+ free(dst);
+
+ /* Claim ownership of both PRIMARY and CLIPBOARD */
+ XSetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE,
+ mSDL_XWindowID, CurrentTime);
+ XSetSelectionOwner(SDL_Display, SL_WRITE_XCLIPBOARD_TYPE,
+ mSDL_XWindowID, CurrentTime);
+
+ Unlock_Display();
+ }
+}
+
+void
+LLWindowSDL::get_x11clipboard(int type, int *dstlen, char **dst)
+{
+ x11clipboard_type format;
+
+ *dstlen = 0;
+ format = convert_format(type);
+
+ Window owner;
+ Atom selection;
+ Atom seln_type;
+ int seln_format;
+ unsigned long nbytes;
+ unsigned long overflow;
+ char *src;
+
+ Lock_Display();
+ owner = XGetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE);
+ Unlock_Display();
+ if (owner == None)
+ {
+ // Fall right back to ancient X10 cut-buffers
+ owner = DefaultRootWindow(SDL_Display);
+ selection = XA_CUT_BUFFER0;
+ } else if (owner == mSDL_XWindowID)
+ {
+ // Use our own uncooked opaque string property
+ owner = DefaultRootWindow(SDL_Display);
+ selection = SL_CUTBUFFER_TYPE;
+ }
+ else
+ {
+ // Use full-on X11-style clipboard negotiation with the owning app
+ int selection_response = 0;
+ SDL_Event event;
+
+ owner = mSDL_XWindowID;
+ Lock_Display();
+ selection = XInternAtom(SDL_Display, "SDL_SELECTION", False);
+ XConvertSelection(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE, format,
+ selection, owner, CurrentTime);
+ Unlock_Display();
+ llinfos << "X11: Waiting for clipboard to arrive." <<llendl;
+ while ( ! selection_response )
+ {
+ // Only look for SYSWMEVENTs, or we may lose keypresses
+ // etc.
+ SDL_PumpEvents();
+ if (1 == SDL_PeepEvents(&event, 1, SDL_GETEVENT,
+ SDL_SYSWMEVENTMASK) )
+ {
+ if ( event.type == SDL_SYSWMEVENT )
+ {
+ XEvent xevent =
+ event.syswm.msg->event.xevent;
+
+ if ( (xevent.type == SelectionNotify)&&
+ (xevent.xselection.requestor == owner) )
+ selection_response = 1;
+ }
+ } else {
+ llinfos << "X11: Waiting for SYSWM event..." << llendl;
+ }
+ }
+ llinfos << "X11: Clipboard arrived." <<llendl;
+ }
+
+ Lock_Display();
+ if ( XGetWindowProperty(SDL_Display, owner, selection, 0, INT_MAX/4,
+ False, format, &seln_type, &seln_format,
+ &nbytes, &overflow, (unsigned char **)&src) == Success )
+ {
+ if ( seln_type == format )
+ {
+ *dstlen = convert_x11clipboard(type, NULL, src, nbytes);
+ *dst = (char *)realloc(*dst, *dstlen);
+ if ( *dst == NULL )
+ *dstlen = 0;
+ else
+ convert_x11clipboard(type, *dst, src, nbytes);
+ }
+ XFree(src);
+ }
+
+ Unlock_Display();
+}
+
+int clipboard_filter_callback(const SDL_Event *event)
+{
+ /* Post all non-window manager specific events */
+ if ( event->type != SDL_SYSWMEVENT )
+ {
+ return(1);
+ }
+
+ /* Handle window-manager specific clipboard events */
+ switch (event->syswm.msg->event.xevent.type) {
+ /* Copy the selection from SL_CUTBUFFER_TYPE to the requested property */
+ case SelectionRequest: {
+ XSelectionRequestEvent *req;
+ XEvent sevent;
+ int seln_format;
+ unsigned long nbytes;
+ unsigned long overflow;
+ unsigned char *seln_data;
+
+ req = &event->syswm.msg->event.xevent.xselectionrequest;
+ sevent.xselection.type = SelectionNotify;
+ sevent.xselection.display = req->display;
+ sevent.xselection.selection = req->selection;
+ sevent.xselection.target = None;
+ sevent.xselection.property = None;
+ sevent.xselection.requestor = req->requestor;
+ sevent.xselection.time = req->time;
+ if ( XGetWindowProperty(SDL_Display, DefaultRootWindow(SDL_Display),
+ SL_CUTBUFFER_TYPE, 0, INT_MAX/4, False, req->target,
+ &sevent.xselection.target, &seln_format,
+ &nbytes, &overflow, &seln_data) == Success )
+ {
+ if ( sevent.xselection.target == req->target)
+ {
+ if ( sevent.xselection.target == XA_STRING ||
+ sevent.xselection.target ==
+ convert_format(SDLCLIPTYPE('U','T','F','8')) )
+ {
+ if ( seln_data[nbytes-1] == '\0' )
+ --nbytes;
+ }
+ XChangeProperty(SDL_Display, req->requestor, req->property,
+ req->target, seln_format, PropModeReplace,
+ seln_data, nbytes);
+ sevent.xselection.property = req->property;
+#define XA_TARGETS XInternAtom(SDL_Display, "TARGETS", False)
+ } else if (XA_TARGETS == req->target) {
+ /* only advertise what we currently support */
+ const int num_supported = 3;
+ Atom supported[num_supported] = {
+ XA_STRING, // will be over-written below
+ XInternAtom(SDL_Display, "TEXT",False),
+ XA_TARGETS
+ };
+ supported[0] = sevent.xselection.target;
+ XChangeProperty(SDL_Display, req->requestor,
+ req->property, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)supported,
+ num_supported);
+ sevent.xselection.property = req->property;
+ llinfos << "Clipboard: An app asked us what selections format we offer." << llendl;
+ } else {
+ llinfos << "Clipboard: An app requested an unsupported selection format " << req->target << ", we have " << sevent.xselection.target << llendl;
+ sevent.xselection.target = None;
+ }
+ XFree(seln_data);
+ }
+ int sendret =
+ XSendEvent(SDL_Display,req->requestor,False,0,&sevent);
+ if ((sendret==BadValue) || (sendret==BadWindow))
+ llwarns << "Clipboard SendEvent failed" << llendl;
+ XSync(SDL_Display, False);
+ }
+ break;
+ }
+
+ /* Post the event for X11 clipboard reading above */
+ return(1);
+}
+
+int
+LLWindowSDL::init_x11clipboard(void)
+{
+ SDL_SysWMinfo info;
+ int retval;
+
+ /* Grab the window manager specific information */
+ retval = -1;
+ SDL_SetError("SDL is not running on known window manager");
+
+ SDL_VERSION(&info.version);
+ if ( SDL_GetWMInfo(&info) )
+ {
+ /* Save the information for later use */
+ if ( info.subsystem == SDL_SYSWM_X11 )
+ {
+ SDL_Display = info.info.x11.display;
+ SDL_XWindowID = info.info.x11.wmwindow;
+ mSDL_XWindowID = info.info.x11.wmwindow;
+ Lock_Display = info.info.x11.lock_func;
+ Unlock_Display = info.info.x11.unlock_func;
+
+ /* Enable the special window hook events */
+ SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
+ SDL_SetEventFilter(clipboard_filter_callback);
+
+ retval = 0;
+ }
+ else
+ {
+ SDL_SetError("SDL is not running on X11");
+ }
+ }
+ return(retval);
+}
+
+void
+LLWindowSDL::quit_x11clipboard(void)
+{
+ SDL_Display = NULL;
+ SDL_XWindowID = None;
+ mSDL_XWindowID = None;
+ Lock_Display = NULL;
+ Unlock_Display = NULL;
+
+ SDL_SetEventFilter(NULL); // Stop custom event filtering
+}
+
+/************************************************/
+
+BOOL LLWindowSDL::isClipboardTextAvailable()
+{
+ return !is_empty_x11clipboard();
+}
+
+BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
+{
+ int cliplen; // seems 1 or 2 bytes longer than expected
+ char *cliptext = NULL;
+ get_x11clipboard(SDLCLIPTYPE('U','T','F','8'), &cliplen, &cliptext);
+ if (cliptext)
+ {
+ llinfos << "X11: Got UTF8 clipboard text." << llendl;
+ // at some future time we can use cliplen instead of relying on \0,
+ // if we ever grok non-ascii, non-utf8 encodings on the clipboard.
+ std::string clip_str(cliptext);
+ // we can't necessarily trust the incoming text to be valid UTF-8,
+ // but utf8str_to_wstring() seems to do an appropriate level of
+ // validation for avoiding over-reads.
+ dst = utf8str_to_wstring(clip_str);
+ /*llinfos << "X11 pasteTextFromClipboard: cliplen=" << cliplen <<
+ " strlen(cliptext)=" << strlen(cliptext) <<
+ " clip_str.length()=" << clip_str.length() <<
+ " dst.length()=" << dst.length() <<
+ llendl;*/
+ free(cliptext);
+ return TRUE; // success
+ }
+ get_x11clipboard(SDLCLIPTYPE('T','E','X','T'), &cliplen, &cliptext);
+ if (cliptext)
+ {
+ llinfos << "X11: Got ISO 8859-1 clipboard text." << llendl;
+ std::string clip_str(cliptext);
+ std::string utf8_str = rawstr_to_utf8(clip_str);
+ dst = utf8str_to_wstring(utf8_str);
+ free(cliptext);
+ }
+ return FALSE; // failure
+}
+
+BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
+{
+ std::string utf8text = wstring_to_utf8str(s);
+ const char* cstr = utf8text.c_str();
+ int cstrlen = strlen(cstr);
+ int i;
+ for (i=0; i<cstrlen; ++i)
+ {
+ if (0x80 & (unsigned char)cstr[i])
+ {
+ // Found an 8-bit character; use new-style UTF8 clipboard
+ llinfos << "X11: UTF8 copyTextToClipboard" << llendl;
+ put_x11clipboard(SDLCLIPTYPE('U','T','F','8'), cstrlen, cstr);
+ return TRUE;
+ }
+ }
+ // Didn't find any 8-bit characters; use old-style ISO 8859-1 clipboard
+ llinfos << "X11: ISO 8859-1 copyTextToClipboard" << llendl;
+ put_x11clipboard(SDLCLIPTYPE('T','E','X','T'), cstrlen, cstr);
+ return TRUE;
+}
+#else
+
+BOOL LLWindowSDL::isClipboardTextAvailable()
+{
+ return FALSE; // unsupported
+}
+
+BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
+{
+ return FALSE; // unsupported
+}
+
+BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
+{
+ return FALSE; // unsupported
+}
+#endif // LL_X11
+
+BOOL LLWindowSDL::sendEmail(const char* address, const char* subject, const char* body_text,
+ const char* attachment, const char* attachment_displayed_name )
+{
+ // MBW -- XXX -- Um... yeah. I'll get to this later.
+
+ return FALSE;
+}
+
+
+LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
+{
+ if (!mSupportedResolutions)
+ {
+ mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
+ mNumSupportedResolutions = 0;
+
+ SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
+ if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) )
+ {
+ int count = 0;
+ while (*modes) // they're sorted biggest to smallest, so find end...
+ {
+ modes++;
+ count++;
+ }
+
+ while (count--)
+ {
+ modes--;
+ SDL_Rect *r = *modes;
+ int w = r->w;
+ int h = r->h;
+ if ((w >= 800) && (h >= 600))
+ {
+ // make sure we don't add the same resolution multiple times!
+ if ( (mNumSupportedResolutions == 0) ||
+ ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) &&
+ (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) )
+ {
+ mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
+ mSupportedResolutions[mNumSupportedResolutions].mHeight = h;
+ mNumSupportedResolutions++;
+ }
+ }
+ }
+ }
+ }
+
+ num_resolutions = mNumSupportedResolutions;
+ return mSupportedResolutions;
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
+{
+ if (!to)
+ return FALSE;
+
+ to->mX = from.mX;
+ to->mY = mWindow->h - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
+{
+ if (!to)
+ return FALSE;
+
+ to->mX = from.mX;
+ to->mY = mWindow->h - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+{
+ if (!to)
+ return FALSE;
+
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return (TRUE);
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+{
+ if (!to)
+ return FALSE;
+
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return (TRUE);
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+
+
+
+void LLWindowSDL::setupFailure(const char* text, const char* caption, U32 type)
+{
+ destroyContext();
+
+ OSMessageBox(text, caption, type);
+}
+
+BOOL LLWindowSDL::SDLReallyCaptureInput(BOOL capture)
+{
+ // note: this used to be safe to call nestedly, but in the
+ // end that's not really a wise usage pattern, so don't.
+
+ if (capture)
+ mReallyCapturedCount = 1;
+ else
+ mReallyCapturedCount = 0;
+
+ SDL_GrabMode wantmode, newmode;
+ if (mReallyCapturedCount <= 0) // uncapture
+ {
+ wantmode = SDL_GRAB_OFF;
+ } else // capture
+ {
+ wantmode = SDL_GRAB_ON;
+ }
+
+ if (mReallyCapturedCount < 0) // yuck, imbalance.
+ {
+ mReallyCapturedCount = 0;
+ llwarns << "ReallyCapture count was < 0" << llendl;
+ }
+
+ if (!mFullscreen) /* only bother if we're windowed anyway */
+ {
+#if LL_X11
+ if (SDL_Display)
+ {
+ /* 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 (wantmode == SDL_GRAB_ON)
+ {
+ //llinfos << "X11 POINTER GRABBY" << llendl;
+ //newmode = SDL_WM_GrabInput(wantmode);
+ result = XGrabPointer(SDL_Display, mSDL_XWindowID,
+ True, 0, GrabModeAsync,
+ GrabModeAsync,
+ None, None, CurrentTime);
+ if (GrabSuccess == result)
+ newmode = SDL_GRAB_ON;
+ else
+ newmode = SDL_GRAB_OFF;
+ } else if (wantmode == SDL_GRAB_OFF)
+ {
+ //llinfos << "X11 POINTER UNGRABBY" << llendl;
+ newmode = SDL_GRAB_OFF;
+ //newmode = SDL_WM_GrabInput(SDL_GRAB_OFF);
+
+ XUngrabPointer(SDL_Display, CurrentTime);
+ // Make sure the ungrab happens RIGHT NOW.
+ XSync(SDL_Display, False);
+ } else
+ {
+ newmode = SDL_GRAB_QUERY; // neutral
+ }
+ } else // not actually running on X11, for some reason
+ newmode = wantmode;
+#endif // LL_X11
+ } else {
+ // pretend we got what we wanted, when really we don't care.
+ newmode = wantmode;
+ }
+
+ // return boolean success for whether we ended up in the desired state
+ return (capture && SDL_GRAB_ON==newmode) ||
+ (!capture && SDL_GRAB_OFF==newmode);
+}
+
+U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain)
+{
+ /* part of the fix for SL-13243: Some popular window managers like
+ to totally eat alt-drag for the purposes of moving windows. We
+ spoil their day by acquiring the exclusive X11 mouse lock for as
+ long as LALT is held down, so the window manager can't easily
+ see what's happening. Tested successfully with Metacity.
+ And... do the same with CTRL, for other darn WMs. We don't
+ care about other metakeys as SL doesn't use them with dragging
+ (for now). */
+
+ /* We maintain a bitmap of critical keys which are up and down
+ instead of simply key-counting, because SDL sometimes reports
+ misbalanced keyup/keydown event pairs to us for whatever reason. */
+
+ U32 mask = 0;
+ switch (keysym)
+ {
+ case SDLK_LALT:
+ mask = 1U << 0; break;
+ case SDLK_LCTRL:
+ mask = 1U << 1; break;
+ case SDLK_RCTRL:
+ mask = 1U << 2; break;
+ default:
+ break;
+ }
+
+ if (gain)
+ mGrabbyKeyFlags |= mask;
+ else
+ mGrabbyKeyFlags &= ~mask;
+
+ //llinfos << "mGrabbyKeyFlags=" << mGrabbyKeyFlags << llendl;
+
+ /* 0 means we don't need to mousegrab, otherwise grab. */
+ return mGrabbyKeyFlags;
+}
+
+void LLWindowSDL::gatherInput()
+{
+ 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;
+
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_MOUSEMOTION:
+ {
+ LLCoordWindow winCoord(event.button.x, event.button.y);
+ LLCoordGL openGlCoord;
+ convertCoords(winCoord, &openGlCoord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+ mCallbacks->handleMouseMove(this, openGlCoord, mask);
+ break;
+ }
+
+ case SDL_KEYDOWN:
+ gKeyboard->handleKeyDown(event.key.keysym.sym, event.key.keysym.mod);
+ // part of the fix for SL-13243
+ if (SDLCheckGrabbyKeys(event.key.keysym.sym, TRUE) != 0)
+ SDLReallyCaptureInput(TRUE);
+
+ if (event.key.keysym.unicode)
+ mCallbacks->handleUnicodeChar(event.key.keysym.unicode, gKeyboard->currentMask(FALSE));
+ break;
+
+ case SDL_KEYUP:
+ if (SDLCheckGrabbyKeys(event.key.keysym.sym, FALSE) == 0)
+ SDLReallyCaptureInput(FALSE); // part of the fix for SL-13243
+
+ // This is a testing hack to pop up a dialog when 4 is pressed
+ //if (event.key.keysym.sym == SDLK_4)
+ //OSMessageBox("a whole bunch of text goes right here, whee! test test test.", "this is the title!", OSMB_YESNO);
+
+ gKeyboard->handleKeyUp(event.key.keysym.sym, event.key.keysym.mod);
+ break;
+
+ 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)
+ mCallbacks->handleDoubleClick(this, openGlCoord, mask);
+ else
+ mCallbacks->handleMouseDown(this, openGlCoord, mask);
+ }
+
+ else if (event.button.button == SDL_BUTTON_RIGHT) // right ... yes, it's 3, not 2, in SDL...
+ {
+ // right double click isn't handled right now in Second Life ... if (isDoubleClick)
+ mCallbacks->handleRightMouseDown(this, openGlCoord, mask);
+ }
+
+ else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
+ ; // Middle mouse isn't handled right now in Second Life ... 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);
+
+ break;
+ }
+
+ case SDL_MOUSEBUTTONUP:
+ {
+ 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) // left
+ mCallbacks->handleMouseUp(this, openGlCoord, mask);
+ else if (event.button.button == SDL_BUTTON_RIGHT) // right ... yes, it's 3, not 2, in SDL...
+ mCallbacks->handleRightMouseUp(this, openGlCoord, mask);
+ else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
+ ; // UNUSED IN SECOND LIFE RIGHT NOW mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask);
+
+ // don't handle mousewheel here...
+
+ break;
+ }
+
+ case SDL_VIDEOEXPOSE: // VIDEOEXPOSE doesn't specify the damage, but hey, it's OpenGL...repaint the whole thing!
+ mCallbacks->handlePaint(this, 0, 0, mWindow->w, mWindow->h);
+ break;
+
+ case SDL_VIDEORESIZE: // !!! FIXME: handle this?
+ llinfos << "Handling a resize event: " << event.resize.w <<
+ "x" << event.resize.h << llendl;
+
+ // !!! FIXME: I'm not sure this is necessary!
+ mWindow = SDL_SetVideoMode(event.resize.w, event.resize.h, 32, mSDLFlags);
+ if (!mWindow)
+ {
+ // FIXME: More informative dialog?
+ llinfos << "Could not recreate context after resize! Quitting..." << llendl;
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ break;
+ }
+
+ mCallbacks->handleResize(this, event.resize.w, event.resize.h );
+ break;
+
+ case SDL_ACTIVEEVENT:
+ if (event.active.state & SDL_APPINPUTFOCUS)
+ {
+ // Note that for SDL (particularly on X11), keyboard
+ // and mouse focus are independent things. Here we are
+ // tracking keyboard focus state changes.
+
+ // 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].
+ if (event.active.gain != mHaveInputFocus)
+ {
+ if (event.active.gain)
+ mCallbacks->handleFocus(this);
+ else
+ mCallbacks->handleFocusLost(this);
+
+ mHaveInputFocus = !!event.active.gain;
+ }
+ }
+ if (event.active.state & SDL_APPACTIVE)
+ {
+ // Change in iconification/minimization state.
+ if ((!event.active.gain) != mIsMinimized)
+ {
+ mCallbacks->handleActivate(this, !!event.active.gain);
+ llinfos << "SDL deiconification state switched to " << BOOL(event.active.gain) << llendl;
+
+ mIsMinimized = (!event.active.gain);
+ }
+ else
+ {
+ llinfos << "Ignored bogus redundant SDL deiconification state switch to " << BOOL(event.active.gain) << llendl;
+ }
+ }
+ break;
+
+ case SDL_QUIT:
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ break;
+ default:
+ //llinfos << "Unhandled SDL event type " << event.type << llendl;
+ break;
+ }
+ }
+
+#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);
+ mFlashing = FALSE;
+ }
+#endif // LL_X11
+}
+
+static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty)
+{
+ SDL_Cursor *sdlcursor = NULL;
+ SDL_Surface *bmpsurface;
+
+ // Load cursor pixel data from BMP file
+ bmpsurface = Load_BMP_Resource(filename);
+ if (bmpsurface && bmpsurface->w%8==0)
+ {
+ SDL_Surface *cursurface;
+ llinfos << "Loaded cursor file " << filename << " "
+ << bmpsurface->w << "x" << bmpsurface->h << llendl;
+ cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE,
+ bmpsurface->w,
+ bmpsurface->h,
+ 32,
+ 0xFFU,
+ 0xFF00U,
+ 0xFF0000U,
+ 0xFF000000U);
+ SDL_FillRect(cursurface, NULL, 0x00000000U);
+
+ // Blit the cursor pixel data onto a 32-bit RGBA surface so we
+ // only have to cope with processing one type of pixel format.
+ if (0 == SDL_BlitSurface(bmpsurface, NULL,
+ cursurface, NULL))
+ {
+ // n.b. we already checked that width is a multiple of 8.
+ const int bitmap_bytes = (cursurface->w * cursurface->h) / 8;
+ unsigned char *cursor_data = new unsigned char[bitmap_bytes];
+ unsigned char *cursor_mask = new unsigned char[bitmap_bytes];
+ memset(cursor_data, 0, bitmap_bytes);
+ memset(cursor_mask, 0, bitmap_bytes);
+ int i,j;
+ // Walk the RGBA cursor pixel data, extracting both data and
+ // mask to build SDL-friendly cursor bitmaps from. The mask
+ // is inferred by color-keying against 200,200,200
+ for (i=0; i<cursurface->h; ++i) {
+ for (j=0; j<cursurface->w; ++j) {
+ unsigned char *pixelp =
+ ((unsigned char *)cursurface->pixels)
+ + cursurface->pitch * i
+ + j*cursurface->format->BytesPerPixel;
+ unsigned char srcred = pixelp[0];
+ unsigned char srcgreen = pixelp[1];
+ unsigned char srcblue = pixelp[2];
+ BOOL mask_bit = (srcred != 200)
+ || (srcgreen != 200)
+ || (srcblue != 200);
+ BOOL data_bit = mask_bit && (srcgreen <= 80);//not 0x80
+ unsigned char bit_offset = (cursurface->w/8) * i
+ + j/8;
+ cursor_data[bit_offset] |= (data_bit) << (7 - (j&7));
+ cursor_mask[bit_offset] |= (mask_bit) << (7 - (j&7));
+ }
+ }
+ sdlcursor = SDL_CreateCursor((Uint8*)cursor_data,
+ (Uint8*)cursor_mask,
+ cursurface->w, cursurface->h,
+ hotx, hoty);
+ delete[] cursor_data;
+ delete[] cursor_mask;
+ } else {
+ llwarns << "CURSOR BLIT FAILURE, cursurface: " << cursurface << llendl;
+ }
+ SDL_FreeSurface(cursurface);
+ SDL_FreeSurface(bmpsurface);
+ } else {
+ llwarns << "CURSOR LOAD FAILURE " << filename << llendl;
+ }
+
+ return sdlcursor;
+}
+
+void LLWindowSDL::setCursor(ECursorType cursor)
+{
+ if (mCurrentCursor != cursor)
+ {
+ if (cursor < UI_CURSOR_COUNT)
+ {
+ SDL_Cursor *sdlcursor = mSDLCursors[cursor];
+ // Try to default to the arrow for any cursors that
+ // did not load correctly.
+ if (!sdlcursor && mSDLCursors[UI_CURSOR_ARROW])
+ sdlcursor = mSDLCursors[UI_CURSOR_ARROW];
+ if (sdlcursor)
+ SDL_SetCursor(sdlcursor);
+ } else {
+ llwarns << "Tried to set invalid cursor number " << cursor << llendl;
+ }
+ mCurrentCursor = cursor;
+ }
+}
+
+ECursorType LLWindowSDL::getCursor()
+{
+ return mCurrentCursor;
+}
+
+void LLWindowSDL::initCursors()
+{
+ int i;
+ // Blank the cursor pointer array for those we may miss.
+ for (i=0; i<UI_CURSOR_COUNT; ++i)
+ {
+ mSDLCursors[i] = NULL;
+ }
+ // 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_NO] = makeSDLCursorFromBMP("llno.BMP",8,8);
+ mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15);
+ 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);
+ mSDLCursors[UI_CURSOR_TOOLCREATE] = makeSDLCursorFromBMP("lltoolcreate.BMP",7,7);
+ mSDLCursors[UI_CURSOR_ARROWDRAG] = makeSDLCursorFromBMP("arrowdrag.BMP",0,0);
+ mSDLCursors[UI_CURSOR_ARROWCOPY] = makeSDLCursorFromBMP("arrowcop.BMP",0,0);
+ mSDLCursors[UI_CURSOR_ARROWDRAGMULTI] = makeSDLCursorFromBMP("llarrowdragmulti.BMP",0,0);
+ mSDLCursors[UI_CURSOR_ARROWCOPYMULTI] = makeSDLCursorFromBMP("arrowcopmulti.BMP",0,0);
+ mSDLCursors[UI_CURSOR_NOLOCKED] = makeSDLCursorFromBMP("llnolocked.BMP",8,8);
+ mSDLCursors[UI_CURSOR_ARROWLOCKED] = makeSDLCursorFromBMP("llarrowlocked.BMP",0,0);
+ mSDLCursors[UI_CURSOR_GRABLOCKED] = makeSDLCursorFromBMP("llgrablocked.BMP",2,13);
+ mSDLCursors[UI_CURSOR_TOOLTRANSLATE] = makeSDLCursorFromBMP("lltooltranslate.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLROTATE] = makeSDLCursorFromBMP("lltoolrotate.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLSCALE] = makeSDLCursorFromBMP("lltoolscale.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLCAMERA] = makeSDLCursorFromBMP("lltoolcamera.BMP",7,5);
+ mSDLCursors[UI_CURSOR_TOOLPAN] = makeSDLCursorFromBMP("lltoolpan.BMP",7,5);
+ mSDLCursors[UI_CURSOR_TOOLZOOMIN] = makeSDLCursorFromBMP("lltoolzoomin.BMP",7,5);
+ mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] = makeSDLCursorFromBMP("toolpickobject3.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLSIT] = makeSDLCursorFromBMP("toolsit.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLBUY] = makeSDLCursorFromBMP("toolbuy.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLPAY] = makeSDLCursorFromBMP("toolpay.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLOPEN] = makeSDLCursorFromBMP("toolopen.BMP",0,0);
+ mSDLCursors[UI_CURSOR_PIPETTE] = makeSDLCursorFromBMP("lltoolpipette.BMP",2,28);
+}
+
+void LLWindowSDL::quitCursors()
+{
+ int i;
+ if (mWindow)
+ {
+ for (i=0; i<UI_CURSOR_COUNT; ++i)
+ {
+ if (mSDLCursors[i])
+ {
+ SDL_FreeCursor(mSDLCursors[i]);
+ mSDLCursors[i] = NULL;
+ }
+ }
+ } else {
+ // SDL doesn't refcount cursors, so if the window has
+ // already been destroyed then the cursors have gone with it.
+ llinfos << "Skipping quitCursors: mWindow already gone." << llendl;
+ for (i=0; i<UI_CURSOR_COUNT; ++i)
+ mSDLCursors[i] = NULL;
+ }
+}
+
+void LLWindowSDL::captureMouse()
+{
+ // SDL already enforces the semantics that captureMouse is
+ // used for, i.e. that we continue to get mouse events as long
+ // as a button is down regardless of whether we left the
+ // window, and in a less obnoxious way than SDL_WM_GrabInput
+ // which would confine the cursor to the window too.
+
+ //llinfos << "LLWindowSDL::captureMouse" << llendl;
+}
+
+void LLWindowSDL::releaseMouse()
+{
+ // see LWindowSDL::captureMouse()
+
+ //llinfos << "LLWindowSDL::releaseMouse" << llendl;
+}
+
+void LLWindowSDL::hideCursor()
+{
+ if(!mCursorHidden)
+ {
+ // llinfos << "hideCursor: hiding" << llendl;
+ mCursorHidden = TRUE;
+ mHideCursorPermanent = TRUE;
+ SDL_ShowCursor(0);
+ }
+ else
+ {
+ // llinfos << "hideCursor: already hidden" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowSDL::showCursor()
+{
+ if(mCursorHidden)
+ {
+ // llinfos << "showCursor: showing" << llendl;
+ mCursorHidden = FALSE;
+ mHideCursorPermanent = FALSE;
+ SDL_ShowCursor(1);
+ }
+ else
+ {
+ // llinfos << "showCursor: already visible" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowSDL::showCursorFromMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ showCursor();
+ }
+}
+
+void LLWindowSDL::hideCursorUntilMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ hideCursor();
+ mHideCursorPermanent = FALSE;
+ }
+}
+
+
+
+//
+// LLSplashScreenSDL
+//
+LLSplashScreenSDL::LLSplashScreenSDL()
+{
+}
+
+LLSplashScreenSDL::~LLSplashScreenSDL()
+{
+}
+
+void LLSplashScreenSDL::showImpl()
+{
+}
+
+void LLSplashScreenSDL::updateImpl(const char* mesg)
+{
+}
+
+
+void LLSplashScreenSDL::hideImpl()
+{
+}
+
+
+
+#if LL_GTK
+static void response_callback (GtkDialog *dialog,
+ gint arg1,
+ gpointer user_data)
+{
+ gint *response = (gint*)user_data;
+ *response = arg1;
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ gtk_main_quit();
+}
+
+S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
+{
+ S32 rtn = OSBTN_CANCEL;
+
+#if LL_GTK
+ maybe_do_gtk_diagnostics();
+#endif // LL_GTK
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->beforeDialog();
+
+ gtk_disable_setlocale();
+ if (gtk_init_check(NULL, NULL)
+ // We can NOT expect to combine GTK and SDL's aggressive fullscreen
+ && ((NULL==gWindowImplementation) || (!was_fullscreen))
+ )
+ {
+ GtkWidget *win = NULL;
+
+ llinfos << "Creating a dialog because we're in windowed mode and GTK is happy." << llendl;
+
+ GtkDialogFlags flags = GTK_DIALOG_MODAL;
+ GtkMessageType messagetype;
+ GtkButtonsType buttons;
+ switch (type)
+ {
+ default:
+ case OSMB_OK:
+ messagetype = GTK_MESSAGE_WARNING;
+ buttons = GTK_BUTTONS_OK;
+ break;
+ case OSMB_OKCANCEL:
+ messagetype = GTK_MESSAGE_QUESTION;
+ buttons = GTK_BUTTONS_OK_CANCEL;
+ break;
+ case OSMB_YESNO:
+ messagetype = GTK_MESSAGE_QUESTION;
+ buttons = GTK_BUTTONS_YES_NO;
+ break;
+ }
+ win = gtk_message_dialog_new(NULL,
+ flags, messagetype, buttons,
+ text);
+
+# if LL_X11
+ // Make GTK tell the window manager to associate this
+ // dialog with our non-GTK SDL window, which should try
+ // to keep it on top etc.
+ if (SDL_XWindowID != None)
+ {
+ gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
+ GdkWindow *gdkwin = gdk_window_foreign_new(SDL_XWindowID);
+ gdk_window_set_transient_for(GTK_WIDGET(win)->window,
+ gdkwin);
+ }
+# endif //LL_X11
+
+ gtk_window_set_position(GTK_WINDOW(win),
+ GTK_WIN_POS_CENTER_ON_PARENT);
+
+ gtk_window_set_type_hint(GTK_WINDOW(win),
+ GDK_WINDOW_TYPE_HINT_DIALOG);
+
+ if (caption)
+ gtk_window_set_title(GTK_WINDOW(win), caption);
+
+ gint response = GTK_RESPONSE_NONE;
+ g_signal_connect (win,
+ "response",
+ G_CALLBACK (response_callback),
+ &response);
+
+ // we should be able to us a gtk_dialog_run(), but it's
+ // apparently not written to exist in a world without a higher
+ // gtk_main(), so we manage its signal/destruction outselves.
+ gtk_widget_show_all (win);
+ gtk_main();
+
+ //llinfos << "response: " << response << llendl;
+ switch (response)
+ {
+ case GTK_RESPONSE_OK: rtn = OSBTN_OK; break;
+ case GTK_RESPONSE_YES: rtn = OSBTN_YES; break;
+ case GTK_RESPONSE_NO: rtn = OSBTN_NO; break;
+ case GTK_RESPONSE_APPLY: rtn = OSBTN_OK; break;
+ case GTK_RESPONSE_NONE:
+ case GTK_RESPONSE_CANCEL:
+ case GTK_RESPONSE_CLOSE:
+ case GTK_RESPONSE_DELETE_EVENT:
+ default: rtn = OSBTN_CANCEL;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "MSGBOX: %s: %s\n", caption, text);
+ llinfos << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << llendl;
+ rtn = OSBTN_OK;
+ }
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->afterDialog();
+
+ return rtn;
+}
+
+static void color_changed_callback(GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION(widget);
+ GdkColor *colorp = (GdkColor*)user_data;
+
+ gtk_color_selection_get_current_color(colorsel, colorp);
+}
+
+BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+{
+ BOOL rtn = FALSE;
+
+ beforeDialog();
+
+ gtk_disable_setlocale();
+ if (gtk_init_check(NULL, NULL)
+ // We can NOT expect to combine GTK and SDL's aggressive fullscreen
+ && !was_fullscreen
+ )
+ {
+ GtkWidget *win = NULL;
+
+ win = gtk_color_selection_dialog_new(NULL);
+
+# if LL_X11
+ // Get GTK to tell the window manager to associate this
+ // dialog with our non-GTK SDL window, which should try
+ // to keep it on top etc.
+ if (SDL_XWindowID != None)
+ {
+ gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
+ GdkWindow *gdkwin = gdk_window_foreign_new(SDL_XWindowID);
+ gdk_window_set_transient_for(GTK_WIDGET(win)->window,
+ gdkwin);
+ }
+# endif //LL_X11
+
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG(win)->colorsel);
+
+ GdkColor color, orig_color;
+ orig_color.red = guint16(65535 * *r);
+ orig_color.green= guint16(65535 * *g);
+ orig_color.blue = guint16(65535 * *b);
+ color = orig_color;
+
+ gtk_color_selection_set_previous_color (colorsel, &color);
+ gtk_color_selection_set_current_color (colorsel, &color);
+ gtk_color_selection_set_has_palette (colorsel, TRUE);
+ gtk_color_selection_set_has_opacity_control(colorsel, FALSE);
+
+ gint response = GTK_RESPONSE_NONE;
+ g_signal_connect (win,
+ "response",
+ G_CALLBACK (response_callback),
+ &response);
+
+ g_signal_connect (G_OBJECT (colorsel), "color_changed",
+ G_CALLBACK (color_changed_callback),
+ &color);
+
+ gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+ gtk_widget_show_all(win);
+ // hide the help button - we don't service it.
+ gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(win)->help_button);
+ gtk_main();
+
+ if (response == GTK_RESPONSE_OK &&
+ (orig_color.red != color.red
+ || orig_color.green != color.green
+ || orig_color.blue != color.blue) )
+ {
+ *r = color.red / 65535.0f;
+ *g = color.green / 65535.0f;
+ *b = color.blue / 65535.0f;
+ rtn = TRUE;
+ }
+ }
+
+ afterDialog();
+
+ return rtn;
+}
+#else
+S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
+{
+ fprintf(stderr, "MSGBOX: %s: %s\n", caption, text);
+ return 0;
+}
+
+BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+{
+ return (FALSE);
+}
+#endif // LL_GTK
+
+// Open a URL with the user's default web browser.
+// Must begin with protocol identifier.
+void spawn_web_browser(const char* escaped_url)
+{
+ llinfos << "spawn_web_browser: " << escaped_url << llendl;
+
+#if LL_LINUX
+# if LL_X11
+ if (SDL_Display) // Just in case - before forking.
+ XSync(SDL_Display, False);
+# endif // LL_X11
+
+ std::string cmd;
+ cmd = gDirUtilp->getAppRODataDir().c_str();
+ cmd += gDirUtilp->getDirDelimiter().c_str();
+ cmd += "launch_url.sh";
+ char* const argv[] = {(char*)cmd.c_str(), (char*)escaped_url, 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);
+ // end ourself by running the command
+ execv(cmd.c_str(), argv);
+ // if execv returns at all, there was a problem.
+ llwarns << "execv failure when trying to start " << cmd << llendl;
+ _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 {
+ llwarns << "fork failure." << llendl;
+ }
+ }
+#endif // LL_LINUX
+
+ llinfos << "spawn_web_browser returning." << llendl;
+}
+
+void shell_open( const char* file_path )
+{
+ // !!! FIXME:
+ fprintf(stderr, "shell_open: %s\n", file_path);
+}
+
+void *LLWindowSDL::getPlatformWindow()
+{
+#if LL_X11
+ // pointer to our static raw X window
+ return (void*)&SDL_XWindowID;
+#else
+ // doubt we really want to return a high-level SDL structure here.
+ return NULL;
+#endif
+}
+
+void LLWindowSDL::bringToFront()
+{
+ // !!! FIXME:
+ fprintf(stderr, "bringToFront\n");
+}
+
+#endif // LL_SDL
diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h
new file mode 100644
index 0000000000..704262061a
--- /dev/null
+++ b/indra/llwindow/llwindowsdl.h
@@ -0,0 +1,198 @@
+/**
+ * @file llwindowsdl.h
+ * @brief SDL implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWSDL_H
+#define LL_LLWINDOWSDL_H
+
+// Simple Directmedia Layer (http://libsdl.org/) implementation of LLWindow class
+
+#include "llwindow.h"
+
+#include "SDL/SDL.h"
+
+#if LL_X11
+// get X11-specific headers for use in low-level stuff like copy-and-paste support
+#include "SDL/SDL_syswm.h"
+#endif
+
+// AssertMacros.h does bad things.
+#undef verify
+#undef check
+#undef require
+
+
+class LLWindowSDL : 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*/ BOOL getFullscreen();
+ /*virtual*/ BOOL getPosition(LLCoordScreen *position);
+ /*virtual*/ BOOL getSize(LLCoordScreen *size);
+ /*virtual*/ BOOL getSize(LLCoordWindow *size);
+ /*virtual*/ BOOL setPosition(LLCoordScreen position);
+ /*virtual*/ BOOL setSize(LLCoordScreen size);
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync);
+ /*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
+ /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
+ /*virtual*/ void showCursor();
+ /*virtual*/ void hideCursor();
+ /*virtual*/ void showCursorFromMouseMove();
+ /*virtual*/ void hideCursorUntilMouseMove();
+ /*virtual*/ BOOL isCursorHidden();
+ /*virtual*/ void setCursor(ECursorType cursor);
+ /*virtual*/ ECursorType getCursor();
+ /*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*/ BOOL restoreGamma(); // Restore original gamma table (before updating gamma)
+ /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput();
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName();
+ /*virtual*/ void deleteFile( const char* file_name );
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info );
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL);
+
+ /*virtual*/ void delayInputProcessing() { };
+
+ // 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*/ void beforeDialog();
+ /*virtual*/ void afterDialog();
+
+ /*virtual*/ BOOL dialog_color_picker(F32 *r, F32 *g, F32 *b);
+
+ /*virtual*/ void *getPlatformWindow();
+ /*virtual*/ void bringToFront();
+
+protected:
+ LLWindowSDL(
+ char *title, int x, int y, int width, int height, U32 flags,
+ BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth);
+ ~LLWindowSDL();
+
+ void initCursors();
+ void quitCursors();
+ BOOL isValid();
+ void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
+
+
+ // Changes display resolution. Returns true if successful
+ BOOL setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
+
+ // Go back to last fullscreen display resolution.
+ BOOL setFullscreenResolution();
+
+ void minimize();
+ void restore();
+
+ BOOL shouldPostQuit() { return mPostQuit; }
+
+
+protected:
+ //
+ // Platform specific methods
+ //
+
+ // create or re-create the GL context/window. Called from the constructor and switchContext().
+ BOOL createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync);
+ void destroyContext();
+ void setupFailure(const char* text, const char* caption, U32 type);
+ void adjustCursorDecouple(bool warpingMouse = false);
+ void fixWindowSize(void);
+ U32 SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain);
+ BOOL SDLReallyCaptureInput(BOOL capture);
+
+ //
+ // Platform specific variables
+ //
+ U32 mGrabbyKeyFlags;
+ int mReallyCapturedCount;
+ SDL_Surface * mWindow;
+ char * mWindowTitle;
+ double mOriginalAspectRatio;
+ BOOL mCursorDecoupled;
+ S32 mCursorLastEventDeltaX;
+ S32 mCursorLastEventDeltaY;
+ BOOL mCursorIgnoreNextDelta;
+ BOOL mNeedsResize; // Constructor figured out the window is too big, it needs a resize.
+ LLCoordScreen mNeedsResizeSize;
+ F32 mOverrideAspectRatio;
+ F32 mGamma;
+
+ int mSDLFlags;
+
+ SDL_Cursor* mSDLCursors[UI_CURSOR_COUNT];
+ int mHaveInputFocus; /* 0=no, 1=yes, else unknown */
+ int mIsMinimized; /* 0=no, 1=yes, else unknown */
+
+ friend class LLWindowManager;
+
+#if LL_X11
+private:
+ // These are set up by the X11 clipboard initialization code
+ Window mSDL_XWindowID;
+ void (*Lock_Display)(void);
+ void (*Unlock_Display)(void);
+ // more X11 clipboard stuff
+ int init_x11clipboard(void);
+ void quit_x11clipboard(void);
+ int is_empty_x11clipboard(void);
+ void put_x11clipboard(int type, int srclen, const char *src);
+ void get_x11clipboard(int type, int *dstlen, char **dst);
+ void x11_set_urgent(BOOL urgent);
+ BOOL mFlashing;
+ LLTimer mFlashTimer;
+#endif //LL_X11
+
+
+};
+
+
+class LLSplashScreenSDL : public LLSplashScreen
+{
+public:
+ LLSplashScreenSDL();
+ virtual ~LLSplashScreenSDL();
+
+ /*virtual*/ void showImpl();
+ /*virtual*/ void updateImpl(const char* mesg);
+ /*virtual*/ void hideImpl();
+};
+
+S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type);
+
+void load_url_external(const char* url);
+void shell_open( const char* file_path );
+
+#endif //LL_LLWINDOWSDL_H
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
new file mode 100644
index 0000000000..ad56b97577
--- /dev/null
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -0,0 +1,3247 @@
+/**
+ * @file llwindowwin32.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#if LL_WINDOWS && !LL_MESA_HEADLESS
+
+#include <commdlg.h>
+#include <WinUser.h>
+#include <mapi.h>
+#include <process.h> // for _spawn
+#include <shellapi.h>
+
+// Require DirectInput version 8
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+#include "llwindowwin32.h"
+#include "llkeyboardwin32.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llstring.h"
+#include "lldir.h"
+
+#include "llglheaders.h"
+
+#include "indra_constants.h"
+
+// culled from winuser.h
+const S32 WM_MOUSEWHEEL = 0x020A;
+const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
+const S32 MAX_MESSAGE_PER_UPDATE = 20;
+const S32 BITS_PER_PIXEL = 32;
+const S32 MAX_NUM_RESOLUTIONS = 32;
+const F32 ICON_FLASH_TIME = 0.5f;
+
+extern BOOL gDebugWindowProc;
+
+LPWSTR gIconResource = IDI_APPLICATION;
+
+LLW32MsgCallback gAsyncMsgCallback = NULL;
+
+//
+// LLWindowWin32
+//
+
+void show_window_creation_error(const char* title)
+{
+ llwarns << title << llendl;
+ shell_open( "help/window_creation_error.html");
+ /*
+ OSMessageBox(
+ "Second Life is unable to run because it can't set up your display.\n"
+ "We need to be able to make a 32-bit color window at 1024x768, with\n"
+ "an 8 bit alpha channel.\n"
+ "\n"
+ "First, be sure your monitor is set to True Color (32-bit) in\n"
+ "Start -> Control Panels -> Display -> Settings.\n"
+ "\n"
+ "Otherwise, this may be due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "ATI drivers are available at http://www.ati.com/\n"
+ "nVidia drivers are available at http://www.nvidia.com/\n"
+ "\n"
+ "If you continue to receive this message, contact customer service.",
+ title,
+ OSMB_OK);
+ */
+}
+
+BOOL check_for_card(const char* RENDERER, const char* bad_card)
+{
+ if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
+ {
+ char buffer[1024];
+ sprintf(buffer,
+ "Your video card appears to be a %s, which Second Life does not support.\n"
+ "\n"
+ "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
+ "multitexture support. We explicitly support nVidia GeForce 2 or better, \n"
+ "and ATI Radeon 8500 or better.\n"
+ "\n"
+ "If you own a supported card and continue to receive this message, try \n"
+ "updating to the latest video card drivers. Otherwise look in the\n"
+ "secondlife.com support section or e-mail technical support\n"
+ "\n"
+ "You can try to run Second Life, but it will probably crash or run\n"
+ "very slowly. Try anyway?",
+ bad_card);
+ S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
+ if (OSBTN_YES == button)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+//static
+BOOL LLWindowWin32::sIsClassRegistered = FALSE;
+
+
+
+LPDIRECTINPUT8 g_pDI = NULL;
+LPDIRECTINPUTDEVICE8 g_pJoystick = NULL;
+BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
+ VOID* pContext );
+BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
+ VOID* pContext );
+
+
+LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width,
+ S32 height, U32 flags,
+ BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags)
+{
+ mIconResource = gIconResource;
+ mOverrideAspectRatio = 0.f;
+ mNativeAspectRatio = 0.f;
+ mMousePositionModified = FALSE;
+ mInputProcessingPaused = FALSE;
+
+ // Initialize the keyboard
+ gKeyboard = new LLKeyboardWin32();
+
+ GLuint pixel_format;
+ WNDCLASS wc;
+ DWORD dw_ex_style;
+ DWORD dw_style;
+ RECT window_rect;
+
+ // Set the window title
+ if (!title)
+ {
+ mWindowTitle = new WCHAR[50];
+ wsprintf(mWindowTitle, L"OpenGL Window");
+ }
+ else
+ {
+ mWindowTitle = new WCHAR[256]; // Assume title length < 255 chars.
+ mbstowcs(mWindowTitle, title, 255);
+ mWindowTitle[255] = 0;
+ }
+
+ // Set the window class name
+ if (!name)
+ {
+ mWindowClassName = new WCHAR[50];
+ wsprintf(mWindowClassName, L"OpenGL Window");
+ }
+ else
+ {
+ mWindowClassName = new WCHAR[256]; // Assume title length < 255 chars.
+ mbstowcs(mWindowClassName, name, 255);
+ mWindowClassName[255] = 0;
+ }
+
+
+ // We're not clipping yet
+ SetRect( &mOldMouseClip, 0, 0, 0, 0 );
+
+ // Make an instance of our window then define the window class
+ mhInstance = GetModuleHandle(NULL);
+ mWndProc = NULL;
+
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+
+ // No WPARAM yet.
+ mLastSizeWParam = 0;
+
+ // Windows GDI rects don't include rightmost pixel
+ window_rect.left = (long) 0;
+ window_rect.right = (long) width;
+ window_rect.top = (long) 0;
+ window_rect.bottom = (long) height;
+
+ // Grab screen size to sanitize the window
+ S32 window_border_y = GetSystemMetrics(SM_CYBORDER);
+ S32 virtual_screen_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ S32 virtual_screen_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ S32 virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ S32 virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+
+ if (x < virtual_screen_x) x = virtual_screen_x;
+ if (y < virtual_screen_y - window_border_y) y = virtual_screen_y - window_border_y;
+
+ if (x + width > virtual_screen_x + virtual_screen_width) x = virtual_screen_x + virtual_screen_width - width;
+ if (y + height > virtual_screen_y + virtual_screen_height) y = virtual_screen_y + virtual_screen_height - height;
+
+ if (!sIsClassRegistered)
+ {
+ // Force redraw when resized and create a private device context
+
+ // Makes double click messages.
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
+
+ // Set message handler function
+ wc.lpfnWndProc = (WNDPROC) mainWindowProc;
+
+ // unused
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+
+ wc.hInstance = mhInstance;
+ wc.hIcon = LoadIcon(mhInstance, mIconResource);
+
+ // We will set the cursor ourselves
+ wc.hCursor = NULL;
+
+ // background color is not used
+ if (clearBg)
+ {
+ wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
+ }
+ else
+ {
+ wc.hbrBackground = (HBRUSH) NULL;
+ }
+
+ // we don't use windows menus
+ wc.lpszMenuName = NULL;
+
+ wc.lpszClassName = mWindowClassName;
+
+ if (!RegisterClass(&wc))
+ {
+ OSMessageBox("RegisterClass failed", "Error", OSMB_OK);
+ return;
+ }
+ sIsClassRegistered = TRUE;
+ }
+
+ //-----------------------------------------------------------------------
+ // Get the current refresh rate
+ //-----------------------------------------------------------------------
+
+ DEVMODE dev_mode;
+ DWORD current_refresh;
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
+ {
+ current_refresh = dev_mode.dmDisplayFrequency;
+ mNativeAspectRatio = ((F32)dev_mode.dmPelsWidth) / ((F32)dev_mode.dmPelsHeight);
+ }
+ else
+ {
+ current_refresh = 60;
+ }
+
+ //-----------------------------------------------------------------------
+ // Drop resolution and go fullscreen
+ // use a display mode with our desired size and depth, with a refresh
+ // rate as close at possible to the users' default
+ //-----------------------------------------------------------------------
+ if (mFullscreen)
+ {
+ BOOL success = FALSE;
+ DWORD closest_refresh = 0;
+
+ for (S32 mode_num = 0;; mode_num++)
+ {
+ if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
+ {
+ break;
+ }
+
+ if (dev_mode.dmPelsWidth == width &&
+ dev_mode.dmPelsHeight == height &&
+ dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
+ {
+ success = TRUE;
+ if ((dev_mode.dmDisplayFrequency - current_refresh)
+ < (closest_refresh - current_refresh))
+ {
+ closest_refresh = dev_mode.dmDisplayFrequency;
+ }
+ }
+ }
+
+ if (closest_refresh == 0)
+ {
+ llwarns << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << llendl;
+ success = FALSE;
+ }
+
+ // If we found a good resolution, use it.
+ if (success)
+ {
+ success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
+ }
+
+ // Keep a copy of the actual current device mode in case we minimize
+ // and change the screen resolution. JC
+ EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
+
+ // If it failed, we don't want to run fullscreen
+ if (success)
+ {
+ mFullscreen = TRUE;
+ mFullscreenWidth = dev_mode.dmPelsWidth;
+ mFullscreenHeight = dev_mode.dmPelsHeight;
+ mFullscreenBits = dev_mode.dmBitsPerPel;
+ mFullscreenRefresh = dev_mode.dmDisplayFrequency;
+
+ llinfos << "Running at " << dev_mode.dmPelsWidth
+ << "x" << dev_mode.dmPelsHeight
+ << "x" << dev_mode.dmBitsPerPel
+ << " @ " << dev_mode.dmDisplayFrequency
+ << llendl;
+ }
+ else
+ {
+ mFullscreen = FALSE;
+ mFullscreenWidth = -1;
+ mFullscreenHeight = -1;
+ mFullscreenBits = -1;
+ mFullscreenRefresh = -1;
+
+ char error[256];
+ sprintf(error, "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
+ OSMessageBox(error, "Error", OSMB_OK);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ // Resize window to account for borders
+ //-----------------------------------------------------------------------
+ if (mFullscreen)
+ {
+ dw_ex_style = WS_EX_APPWINDOW;
+ dw_style = WS_POPUP;
+
+ // Move window borders out not to cover window contents
+ AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style);
+ }
+ else
+ {
+ // Window with an edge
+ dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
+ dw_style = WS_OVERLAPPEDWINDOW;
+ }
+
+ //-----------------------------------------------------------------------
+ // Create the window
+ // Microsoft help indicates that GL windows must be created with
+ // WS_CLIPSIBLINGS and WS_CLIPCHILDREN, but not CS_PARENTDC
+ //-----------------------------------------------------------------------
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ x, // x pos
+ y, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ if (!mWindowHandle)
+ {
+ DestroyWindow(mWindowHandle);
+ OSMessageBox("Window creation error", "Error", OSMB_OK);
+ return;
+ }
+
+ // TODO: add this after resolving _WIN32_WINNT issue
+ // if (!fullscreen)
+ // {
+ // TRACKMOUSEEVENT track_mouse_event;
+ // track_mouse_event.cbSize = sizeof( TRACKMOUSEEVENT );
+ // track_mouse_event.dwFlags = TME_LEAVE;
+ // track_mouse_event.hwndTrack = mWindowHandle;
+ // track_mouse_event.dwHoverTime = HOVER_DEFAULT;
+ // TrackMouseEvent( &track_mouse_event );
+ // }
+
+
+
+ S32 pfdflags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+ if (use_gl)
+ {
+ pfdflags |= PFD_SUPPORT_OPENGL;
+ }
+
+ //-----------------------------------------------------------------------
+ // Create GL drawing context
+ //-----------------------------------------------------------------------
+ PIXELFORMATDESCRIPTOR pfd =
+ {
+ sizeof(PIXELFORMATDESCRIPTOR),
+ 1,
+ pfdflags,
+ PFD_TYPE_RGBA,
+ BITS_PER_PIXEL,
+ 0, 0, 0, 0, 0, 0, // RGB bits and shift, unused
+ 8, // alpha bits
+ 0, // alpha shift
+ 0, // accum bits
+ 0, 0, 0, 0, // accum RGBA
+ 24, // depth bits
+ 8, // stencil bits, avi added for stencil test
+ 0,
+ PFD_MAIN_PLANE,
+ 0,
+ 0, 0, 0
+ };
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox("Can't make GL device context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!(pixel_format = ChoosePixelFormat(mhDC, &pfd)))
+ {
+ close();
+ OSMessageBox("Can't find suitable pixel format", "Error", OSMB_OK);
+ return;
+ }
+
+ // Verify what pixel format we actually received.
+ if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd))
+ {
+ close();
+ OSMessageBox("Can't get pixel format description", "Error", OSMB_OK);
+ return;
+ }
+
+ // sanity check pfd returned by Windows
+ if (!ignore_pixel_depth && (pfd.cColorBits < 32))
+ {
+ close();
+ OSMessageBox(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ if (!ignore_pixel_depth && (pfd.cAlphaBits < 8))
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox("Can't set pixel format", "Error", OSMB_OK);
+ return;
+ }
+
+ if (use_gl)
+ {
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK);
+ return;
+ }
+
+ // Check for some explicitly unsupported cards.
+ const char* RENDERER = (const char*) glGetString(GL_RENDERER);
+
+ const char* CARD_LIST[] =
+ { "RAGE 128",
+ "RIVA TNT2",
+ "Intel 810",
+ "3Dfx/Voodoo3",
+ "Radeon 7000",
+ "Radeon 7200",
+ "Radeon 7500",
+ "Radeon DDR",
+ "Radeon VE",
+ "GDI Generic" };
+ const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
+
+ // Future candidates:
+ // ProSavage/Twister
+ // SuperSavage
+
+ S32 i;
+ for (i = 0; i < CARD_COUNT; i++)
+ {
+ if (check_for_card(RENDERER, CARD_LIST[i]))
+ {
+ close();
+ shell_open( "help/unsupported_card.html" );
+ return;
+ }
+ }
+
+ gGLManager.initWGL();
+
+ if (gGLManager.mHasWGLARBPixelFormat && (wglChoosePixelFormatARB != NULL))
+ {
+ // OK, at this point, use the ARB wglChoosePixelFormatsARB function to see if we
+ // can get exactly what we want.
+ GLint attrib_list[256];
+ S32 cur_attrib = 0;
+
+ attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_ACCELERATION_ARB;
+ attrib_list[cur_attrib++] = WGL_FULL_ACCELERATION_ARB;
+
+ attrib_list[cur_attrib++] = WGL_SUPPORT_OPENGL_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_DOUBLE_BUFFER_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_COLOR_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_RED_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_GREEN_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_BLUE_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ // End the list
+ attrib_list[cur_attrib++] = 0;
+
+ GLint pixel_formats[256];
+ U32 num_formats = 0;
+
+ // First we try and get a 32 bit depth pixel format
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit");
+ return;
+ }
+
+ if (!num_formats)
+ {
+ llinfos << "No 32 bit z-buffer, trying 24 bits instead" << llendl;
+ // Try 24-bit format
+ attrib_list[1] = 24;
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 24-bit");
+ return;
+ }
+
+ if (!num_formats)
+ {
+ llwarns << "Couldn't get 24 bit z-buffer,trying 16 bits instead!" << llendl;
+ attrib_list[1] = 16;
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result || !num_formats)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 16-bit");
+ return;
+ }
+ }
+
+ llinfos << "Choosing pixel formats: " << num_formats << " pixel formats returned" << llendl;
+
+ pixel_format = pixel_formats[0];
+ }
+
+ DestroyWindow(mWindowHandle);
+
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ x, // x pos
+ y, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox("Can't make GL device context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox("Can't set pixel format", "Error", OSMB_OK);
+ return;
+ }
+
+ int swap_method = 0;
+ GLint swap_query = WGL_SWAP_METHOD_ARB;
+
+ if (wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method))
+ {
+ switch (swap_method)
+ {
+ case WGL_SWAP_EXCHANGE_ARB:
+ mSwapMethod = SWAP_METHOD_EXCHANGE;
+ llinfos << "Swap Method: Exchange" << llendl;
+ break;
+ case WGL_SWAP_COPY_ARB:
+ mSwapMethod = SWAP_METHOD_COPY;
+ llinfos << "Swap Method: Copy" << llendl;
+ break;
+ case WGL_SWAP_UNDEFINED_ARB:
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+ llinfos << "Swap Method: Undefined" << llendl;
+ break;
+ default:
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+ llinfos << "Swap Method: Unknown" << llendl;
+ break;
+ }
+ }
+ }
+ else
+ {
+ llwarns << "No wgl_ARB_pixel_format extension, using default ChoosePixelFormat!" << llendl;
+ }
+
+ // Verify what pixel format we actually received.
+ if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd))
+ {
+ close();
+ OSMessageBox("Can't get pixel format description", "Error", OSMB_OK);
+ return;
+ }
+ llinfos << "GL buffer: Color Bits " << S32(pfd.cColorBits)
+ << " Alpha Bits " << S32(pfd.cAlphaBits)
+ << " Depth Bits " << S32(pfd.cDepthBits)
+ << llendl;
+
+ if (pfd.cColorBits < 32)
+ {
+ close();
+ OSMessageBox(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ if (pfd.cAlphaBits < 8)
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!gGLManager.initGL())
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because your video card drivers\n"
+ "are out of date or unsupported. Please make sure you have\n"
+ "the latest video card drivers installed.\n\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ // Disable vertical sync for swap
+ if (disable_vsync && wglSwapIntervalEXT)
+ {
+ llinfos << "Disabling vertical sync" << llendl;
+ wglSwapIntervalEXT(0);
+ }
+ else
+ {
+ llinfos << "Keeping vertical sync" << llendl;
+ }
+
+
+ // OK, let's get the current gamma information and store it off.
+ mCurrentGamma = 0.f; // Not set, default;
+ if (!GetDeviceGammaRamp(mhDC, mPrevGammaRamp))
+ {
+ llwarns << "Unable to get device gamma ramp" << llendl;
+ }
+
+ // Calculate what the current gamma is. From a posting by Garrett T. Bass, Get/SetDeviceGammaRamp Demystified
+ // http://apollo.iwt.uni-bielefeld.de/~ml_robot/OpenGL-04-2000/0058.html
+
+ // We're going to assume that gamma's the same for all 3 channels, because I don't feel like doing it otherwise.
+ // Using the red channel.
+
+ F32 Csum = 0.0;
+ S32 Ccount = 0;
+ for (i = 0; i < 256; i++)
+ {
+ if (i != 0 && mPrevGammaRamp[i] != 0 && mPrevGammaRamp[i] != 65536)
+ {
+ F64 B = (i % 256) / 256.0;
+ F64 A = mPrevGammaRamp[i] / 65536.0;
+ F32 C = (F32) ( log(A) / log(B) );
+ Csum += C;
+ Ccount++;
+ }
+ }
+ mCurrentGamma = Csum / Ccount;
+
+ llinfos << "Previous gamma: " << mCurrentGamma << llendl;
+ }
+
+
+ //store this pointer for wndProc callback
+ SetWindowLong(mWindowHandle, GWL_USERDATA, (U32)this);
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+
+ // Direct Input
+ HRESULT hr;
+
+ if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION,
+ IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
+ {
+ llwarns << "Direct8InputCreate failed!" << llendl;
+ }
+ else
+ {
+ while(1)
+ {
+ // Look for a simple joystick we can use for this sample program.
+ if (FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL,
+ EnumJoysticksCallback,
+ NULL, DIEDFL_ATTACHEDONLY ) ) )
+ break;
+ if (!g_pJoystick)
+ break;
+ if( FAILED( hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick ) ) )
+ break;
+ if( FAILED( hr = g_pJoystick->EnumObjects( EnumObjectsCallback,
+ (VOID*)mWindowHandle, DIDFT_ALL ) ) )
+ break;
+ g_pJoystick->Acquire();
+ break;
+ }
+ }
+
+ SetTimer( mWindowHandle, 0, 1000 / 30, NULL ); // 30 fps timer
+ mJoyStickState = 0;
+ mJoyButtonState = 0;
+}
+
+
+LLWindowWin32::~LLWindowWin32()
+{
+ delete [] mWindowTitle;
+ mWindowTitle = NULL;
+
+ delete [] mSupportedResolutions;
+ mSupportedResolutions = NULL;
+
+ delete mWindowClassName;
+ mWindowClassName = NULL;
+}
+
+void LLWindowWin32::show()
+{
+ ShowWindow(mWindowHandle, SW_SHOW);
+ SetForegroundWindow(mWindowHandle);
+ SetFocus(mWindowHandle);
+}
+
+void LLWindowWin32::hide()
+{
+ setMouseClipping(FALSE);
+ ShowWindow(mWindowHandle, SW_HIDE);
+}
+
+void LLWindowWin32::minimize()
+{
+ setMouseClipping(FALSE);
+ showCursor();
+ ShowWindow(mWindowHandle, SW_MINIMIZE);
+}
+
+
+void LLWindowWin32::restore()
+{
+ ShowWindow(mWindowHandle, SW_RESTORE);
+ SetForegroundWindow(mWindowHandle);
+ SetFocus(mWindowHandle);
+}
+
+
+// close() destroys all OS-specific code associated with a window.
+// Usually called from LLWindowManager::destroyWindow()
+void LLWindowWin32::close()
+{
+ llinfos << "Closing LLWindowWin32" << llendl;
+ // Is window is already closed?
+ if (!mWindowHandle)
+ {
+ return;
+ }
+
+ // Make sure cursor is visible and we haven't mangled the clipping state.
+ setMouseClipping(FALSE);
+ showCursor();
+
+ // Go back to screen mode written in the registry.
+ if (mFullscreen)
+ {
+ resetDisplayResolution();
+ }
+
+ // Clean up remaining GL state
+ llinfos << "Shutting down GL" << llendl;
+ gGLManager.shutdownGL();
+
+ llinfos << "Releasing Context" << llendl;
+ if (mhRC)
+ {
+ if (!wglMakeCurrent(NULL, NULL))
+ {
+ llwarns << "Release of DC and RC failed" << llendl;
+ }
+
+ if (!wglDeleteContext(mhRC))
+ {
+ llwarns << "Release of rendering context failed" << llendl;
+ }
+
+ mhRC = NULL;
+ }
+
+ // Restore gamma to the system values.
+ restoreGamma();
+
+ if (mhDC && !ReleaseDC(mWindowHandle, mhDC))
+ {
+ llwarns << "Release of ghDC failed" << llendl;
+ mhDC = NULL;
+ }
+
+ llinfos << "Destroying Window" << llendl;
+
+ // Don't process events in our mainWindowProc any longer.
+ SetWindowLong(mWindowHandle, GWL_USERDATA, NULL);
+
+ // Make sure we don't leave a blank toolbar button.
+ ShowWindow(mWindowHandle, SW_HIDE);
+
+ // This causes WM_DESTROY to be sent *immediately*
+ if (!DestroyWindow(mWindowHandle))
+ {
+ OSMessageBox("DestroyWindow(mWindowHandle) failed", "Shutdown Error", OSMB_OK);
+ }
+
+ mWindowHandle = NULL;
+}
+
+BOOL LLWindowWin32::isValid()
+{
+ return (mWindowHandle != NULL);
+}
+
+BOOL LLWindowWin32::getVisible()
+{
+ return (mWindowHandle && IsWindowVisible(mWindowHandle));
+}
+
+BOOL LLWindowWin32::getMinimized()
+{
+ return (mWindowHandle && IsIconic(mWindowHandle));
+}
+
+BOOL LLWindowWin32::getMaximized()
+{
+ return (mWindowHandle && IsZoomed(mWindowHandle));
+}
+
+BOOL LLWindowWin32::maximize()
+{
+ BOOL success = FALSE;
+ if (!mWindowHandle) return success;
+
+ WINDOWPLACEMENT placement;
+ placement.length = sizeof(WINDOWPLACEMENT);
+
+ success = GetWindowPlacement(mWindowHandle, &placement);
+ if (!success) return success;
+
+ placement.showCmd = SW_MAXIMIZE;
+
+ success = SetWindowPlacement(mWindowHandle, &placement);
+ return success;
+}
+
+BOOL LLWindowWin32::getFullscreen()
+{
+ return mFullscreen;
+}
+
+BOOL LLWindowWin32::getPosition(LLCoordScreen *position)
+{
+ RECT window_rect;
+
+ if (!mWindowHandle ||
+ !GetWindowRect(mWindowHandle, &window_rect) ||
+ NULL == position)
+ {
+ return FALSE;
+ }
+
+ position->mX = window_rect.left;
+ position->mY = window_rect.top;
+ return TRUE;
+}
+
+BOOL LLWindowWin32::getSize(LLCoordScreen *size)
+{
+ RECT window_rect;
+
+ if (!mWindowHandle ||
+ !GetWindowRect(mWindowHandle, &window_rect) ||
+ NULL == size)
+ {
+ return FALSE;
+ }
+
+ size->mX = window_rect.right - window_rect.left;
+ size->mY = window_rect.bottom - window_rect.top;
+ return TRUE;
+}
+
+BOOL LLWindowWin32::getSize(LLCoordWindow *size)
+{
+ RECT client_rect;
+
+ if (!mWindowHandle ||
+ !GetClientRect(mWindowHandle, &client_rect) ||
+ NULL == size)
+ {
+ return FALSE;
+ }
+
+ size->mX = client_rect.right - client_rect.left;
+ size->mY = client_rect.bottom - client_rect.top;
+ return TRUE;
+}
+
+BOOL LLWindowWin32::setPosition(const LLCoordScreen position)
+{
+ LLCoordScreen size;
+
+ if (!mWindowHandle)
+ {
+ return FALSE;
+ }
+ getSize(&size);
+ moveWindow(position, size);
+ return TRUE;
+}
+
+BOOL LLWindowWin32::setSize(const LLCoordScreen size)
+{
+ LLCoordScreen position;
+
+ getPosition(&position);
+ if (!mWindowHandle)
+ {
+ return FALSE;
+ }
+
+ moveWindow(position, size);
+ return TRUE;
+}
+
+// changing fullscreen resolution
+BOOL LLWindowWin32::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
+{
+ GLuint pixel_format;
+ DEVMODE dev_mode;
+ DWORD current_refresh;
+ DWORD dw_ex_style;
+ DWORD dw_style;
+ RECT window_rect;
+ S32 width = size.mX;
+ S32 height = size.mY;
+
+ resetDisplayResolution();
+
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
+ {
+ current_refresh = dev_mode.dmDisplayFrequency;
+ }
+ else
+ {
+ current_refresh = 60;
+ }
+
+ gGLManager.shutdownGL();
+ //destroy gl context
+ if (mhRC)
+ {
+ if (!wglMakeCurrent(NULL, NULL))
+ {
+ llwarns << "Release of DC and RC failed" << llendl;
+ }
+
+ if (!wglDeleteContext(mhRC))
+ {
+ llwarns << "Release of rendering context failed" << llendl;
+ }
+
+ mhRC = NULL;
+ }
+
+ if (fullscreen)
+ {
+ mFullscreen = TRUE;
+ BOOL success = FALSE;
+ DWORD closest_refresh = 0;
+
+ for (S32 mode_num = 0;; mode_num++)
+ {
+ if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
+ {
+ break;
+ }
+
+ if (dev_mode.dmPelsWidth == width &&
+ dev_mode.dmPelsHeight == height &&
+ dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
+ {
+ success = TRUE;
+ if ((dev_mode.dmDisplayFrequency - current_refresh)
+ < (closest_refresh - current_refresh))
+ {
+ closest_refresh = dev_mode.dmDisplayFrequency;
+ }
+ }
+ }
+
+ if (closest_refresh == 0)
+ {
+ llwarns << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << llendl;
+ return FALSE;
+ }
+
+ // If we found a good resolution, use it.
+ if (success)
+ {
+ success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
+ }
+
+ // Keep a copy of the actual current device mode in case we minimize
+ // and change the screen resolution. JC
+ EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
+
+ if (success)
+ {
+ mFullscreen = TRUE;
+ mFullscreenWidth = dev_mode.dmPelsWidth;
+ mFullscreenHeight = dev_mode.dmPelsHeight;
+ mFullscreenBits = dev_mode.dmBitsPerPel;
+ mFullscreenRefresh = dev_mode.dmDisplayFrequency;
+
+ llinfos << "Running at " << dev_mode.dmPelsWidth
+ << "x" << dev_mode.dmPelsHeight
+ << "x" << dev_mode.dmBitsPerPel
+ << " @ " << dev_mode.dmDisplayFrequency
+ << llendl;
+
+ window_rect.left = (long) 0;
+ window_rect.right = (long) width; // Windows GDI rects don't include rightmost pixel
+ window_rect.top = (long) 0;
+ window_rect.bottom = (long) height;
+ dw_ex_style = WS_EX_APPWINDOW;
+ dw_style = WS_POPUP;
+
+ // Move window borders out not to cover window contents
+ AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style);
+ }
+ // If it failed, we don't want to run fullscreen
+ else
+ {
+ mFullscreen = FALSE;
+ mFullscreenWidth = -1;
+ mFullscreenHeight = -1;
+ mFullscreenBits = -1;
+ mFullscreenRefresh = -1;
+
+ llinfos << "Unable to run fullscreen at " << width << "x" << height << llendl;
+ llinfos << "Running in window." << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ mFullscreen = FALSE;
+ window_rect.left = (long) 0;
+ window_rect.right = (long) width; // Windows GDI rects don't include rightmost pixel
+ window_rect.top = (long) 0;
+ window_rect.bottom = (long) height;
+ // Window with an edge
+ dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
+ dw_style = WS_OVERLAPPEDWINDOW;
+ }
+
+ // don't post quit messages when destroying old windows
+ mPostQuit = FALSE;
+
+ // create window
+ DestroyWindow(mWindowHandle);
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ window_rect.left, // x pos
+ window_rect.top, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ //-----------------------------------------------------------------------
+ // Create GL drawing context
+ //-----------------------------------------------------------------------
+ static PIXELFORMATDESCRIPTOR pfd =
+ {
+ sizeof(PIXELFORMATDESCRIPTOR),
+ 1,
+ PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+ PFD_TYPE_RGBA,
+ BITS_PER_PIXEL,
+ 0, 0, 0, 0, 0, 0, // RGB bits and shift, unused
+ 8, // alpha bits
+ 0, // alpha shift
+ 0, // accum bits
+ 0, 0, 0, 0, // accum RGBA
+ 24, // depth bits
+ 8, // stencil bits, avi added for stencil test
+ 0,
+ PFD_MAIN_PLANE,
+ 0,
+ 0, 0, 0
+ };
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox("Can't make GL device context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!(pixel_format = ChoosePixelFormat(mhDC, &pfd)))
+ {
+ close();
+ OSMessageBox("Can't find suitable pixel format", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ // Verify what pixel format we actually received.
+ if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd))
+ {
+ close();
+ OSMessageBox("Can't get pixel format description", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (pfd.cColorBits < 32)
+ {
+ close();
+ OSMessageBox(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (pfd.cAlphaBits < 8)
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox("Can't set pixel format", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ gGLManager.initWGL();
+
+ if (wglChoosePixelFormatARB)
+ {
+ // OK, at this point, use the ARB wglChoosePixelFormatsARB function to see if we
+ // can get exactly what we want.
+ GLint attrib_list[256];
+ S32 cur_attrib = 0;
+
+ attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_ACCELERATION_ARB;
+ attrib_list[cur_attrib++] = WGL_FULL_ACCELERATION_ARB;
+
+ attrib_list[cur_attrib++] = WGL_SUPPORT_OPENGL_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_DOUBLE_BUFFER_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_COLOR_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_RED_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_GREEN_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_BLUE_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ // End the list
+ attrib_list[cur_attrib++] = 0;
+
+ GLint pixel_formats[256];
+ U32 num_formats = 0;
+
+ // First we try and get a 32 bit depth pixel format
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit");
+ return FALSE;
+ }
+
+ if (!num_formats)
+ {
+ llinfos << "No 32 bit z-buffer, trying 24 bits instead" << llendl;
+ // Try 24-bit format
+ attrib_list[1] = 24;
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 24-bit");
+ return FALSE;
+ }
+
+ if (!num_formats)
+ {
+ llwarns << "Couldn't get 24 bit z-buffer,trying 16 bits instead!" << llendl;
+ attrib_list[1] = 16;
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result || !num_formats)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 16-bit");
+ return FALSE;
+ }
+ }
+
+ llinfos << "Choosing pixel formats: " << num_formats << " pixel formats returned" << llendl;
+
+ pixel_format = pixel_formats[0];
+ }
+
+ DestroyWindow(mWindowHandle);
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ window_rect.left, // x pos
+ window_rect.top, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox("Can't make GL device context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox("Can't set pixel format", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ int swap_method = 0;
+ GLint swap_query = WGL_SWAP_METHOD_ARB;
+
+ if (wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method))
+ {
+ switch (swap_method)
+ {
+ case WGL_SWAP_EXCHANGE_ARB:
+ mSwapMethod = SWAP_METHOD_EXCHANGE;
+ llinfos << "Swap Method: Exchange" << llendl;
+ break;
+ case WGL_SWAP_COPY_ARB:
+ mSwapMethod = SWAP_METHOD_COPY;
+ llinfos << "Swap Method: Copy" << llendl;
+ break;
+ case WGL_SWAP_UNDEFINED_ARB:
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+ llinfos << "Swap Method: Undefined" << llendl;
+ break;
+ default:
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+ llinfos << "Swap Method: Unknown" << llendl;
+ break;
+ }
+ }
+ }
+ else
+ {
+ llwarns << "No wgl_ARB_pixel_format extension, using default ChoosePixelFormat!" << llendl;
+ }
+
+ // Verify what pixel format we actually received.
+ if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd))
+ {
+ close();
+ OSMessageBox("Can't get pixel format description", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ llinfos << "GL buffer: Color Bits " << S32(pfd.cColorBits)
+ << " Alpha Bits " << S32(pfd.cAlphaBits)
+ << " Depth Bits " << S32(pfd.cDepthBits)
+ << llendl;
+
+ if (pfd.cColorBits < 32)
+ {
+ close();
+ OSMessageBox(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (pfd.cAlphaBits < 8)
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!gGLManager.initGL())
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because your video card drivers\n"
+ "are out of date or unsupported. Please make sure you have\n"
+ "the latest video card drivers installed.\n\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ // Disable vertical sync for swap
+ if (disable_vsync && wglSwapIntervalEXT)
+ {
+ llinfos << "Disabling vertical sync" << llendl;
+ wglSwapIntervalEXT(0);
+ }
+ else
+ {
+ llinfos << "Keeping vertical sync" << llendl;
+ }
+
+ SetWindowLong(mWindowHandle, GWL_USERDATA, (U32)this);
+ show();
+
+ // ok to post quit messages now
+ mPostQuit = TRUE;
+ return TRUE;
+}
+
+void LLWindowWin32::moveWindow( const LLCoordScreen& position, const LLCoordScreen& size )
+{
+ if( mIsMouseClipping )
+ {
+ RECT client_rect_in_screen_space;
+ if( getClientRectInScreenSpace( &client_rect_in_screen_space ) )
+ {
+ ClipCursor( &client_rect_in_screen_space );
+ }
+ }
+
+ MoveWindow(mWindowHandle, position.mX, position.mY, size.mX, size.mY, TRUE);
+}
+
+BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position)
+{
+ LLCoordScreen screen_pos;
+
+ mMousePositionModified = TRUE;
+ if (!mWindowHandle)
+ {
+ return FALSE;
+ }
+
+ if (!convertCoords(position, &screen_pos))
+ {
+ return FALSE;
+ }
+
+ return SetCursorPos(screen_pos.mX, screen_pos.mY);
+}
+
+BOOL LLWindowWin32::getCursorPosition(LLCoordWindow *position)
+{
+ POINT cursor_point;
+ LLCoordScreen screen_pos;
+
+ if (!mWindowHandle ||
+ !GetCursorPos(&cursor_point))
+ {
+ return FALSE;
+ }
+
+ screen_pos.mX = cursor_point.x;
+ screen_pos.mY = cursor_point.y;
+
+ return convertCoords(screen_pos, position);
+}
+
+void LLWindowWin32::hideCursor()
+{
+ while (ShowCursor(FALSE) >= 0)
+ {
+ // nothing, wait for cursor to push down
+ }
+ mCursorHidden = TRUE;
+ mHideCursorPermanent = TRUE;
+}
+
+void LLWindowWin32::showCursor()
+{
+ // makes sure the cursor shows up
+ while (ShowCursor(TRUE) < 0)
+ {
+ // do nothing, wait for cursor to pop out
+ }
+ mCursorHidden = FALSE;
+ mHideCursorPermanent = FALSE;
+}
+
+void LLWindowWin32::showCursorFromMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ showCursor();
+ }
+}
+
+void LLWindowWin32::hideCursorUntilMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ hideCursor();
+ mHideCursorPermanent = FALSE;
+ }
+}
+
+BOOL LLWindowWin32::isCursorHidden()
+{
+ return mCursorHidden;
+}
+
+
+HCURSOR LLWindowWin32::loadColorCursor(LPCTSTR name)
+{
+ return (HCURSOR)LoadImage(mhInstance,
+ name,
+ IMAGE_CURSOR,
+ 0, // default width
+ 0, // default height
+ LR_DEFAULTCOLOR);
+}
+
+
+void LLWindowWin32::initCursors()
+{
+ mCursor[ UI_CURSOR_ARROW ] = LoadCursor(NULL, IDC_ARROW);
+ mCursor[ UI_CURSOR_WAIT ] = LoadCursor(NULL, IDC_WAIT);
+ mCursor[ UI_CURSOR_HAND ] = LoadCursor(NULL, IDC_HAND);
+ mCursor[ UI_CURSOR_IBEAM ] = LoadCursor(NULL, IDC_IBEAM);
+ mCursor[ UI_CURSOR_CROSS ] = LoadCursor(NULL, IDC_CROSS);
+ mCursor[ UI_CURSOR_SIZENWSE ] = LoadCursor(NULL, IDC_SIZENWSE);
+ mCursor[ UI_CURSOR_SIZENESW ] = LoadCursor(NULL, IDC_SIZENESW);
+ mCursor[ UI_CURSOR_SIZEWE ] = LoadCursor(NULL, IDC_SIZEWE);
+ mCursor[ UI_CURSOR_SIZENS ] = LoadCursor(NULL, IDC_SIZENS);
+ mCursor[ UI_CURSOR_NO ] = LoadCursor(NULL, IDC_NO);
+ mCursor[ UI_CURSOR_WORKING ] = LoadCursor(NULL, IDC_APPSTARTING);
+
+ HMODULE module = GetModuleHandle(NULL);
+ mCursor[ UI_CURSOR_TOOLGRAB ] = LoadCursor(module, TEXT("TOOLGRAB"));
+ mCursor[ UI_CURSOR_TOOLLAND ] = LoadCursor(module, TEXT("TOOLLAND"));
+ mCursor[ UI_CURSOR_TOOLFOCUS ] = LoadCursor(module, TEXT("TOOLFOCUS"));
+ mCursor[ UI_CURSOR_TOOLCREATE ] = LoadCursor(module, TEXT("TOOLCREATE"));
+ mCursor[ UI_CURSOR_ARROWDRAG ] = LoadCursor(module, TEXT("ARROWDRAG"));
+ mCursor[ UI_CURSOR_ARROWCOPY ] = LoadCursor(module, TEXT("ARROWCOPY"));
+ mCursor[ UI_CURSOR_ARROWDRAGMULTI ] = LoadCursor(module, TEXT("ARROWDRAGMULTI"));
+ mCursor[ UI_CURSOR_ARROWCOPYMULTI ] = LoadCursor(module, TEXT("ARROWCOPYMULTI"));
+ mCursor[ UI_CURSOR_NOLOCKED ] = LoadCursor(module, TEXT("NOLOCKED"));
+ mCursor[ UI_CURSOR_ARROWLOCKED ]= LoadCursor(module, TEXT("ARROWLOCKED"));
+ mCursor[ UI_CURSOR_GRABLOCKED ] = LoadCursor(module, TEXT("GRABLOCKED"));
+ mCursor[ UI_CURSOR_TOOLTRANSLATE ] = LoadCursor(module, TEXT("TOOLTRANSLATE"));
+ mCursor[ UI_CURSOR_TOOLROTATE ] = LoadCursor(module, TEXT("TOOLROTATE"));
+ mCursor[ UI_CURSOR_TOOLSCALE ] = LoadCursor(module, TEXT("TOOLSCALE"));
+ mCursor[ UI_CURSOR_TOOLCAMERA ] = LoadCursor(module, TEXT("TOOLCAMERA"));
+ mCursor[ UI_CURSOR_TOOLPAN ] = LoadCursor(module, TEXT("TOOLPAN"));
+ mCursor[ UI_CURSOR_TOOLZOOMIN ] = LoadCursor(module, TEXT("TOOLZOOMIN"));
+ mCursor[ UI_CURSOR_TOOLPICKOBJECT3 ] = LoadCursor(module, TEXT("TOOLPICKOBJECT3"));
+ mCursor[ UI_CURSOR_PIPETTE ] = LoadCursor(module, TEXT("TOOLPIPETTE"));
+
+ // Color cursors
+ mCursor[UI_CURSOR_TOOLSIT] = loadColorCursor(TEXT("TOOLSIT"));
+ mCursor[UI_CURSOR_TOOLBUY] = loadColorCursor(TEXT("TOOLBUY"));
+ mCursor[UI_CURSOR_TOOLPAY] = loadColorCursor(TEXT("TOOLPAY"));
+ mCursor[UI_CURSOR_TOOLOPEN] = loadColorCursor(TEXT("TOOLOPEN"));
+
+ // Note: custom cursors that are not found make LoadCursor() return NULL.
+ for( S32 i = 0; i < UI_CURSOR_COUNT; i++ )
+ {
+ if( !mCursor[i] )
+ {
+ mCursor[i] = LoadCursor(NULL, IDC_ARROW);
+ }
+ }
+}
+
+
+
+void LLWindowWin32::setCursor(ECursorType cursor)
+{
+ if (cursor == UI_CURSOR_ARROW
+ && mBusyCount > 0)
+ {
+ cursor = UI_CURSOR_WORKING;
+ }
+
+ if( mCurrentCursor != cursor )
+ {
+ mCurrentCursor = cursor;
+ SetCursor( mCursor[cursor] );
+ }
+}
+
+ECursorType LLWindowWin32::getCursor()
+{
+ return mCurrentCursor;
+}
+
+void LLWindowWin32::captureMouse()
+{
+ SetCapture(mWindowHandle);
+}
+
+void LLWindowWin32::releaseMouse()
+{
+ ReleaseCapture();
+}
+
+
+void LLWindowWin32::delayInputProcessing()
+{
+ mInputProcessingPaused = TRUE;
+}
+
+void LLWindowWin32::gatherInput()
+{
+ MSG msg;
+ int msg_count = 0;
+
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg_count < MAX_MESSAGE_PER_UPDATE)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ msg_count++;
+
+ if ( mInputProcessingPaused )
+ {
+ break;
+ }
+ /* Attempted workaround for problem where typing fast and hitting
+ return would result in only part of the text being sent. JC
+
+ BOOL key_posted = TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ msg_count++;
+
+ // If a key was translated, a WM_CHAR might have been posted to the end
+ // of the event queue. We need it immediately.
+ if (key_posted && msg.message == WM_KEYDOWN)
+ {
+ if (PeekMessage(&msg, NULL, WM_CHAR, WM_CHAR, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ msg_count++;
+ }
+ }
+ */
+
+ // For async host by name support. Really hacky.
+ if (gAsyncMsgCallback && (LL_WM_HOST_RESOLVED == msg.message))
+ {
+ gAsyncMsgCallback(msg);
+ }
+ }
+
+ mInputProcessingPaused = FALSE;
+
+ // clear this once we've processed all mouse messages that might have occurred after
+ // we slammed the mouse position
+ mMousePositionModified = FALSE;
+}
+
+LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param)
+{
+ LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(h_wnd, GWL_USERDATA);
+
+ if (NULL != window_imp)
+ {
+ // Has user provided their own window callback?
+ if (NULL != window_imp->mWndProc)
+ {
+ if (!window_imp->mWndProc(h_wnd, u_msg, w_param, l_param))
+ {
+ // user has handled window message
+ return 0;
+ }
+ }
+
+ // Juggle to make sure we can get negative positions for when
+ // mouse is outside window.
+ LLCoordWindow window_coord((S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param));
+
+ // This doesn't work, as LOWORD returns unsigned short.
+ //LLCoordWindow window_coord(LOWORD(l_param), HIWORD(l_param));
+ LLCoordGL gl_coord;
+
+ // pass along extended flag in mask
+ MASK mask = (l_param>>16 & KF_EXTENDED) ? MASK_EXTENDED : 0x0;
+ BOOL eat_keystroke = TRUE;
+
+ switch(u_msg)
+ {
+ RECT update_rect;
+ S32 update_width;
+ S32 update_height;
+
+ case WM_TIMER:
+ window_imp->updateJoystick( );
+ break;
+
+ case WM_PAINT:
+ GetUpdateRect(window_imp->mWindowHandle, &update_rect, FALSE);
+ update_width = update_rect.right - update_rect.left + 1;
+ update_height = update_rect.bottom - update_rect.top + 1;
+ window_imp->mCallbacks->handlePaint(window_imp, update_rect.left, update_rect.top,
+ update_width, update_height);
+ break;
+ case WM_PARENTNOTIFY:
+ u_msg = u_msg;
+ break;
+
+ case WM_SETCURSOR:
+ // This message is sent whenever the cursor is moved in a window.
+ // You need to set the appropriate cursor appearance.
+
+ // Only take control of cursor over client region of window
+ // This allows Windows(tm) to handle resize cursors, etc.
+ if (LOWORD(l_param) == HTCLIENT)
+ {
+ SetCursor(window_imp->mCursor[ window_imp->mCurrentCursor] );
+ return 0;
+ }
+ break;
+
+ case WM_ENTERMENULOOP:
+ window_imp->mCallbacks->handleWindowBlock(window_imp);
+ break;
+
+ case WM_EXITMENULOOP:
+ window_imp->mCallbacks->handleWindowUnblock(window_imp);
+ break;
+
+ case WM_ACTIVATEAPP:
+ {
+ // This message should be sent whenever the app gains or loses focus.
+ BOOL activating = (BOOL) w_param;
+ BOOL minimized = window_imp->getMinimized();
+
+ if (gDebugWindowProc)
+ {
+ llinfos << "WINDOWPROC ActivateApp "
+ << " activating " << S32(activating)
+ << " minimized " << S32(minimized)
+ << " fullscreen " << S32(window_imp->mFullscreen)
+ << llendl;
+ }
+
+ if (window_imp->mFullscreen)
+ {
+ // When we run fullscreen, restoring or minimizing the app needs
+ // to switch the screen resolution
+ if (activating)
+ {
+ window_imp->setFullscreenResolution();
+ window_imp->restore();
+ }
+ else
+ {
+ window_imp->minimize();
+ window_imp->resetDisplayResolution();
+ }
+ }
+ break;
+ }
+
+ case WM_ACTIVATE:
+ {
+ // Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE
+ BOOL activating = (LOWORD(w_param) != WA_INACTIVE);
+
+ BOOL minimized = BOOL(HIWORD(w_param));
+
+ // JC - I'm not sure why, but if we don't report that we handled the
+ // WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work
+ // properly when we run fullscreen.
+ if (gDebugWindowProc)
+ {
+ llinfos << "WINDOWPROC Activate "
+ << " activating " << S32(activating)
+ << " minimized " << S32(minimized)
+ << llendl;
+ }
+
+ // Don't handle this.
+ break;
+ }
+
+ case WM_QUERYOPEN:
+ // TODO: use this to return a nice icon
+ break;
+
+ case WM_SYSCOMMAND:
+ switch(w_param)
+ {
+ case SC_KEYMENU:
+ // Disallow the ALT key from triggering the default system menu.
+ return 0;
+
+ case SC_SCREENSAVE:
+ case SC_MONITORPOWER:
+ // eat screen save messages and prevent them!
+ return 0;
+ }
+ break;
+
+ case WM_CLOSE:
+ // Will the app allow the window to close?
+ if (window_imp->mCallbacks->handleCloseRequest(window_imp))
+ {
+ // Get the app to initiate cleanup.
+ window_imp->mCallbacks->handleQuit(window_imp);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ return 0;
+
+ case WM_DESTROY:
+ if (window_imp->shouldPostQuit())
+ {
+ PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0
+ }
+ return 0;
+
+ case WM_COMMAND:
+ if (!HIWORD(w_param)) // this message is from a menu
+ {
+ window_imp->mCallbacks->handleMenuSelect(window_imp, LOWORD(w_param));
+ }
+ break;
+
+ case WM_SYSKEYDOWN:
+ // allow system keys, such as ALT-F4 to be processed by Windows
+ eat_keystroke = FALSE;
+ case WM_KEYDOWN:
+ if (gDebugWindowProc)
+ {
+ llinfos << "Debug WindowProc WM_KEYDOWN "
+ << " key " << S32(w_param)
+ << llendl;
+ }
+ if (gKeyboard->handleKeyDown(w_param, mask) && eat_keystroke)
+ {
+ return 0;
+ }
+ // pass on to windows if we didn't handle it
+ break;
+
+ case WM_SYSKEYUP:
+ eat_keystroke = FALSE;
+ case WM_KEYUP:
+ if (gDebugWindowProc)
+ {
+ llinfos << "Debug WindowProc WM_KEYUP "
+ << " key " << S32(w_param)
+ << llendl;
+ }
+ if (gKeyboard->handleKeyUp(w_param, mask) && eat_keystroke)
+ {
+ return 0;
+ }
+
+ // pass on to windows
+ break;
+
+
+ case WM_CHAR:
+ // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need
+ // to figure out how that works. - Doug
+ // llinfos << "WM_CHAR: " << w_param << llendl;
+ if (gDebugWindowProc)
+ {
+ llinfos << "Debug WindowProc WM_CHAR "
+ << " key " << S32(w_param)
+ << llendl;
+ }
+ if (window_imp->mCallbacks->handleUnicodeChar(w_param, gKeyboard->currentMask(FALSE)))
+ {
+ return 0;
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ {
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleMouseDown(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ //RN: ignore right button double clicks for now
+ //case WM_RBUTTONDBLCLK:
+ {
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleDoubleClick(window_imp, gl_coord, mask) )
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ {
+ //if (gDebugClicks)
+ //{
+ // llinfos << "WndProc left button up" << llendl;
+ //}
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleMouseUp(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_RBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ {
+ // Because we move the cursor position in tllviewerhe app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleRightMouseDown(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_RBUTTONUP:
+ {
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleRightMouseUp(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_MBUTTONDOWN:
+ // Handle middle button click
+ break;
+
+ case WM_MOUSEWHEEL:
+ {
+ static short z_delta = 0;
+
+ z_delta += HIWORD(w_param);
+ // cout << "z_delta " << z_delta << endl;
+
+ // current mouse wheels report changes in increments of zDelta (+120, -120)
+ // Future, higher resolution mouse wheels may report smaller deltas.
+ // So we sum the deltas and only act when we've exceeded WHEEL_DELTA
+ //
+ // If the user rapidly spins the wheel, we can get messages with
+ // large deltas, like 480 or so. Thus we need to scroll more quickly.
+ if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta)
+ {
+ window_imp->mCallbacks->handleScrollWheel(window_imp, -z_delta / WHEEL_DELTA);
+ z_delta = 0;
+ }
+ return 0;
+ }
+ /*
+ // TODO: add this after resolving _WIN32_WINNT issue
+ case WM_MOUSELEAVE:
+ {
+ window_imp->mCallbacks->handleMouseLeave(window_imp);
+
+ // TRACKMOUSEEVENT track_mouse_event;
+ // track_mouse_event.cbSize = sizeof( TRACKMOUSEEVENT );
+ // track_mouse_event.dwFlags = TME_LEAVE;
+ // track_mouse_event.hwndTrack = h_wnd;
+ // track_mouse_event.dwHoverTime = HOVER_DEFAULT;
+ // TrackMouseEvent( &track_mouse_event );
+ return 0;
+ }
+ */
+ // Handle mouse movement within the window
+ case WM_MOUSEMOVE:
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ return 0;
+ }
+
+ case WM_SIZE:
+ {
+ S32 width = S32( LOWORD(l_param) );
+ S32 height = S32( HIWORD(l_param) );
+
+ if (gDebugWindowProc)
+ {
+ BOOL maximized = ( w_param == SIZE_MAXIMIZED );
+ BOOL restored = ( w_param == SIZE_RESTORED );
+ BOOL minimized = ( w_param == SIZE_MINIMIZED );
+
+ llinfos << "WINDOWPROC Size "
+ << width << "x" << height
+ << " max " << S32(maximized)
+ << " min " << S32(minimized)
+ << " rest " << S32(restored)
+ << llendl;
+ }
+
+ // If we are now restored, but we weren't before, this
+ // means that the window was un-minimized.
+ if (w_param == SIZE_RESTORED && window_imp->mLastSizeWParam != SIZE_RESTORED)
+ {
+ window_imp->mCallbacks->handleActivate(window_imp, TRUE);
+ }
+
+ // handle case of window being maximized from fully minimized state
+ if (w_param == SIZE_MAXIMIZED && window_imp->mLastSizeWParam != SIZE_MAXIMIZED)
+ {
+ window_imp->mCallbacks->handleActivate(window_imp, TRUE);
+ }
+
+ // Also handle the minimization case
+ if (w_param == SIZE_MINIMIZED && window_imp->mLastSizeWParam != SIZE_MINIMIZED)
+ {
+ window_imp->mCallbacks->handleActivate(window_imp, FALSE);
+ }
+
+ // Actually resize all of our views
+ if (w_param != SIZE_MINIMIZED)
+ {
+ // Ignore updates for minimizing and minimized "windows"
+ window_imp->mCallbacks->handleResize( window_imp,
+ LOWORD(l_param),
+ HIWORD(l_param) );
+ }
+
+ window_imp->mLastSizeWParam = w_param;
+
+ return 0;
+ }
+
+ case WM_SETFOCUS:
+ if (gDebugWindowProc)
+ {
+ llinfos << "WINDOWPROC SetFocus" << llendl;
+ }
+ window_imp->mCallbacks->handleFocus(window_imp);
+ return 0;
+
+ case WM_KILLFOCUS:
+ if (gDebugWindowProc)
+ {
+ llinfos << "WINDOWPROC KillFocus" << llendl;
+ }
+ window_imp->mCallbacks->handleFocusLost(window_imp);
+ return 0;
+
+ case WM_COPYDATA:
+ // received a URL
+ PCOPYDATASTRUCT myCDS = (PCOPYDATASTRUCT) l_param;
+ window_imp->mCallbacks->handleDataCopy(window_imp, myCDS->dwData, myCDS->lpData);
+ return 0;
+ }
+ }
+
+ // pass unhandled messages down to Windows
+ return DefWindowProc(h_wnd, u_msg, w_param, l_param);
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to)
+{
+ S32 client_height;
+ RECT client_rect;
+ LLCoordWindow window_position;
+
+ if (!mWindowHandle ||
+ !GetClientRect(mWindowHandle, &client_rect) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to)
+{
+ S32 client_height;
+ RECT client_rect;
+
+ if (!mWindowHandle ||
+ !GetClientRect(mWindowHandle, &client_rect) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+{
+ POINT mouse_point;
+
+ mouse_point.x = from.mX;
+ mouse_point.y = from.mY;
+ BOOL result = ScreenToClient(mWindowHandle, &mouse_point);
+
+ if (result)
+ {
+ to->mX = mouse_point.x;
+ to->mY = mouse_point.y;
+ }
+
+ return result;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+{
+ POINT mouse_point;
+
+ mouse_point.x = from.mX;
+ mouse_point.y = from.mY;
+ BOOL result = ClientToScreen(mWindowHandle, &mouse_point);
+
+ if (result)
+ {
+ to->mX = mouse_point.x;
+ to->mY = mouse_point.y;
+ }
+
+ return result;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to)
+{
+ LLCoordWindow window_coord;
+
+ if (!mWindowHandle || (NULL == to))
+ {
+ return FALSE;
+ }
+
+ convertCoords(from, &window_coord);
+ convertCoords(window_coord, to);
+ return TRUE;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen *to)
+{
+ LLCoordWindow window_coord;
+
+ if (!mWindowHandle || (NULL == to))
+ {
+ return FALSE;
+ }
+
+ convertCoords(from, &window_coord);
+ convertCoords(window_coord, to);
+ return TRUE;
+}
+
+
+BOOL LLWindowWin32::isClipboardTextAvailable()
+{
+ return IsClipboardFormatAvailable(CF_UNICODETEXT) || IsClipboardFormatAvailable( CF_TEXT );
+}
+
+
+BOOL LLWindowWin32::pasteTextFromClipboard(LLWString &dst)
+{
+ BOOL success = FALSE;
+
+ if (IsClipboardFormatAvailable(CF_UNICODETEXT))
+ {
+ if (OpenClipboard(mWindowHandle))
+ {
+ HGLOBAL h_data = GetClipboardData(CF_UNICODETEXT);
+ if (h_data)
+ {
+ WCHAR *utf16str = (WCHAR*) GlobalLock(h_data);
+ if (utf16str)
+ {
+ dst = utf16str_to_wstring(utf16str);
+ LLWString::removeCRLF(dst);
+ GlobalUnlock(h_data);
+ success = TRUE;
+ }
+ }
+ CloseClipboard();
+ }
+ }
+ else if (IsClipboardFormatAvailable(CF_TEXT))
+ {
+ // This must be an OLD OS. We don't do non-ASCII for old OSes
+ if (OpenClipboard(mWindowHandle))
+ {
+ HGLOBAL h_data = GetClipboardData(CF_TEXT);
+ if (h_data)
+ {
+ char* str = (char*) GlobalLock(h_data);
+ if (str)
+ {
+ // Strip non-ASCII characters
+ dst = utf8str_to_wstring(mbcsstring_makeASCII(str));
+ LLWString::removeCRLF(dst);
+ GlobalUnlock(h_data);
+ success = TRUE;
+ }
+ }
+ CloseClipboard();
+ }
+ }
+
+ return success;
+}
+
+
+BOOL LLWindowWin32::copyTextToClipboard(const LLWString& wstr)
+{
+ BOOL success = FALSE;
+
+ if (OpenClipboard(mWindowHandle))
+ {
+ EmptyClipboard();
+
+ // Provide a copy of the data in Unicode format.
+ LLWString sanitized_string(wstr);
+ LLWString::addCRLF(sanitized_string);
+ llutf16string out_utf16 = wstring_to_utf16str(sanitized_string);
+ const size_t size_utf16 = (out_utf16.length() + 1) * sizeof(WCHAR);
+
+ // Memory is allocated and then ownership of it is transfered to the system.
+ HGLOBAL hglobal_copy_utf16 = GlobalAlloc(GMEM_MOVEABLE, size_utf16);
+ if (hglobal_copy_utf16)
+ {
+ WCHAR* copy_utf16 = (WCHAR*) GlobalLock(hglobal_copy_utf16);
+ if (copy_utf16)
+ {
+ memcpy(copy_utf16, out_utf16.c_str(), size_utf16);
+ GlobalUnlock(hglobal_copy_utf16);
+
+ if (SetClipboardData(CF_UNICODETEXT, hglobal_copy_utf16))
+ {
+ success = TRUE;
+ }
+ }
+ }
+
+ // Also provide a copy as raw ASCII text.
+ LLWString ascii_string(wstr);
+ LLWString::_makeASCII(ascii_string);
+ LLWString::addCRLF(ascii_string);
+ std::string out_s = wstring_to_utf8str(ascii_string);
+ const size_t size = (out_s.length() + 1) * sizeof(char);
+
+ // Memory is allocated and then ownership of it is transfered to the system.
+ HGLOBAL hglobal_copy = GlobalAlloc(GMEM_MOVEABLE, size);
+ if (hglobal_copy)
+ {
+ char* copy = (char*) GlobalLock(hglobal_copy);
+ if( copy )
+ {
+ memcpy(copy, out_s.c_str(), size);
+ GlobalUnlock(hglobal_copy);
+
+ if (SetClipboardData(CF_TEXT, hglobal_copy))
+ {
+ success = TRUE;
+ }
+ }
+ }
+
+ CloseClipboard();
+ }
+
+ return success;
+}
+
+// Constrains the mouse to the window.
+void LLWindowWin32::setMouseClipping( BOOL b )
+{
+ if( b != mIsMouseClipping )
+ {
+ BOOL success = FALSE;
+
+ if( b )
+ {
+ GetClipCursor( &mOldMouseClip );
+
+ RECT client_rect_in_screen_space;
+ if( getClientRectInScreenSpace( &client_rect_in_screen_space ) )
+ {
+ success = ClipCursor( &client_rect_in_screen_space );
+ }
+ }
+ else
+ {
+ // Must restore the old mouse clip, which may be set by another window.
+ success = ClipCursor( &mOldMouseClip );
+ SetRect( &mOldMouseClip, 0, 0, 0, 0 );
+ }
+
+ if( success )
+ {
+ mIsMouseClipping = b;
+ }
+ }
+}
+
+BOOL LLWindowWin32::getClientRectInScreenSpace( RECT* rectp )
+{
+ BOOL success = FALSE;
+
+ RECT client_rect;
+ if( mWindowHandle && GetClientRect(mWindowHandle, &client_rect) )
+ {
+ POINT top_left;
+ top_left.x = client_rect.left;
+ top_left.y = client_rect.top;
+ ClientToScreen(mWindowHandle, &top_left);
+
+ POINT bottom_right;
+ bottom_right.x = client_rect.right;
+ bottom_right.y = client_rect.bottom;
+ ClientToScreen(mWindowHandle, &bottom_right);
+
+ SetRect( rectp,
+ top_left.x,
+ top_left.y,
+ bottom_right.x,
+ bottom_right.y );
+
+ success = TRUE;
+ }
+
+ return success;
+}
+
+
+BOOL LLWindowWin32::sendEmail(const char* address, const char* subject, const char* body_text,
+ const char* attachment, const char* attachment_displayed_name )
+{
+ // Based on "A SendMail() DLL" by Greg Turner, Windows Developer Magazine, Nov. 1997.
+ // See article for use of GetProcAddress
+ // No restrictions on use.
+
+ enum SendResult
+ {
+ LL_EMAIL_SUCCESS,
+ LL_EMAIL_MAPI_NOT_INSTALLED, // No MAPI Server (eg Microsoft Exchange) installed
+ LL_EMAIL_MAPILOAD_FAILED, // Load of MAPI32.DLL failed
+ LL_EMAIL_SEND_FAILED // The message send itself failed
+ };
+
+ SendResult result = LL_EMAIL_SUCCESS;
+
+ U32 mapi_installed = GetProfileInt(L"Mail", L"MAPI", 0);
+ if( !mapi_installed)
+ {
+ result = LL_EMAIL_MAPI_NOT_INSTALLED;
+ }
+ else
+ {
+ HINSTANCE hMAPIInst = LoadLibrary(L"MAPI32.DLL");
+ if(!hMAPIInst)
+ {
+ result = LL_EMAIL_MAPILOAD_FAILED;
+ }
+ else
+ {
+ LPMAPISENDMAIL pMAPISendMail = (LPMAPISENDMAIL) GetProcAddress(hMAPIInst, "MAPISendMail");
+
+ // Send the message
+ MapiRecipDesc recipients[1];
+ recipients[0].ulReserved = 0;
+ recipients[0].ulRecipClass = MAPI_TO;
+ recipients[0].lpszName = (char*)address;
+ recipients[0].lpszAddress = (char*)address;
+ recipients[0].ulEIDSize = 0;
+ recipients[0].lpEntryID = 0;
+
+ MapiFileDesc files[1];
+ files[0].ulReserved = 0;
+ files[0].flFlags = 0; // non-OLE file
+ files[0].nPosition = -1; // Leave file location in email unspecified.
+ files[0].lpszPathName = (char*)attachment; // Must be fully qualified name, including drive letter.
+ files[0].lpszFileName = (char*)attachment_displayed_name; // If NULL, uses attachment as displayed name.
+ files[0].lpFileType = NULL; // Recipient will have to figure out what kind of file this is.
+
+ MapiMessage msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.lpszSubject = (char*)subject; // may be NULL
+ msg.lpszNoteText = (char*)body_text;
+ msg.nRecipCount = address ? 1 : 0;
+ msg.lpRecips = address ? recipients : NULL;
+ msg.nFileCount = attachment ? 1 : 0;
+ msg.lpFiles = attachment ? files : NULL;
+
+ U32 success = pMAPISendMail(0, (U32) mWindowHandle, &msg, MAPI_DIALOG|MAPI_LOGON_UI|MAPI_NEW_SESSION, 0);
+ if(success != SUCCESS_SUCCESS)
+ {
+ result = LL_EMAIL_SEND_FAILED;
+ }
+
+ FreeLibrary(hMAPIInst);
+ }
+ }
+
+ return result == LL_EMAIL_SUCCESS;
+}
+
+
+S32 LLWindowWin32::stat(const char* file_name, struct stat* stat_info)
+{
+ llassert( sizeof(struct stat) == sizeof(struct _stat) ); // They are defined identically in sys/stat.h, but I'm paranoid.
+ return LLFile::stat( file_name, (struct _stat*) stat_info );
+}
+
+void LLWindowWin32::flashIcon(F32 seconds)
+{
+ FLASHWINFO flash_info;
+
+ flash_info.cbSize = sizeof(FLASHWINFO);
+ flash_info.hwnd = mWindowHandle;
+ flash_info.dwFlags = FLASHW_TRAY;
+ flash_info.uCount = UINT(seconds / ICON_FLASH_TIME);
+ flash_info.dwTimeout = DWORD(1000.f * ICON_FLASH_TIME); // milliseconds
+ FlashWindowEx(&flash_info);
+}
+
+F32 LLWindowWin32::getGamma()
+{
+ return mCurrentGamma;
+}
+
+BOOL LLWindowWin32::restoreGamma()
+{
+ return SetDeviceGammaRamp(mhDC, mPrevGammaRamp);
+}
+
+BOOL LLWindowWin32::setGamma(const F32 gamma)
+{
+ mCurrentGamma = gamma;
+
+ llinfos << "Setting gamma to " << gamma << llendl;
+
+ for ( int i = 0; i < 256; ++i )
+ {
+ int mult = 256 - ( int ) ( ( gamma - 1.0f ) * 128.0f );
+
+ int value = mult * i;
+
+ if ( value > 0xffff )
+ value = 0xffff;
+
+ mCurrentGammaRamp [ 0 * 256 + i ] =
+ mCurrentGammaRamp [ 1 * 256 + i ] =
+ mCurrentGammaRamp [ 2 * 256 + i ] = ( WORD )value;
+ };
+
+ return SetDeviceGammaRamp ( mhDC, mCurrentGammaRamp );
+}
+
+LLWindow::LLWindowResolution* LLWindowWin32::getSupportedResolutions(S32 &num_resolutions)
+{
+ if (!mSupportedResolutions)
+ {
+ mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
+ DEVMODE dev_mode;
+
+ mNumSupportedResolutions = 0;
+ for (S32 mode_num = 0; mNumSupportedResolutions < MAX_NUM_RESOLUTIONS; mode_num++)
+ {
+ if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
+ {
+ break;
+ }
+
+ if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL &&
+ dev_mode.dmPelsWidth >= 800 &&
+ dev_mode.dmPelsHeight >= 600)
+ {
+ BOOL resolution_exists = FALSE;
+ for(S32 i = 0; i < mNumSupportedResolutions; i++)
+ {
+ if (mSupportedResolutions[i].mWidth == dev_mode.dmPelsWidth &&
+ mSupportedResolutions[i].mHeight == dev_mode.dmPelsHeight)
+ {
+ resolution_exists = TRUE;
+ }
+ }
+ if (!resolution_exists)
+ {
+ mSupportedResolutions[mNumSupportedResolutions].mWidth = dev_mode.dmPelsWidth;
+ mSupportedResolutions[mNumSupportedResolutions].mHeight = dev_mode.dmPelsHeight;
+ mNumSupportedResolutions++;
+ }
+ }
+ }
+ }
+
+ num_resolutions = mNumSupportedResolutions;
+ return mSupportedResolutions;
+}
+
+
+F32 LLWindowWin32::getNativeAspectRatio()
+{
+ if (mOverrideAspectRatio > 0.f)
+ {
+ return mOverrideAspectRatio;
+ }
+ else if (mNativeAspectRatio > 0.f)
+ {
+ // we grabbed this value at startup, based on the user's desktop settings
+ return mNativeAspectRatio;
+ }
+ // RN: this hack presumes that the largest supported resolution is monitor-limited
+ // and that pixels in that mode are square, therefore defining the native aspect ratio
+ // of the monitor...this seems to work to a close approximation for most CRTs/LCDs
+ S32 num_resolutions;
+ LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
+
+ return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
+}
+
+F32 LLWindowWin32::getPixelAspectRatio()
+{
+ F32 pixel_aspect = 1.f;
+ if (getFullscreen())
+ {
+ LLCoordScreen screen_size;
+ getSize(&screen_size);
+ pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
+ }
+
+ return pixel_aspect;
+}
+
+// Change display resolution. Returns true if successful.
+// protected
+BOOL LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh)
+{
+ DEVMODE dev_mode;
+ dev_mode.dmSize = sizeof(dev_mode);
+ BOOL success = FALSE;
+
+ // Don't change anything if we don't have to
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
+ {
+ if (dev_mode.dmPelsWidth == width &&
+ dev_mode.dmPelsHeight == height &&
+ dev_mode.dmBitsPerPel == bits &&
+ dev_mode.dmDisplayFrequency == refresh )
+ {
+ // ...display mode identical, do nothing
+ return TRUE;
+ }
+ }
+
+ memset(&dev_mode, 0, sizeof(dev_mode));
+ dev_mode.dmSize = sizeof(dev_mode);
+ dev_mode.dmPelsWidth = width;
+ dev_mode.dmPelsHeight = height;
+ dev_mode.dmBitsPerPel = bits;
+ dev_mode.dmDisplayFrequency = refresh;
+ dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
+
+ // CDS_FULLSCREEN indicates that this is a temporary change to the device mode.
+ LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN);
+
+ success = (DISP_CHANGE_SUCCESSFUL == cds_result);
+
+ if (!success)
+ {
+ llwarns << "setDisplayResolution failed, "
+ << width << "x" << height << "x" << bits << " @ " << refresh << llendl;
+ }
+
+ return success;
+}
+
+// protected
+BOOL LLWindowWin32::setFullscreenResolution()
+{
+ if (mFullscreen)
+ {
+ return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenBits, mFullscreenRefresh);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// protected
+BOOL LLWindowWin32::resetDisplayResolution()
+{
+ llinfos << "resetDisplayResolution START" << llendl;
+
+ LONG cds_result = ChangeDisplaySettings(NULL, 0);
+
+ BOOL success = (DISP_CHANGE_SUCCESSFUL == cds_result);
+
+ if (!success)
+ {
+ llwarns << "resetDisplayResolution failed" << llendl;
+ }
+
+ llinfos << "resetDisplayResolution END" << llendl;
+
+ return success;
+}
+
+void LLWindowWin32::swapBuffers()
+{
+ SwapBuffers(mhDC);
+}
+
+
+BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
+ VOID* pContext )
+{
+ HRESULT hr;
+
+ // Obtain an interface to the enumerated joystick.
+ hr = g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pJoystick, NULL );
+
+ // If it failed, then we can't use this joystick. (Maybe the user unplugged
+ // it while we were in the middle of enumerating it.)
+ if( FAILED(hr) )
+ return DIENUM_CONTINUE;
+
+ // Stop enumeration. Note: we're just taking the first joystick we get. You
+ // could store all the enumerated joysticks and let the user pick.
+ return DIENUM_STOP;
+}
+
+BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
+ VOID* pContext )
+{
+ if( pdidoi->dwType & DIDFT_AXIS )
+ {
+ DIPROPRANGE diprg;
+ diprg.diph.dwSize = sizeof(DIPROPRANGE);
+ diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+ diprg.diph.dwHow = DIPH_BYID;
+ diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis
+ diprg.lMin = -1000;
+ diprg.lMax = +1000;
+
+ // Set the range for the axis
+ if( FAILED( g_pJoystick->SetProperty( DIPROP_RANGE, &diprg.diph ) ) )
+ return DIENUM_STOP;
+
+ }
+ return DIENUM_CONTINUE;
+}
+
+void LLWindowWin32::updateJoystick( )
+{
+ HRESULT hr;
+ DIJOYSTATE js; // DInput joystick state
+
+ if (!g_pJoystick)
+ return;
+ hr = g_pJoystick->Poll();
+ if ( hr == DIERR_INPUTLOST )
+ {
+ hr = g_pJoystick->Acquire();
+ return;
+ }
+ else if ( FAILED(hr) )
+ return;
+
+ // Get the input's device state
+ if( FAILED( hr = g_pJoystick->GetDeviceState( sizeof(DIJOYSTATE), &js ) ) )
+ return; // The device should have been acquired during the Poll()
+
+ if (js.lX <= -500)
+ {
+ if (!(mJoyStickState & 0x1))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_PAD_LEFT, 0);
+ mJoyStickState |= 0x1;
+ }
+ }
+ else
+ {
+ if (mJoyStickState & 0x1)
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_PAD_LEFT, 0);
+ mJoyStickState &= ~0x1;
+ }
+ }
+ if (js.lX >= 500)
+ {
+ if (!(mJoyStickState & 0x2))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_PAD_RIGHT, 0);
+ mJoyStickState |= 0x2;
+ }
+ }
+ else
+ {
+ if (mJoyStickState & 0x2)
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_PAD_RIGHT, 0);
+ mJoyStickState &= ~0x2;
+ }
+ }
+ if (js.lY <= -500)
+ {
+ if (!(mJoyStickState & 0x4))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_PAD_UP, 0);
+ mJoyStickState |= 0x4;
+ }
+ }
+ else
+ {
+ if (mJoyStickState & 0x4)
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_PAD_UP, 0);
+ mJoyStickState &= ~0x4;
+ }
+ }
+ if (js.lY >= 500)
+ {
+ if (!(mJoyStickState & 0x8))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_PAD_DOWN, 0);
+ mJoyStickState |= 0x8;
+ }
+ }
+ else
+ {
+ if (mJoyStickState & 0x8)
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_PAD_DOWN, 0);
+ mJoyStickState &= ~0x8;
+ }
+ }
+
+ for( int i = 0; i < 15; i++ )
+ {
+ if ( js.rgbButtons[i] & 0x80 )
+ {
+ if (!(mJoyButtonState & (1<<i)))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_BUTTON1+i, 0);
+ mJoyButtonState |= (1<<i);
+ }
+ }
+ else
+ {
+ if (mJoyButtonState & (1<<i))
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_BUTTON1+i, 0);
+ mJoyButtonState &= ~(1<<i);
+ }
+ }
+ }
+}
+
+
+//
+// LLSplashScreenImp
+//
+LLSplashScreenWin32::LLSplashScreenWin32()
+: mWindow(NULL)
+{
+}
+
+LLSplashScreenWin32::~LLSplashScreenWin32()
+{
+}
+
+void LLSplashScreenWin32::showImpl()
+{
+ // This appears to work. ???
+ HINSTANCE hinst = GetModuleHandle(NULL);
+
+ mWindow = CreateDialog(hinst,
+ TEXT("SPLASHSCREEN"),
+ NULL, // no parent
+ (DLGPROC) LLSplashScreenWin32::windowProc);
+ ShowWindow(mWindow, SW_SHOW);
+}
+
+
+void LLSplashScreenWin32::updateImpl(const char *mesg)
+{
+ if (!mWindow) return;
+
+ WCHAR w_mesg[1024];
+ mbstowcs(w_mesg, mesg, 1024);
+
+ SendDlgItemMessage(mWindow,
+ 666, // HACK: text id
+ WM_SETTEXT,
+ FALSE,
+ (LPARAM)w_mesg);
+}
+
+
+void LLSplashScreenWin32::hideImpl()
+{
+ if (mWindow)
+ {
+ DestroyWindow(mWindow);
+ mWindow = NULL;
+ }
+}
+
+
+// static
+LRESULT CALLBACK LLSplashScreenWin32::windowProc(HWND h_wnd, UINT u_msg,
+ WPARAM w_param, LPARAM l_param)
+{
+ // Just give it to windows
+ return DefWindowProc(h_wnd, u_msg, w_param, l_param);
+}
+
+//
+// Helper Funcs
+//
+
+HWND llwindow_get_hwnd(LLWindow *window)
+{
+ //assumes we are dealing with a Win32 window
+ return ((LLWindowWin32*)window)->mWindowHandle;
+}
+
+
+void llwindow_install_wndproc(LLWindow *window, WNDPROC wnd_proc)
+{
+ //assumes we are dealing with a Win32 window
+ ((LLWindowWin32*)window)->mWndProc = wnd_proc;
+}
+
+S32 OSMessageBoxWin32(const char* text, const char* caption, U32 type)
+{
+ UINT uType;
+
+ switch(type)
+ {
+ case OSMB_OK:
+ uType = MB_OK;
+ break;
+ case OSMB_OKCANCEL:
+ uType = MB_OKCANCEL;
+ break;
+ case OSMB_YESNO:
+ uType = MB_YESNO;
+ break;
+ default:
+ uType = MB_OK;
+ break;
+ }
+
+ // HACK! Doesn't properly handle wide strings!
+ int retval_win = MessageBoxA(NULL, text, caption, uType);
+ S32 retval;
+
+ switch(retval_win)
+ {
+ case IDYES:
+ retval = OSBTN_YES;
+ break;
+ case IDNO:
+ retval = OSBTN_NO;
+ break;
+ case IDOK:
+ retval = OSBTN_OK;
+ break;
+ case IDCANCEL:
+ retval = OSBTN_CANCEL;
+ break;
+ default:
+ retval = OSBTN_CANCEL;
+ break;
+ }
+
+ return retval;
+}
+
+
+void spawn_web_browser(const char* escaped_url )
+{
+ bool found = false;
+ S32 i;
+ for (i = 0; i < gURLProtocolWhitelistCount; i++)
+ {
+ S32 len = strlen(gURLProtocolWhitelist[i]);
+ if (!strncmp(escaped_url, gURLProtocolWhitelist[i], len)
+ && escaped_url[len] == ':')
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ llwarns << "spawn_web_browser() called for url with protocol not on whitelist: " << escaped_url << llendl;
+ return;
+ }
+
+ llinfos << "Opening URL " << escaped_url << llendl;
+
+ // Figure out the user's default web browser
+ // HKEY_CLASSES_ROOT\http\shell\open\command
+ char reg_path_str[256];
+ sprintf(reg_path_str, "%s\\shell\\open\\command", gURLProtocolWhitelistHandler[i]);
+ WCHAR reg_path_wstr[256];
+ mbstowcs(reg_path_wstr, reg_path_str, 1024);
+
+ HKEY key;
+ WCHAR browser_open_wstr[1024];
+ DWORD buffer_length = 1024;
+ RegOpenKeyEx(HKEY_CLASSES_ROOT, reg_path_wstr, 0, KEY_QUERY_VALUE, &key);
+ RegQueryValueEx(key, NULL, NULL, NULL, (LPBYTE)browser_open_wstr, &buffer_length);
+ RegCloseKey(key);
+
+ // Convert to STL string
+ LLWString browser_open_wstring = utf16str_to_wstring(browser_open_wstr);
+
+ if (browser_open_wstring.length() < 2)
+ {
+ llwarns << "Invalid browser executable in registry " << browser_open_wstring << llendl;
+ return;
+ }
+
+ // Extract the process that's supposed to be launched
+ LLWString browser_executable;
+ if (browser_open_wstring[0] == '"')
+ {
+ // executable is quoted, find the matching quote
+ size_t quote_pos = browser_open_wstring.find('"', 1);
+ // copy out the string including both quotes
+ browser_executable = browser_open_wstring.substr(0, quote_pos+1);
+ }
+ else
+ {
+ // executable not quoted, find a space
+ size_t space_pos = browser_open_wstring.find(' ', 1);
+ browser_executable = browser_open_wstring.substr(0, space_pos);
+ }
+
+ llinfos << "Browser reg key: " << wstring_to_utf8str(browser_open_wstring) << llendl;
+ llinfos << "Browser executable: " << wstring_to_utf8str(browser_executable) << llendl;
+
+ // Convert URL to wide string for Windows API
+ // Assume URL is UTF8, as can come from scripts
+ LLWString url_wstring = utf8str_to_wstring(escaped_url);
+ llutf16string url_utf16 = wstring_to_utf16str(url_wstring);
+
+ // Convert executable and path to wide string for Windows API
+ llutf16string browser_exec_utf16 = wstring_to_utf16str(browser_executable);
+
+ // ShellExecute returns HINSTANCE for backwards compatiblity.
+ // MS docs say to cast to int and compare to 32.
+ HWND our_window = NULL;
+ LPCWSTR directory_wstr = NULL;
+ int retval = (int) ShellExecute(our_window,
+ L"open",
+ browser_exec_utf16.c_str(),
+ url_utf16.c_str(),
+ directory_wstr,
+ SW_SHOWNORMAL);
+ if (retval > 32)
+ {
+ llinfos << "load_url success with " << retval << llendl;
+ }
+ else
+ {
+ llinfos << "load_url failure with " << retval << llendl;
+ }
+}
+
+void shell_open( const char* file_path )
+{
+ llinfos << "Opening " << file_path << llendl;
+
+ WCHAR wstr[1024];
+ mbstowcs(wstr, file_path, 1024);
+
+ HWND our_window = NULL;
+ int retval = (int) ShellExecute(our_window, L"open", wstr, NULL, NULL, SW_SHOWNORMAL);
+ if (retval > 32)
+ {
+ llinfos << "ShellExecute success with " << retval << llendl;
+ }
+ else
+ {
+ llinfos << "ShellExecute failure with " << retval << llendl;
+ }
+}
+
+BOOL LLWindowWin32::dialog_color_picker ( F32 *r, F32 *g, F32 *b )
+{
+ BOOL retval = FALSE;
+
+ static CHOOSECOLOR cc;
+ static COLORREF crCustColors[16];
+ cc.lStructSize = sizeof(CHOOSECOLOR);
+ cc.hwndOwner = mWindowHandle;
+ cc.hInstance = NULL;
+ cc.rgbResult = RGB ((*r * 255.f),(*g *255.f),(*b * 255.f));
+ //cc.rgbResult = RGB (0x80,0x80,0x80);
+ cc.lpCustColors = crCustColors;
+ cc.Flags = CC_RGBINIT | CC_FULLOPEN;
+ cc.lCustData = 0;
+ cc.lpfnHook = NULL;
+ cc.lpTemplateName = NULL;
+
+ // This call is modal, so pause agent
+ //send_agent_pause(); // this is in newview and we don't want to set up a dependency
+ {
+ retval = ChooseColor(&cc);
+ }
+ //send_agent_resume(); // this is in newview and we don't want to set up a dependency
+
+ *b = ((F32)((cc.rgbResult >> 16) & 0xff)) / 255.f;
+
+ *g = ((F32)((cc.rgbResult >> 8) & 0xff)) / 255.f;
+
+ *r = ((F32)(cc.rgbResult & 0xff)) / 255.f;
+
+ return (retval);
+}
+
+void *LLWindowWin32::getPlatformWindow()
+{
+ return (void*)mWindowHandle;
+}
+
+void LLWindowWin32::bringToFront()
+{
+ BringWindowToTop(mWindowHandle);
+}
+
+// set (OS) window focus back to the client
+void LLWindowWin32::focusClient()
+{
+ SetFocus ( mWindowHandle );
+};
+
+#endif // LL_WINDOWS
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
new file mode 100644
index 0000000000..6803ad6f2a
--- /dev/null
+++ b/indra/llwindow/llwindowwin32.h
@@ -0,0 +1,187 @@
+/**
+ * @file llwindowwin32.h
+ * @brief Windows implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWWIN32_H
+#define LL_LLWINDOWWIN32_H
+
+#include "llwindow.h"
+
+// Hack for async host by name
+#define LL_WM_HOST_RESOLVED (WM_APP + 1)
+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*/ BOOL getFullscreen();
+ /*virtual*/ BOOL getPosition(LLCoordScreen *position);
+ /*virtual*/ BOOL getSize(LLCoordScreen *size);
+ /*virtual*/ BOOL getSize(LLCoordWindow *size);
+ /*virtual*/ BOOL setPosition(LLCoordScreen position);
+ /*virtual*/ BOOL setSize(LLCoordScreen size);
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync);
+ /*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
+ /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
+ /*virtual*/ void showCursor();
+ /*virtual*/ void hideCursor();
+ /*virtual*/ void showCursorFromMouseMove();
+ /*virtual*/ void hideCursorUntilMouseMove();
+ /*virtual*/ BOOL isCursorHidden();
+ /*virtual*/ void setCursor(ECursorType cursor);
+ /*virtual*/ ECursorType getCursor();
+ /*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*/ BOOL restoreGamma(); // Restore original gamma table (before updating gamma)
+ /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput();
+ /*virtual*/ void delayInputProcessing();
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName();
+ /*virtual*/ void deleteFile( const char* file_name );
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info );
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL);
+
+
+ // 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 dialog_color_picker (F32 *r, F32 *g, F32 *b );
+
+ /*virtual*/ void *getPlatformWindow();
+ /*virtual*/ void bringToFront();
+ /*virtual*/ void focusClient();
+
+protected:
+ LLWindowWin32(
+ char *title, char *name, int x, int y, int width, int height, U32 flags,
+ BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth);
+ ~LLWindowWin32();
+
+ void initCursors();
+ HCURSOR loadColorCursor(LPCTSTR name);
+ BOOL isValid();
+ void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
+
+
+ // Changes display resolution. Returns true if successful
+ BOOL setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
+
+ // Go back to last fullscreen display resolution.
+ BOOL setFullscreenResolution();
+
+ // Restore the display resolution to its value before we ran the app.
+ BOOL resetDisplayResolution();
+
+ void minimize();
+ void restore();
+
+ BOOL shouldPostQuit() { return mPostQuit; }
+
+
+protected:
+ //
+ // Platform specific methods
+ //
+
+ BOOL getClientRectInScreenSpace(RECT* rectp);
+ void updateJoystick( );
+
+ static LRESULT CALLBACK mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param);
+ static BOOL CALLBACK enumChildWindows(HWND h_wnd, LPARAM l_param);
+
+
+ //
+ // Platform specific variables
+ //
+ WCHAR *mWindowTitle;
+ WCHAR *mWindowClassName;
+
+ HWND mWindowHandle; // window handle
+ HGLRC mhRC; // OpenGL rendering context
+ HDC mhDC; // Windows Device context handle
+ HINSTANCE mhInstance; // handle to application instance
+ WNDPROC mWndProc; // user-installable window proc
+ RECT mOldMouseClip; // Screen rect to which the mouse cursor was globally constrained before we changed it in clipMouse()
+ WPARAM mLastSizeWParam;
+ F32 mOverrideAspectRatio;
+ F32 mNativeAspectRatio;
+
+ HCURSOR mCursor[ UI_CURSOR_COUNT ]; // Array of all mouse cursors
+
+ static BOOL sIsClassRegistered; // has the window class been registered?
+
+ F32 mCurrentGamma;
+ WORD mPrevGammaRamp[256*3];
+ WORD mCurrentGammaRamp[256*3];
+
+ U32 mJoyStickState;
+ U32 mJoyButtonState;
+
+ LPWSTR mIconResource;
+ BOOL mMousePositionModified;
+ BOOL mInputProcessingPaused;
+
+ friend HWND llwindow_get_hwnd(LLWindow *window);
+ friend void llwindow_install_wndproc(LLWindow *window, WNDPROC wnd_proc);
+ friend class LLWindowManager;
+};
+
+class LLSplashScreenWin32 : public LLSplashScreen
+{
+public:
+ LLSplashScreenWin32();
+ virtual ~LLSplashScreenWin32();
+
+ /*virtual*/ void showImpl();
+ /*virtual*/ void updateImpl(const char* mesg);
+ /*virtual*/ void hideImpl();
+
+#if LL_WINDOWS
+ static LRESULT CALLBACK windowProc(HWND h_wnd, UINT u_msg,
+ WPARAM w_param, LPARAM l_param);
+#endif
+
+private:
+#if LL_WINDOWS
+ HWND mWindow;
+#endif
+};
+
+extern LLW32MsgCallback gAsyncMsgCallback;
+
+static void handleMessage( const MSG& msg );
+
+S32 OSMessageBoxWin32(const char* text, const char* caption, U32 type);
+
+#endif //LL_LLWINDOWWIN32_H