From 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:44:46 -0500 Subject: Second Life viewer sources 1.13.2.12 --- linden/indra/llwindow/files.darwin.lst | 2 + linden/indra/llwindow/files.linux.lst | 4 + linden/indra/llwindow/files.lst | 5 + linden/indra/llwindow/lldxhardware.cpp | 527 ++++ linden/indra/llwindow/lldxhardware.h | 109 + linden/indra/llwindow/llgl.cpp | 1378 ++++++++++ linden/indra/llwindow/llgl.h | 272 ++ linden/indra/llwindow/llglheaders.h | 486 ++++ linden/indra/llwindow/llglstates.h | 339 +++ linden/indra/llwindow/llglstubs.h | 232 ++ linden/indra/llwindow/llgltypes.h | 40 + linden/indra/llwindow/llkeyboard.cpp | 392 +++ linden/indra/llwindow/llkeyboard.h | 144 ++ linden/indra/llwindow/llkeyboardmacosx.cpp | 328 +++ linden/indra/llwindow/llkeyboardmacosx.h | 55 + linden/indra/llwindow/llkeyboardsdl.cpp | 343 +++ linden/indra/llwindow/llkeyboardsdl.h | 56 + linden/indra/llwindow/llkeyboardwin32.cpp | 401 +++ linden/indra/llwindow/llkeyboardwin32.h | 59 + linden/indra/llwindow/llmousehandler.h | 58 + linden/indra/llwindow/llwindow.cpp | 420 +++ linden/indra/llwindow/llwindow.h | 334 +++ linden/indra/llwindow/llwindow.vcproj | 270 ++ linden/indra/llwindow/llwindowheadless.cpp | 51 + linden/indra/llwindow/llwindowheadless.h | 117 + linden/indra/llwindow/llwindowlinux.cpp | 57 + linden/indra/llwindow/llwindowlinux.h | 115 + linden/indra/llwindow/llwindowmacosx-objc.h | 38 + linden/indra/llwindow/llwindowmacosx-objc.mm | 106 + linden/indra/llwindow/llwindowmacosx.cpp | 2923 +++++++++++++++++++++ linden/indra/llwindow/llwindowmacosx.h | 208 ++ linden/indra/llwindow/llwindowmesaheadless.cpp | 83 + linden/indra/llwindow/llwindowmesaheadless.h | 123 + linden/indra/llwindow/llwindowsdl.cpp | 2506 ++++++++++++++++++ linden/indra/llwindow/llwindowsdl.h | 217 ++ linden/indra/llwindow/llwindowwin32.cpp | 3255 ++++++++++++++++++++++++ linden/indra/llwindow/llwindowwin32.h | 209 ++ 37 files changed, 16262 insertions(+) create mode 100644 linden/indra/llwindow/files.darwin.lst create mode 100644 linden/indra/llwindow/files.linux.lst create mode 100644 linden/indra/llwindow/files.lst create mode 100644 linden/indra/llwindow/lldxhardware.cpp create mode 100644 linden/indra/llwindow/lldxhardware.h create mode 100644 linden/indra/llwindow/llgl.cpp create mode 100644 linden/indra/llwindow/llgl.h create mode 100644 linden/indra/llwindow/llglheaders.h create mode 100644 linden/indra/llwindow/llglstates.h create mode 100644 linden/indra/llwindow/llglstubs.h create mode 100644 linden/indra/llwindow/llgltypes.h create mode 100644 linden/indra/llwindow/llkeyboard.cpp create mode 100644 linden/indra/llwindow/llkeyboard.h create mode 100644 linden/indra/llwindow/llkeyboardmacosx.cpp create mode 100644 linden/indra/llwindow/llkeyboardmacosx.h create mode 100644 linden/indra/llwindow/llkeyboardsdl.cpp create mode 100644 linden/indra/llwindow/llkeyboardsdl.h create mode 100644 linden/indra/llwindow/llkeyboardwin32.cpp create mode 100644 linden/indra/llwindow/llkeyboardwin32.h create mode 100644 linden/indra/llwindow/llmousehandler.h create mode 100644 linden/indra/llwindow/llwindow.cpp create mode 100644 linden/indra/llwindow/llwindow.h create mode 100644 linden/indra/llwindow/llwindow.vcproj create mode 100644 linden/indra/llwindow/llwindowheadless.cpp create mode 100644 linden/indra/llwindow/llwindowheadless.h create mode 100644 linden/indra/llwindow/llwindowlinux.cpp create mode 100644 linden/indra/llwindow/llwindowlinux.h create mode 100644 linden/indra/llwindow/llwindowmacosx-objc.h create mode 100644 linden/indra/llwindow/llwindowmacosx-objc.mm create mode 100644 linden/indra/llwindow/llwindowmacosx.cpp create mode 100644 linden/indra/llwindow/llwindowmacosx.h create mode 100644 linden/indra/llwindow/llwindowmesaheadless.cpp create mode 100644 linden/indra/llwindow/llwindowmesaheadless.h create mode 100644 linden/indra/llwindow/llwindowsdl.cpp create mode 100644 linden/indra/llwindow/llwindowsdl.h create mode 100644 linden/indra/llwindow/llwindowwin32.cpp create mode 100644 linden/indra/llwindow/llwindowwin32.h (limited to 'linden/indra/llwindow') diff --git a/linden/indra/llwindow/files.darwin.lst b/linden/indra/llwindow/files.darwin.lst new file mode 100644 index 0000000..e8eca9b --- /dev/null +++ b/linden/indra/llwindow/files.darwin.lst @@ -0,0 +1,2 @@ +llwindow/llkeyboardmacosx.cpp +llwindow/llwindowmacosx.cpp diff --git a/linden/indra/llwindow/files.linux.lst b/linden/indra/llwindow/files.linux.lst new file mode 100644 index 0000000..e41ac2a --- /dev/null +++ b/linden/indra/llwindow/files.linux.lst @@ -0,0 +1,4 @@ +llwindow/llkeyboardsdl.cpp +llwindow/llwindowsdl.cpp +llwindow/llwindowlinux.cpp +llwindow/llwindowmesaheadless.cpp diff --git a/linden/indra/llwindow/files.lst b/linden/indra/llwindow/files.lst new file mode 100644 index 0000000..1c16b09 --- /dev/null +++ b/linden/indra/llwindow/files.lst @@ -0,0 +1,5 @@ +llwindow/lldxhardware.cpp +llwindow/llgl.cpp +llwindow/llkeyboard.cpp +llwindow/llwindow.cpp +llwindow/llwindowheadless.cpp diff --git a/linden/indra/llwindow/lldxhardware.cpp b/linden/indra/llwindow/lldxhardware.cpp new file mode 100644 index 0000000..5dd632b --- /dev/null +++ b/linden/indra/llwindow/lldxhardware.cpp @@ -0,0 +1,527 @@ +/** + * @file lldxhardware.cpp + * @brief LLDXHardware implementation + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifdef LL_WINDOWS + +// Culled from some Microsoft sample code + +#include "linden_common.h" + +#include +#include + +#include + +#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 > tokenizer; + boost::char_separator 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 > tokenizer; + boost::char_separator 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 > tokenizer; + boost::char_separator 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/linden/indra/llwindow/lldxhardware.h b/linden/indra/llwindow/lldxhardware.h new file mode 100644 index 0000000..5d8b925 --- /dev/null +++ b/linden/indra/llwindow/lldxhardware.h @@ -0,0 +1,109 @@ +/** + * @file lldxhardware.h + * @brief LLDXHardware definition + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLDXHARDWARE_H +#define LL_LLDXHARDWARE_H + +#include + +#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 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 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/linden/indra/llwindow/llgl.cpp b/linden/indra/llwindow/llgl.cpp new file mode 100644 index 0000000..547a353 --- /dev/null +++ b/linden/indra/llwindow/llgl.cpp @@ -0,0 +1,1378 @@ +/** + * @file llgl.cpp +* @brief LLGL implementation + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +// This file sets some global GL parameters, and implements some +// useful functions for GL operations. + +#define GLH_EXT_SINGLE_FILE + +#include "linden_common.h" + +#include "llsys.h" + +#include "llgl.h" + +#include "llerror.h" +#include "llquaternion.h" +#include "llmath.h" +#include "m4math.h" +#include "llstring.h" + +#include "llglheaders.h" + + +#if LL_LINUX && !LL_MESA_HEADLESS +// The __APPLE__ hack is to make glh_extensions.h not symbol-clash horribly +# define __APPLE__ +# include "GL/glh_extensions.h" +# undef __APPLE__ + +/* Although SDL very likely ends up calling glXGetProcAddress() itself, + if we do it ourselves then we avoid getting bogus addresses back on + some systems. Weird. */ +/*# include "SDL/SDL.h" + # define GLH_EXT_GET_PROC_ADDRESS(p) SDL_GL_GetProcAddress(p) */ +#define GLX_GLXEXT_PROTOTYPES 1 +# include "GL/glx.h" +# include "GL/glxext.h" +// Use glXGetProcAddressARB instead of glXGetProcAddress - the ARB symbol +// is considered 'legacy' but works on more machines. +# define GLH_EXT_GET_PROC_ADDRESS(p) glXGetProcAddressARB((const GLubyte*)(p)) +#endif // LL_LINUX && !LL_MESA_HEADLESS + + +#ifdef _DEBUG +//#define GL_STATE_VERIFY +#endif + +BOOL gClothRipple = FALSE; +BOOL gNoRender = FALSE; + + +#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS +// ATI prototypes +// vertex blending prototypes +PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB = NULL; +PFNGLVERTEXBLENDARBPROC glVertexBlendARB = NULL; +PFNGLWEIGHTFVARBPROC glWeightfvARB = NULL; + +// Vertex buffer object prototypes +PFNGLBINDBUFFERARBPROC glBindBufferARB = NULL; +PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = NULL; +PFNGLGENBUFFERSARBPROC glGenBuffersARB = NULL; +PFNGLISBUFFERARBPROC glIsBufferARB = NULL; +PFNGLBUFFERDATAARBPROC glBufferDataARB = NULL; +PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB = NULL; +PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubDataARB = NULL; +PFNGLMAPBUFFERARBPROC glMapBufferARB = NULL; +PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB = NULL; +PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB = NULL; +PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB = NULL; + +// vertex object prototypes +PFNGLNEWOBJECTBUFFERATIPROC glNewObjectBufferATI = NULL; +PFNGLISOBJECTBUFFERATIPROC glIsObjectBufferATI = NULL; +PFNGLUPDATEOBJECTBUFFERATIPROC glUpdateObjectBufferATI = NULL; +PFNGLGETOBJECTBUFFERFVATIPROC glGetObjectBufferfvATI = NULL; +PFNGLGETOBJECTBUFFERIVATIPROC glGetObjectBufferivATI = NULL; +PFNGLFREEOBJECTBUFFERATIPROC glFreeObjectBufferATI = NULL; +PFNGLARRAYOBJECTATIPROC glArrayObjectATI = NULL; +PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glVertexAttribArrayObjectATI = NULL; +PFNGLGETARRAYOBJECTFVATIPROC glGetArrayObjectfvATI = NULL; +PFNGLGETARRAYOBJECTIVATIPROC glGetArrayObjectivATI = NULL; +PFNGLVARIANTARRAYOBJECTATIPROC glVariantObjectArrayATI = NULL; +PFNGLGETVARIANTARRAYOBJECTFVATIPROC glGetVariantArrayObjectfvATI = NULL; +PFNGLGETVARIANTARRAYOBJECTIVATIPROC glGetVariantArrayObjectivATI = NULL; + +// GL_ARB_occlusion_query +PFNGLGENQUERIESARBPROC glGenQueriesARB = NULL; +PFNGLDELETEQUERIESARBPROC glDeleteQueriesARB = NULL; +PFNGLISQUERYARBPROC glIsQueryARB = NULL; +PFNGLBEGINQUERYARBPROC glBeginQueryARB = NULL; +PFNGLENDQUERYARBPROC glEndQueryARB = NULL; +PFNGLGETQUERYIVARBPROC glGetQueryivARB = NULL; +PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectivARB = NULL; +PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB = NULL; + +//shader object prototypes +PFNGLDELETEOBJECTARBPROC glDeleteObjectARB = NULL; +PFNGLGETHANDLEARBPROC glGetHandleARB = NULL; +PFNGLDETACHOBJECTARBPROC glDetachObjectARB = NULL; +PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB = NULL; +PFNGLSHADERSOURCEARBPROC glShaderSourceARB = NULL; +PFNGLCOMPILESHADERARBPROC glCompileShaderARB = NULL; +PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB = NULL; +PFNGLATTACHOBJECTARBPROC glAttachObjectARB = NULL; +PFNGLLINKPROGRAMARBPROC glLinkProgramARB = NULL; +PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB = NULL; +PFNGLVALIDATEPROGRAMARBPROC glValidateProgramARB = NULL; +PFNGLUNIFORM1FARBPROC glUniform1fARB = NULL; +PFNGLUNIFORM2FARBPROC glUniform2fARB = NULL; +PFNGLUNIFORM3FARBPROC glUniform3fARB = NULL; +PFNGLUNIFORM4FARBPROC glUniform4fARB = NULL; +PFNGLUNIFORM1IARBPROC glUniform1iARB = NULL; +PFNGLUNIFORM2IARBPROC glUniform2iARB = NULL; +PFNGLUNIFORM3IARBPROC glUniform3iARB = NULL; +PFNGLUNIFORM4IARBPROC glUniform4iARB = NULL; +PFNGLUNIFORM1FVARBPROC glUniform1fvARB = NULL; +PFNGLUNIFORM2FVARBPROC glUniform2fvARB = NULL; +PFNGLUNIFORM3FVARBPROC glUniform3fvARB = NULL; +PFNGLUNIFORM4FVARBPROC glUniform4fvARB = NULL; +PFNGLUNIFORM1IVARBPROC glUniform1ivARB = NULL; +PFNGLUNIFORM2IVARBPROC glUniform2ivARB = NULL; +PFNGLUNIFORM3IVARBPROC glUniform3ivARB = NULL; +PFNGLUNIFORM4IVARBPROC glUniform4ivARB = NULL; +PFNGLUNIFORMMATRIX2FVARBPROC glUniformMatrix2fvARB = NULL; +PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fvARB = NULL; +PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fvARB = NULL; +PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB = NULL; +PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB = NULL; +PFNGLGETINFOLOGARBPROC glGetInfoLogARB = NULL; +PFNGLGETATTACHEDOBJECTSARBPROC glGetAttachedObjectsARB = NULL; +PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB = NULL; +PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniformARB = NULL; +PFNGLGETUNIFORMFVARBPROC glGetUniformfvARB = NULL; +PFNGLGETUNIFORMIVARBPROC glGetUniformivARB = NULL; +PFNGLGETSHADERSOURCEARBPROC glGetShaderSourceARB = NULL; + +// vertex shader prototypes +#if LL_LINUX +PFNGLVERTEXATTRIB1DARBPROC glVertexAttrib1dARB = NULL; +PFNGLVERTEXATTRIB1DVARBPROC glVertexAttrib1dvARB = NULL; +PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB = NULL; +PFNGLVERTEXATTRIB1FVARBPROC glVertexAttrib1fvARB = NULL; +PFNGLVERTEXATTRIB1SARBPROC glVertexAttrib1sARB = NULL; +PFNGLVERTEXATTRIB1SVARBPROC glVertexAttrib1svARB = NULL; +PFNGLVERTEXATTRIB2DARBPROC glVertexAttrib2dARB = NULL; +PFNGLVERTEXATTRIB2DVARBPROC glVertexAttrib2dvARB = NULL; +PFNGLVERTEXATTRIB2FARBPROC glVertexAttrib2fARB = NULL; +PFNGLVERTEXATTRIB2FVARBPROC glVertexAttrib2fvARB = NULL; +PFNGLVERTEXATTRIB2SARBPROC glVertexAttrib2sARB = NULL; +PFNGLVERTEXATTRIB2SVARBPROC glVertexAttrib2svARB = NULL; +PFNGLVERTEXATTRIB3DARBPROC glVertexAttrib3dARB = NULL; +PFNGLVERTEXATTRIB3DVARBPROC glVertexAttrib3dvARB = NULL; +PFNGLVERTEXATTRIB3FARBPROC glVertexAttrib3fARB = NULL; +PFNGLVERTEXATTRIB3FVARBPROC glVertexAttrib3fvARB = NULL; +PFNGLVERTEXATTRIB3SARBPROC glVertexAttrib3sARB = NULL; +PFNGLVERTEXATTRIB3SVARBPROC glVertexAttrib3svARB = NULL; +#endif // LL_LINUX +PFNGLVERTEXATTRIB4NBVARBPROC glVertexAttrib4nbvARB = NULL; +PFNGLVERTEXATTRIB4NIVARBPROC glVertexAttrib4nivARB = NULL; +PFNGLVERTEXATTRIB4NSVARBPROC glVertexAttrib4nsvARB = NULL; +PFNGLVERTEXATTRIB4NUBARBPROC glVertexAttrib4nubARB = NULL; +PFNGLVERTEXATTRIB4NUBVARBPROC glVertexAttrib4nubvARB = NULL; +PFNGLVERTEXATTRIB4NUIVARBPROC glVertexAttrib4nuivARB = NULL; +PFNGLVERTEXATTRIB4NUSVARBPROC glVertexAttrib4nusvARB = NULL; +#if LL_LINUX +PFNGLVERTEXATTRIB4BVARBPROC glVertexAttrib4bvARB = NULL; +PFNGLVERTEXATTRIB4DARBPROC glVertexAttrib4dARB = NULL; +PFNGLVERTEXATTRIB4DVARBPROC glVertexAttrib4dvARB = NULL; +PFNGLVERTEXATTRIB4FARBPROC glVertexAttrib4fARB = NULL; +PFNGLVERTEXATTRIB4FVARBPROC glVertexAttrib4fvARB = NULL; +PFNGLVERTEXATTRIB4IVARBPROC glVertexAttrib4ivARB = NULL; +PFNGLVERTEXATTRIB4SARBPROC glVertexAttrib4sARB = NULL; +PFNGLVERTEXATTRIB4SVARBPROC glVertexAttrib4svARB = NULL; +PFNGLVERTEXATTRIB4UBVARBPROC glVertexAttrib4ubvARB = NULL; +PFNGLVERTEXATTRIB4UIVARBPROC glVertexAttrib4uivARB = NULL; +PFNGLVERTEXATTRIB4USVARBPROC glVertexAttrib4usvARB = NULL; +PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB = NULL; +PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB = NULL; +PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB = NULL; +PFNGLPROGRAMSTRINGARBPROC glProgramStringARB = NULL; +PFNGLBINDPROGRAMARBPROC glBindProgramARB = NULL; +PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB = NULL; +PFNGLGENPROGRAMSARBPROC glGenProgramsARB = NULL; +PFNGLPROGRAMENVPARAMETER4DARBPROC glProgramEnvParameter4dARB = NULL; +PFNGLPROGRAMENVPARAMETER4DVARBPROC glProgramEnvParameter4dvARB = NULL; +PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB = NULL; +PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glProgramLocalParameter4dvARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB = NULL; +PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB = NULL; +PFNGLGETPROGRAMENVPARAMETERDVARBPROC glGetProgramEnvParameterdvARB = NULL; +PFNGLGETPROGRAMENVPARAMETERFVARBPROC glGetProgramEnvParameterfvARB = NULL; +PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB = NULL; +PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glGetProgramLocalParameterfvARB = NULL; +PFNGLGETPROGRAMIVARBPROC glGetProgramivARB = NULL; +PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB = NULL; +PFNGLGETVERTEXATTRIBDVARBPROC glGetVertexAttribdvARB = NULL; +PFNGLGETVERTEXATTRIBFVARBPROC glGetVertexAttribfvARB = NULL; +PFNGLGETVERTEXATTRIBIVARBPROC glGetVertexAttribivARB = NULL; +PFNGLGETVERTEXATTRIBPOINTERVARBPROC glGetVertexAttribPointervARB = NULL; +PFNGLISPROGRAMARBPROC glIsProgramARB = NULL; +#endif // LL_LINUX +PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocationARB = NULL; +PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB = NULL; +PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB = NULL; + +#if LL_WINDOWS +PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL; +#endif + +#if LL_LINUX +PFNGLCOLORTABLEEXTPROC glColorTableEXT = NULL; +#endif // LL_LINUX + +#endif + +LLGLManager gGLManager; + +LLGLManager::LLGLManager() +{ + mInited = FALSE; + mIsDisabled = FALSE; + mHasCubeMap = FALSE; + mHasMultitexture = FALSE; + mHasAnyAGP = FALSE; + mHasMipMapGeneration = FALSE; + mHasAnisotropic = FALSE; + mHasCompressedTextures = FALSE; + mHasNVVertexArrayRange = FALSE; + mHasNVFence = FALSE; + mHasARBEnvCombine = FALSE; + mHasATIVAO = FALSE; + mIsRadeon8500 = FALSE; + mIsRadeon9700 = FALSE; + mIsMobilityRadeon9000 = FALSE; + mIsGF2or4MX = FALSE; + mIsGF3 = FALSE; + mIsGFFX = FALSE; + mIsATI = FALSE; + mATIOffsetVerticalLines = FALSE; + mHasVertexShader = FALSE; + mHasFragmentShader = FALSE; + mHasShaderObjects = FALSE; + +#if LL_WINDOWS + mHasWGLARBPixelFormat = FALSE; +#endif // LL_WINDOWS + +#if LL_DARWIN + mHasAPPLEVertexArrayRange = FALSE; + mHasAPPLEFence = FALSE; + mHasAPPLEVAO = FALSE; +#endif + + mIsNVIDIA = FALSE; + mIsIntel = FALSE; + + mDriverVersionMajor = 1; + mDriverVersionMinor = 0; + mDriverVersionRelease = 0; + mGLVersion = 1.0f; + + mNumTextureUnits = 1; + mVRAM = 0; + mGLMaxVertexRange = 0; + mGLMaxIndexRange = 0; + mSoftwareBlendSSE = TRUE; +} + +//--------------------------------------------------------------------- +// Global initialization for GL +//--------------------------------------------------------------------- +void LLGLManager::initWGL() +{ + mHasPBuffer = FALSE; +#if LL_WINDOWS && !LL_MESA_HEADLESS + if (!glh_init_extensions("WGL_ARB_pixel_format")) + { + llwarns << "No ARB pixel format extensions" << llendl; + } + + if (ExtensionExists("WGL_EXT_swap_control", gGLHExts.mSysExts)) + { + GLH_EXT_NAME(wglSwapIntervalEXT) = (PFNWGLSWAPINTERVALEXTPROC)GLH_EXT_GET_PROC_ADDRESS("wglSwapIntervalEXT"); + } + + mHasWGLARBPixelFormat = glh_init_extensions("WGL_ARB_pbuffer"); + if( !mHasWGLARBPixelFormat ) + { + llwarns << "No ARB WGL PBuffer extensions" << llendl; + } + + if( !glh_init_extensions("WGL_ARB_render_texture") ) + { + llwarns << "No ARB WGL render texture extensions" << llendl; + } + + mHasPBuffer = ExtensionExists("WGL_ARB_pbuffer", gGLHExts.mSysExts) && + ExtensionExists("WGL_ARB_render_texture", gGLHExts.mSysExts) && + ExtensionExists("WGL_ARB_pixel_format", gGLHExts.mSysExts); +#endif +} + +// return false if unable (or unwilling due to old drivers) to init GL +bool LLGLManager::initGL() +{ + if (mInited) + { + llerrs << "Calling init on LLGLManager after already initialized!" << llendl; + } + + GLint alpha_bits; + glGetIntegerv( GL_ALPHA_BITS, &alpha_bits ); + if( 8 != alpha_bits ) + { + llwarns << "Frame buffer has less than 8 bits of alpha. Avatar texture compositing will fail." << llendl; + } + + // This function uses at least one variable that's initialized below. + // Moved this call down to after we figure out which card we're dealing with. -- MBW 2003.10.07 +// initExtensions(); + + // Extract video card strings and convert to upper case to + // work around driver-to-driver variation in capitalization. + mGLVendor = LLString((const char *)glGetString(GL_VENDOR)); + LLString::toUpper(mGLVendor); + + mGLRenderer = LLString((const char *)glGetString(GL_RENDERER)); + LLString::toUpper(mGLRenderer); + + parse_gl_version( &mDriverVersionMajor, + &mDriverVersionMinor, + &mDriverVersionRelease, + &mDriverVersionVendorString ); + + mGLVersion = mDriverVersionMajor + mDriverVersionMinor * .1f; + + // Trailing space necessary to keep "nVidia Corpor_ati_on" cards + // from being recognized as ATI. + if (mGLVendor.substr(0,4) == "ATI ") + { + BOOL mobile = FALSE; + if (mGLRenderer.find("MOBILITY") != LLString::npos) + { + mobile = TRUE; + } + mIsATI = TRUE; + if ( mGLRenderer.find("9500") != LLString::npos + || mGLRenderer.find("9600") != LLString::npos + || mGLRenderer.find("9700") != LLString::npos + || mGLRenderer.find("9800") != LLString::npos ) + { + mIsRadeon9700 = TRUE; + } + else if (mGLRenderer.find("8500") != LLString::npos + || mGLRenderer.find( "9000") != LLString::npos + || mGLRenderer.find("9100") != LLString::npos + || mGLRenderer.find("9200") != LLString::npos) + { + mIsRadeon8500 = TRUE; + if (mobile && mGLRenderer.find("9000") != LLString::npos) + { + mIsMobilityRadeon9000 = TRUE; + } + } + +#if LL_WINDOWS && !LL_MESA_HEADLESS + if (mIsRadeon9700 && mDriverVersionRelease < 3833) + { + return false; // Unsupported hardware + } + + if (mDriverVersionRelease < 3842) + { + mATIOffsetVerticalLines = TRUE; + } +#endif // LL_WINDOWS + } + else if (mGLVendor.find("NVIDIA ") != LLString::npos) + { + mIsNVIDIA = TRUE; + if ( mGLRenderer.find("GEFORCE4 MX") != LLString::npos + || mGLRenderer.find("GEFORCE2") != LLString::npos + || mGLRenderer.find("GEFORCE 2") != LLString::npos + || mGLRenderer.find("GEFORCE4 460 GO") != LLString::npos + || mGLRenderer.find("GEFORCE4 440 GO") != LLString::npos + || mGLRenderer.find("GEFORCE4 420 GO") != LLString::npos) + { + mIsGF2or4MX = TRUE; + } + else if (mGLRenderer.find("GEFORCE FX") != LLString::npos + || mGLRenderer.find("QUADRO FX") != LLString::npos + || mGLRenderer.find("NV34") != LLString::npos) + { + mIsGFFX = TRUE; + } + else if(mGLRenderer.find("GEFORCE3") != LLString::npos) + { + mIsGF3 = TRUE; + } + + } + else if (mGLVendor.find("INTEL") != LLString::npos) + { + mIsIntel = TRUE; + } + + // This is called here because it depends on the setting of mIsGF2or4MX, and sets up mHasMultitexture. + initExtensions(); + + if (mHasMultitexture) + { + GLint num_tex_units; + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &num_tex_units); + mNumTextureUnits = llmin(num_tex_units, (GLint)MAX_GL_TEXTURE_UNITS); + if (mIsIntel) + { + mNumTextureUnits = llmin(mNumTextureUnits, 2); + } + } + else + { + // We don't support cards that don't support the GL_ARB_multitexture extension + llwarns << "GL Drivers do not support GL_ARB_multitexture" << llendl; + return false; + } + + + initGLStates(); + return true; +} + +LLString LLGLManager::getGLInfoString() +{ + LLString info_str; + LLString all_exts, line; + + info_str += LLString("GL_VENDOR ") + LLString((const char *)glGetString(GL_VENDOR)) + LLString("\n"); + info_str += LLString("GL_RENDERER ") + LLString((const char *)glGetString(GL_RENDERER)) + LLString("\n"); + info_str += LLString("GL_VERSION ") + LLString((const char *)glGetString(GL_VERSION)) + LLString("\n"); + +#if !LL_MESA_HEADLESS + all_exts = (const char *)gGLHExts.mSysExts; + LLString::replaceChar(all_exts, ' ', '\n'); + info_str += LLString("GL_EXTENSIONS:\n") + all_exts + LLString("\n"); +#endif + + return info_str; +} + +LLString LLGLManager::getRawGLString() +{ + LLString gl_string; + gl_string.assign((char*)glGetString(GL_VENDOR)); + gl_string.append(" "); + gl_string.append((char*)glGetString(GL_RENDERER)); + return gl_string; +} + +void LLGLManager::shutdownGL() +{ + if (mInited) + { + stop_glerror(); + mInited = FALSE; + } +} + +// these are used to turn software blending on. They appear in the Debug/Avatar menu +// presence of vertex skinning/blending or vertex programs will set these to FALSE by default. + +extern LLCPUInfo gSysCPU; + +void LLGLManager::initExtensions() +{ + mSoftwareBlendSSE = gSysCPU.hasSSE(); + +#if LL_MESA_HEADLESS +# if GL_ARB_multitexture + mHasMultitexture = TRUE; +# else + mHasMultitexture = FALSE; +# endif +# if GL_ARB_texture_env_combine + mHasARBEnvCombine = TRUE; +# else + mHasARBEnvCombine = FALSE; +# endif +# if GL_ARB_texture_compression + mHasCompressedTextures = TRUE; +# else + mHasCompressedTextures = FALSE; +# endif +# if GL_ARB_vertex_buffer_object + mHasVertexBufferObject = TRUE; +# else + mHasVertexBufferObject = FALSE; +# endif + mHasMipMapGeneration = FALSE; + mHasPalettedTextures = FALSE; + mHasNVVertexArrayRange = FALSE; + mHasNVFence = FALSE; + mHasSeparateSpecularColor = FALSE; + mHasAnisotropic = FALSE; + mHasCubeMap = FALSE; + mHasATIVAO = FALSE; + mHasOcclusionQuery = FALSE; + mHasShaderObjects = FALSE; + mHasVertexShader = FALSE; + mHasFragmentShader = FALSE; +#else // LL_MESA_HEADLESS + mHasMultitexture = glh_init_extensions("GL_ARB_multitexture"); + mHasMipMapGeneration = glh_init_extensions("GL_SGIS_generate_mipmap"); + mHasPalettedTextures = glh_init_extension("GL_EXT_paletted_texture"); + mHasNVVertexArrayRange = glh_init_extensions("GL_NV_vertex_array_range"); + mHasNVFence = glh_init_extensions("GL_NV_fence"); + mHasSeparateSpecularColor = glh_init_extensions("GL_EXT_separate_specular_color"); + mHasAnisotropic = glh_init_extensions("GL_EXT_texture_filter_anisotropic"); + glh_init_extensions("GL_ARB_texture_cube_map"); + mHasCubeMap = ExtensionExists("GL_ARB_texture_cube_map", gGLHExts.mSysExts); + mHasARBEnvCombine = ExtensionExists("GL_ARB_texture_env_combine", gGLHExts.mSysExts); + mHasCompressedTextures = glh_init_extensions("GL_ARB_texture_compression"); + mHasVertexBufferObject = ExtensionExists("GL_ARB_vertex_buffer_object", gGLHExts.mSysExts); + mHasATIVAO = ExtensionExists("GL_ATI_vertex_array_object", gGLHExts.mSysExts); + mHasOcclusionQuery = ExtensionExists("GL_ARB_occlusion_query", gGLHExts.mSysExts); + mHasShaderObjects = ExtensionExists("GL_ARB_shader_objects", gGLHExts.mSysExts) && ExtensionExists("GL_ARB_shading_language_100", gGLHExts.mSysExts); + mHasVertexShader = ExtensionExists("GL_ARB_vertex_program", gGLHExts.mSysExts) && ExtensionExists("GL_ARB_vertex_shader", gGLHExts.mSysExts) + && ExtensionExists("GL_ARB_shading_language_100", gGLHExts.mSysExts); + mHasFragmentShader = ExtensionExists("GL_ARB_fragment_shader", gGLHExts.mSysExts) && ExtensionExists("GL_ARB_shading_language_100", gGLHExts.mSysExts); +#endif + +#if LL_LINUX + // Our extension support for the Linux Client is very young with some + // potential driver gotchas, so offer a semi-secret way to turn it off. + if (getenv("LL_GL_NOEXT")) + { + //mHasMultitexture = FALSE; // NEEDED! + mHasARBEnvCombine = FALSE; + mHasCompressedTextures = FALSE; + mHasVertexBufferObject = FALSE; + mHasMipMapGeneration = FALSE; + mHasPalettedTextures = FALSE; + mHasNVVertexArrayRange = FALSE; + mHasNVFence = FALSE; + mHasSeparateSpecularColor = FALSE; + mHasAnisotropic = FALSE; + mHasCubeMap = FALSE; + mHasATIVAO = FALSE; + mHasOcclusionQuery = FALSE; + mHasShaderObjects = FALSE; + mHasVertexShader = FALSE; + mHasFragmentShader = FALSE; + llwarns << "GL extension support DISABLED via LL_GL_NOEXT" << + llendl; + } + else if (getenv("LL_GL_BASICEXT")) + { + // This switch attempts to turn off all support for exotic + // extensions which I believe correspond to fatal driver + // bug reports. This should be the default until we get a + // proper blacklist/whitelist on Linux. + mHasMipMapGeneration = FALSE; + mHasPalettedTextures = FALSE; + mHasNVVertexArrayRange = FALSE; + mHasNVFence = FALSE; + mHasAnisotropic = FALSE; + mHasATIVAO = FALSE; + mHasOcclusionQuery = FALSE; // source of many ATI system hangs + mHasShaderObjects = FALSE; + mHasVertexShader = FALSE; + mHasFragmentShader = FALSE; + llwarns << "GL extension support forced to SIMPLE level via LL_GL_BASICEXT" << + llendl; + } + if (getenv("LL_GL_BLACKLIST")) + { + // This lets advanced troubleshooters disable specific + // GL extensions to isolate problems with their hardware. + // SL-28126 + const char *const blacklist = getenv("LL_GL_BLACKLIST"); + llwarns << "GL extension support partially disabled via LL_GL_BLACKLIST: " << blacklist << llendl; + if (strchr(blacklist,'a')) mHasARBEnvCombine = FALSE; + if (strchr(blacklist,'b')) mHasCompressedTextures = FALSE; + if (strchr(blacklist,'c')) mHasVertexBufferObject = FALSE; + if (strchr(blacklist,'d')) mHasMipMapGeneration = FALSE;//S + if (strchr(blacklist,'e')) mHasPalettedTextures = FALSE;//S + if (strchr(blacklist,'f')) mHasNVVertexArrayRange = FALSE;//S + if (strchr(blacklist,'g')) mHasNVFence = FALSE;//S + if (strchr(blacklist,'h')) mHasSeparateSpecularColor = FALSE; + if (strchr(blacklist,'i')) mHasAnisotropic = FALSE;//S + if (strchr(blacklist,'j')) mHasCubeMap = FALSE; + if (strchr(blacklist,'k')) mHasATIVAO = FALSE;//S + if (strchr(blacklist,'l')) mHasOcclusionQuery = FALSE; + if (strchr(blacklist,'m')) mHasShaderObjects = FALSE;//S + if (strchr(blacklist,'n')) mHasVertexShader = FALSE;//S + if (strchr(blacklist,'o')) mHasFragmentShader = FALSE;//S + } +#endif // LL_LINUX + +#if LL_DARWIN || LL_LINUX + // MBW -- 12/4/2003 -- Using paletted textures causes a bunch of avatar rendering problems on the Mac. + // Not sure if this is due to driver problems or incorrect use of the extension, but I'm disabling it for now. + // Tofu - 2006-10-03 -- Same problem on Linux. + mHasPalettedTextures = false; +#endif + + if (!mHasMultitexture) + { + llinfos << "Couldn't initialize multitexturing" << llendl; + } + if (!mHasMipMapGeneration) + { + llinfos << "Couldn't initialize mipmap generation" << llendl; + } + if (!mHasARBEnvCombine) + { + llinfos << "Couldn't initialize GL_ARB_texture_env_combine" << llendl; + } + if (!mHasPalettedTextures) + { + llinfos << "Couldn't initialize GL_EXT_paletted_texture" << llendl; + } + if (!mHasNVVertexArrayRange) + { + llinfos << "Couldn't initialize GL_NV_vertex_array_range" << llendl; + } + if (!mHasNVFence) + { + llinfos << "Couldn't initialize GL_NV_fence" << llendl; + } + if (!mHasSeparateSpecularColor) + { + llinfos << "Couldn't initialize separate specular color" << llendl; + } + if (!mHasAnisotropic) + { + llinfos << "Couldn't initialize anisotropic filtering" << llendl; + } + if (!mHasCompressedTextures) + { + llinfos << "Couldn't initialize GL_ARB_texture_compression" << llendl; + } + if (!mHasOcclusionQuery) + { + llinfos << "Couldn't initialize GL_ARB_occlusion_query" << llendl; + } + if (!mHasShaderObjects) + { + llinfos << "Couldn't initialize GL_ARB_shader_objects" << llendl; + } + if (!mHasVertexShader) + { + llinfos << "Couldn't initialize GL_ARB_vertex_shader" << llendl; + } + if (!mHasFragmentShader) + { + llinfos << "Couldn't initialize GL_ARB_fragment_shader" << llendl; + } + + // Disable certain things due to known bugs + if (mIsIntel && mHasMipMapGeneration) + { + llinfos << "Disabling mip-map generation for Intel GPUs" << llendl; + mHasMipMapGeneration = FALSE; + } + if (mIsATI && mHasMipMapGeneration) + { + llinfos << "Disabling mip-map generation for ATI GPUs (performance opt)" << llendl; + mHasMipMapGeneration = FALSE; + } + + // Misc + if (mHasNVFence || mHasATIVAO) + { + mHasAnyAGP = TRUE; + } + glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, (GLint*) &mGLMaxVertexRange); + glGetIntegerv(GL_MAX_ELEMENTS_INDICES, (GLint*) &mGLMaxIndexRange); + + // Apple specific +#if LL_DARWIN + mHasAPPLEVertexArrayRange = glh_init_extensions("GL_APPLE_vertex_array_range"); + if (!mHasAPPLEVertexArrayRange) + { + llinfos << "Couldn't initialize GL_APPLE_vertex_array_range" << llendl; + } + + mHasAPPLEFence = glh_init_extensions("GL_APPLE_fence"); + if (!mHasAPPLEFence) + { + llinfos << "Couldn't initialize GL_APPLE_fence" << llendl; + } + + mHasAPPLEVAO = glh_init_extensions("GL_APPLE_vertex_array_object"); + if (mHasAPPLEVAO) + { + llinfos << "Has GL_APPLE_vertex_array_object!" << llendl; + } + + if(mHasAPPLEFence) + { + mHasAnyAGP = TRUE; + } +#endif // LL_DARWIN + +#if (LL_WINDOWS || LL_LINUX) && !LL_MESA_HEADLESS + llinfos << "GL Probe: Getting symbols" << llendl; + if (mHasVertexBufferObject) + { + glBindBufferARB = (PFNGLBINDBUFFERARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBindBufferARB"); + glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteBuffersARB"); + glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGenBuffersARB"); + glIsBufferARB = (PFNGLISBUFFERARBPROC)GLH_EXT_GET_PROC_ADDRESS("glIsBufferARB"); + glBufferDataARB = (PFNGLBUFFERDATAARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferDataARB"); + glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBufferSubDataARB"); + glGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferSubDataARB"); + glMapBufferARB = (PFNGLMAPBUFFERARBPROC)GLH_EXT_GET_PROC_ADDRESS("glMapBufferARB"); + glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)GLH_EXT_GET_PROC_ADDRESS("glUnmapBufferARB"); + glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferParameterivARB"); + glGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetBufferPointervARB"); + } + if (mHasATIVAO) + { + // Initialize the extension. + llinfos << "Has ATI_vertex_array_object!" << llendl; + glNewObjectBufferATI = (PFNGLNEWOBJECTBUFFERATIPROC)GLH_EXT_GET_PROC_ADDRESS("glNewObjectBufferATI"); + glIsObjectBufferATI = (PFNGLISOBJECTBUFFERATIPROC)GLH_EXT_GET_PROC_ADDRESS("glIsObjectBufferATI"); + glUpdateObjectBufferATI = (PFNGLUPDATEOBJECTBUFFERATIPROC)GLH_EXT_GET_PROC_ADDRESS("glUpdateObjectBufferATI"); + glGetObjectBufferfvATI = (PFNGLGETOBJECTBUFFERFVATIPROC)GLH_EXT_GET_PROC_ADDRESS("glGetObjectBufferfvATI"); + glGetObjectBufferivATI = (PFNGLGETOBJECTBUFFERIVATIPROC)GLH_EXT_GET_PROC_ADDRESS("glGetObjectBufferivATI"); + glFreeObjectBufferATI = (PFNGLFREEOBJECTBUFFERATIPROC)GLH_EXT_GET_PROC_ADDRESS("glFreeObjectBufferATI"); + glArrayObjectATI = (PFNGLARRAYOBJECTATIPROC)GLH_EXT_GET_PROC_ADDRESS("glArrayObjectATI"); + glVertexAttribArrayObjectATI = (PFNGLVERTEXATTRIBARRAYOBJECTATIPROC)GLH_EXT_GET_PROC_ADDRESS("glVertexAttribArrayObjectATI"); + glGetArrayObjectfvATI = (PFNGLGETARRAYOBJECTFVATIPROC)GLH_EXT_GET_PROC_ADDRESS("glGetArrayObjectfvATI"); + glGetArrayObjectivATI = (PFNGLGETARRAYOBJECTIVATIPROC)GLH_EXT_GET_PROC_ADDRESS("glGetArrayObjectivATI"); + glVariantObjectArrayATI = (PFNGLVARIANTARRAYOBJECTATIPROC)GLH_EXT_GET_PROC_ADDRESS("glVariantObjectArrayATI"); + glGetVariantArrayObjectfvATI = (PFNGLGETVARIANTARRAYOBJECTFVATIPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVariantArrayObjectfvATI"); + glGetVariantArrayObjectivATI = (PFNGLGETVARIANTARRAYOBJECTIVATIPROC)GLH_EXT_GET_PROC_ADDRESS("glGetVariantArrayObjectivATI"); + } +#if !LL_LINUX + // This is expected to be a static symbol on Linux GL implementations + glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)GLH_EXT_GET_PROC_ADDRESS("glDrawRangeElements"); + if (!glDrawRangeElements) + { + mGLMaxVertexRange = 0; + mGLMaxIndexRange = 0; + } +#endif // !LL_LINUX +#if LL_LINUX + // On Linux we need to get glColorTableEXT dynamically. + if (mHasPalettedTextures) + { + glColorTableEXT = (PFNGLCOLORTABLEEXTPROC)GLH_EXT_GET_PROC_ADDRESS("glColorTableEXT"); + } +#endif // LL_LINUX + if (mHasOcclusionQuery) + { + glGenQueriesARB = (PFNGLGENQUERIESARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGenQueriesARB"); + glDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC)GLH_EXT_GET_PROC_ADDRESS("glDeleteQueriesARB"); + glIsQueryARB = (PFNGLISQUERYARBPROC)GLH_EXT_GET_PROC_ADDRESS("glIsQueryARB"); + glBeginQueryARB = (PFNGLBEGINQUERYARBPROC)GLH_EXT_GET_PROC_ADDRESS("glBeginQueryARB"); + glEndQueryARB = (PFNGLENDQUERYARBPROC)GLH_EXT_GET_PROC_ADDRESS("glEndQueryARB"); + glGetQueryivARB = (PFNGLGETQUERYIVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryivARB"); + glGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectivARB"); + glGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC)GLH_EXT_GET_PROC_ADDRESS("glGetQueryObjectuivARB"); + } + if (mHasShaderObjects) + { + glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteObjectARB"); + glGetHandleARB = (PFNGLGETHANDLEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetHandleARB"); + glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDetachObjectARB"); + glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCreateShaderObjectARB"); + glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glShaderSourceARB"); + glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCompileShaderARB"); + glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glCreateProgramObjectARB"); + glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glAttachObjectARB"); + glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glLinkProgramARB"); + glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUseProgramObjectARB"); + glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glValidateProgramARB"); + glUniform1fARB = (PFNGLUNIFORM1FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1fARB"); + glUniform2fARB = (PFNGLUNIFORM2FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2fARB"); + glUniform3fARB = (PFNGLUNIFORM3FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3fARB"); + glUniform4fARB = (PFNGLUNIFORM4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4fARB"); + glUniform1iARB = (PFNGLUNIFORM1IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1iARB"); + glUniform2iARB = (PFNGLUNIFORM2IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2iARB"); + glUniform3iARB = (PFNGLUNIFORM3IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3iARB"); + glUniform4iARB = (PFNGLUNIFORM4IARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4iARB"); + glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1fvARB"); + glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2fvARB"); + glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3fvARB"); + glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4fvARB"); + glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform1ivARB"); + glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform2ivARB"); + glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform3ivARB"); + glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniform4ivARB"); + glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix2fvARB"); + glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix3fvARB"); + glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glUniformMatrix4fvARB"); + glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetObjectParameterfvARB"); + glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetObjectParameterivARB"); + glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetInfoLogARB"); + glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttachedObjectsARB"); + glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformLocationARB"); + glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetActiveUniformARB"); + glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformfvARB"); + glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetUniformivARB"); + glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetShaderSourceARB"); + } + if (mHasVertexShader) + { + glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetAttribLocationARB"); + glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindAttribLocationARB"); + glGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetActiveAttribARB"); + glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dARB"); + glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1dvARB"); + glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fARB"); + glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1fvARB"); + glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1sARB"); + glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib1svARB"); + glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dARB"); + glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2dvARB"); + glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fARB"); + glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2fvARB"); + glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2sARB"); + glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib2svARB"); + glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dARB"); + glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3dvARB"); + glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fARB"); + glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3fvARB"); + glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3sARB"); + glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib3svARB"); + glVertexAttrib4nbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nbvARB"); + glVertexAttrib4nivARB = (PFNGLVERTEXATTRIB4NIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nivARB"); + glVertexAttrib4nsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nsvARB"); + glVertexAttrib4nubARB = (PFNGLVERTEXATTRIB4NUBARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nubARB"); + glVertexAttrib4nubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nubvARB"); + glVertexAttrib4nuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nuivARB"); + glVertexAttrib4nusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4nusvARB"); + glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4bvARB"); + glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dARB"); + glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4dvARB"); + glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fARB"); + glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4fvARB"); + glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ivARB"); + glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4sARB"); + glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4svARB"); + glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4ubvARB"); + glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4uivARB"); + glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttrib4usvARB"); + glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) GLH_EXT_GET_PROC_ADDRESS("glVertexAttribPointerARB"); + glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) GLH_EXT_GET_PROC_ADDRESS("glEnableVertexAttribArrayARB"); + glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDisableVertexAttribArrayARB"); + glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramStringARB"); + glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glBindProgramARB"); + glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glDeleteProgramsARB"); + glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGenProgramsARB"); + glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4dARB"); + glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4dvARB"); + glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4fARB"); + glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramEnvParameter4fvARB"); + glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4dARB"); + glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4dvARB"); + glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4fARB"); + glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glProgramLocalParameter4fvARB"); + glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramEnvParameterdvARB"); + glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramEnvParameterfvARB"); + glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramLocalParameterdvARB"); + glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramLocalParameterfvARB"); + glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramivARB"); + glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetProgramStringARB"); + glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribdvARB"); + glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribfvARB"); + glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glGetVertexAttribivARB"); + glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC) GLH_EXT_GET_PROC_ADDRESS("glgetVertexAttribPointervARB"); + glIsProgramARB = (PFNGLISPROGRAMARBPROC) GLH_EXT_GET_PROC_ADDRESS("glIsProgramARB"); + } + llinfos << "GL Probe: Got symbols" << llendl; +#endif + + mInited = TRUE; +} + +void rotate_quat(LLQuaternion& rotation) +{ + F32 angle_radians, x, y, z; + rotation.getAngleAxis(&angle_radians, &x, &y, &z); + glRotatef(angle_radians * RAD_TO_DEG, x, y, z); +} + +void flush_glerror() +{ + glGetError(); +} + +void assert_glerror() +{ + if (gNoRender) + { + return; + } + if (!gGLManager.mInited) + { + llerrs << "GL not initialized" << llendl; + } + // Create or update texture to be used with this data + GLenum error; + error = glGetError(); + if (error) + { +#ifndef LL_LINUX // *FIX: ! This should be an error for linux as well. + llerrs << "GL Error:" << gluErrorString(error) << llendl; +#endif + } +} + +void clear_glerror() +{ + // Create or update texture to be used with this data + GLenum error; + error = glGetError(); +} + +//============================================================================ + +// +// LLGLState +// + +// Static members +std::map LLGLState::sStateMap; + +GLboolean LLGLDepthTest::sDepthEnabled = GL_FALSE; // OpenGL default +GLenum LLGLDepthTest::sDepthFunc = GL_LESS; // OpenGL default +GLboolean LLGLDepthTest::sWriteEnabled = GL_TRUE; // OpenGL default + +//static +void LLGLState::initClass() +{ + sStateMap[GL_DITHER] = GL_TRUE; +} + +//static +void LLGLState::restoreGL() +{ + sStateMap.clear(); + initClass(); +} + +void LLGLState::dumpStates() +{ + llinfos << "GL States:" << llendl; + for (std::map::iterator iter = sStateMap.begin(); + iter != sStateMap.end(); ++iter) + { + llinfos << llformat(" 0x%04x : %s",(S32)iter->first,iter->second?"TRUE":"FALSE") << llendl; + } +} + +void LLGLState::checkStates() +{ + stop_glerror(); + + GLint activeTexture; + glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &activeTexture); + + if (activeTexture != GL_TEXTURE0_ARB) + { + LL_GL_ERRS << "Texture channel corrupted. " << llendl; + } + + GLint src; + GLint dst; + glGetIntegerv(GL_BLEND_SRC, &src); + glGetIntegerv(GL_BLEND_DST, &dst); + + if (src != GL_SRC_ALPHA || dst != GL_ONE_MINUS_SRC_ALPHA) + { + LL_GL_ERRS << "Blend function corrupted: " << std::hex << src << " " << std::hex << dst << llendl; + } + + for (std::map::iterator iter = sStateMap.begin(); + iter != sStateMap.end(); ++iter) + { + LLGLenum state = iter->first; + LLGLboolean cur_state = iter->second; + LLGLboolean gl_state = glIsEnabled(state); + if(cur_state != gl_state) + { + dumpStates(); + LL_GL_ERRS << llformat("LLGLState error. State: 0x%04x",state) << llendl; + } + } + + stop_glerror(); +} + +void LLGLState::checkTextureChannels() +{ + GLint activeTexture; + glGetIntegerv(GL_ACTIVE_TEXTURE_ARB, &activeTexture); + + BOOL error = FALSE; + + if (activeTexture != GL_TEXTURE0_ARB) + { + error = TRUE; + llwarns << "Active texture channel corrupted. " << llendl; + } + + GLint maxTextureUnits; + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTextureUnits); + + static const char* label[] = + { + "GL_TEXTURE_2D", + "GL_TEXTURE_COORD_ARRAY", + "GL_TEXTURE_1D", + "GL_TEXTURE_CUBE_MAP_ARB", + "GL_TEXTURE_GEN_S", + "GL_TEXTURE_GEN_T", + "GL_TEXTURE_GEN_Q", + "GL_TEXTURE_GEN_R" + }; + + static GLint value[] = + { + GL_TEXTURE_2D, + GL_TEXTURE_COORD_ARRAY, + GL_TEXTURE_1D, + GL_TEXTURE_CUBE_MAP_ARB, + GL_TEXTURE_GEN_S, + GL_TEXTURE_GEN_T, + GL_TEXTURE_GEN_Q, + GL_TEXTURE_GEN_R + }; + + GLint stackDepth = 0; + LLMatrix4 identity; + LLMatrix4 matrix; + + for (GLint i = 0; i < maxTextureUnits; i++) + { + glActiveTextureARB(GL_TEXTURE0_ARB+i); + glClientActiveTextureARB(GL_TEXTURE0_ARB+i); + + glGetIntegerv(GL_TEXTURE_STACK_DEPTH, &stackDepth); + + if (stackDepth != 1) + { + error = TRUE; + llwarns << "Texture matrix stack corrupted." << llendl; + } + + glGetFloatv(GL_TEXTURE_MATRIX, (GLfloat*) matrix.mMatrix); + + if (matrix != identity) + { + error = TRUE; + llwarns << "Texture matrix in channel " << i << " corrupt." << llendl; + } + + for (S32 j = (i == 0 ? 2 : 0); j < 8; j++) + { + if (glIsEnabled(value[j])) + { + error = TRUE; + llwarns << "Texture channel " << i << " still has " << label[j] << " enabled." << llendl; + } + } + } + + glActiveTextureARB(GL_TEXTURE0_ARB); + glClientActiveTextureARB(GL_TEXTURE0_ARB); + + if (error) + { + LL_GL_ERRS << "GL texture state corruption detected." << llendl; + } +} + +void LLGLState::checkClientArrays() +{ + BOOL error = FALSE; + static const char* label[] = + { + //"GL_INDEX_ARRAY", + "GL_NORMAL_ARRAY", + //"GL_VERTEX_ARRAY", + "GL_COLOR_ARRAY", + "GL_TEXTURE_COORD_ARRAY" + }; + + static GLint value[] = + { + //GL_INDEX_ARRAY, + GL_NORMAL_ARRAY, + //GL_VERTEX_ARRAY, + GL_COLOR_ARRAY, + GL_TEXTURE_COORD_ARRAY + }; + + for (S32 j = 0; j < 3; j++) + { + if (glIsEnabled(value[j])) + { + error = TRUE; + llwarns << "GL still has " << label[j] << " enabled." << llendl; + } + } + + if (error) + { + LL_GL_ERRS << "GL client array corruption detected." << llendl; + } +} + +//============================================================================ + +LLGLState::LLGLState(LLGLenum state, S32 enabled) +{ + stop_glerror(); + mState = state; + if (state) + { + mWasEnabled = sStateMap[state]; + llassert(mWasEnabled == glIsEnabled(state)); + setEnabled(enabled); + stop_glerror(); + } +} + +void LLGLState::setEnabled(S32 enabled) +{ + if (!mState) + { + return; + } + if (enabled == CURRENT_STATE) + { + enabled = sStateMap[mState] == GL_TRUE ? TRUE : FALSE; + } + else if (enabled == TRUE && sStateMap[mState] != GL_TRUE) + { + glEnable(mState); + sStateMap[mState] = GL_TRUE; + } + else if (enabled == FALSE && sStateMap[mState] != GL_FALSE) + { + glDisable(mState); + sStateMap[mState] = GL_FALSE; + } + mIsEnabled = enabled; +} + +LLGLState::~LLGLState() +{ + stop_glerror(); + if (mState) + { +#if LL_DEBUG + LLGLboolean cur_state = sStateMap[mState]; + llassert(cur_state == glIsEnabled(mState)); +#endif + if (mIsEnabled != mWasEnabled) + { + if (mWasEnabled) + { + glEnable(mState); + sStateMap[mState] = GL_TRUE; + } + else + { + glDisable(mState); + sStateMap[mState] = GL_FALSE; + } + } + } + stop_glerror(); +} + +//============================================================================ + +void LLGLManager::initGLStates() +{ + //gl states moved to classes in llglstates.h + LLGLState::initClass(); +} + +//============================================================================ + +void enable_vertex_weighting(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) glEnableVertexAttribArrayARB(index); // vertex weights +#endif +} + +void disable_vertex_weighting(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) glDisableVertexAttribArrayARB(index); // vertex weights +#endif +} + +void enable_binormals(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) + { + glEnableVertexAttribArrayARB(index); // binormals + } +#endif +} + +void disable_binormals(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) + { + glDisableVertexAttribArrayARB(index); // binormals + } +#endif +} + + +void enable_cloth_weights(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) glEnableVertexAttribArrayARB(index); +#endif +} + +void disable_cloth_weights(const S32 index) +{ +#if GL_ARB_vertex_program + if (index > 0) glDisableVertexAttribArrayARB(index); +#endif +} + +void set_vertex_weights(const S32 index, const F32 *weights) +{ +#if GL_ARB_vertex_program + if (index > 0) glVertexAttribPointerARB(index, 1, GL_FLOAT, FALSE, 0, weights); + stop_glerror(); +#endif +} + +void set_vertex_clothing_weights(const S32 index, const U32 stride, const LLVector4 *weights) +{ +#if GL_ARB_vertex_program + if (index > 0) glVertexAttribPointerARB(index, 4, GL_FLOAT, TRUE, stride, weights); + stop_glerror(); +#endif +} + +void set_binormals(const S32 index, const U32 stride,const LLVector3 *binormals) +{ +#if GL_ARB_vertex_program + if (index > 0) glVertexAttribPointerARB(index, 3, GL_FLOAT, FALSE, stride, binormals); + stop_glerror(); +#endif +} + + +void set_palette(U8 *palette_data) +{ + if (gGLManager.mHasPalettedTextures) + { + glColorTableEXT(GL_TEXTURE_2D, GL_RGBA8, 256, GL_RGBA, GL_UNSIGNED_BYTE, palette_data); + } +} + + +void parse_gl_version( S32* major, S32* minor, S32* release, LLString* vendor_specific ) +{ + // GL_VERSION returns a null-terminated string with the format: + // .[.] [] + + const char* version = (const char*) glGetString(GL_VERSION); + *major = 0; + *minor = 0; + *release = 0; + vendor_specific->assign(""); + + if( !version ) + { + return; + } + + LLString ver_copy( version ); + S32 len = (S32)strlen( version ); + S32 i = 0; + S32 start; + // Find the major version + start = i; + for( ; i < len; i++ ) + { + if( '.' == version[i] ) + { + break; + } + } + LLString major_str = ver_copy.substr(start,i-start); + LLString::convertToS32(major_str, *major); + + if( '.' == version[i] ) + { + i++; + } + + // Find the minor version + start = i; + for( ; i < len; i++ ) + { + if( ('.' == version[i]) || isspace(version[i]) ) + { + break; + } + } + LLString minor_str = ver_copy.substr(start,i-start); + LLString::convertToS32(minor_str, *minor); + + // Find the release number (optional) + if( '.' == version[i] ) + { + i++; + + start = i; + for( ; i < len; i++ ) + { + if( isspace(version[i]) ) + { + break; + } + } + + LLString release_str = ver_copy.substr(start,i-start); + LLString::convertToS32(release_str, *release); + } + + // Skip over any white space + while( version[i] && isspace( version[i] ) ) + { + i++; + } + + // Copy the vendor-specific string (optional) + if( version[i] ) + { + vendor_specific->assign( version + i ); + } +} diff --git a/linden/indra/llwindow/llgl.h b/linden/indra/llwindow/llgl.h new file mode 100644 index 0000000..1c93b63 --- /dev/null +++ b/linden/indra/llwindow/llgl.h @@ -0,0 +1,272 @@ +/** + * @file llgl.h + * @brief LLGL definition + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLGL_H +#define LL_LLGL_H + +// This file contains various stuff for handling gl extensions and other gl related stuff. + +#include +#include + +#include "llerror.h" +#include "v4color.h" +#include "llstring.h" +#include "stdtypes.h" +#include "v4math.h" +#include "llgltypes.h" + +#define LL_GL_ERRS llerrs + +// Manage GL extensions... +class LLGLManager +{ +public: + LLGLManager(); + + bool initGL(); + void shutdownGL(); + + void initWGL(); // Initializes stupid WGL extensions + + LLString getRawGLString(); // For sending to simulator + + BOOL mInited; + BOOL mIsDisabled; + + // Extensions used by everyone + BOOL mHasMultitexture; + S32 mNumTextureUnits; + BOOL mHasMipMapGeneration; + BOOL mHasAnyAGP; + BOOL mHasPalettedTextures; + BOOL mHasCompressedTextures; + + // ARB Extensions + BOOL mHasVertexBufferObject; + BOOL mHasPBuffer; + BOOL mHasShaderObjects; + BOOL mHasVertexShader; + BOOL mHasFragmentShader; + BOOL mHasOcclusionQuery; + + // nVidia extensions. + BOOL mHasAnisotropic; + BOOL mHasNVVertexArrayRange; + BOOL mHasNVFence; + BOOL mHasARBEnvCombine; + + // ATI extensions. + BOOL mHasATIVAO; + BOOL mIsRadeon8500; // Radeon 8500/9000 + BOOL mIsRadeon9700; + BOOL mIsMobilityRadeon9000; + BOOL mIsGF2or4MX; + BOOL mIsGF3; + BOOL mIsGFFX; + BOOL mIsATI; + BOOL mATIOffsetVerticalLines; + BOOL mIsNVIDIA; + BOOL mIsIntel; + BOOL mHasCubeMap; + +#if LL_WINDOWS + BOOL mHasWGLARBPixelFormat; +#endif // LL_WINDOWS + +#if LL_DARWIN + // Apple extensions. + BOOL mHasAPPLEVertexArrayRange; + BOOL mHasAPPLEFence; + BOOL mHasAPPLEVAO; +#endif + + // Misc exitensions + BOOL mHasSeparateSpecularColor; + + S32 mDriverVersionMajor; + S32 mDriverVersionMinor; + S32 mDriverVersionRelease; + F32 mGLVersion; // e.g = 1.4 + LLString mDriverVersionVendorString; + + S32 mVRAM; // VRAM in MB + S32 mGLMaxVertexRange; + S32 mGLMaxIndexRange; + BOOL mSoftwareBlendSSE; + + void getPixelFormat(); // Get the best pixel format + + LLString getGLInfoString(); + + // In ALL CAPS + LLString mGLVendor; + + // In ALL CAPS + LLString mGLRenderer; + +private: + void initExtensions(); + void initGLStates(); + void initGLImages(); +}; + +extern LLGLManager gGLManager; + +class LLQuaternion; +class LLMatrix4; + +void rotate_quat(LLQuaternion& rotation); + +void flush_glerror(); // Flush GL errors when we know we're handling them correctly. + +void assert_glerror(); + +void clear_glerror(); + +#if LL_DEBUG +# define stop_glerror() assert_glerror() +# define llglassertok() assert_glerror() +#else +# define stop_glerror() +# define llglassertok() +#endif + +#define llglassertok_always() assert_glerror() + +//////////////////////// +// +// Note: U32's are GLEnum's... +// + +// This is a class for GL state management + +/* + GL STATE MANAGEMENT DESCRIPTION + + LLGLState and its two subclasses, LLGLEnable and LLGLDisable, manage the current + enable/disable states of the GL to prevent redundant setting of state within a + render path or the accidental corruption of what state the next path expects. + + Essentially, wherever you would call glEnable set a state and then + subsequently reset it by calling glDisable (or vice versa), make an instance of + LLGLEnable with the state you want to set, and assume it will be restored to its + original state when that instance of LLGLEnable is destroyed. It is good practice + to exploit stack frame controls for optimal setting/unsetting and readability of + code. In llglstates.h, there are a collection of helper classes that define groups + of enables/disables that can cause multiple states to be set with the creation of + one instance. + + Sample usage: + + //disable lighting for rendering hud objects + //INCORRECT USAGE + LLGLEnable lighting(GL_LIGHTING); + renderHUD(); + LLGLDisable lighting(GL_LIGHTING); + + //CORRECT USAGE + { + LLGLEnable lighting(GL_LIGHTING); + renderHUD(); + } + + If a state is to be set on a conditional, the following mechanism + is useful: + + { + LLGLEnable lighting(light_hud ? GL_LIGHTING : 0); + renderHUD(); + } + + A LLGLState initialized with a parameter of 0 does nothing. + + LLGLState works by maintaining a map of the current GL states, and ignoring redundant + enables/disables. If a redundant call is attempted, it becomes a noop, otherwise, + it is set in the constructor and reset in the destructor. + + For debugging GL state corruption, running with debug enabled will trigger asserts + if the existing GL state does not match the expected GL state. + +*/ +class LLGLState +{ +public: + static void initClass(); + static void restoreGL(); + + static void dumpStates(); + static void checkStates(); + static void checkTextureChannels(); + static void checkClientArrays(); + +protected: + static std::map sStateMap; + +public: + enum { CURRENT_STATE = -2 }; + LLGLState(LLGLenum state, S32 enabled = CURRENT_STATE); + ~LLGLState(); + void setEnabled(S32 enabled); + void enable() { setEnabled(TRUE); } + void disable() { setEnabled(FALSE); } +protected: + LLGLenum mState; + BOOL mWasEnabled; + BOOL mIsEnabled; +}; + +class LLGLEnable : public LLGLState +{ +public: + LLGLEnable(LLGLenum state) : LLGLState(state, TRUE) {} +}; + +class LLGLDisable : public LLGLState +{ +public: + LLGLDisable(LLGLenum state) : LLGLState(state, FALSE) {} +}; + +#include "llglstates.h" + +void init_glstates(); +void enable_vertex_weighting(const S32 index); +void disable_vertex_weighting(const S32 index); +void enable_binormals(const S32 index); +void disable_binormals(const S32 index); +void enable_cloth_weights(const S32 index); +void disable_cloth_weights(const S32 index); +void set_vertex_weights(const S32 index, const F32 *weights); +void set_vertex_clothing_weights(const S32 index, const U32 stride, const LLVector4 *weights); +void set_binormals(const S32 index, const U32 stride, const LLVector3 *binormals); +void set_palette(U8* palette_data); +void parse_gl_version( S32* major, S32* minor, S32* release, LLString* vendor_specific ); + +extern BOOL gClothRipple; +extern BOOL gNoRender; +#endif // LL_LLGL_H diff --git a/linden/indra/llwindow/llglheaders.h b/linden/indra/llwindow/llglheaders.h new file mode 100644 index 0000000..e67b6c7 --- /dev/null +++ b/linden/indra/llwindow/llglheaders.h @@ -0,0 +1,486 @@ +/** + * @file llglheaders.h + * @brief LLGL definitions + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLGLHEADERS_H +#define LL_LLGLHEADERS_H + +#if LL_LINUX +# ifndef LL_MESA +# define LL_MESA 1 // force MESA +# endif +# ifndef LL_MESA_HEADLESS +# define LL_MESA_HEADLESS 1 // force MESA HEADLESS +# endif +#endif + +#if LL_MESA +//---------------------------------------------------------------------------- +// MESA headers +// quotes so we get libraries/.../GL/ version +#define GL_GLEXT_PROTOTYPES +#include "GL/gl.h" +#include "GL/glext.h" +#include "GL/glu.h" + +#elif LL_LINUX +//---------------------------------------------------------------------------- +// Linux, MESA headers, but not necessarily assuming MESA runtime. +// quotes so we get libraries/.../GL/ version +#include "GL/gl.h" +#include "GL/glext.h" +#include "GL/glu.h" + +// GL_ARB_vertex_buffer_object +extern PFNGLBINDBUFFERARBPROC glBindBufferARB; +extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB; +extern PFNGLGENBUFFERSARBPROC glGenBuffersARB; +extern PFNGLISBUFFERARBPROC glIsBufferARB; +extern PFNGLBUFFERDATAARBPROC glBufferDataARB; +extern PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB; +extern PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubDataARB; +extern PFNGLMAPBUFFERARBPROC glMapBufferARB; +extern PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB; +extern PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB; +extern PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; + +// GL_ATI_vertex_array_object +extern PFNGLNEWOBJECTBUFFERATIPROC glNewObjectBufferATI; +extern PFNGLISOBJECTBUFFERATIPROC glIsObjectBufferATI; +extern PFNGLUPDATEOBJECTBUFFERATIPROC glUpdateObjectBufferATI; +extern PFNGLGETOBJECTBUFFERFVATIPROC glGetObjectBufferfvATI; +extern PFNGLGETOBJECTBUFFERIVATIPROC glGetObjectBufferivATI; +extern PFNGLFREEOBJECTBUFFERATIPROC glFreeObjectBufferATI; +extern PFNGLARRAYOBJECTATIPROC glArrayObjectATI; +extern PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glVertexAttribArrayObjectATI; +extern PFNGLGETARRAYOBJECTFVATIPROC glGetArrayObjectfvATI; +extern PFNGLGETARRAYOBJECTIVATIPROC glGetArrayObjectivATI; +extern PFNGLVARIANTARRAYOBJECTATIPROC glVariantObjectArrayATI; +extern PFNGLGETVARIANTARRAYOBJECTFVATIPROC glGetVariantArrayObjectfvATI; +extern PFNGLGETVARIANTARRAYOBJECTIVATIPROC glGetVariantArrayObjectivATI; + +// GL_ARB_occlusion_query +extern PFNGLGENQUERIESARBPROC glGenQueriesARB; +extern PFNGLDELETEQUERIESARBPROC glDeleteQueriesARB; +extern PFNGLISQUERYARBPROC glIsQueryARB; +extern PFNGLBEGINQUERYARBPROC glBeginQueryARB; +extern PFNGLENDQUERYARBPROC glEndQueryARB; +extern PFNGLGETQUERYIVARBPROC glGetQueryivARB; +extern PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectivARB; +extern PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB; + +// GL_ARB_shader_objects +extern PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; +extern PFNGLGETHANDLEARBPROC glGetHandleARB; +extern PFNGLDETACHOBJECTARBPROC glDetachObjectARB; +extern PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; +extern PFNGLSHADERSOURCEARBPROC glShaderSourceARB; +extern PFNGLCOMPILESHADERARBPROC glCompileShaderARB; +extern PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; +extern PFNGLATTACHOBJECTARBPROC glAttachObjectARB; +extern PFNGLLINKPROGRAMARBPROC glLinkProgramARB; +extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; +extern PFNGLVALIDATEPROGRAMARBPROC glValidateProgramARB; +extern PFNGLUNIFORM1FARBPROC glUniform1fARB; +extern PFNGLUNIFORM2FARBPROC glUniform2fARB; +extern PFNGLUNIFORM3FARBPROC glUniform3fARB; +extern PFNGLUNIFORM4FARBPROC glUniform4fARB; +extern PFNGLUNIFORM1IARBPROC glUniform1iARB; +extern PFNGLUNIFORM2IARBPROC glUniform2iARB; +extern PFNGLUNIFORM3IARBPROC glUniform3iARB; +extern PFNGLUNIFORM4IARBPROC glUniform4iARB; +extern PFNGLUNIFORM1FVARBPROC glUniform1fvARB; +extern PFNGLUNIFORM2FVARBPROC glUniform2fvARB; +extern PFNGLUNIFORM3FVARBPROC glUniform3fvARB; +extern PFNGLUNIFORM4FVARBPROC glUniform4fvARB; +extern PFNGLUNIFORM1IVARBPROC glUniform1ivARB; +extern PFNGLUNIFORM2IVARBPROC glUniform2ivARB; +extern PFNGLUNIFORM3IVARBPROC glUniform3ivARB; +extern PFNGLUNIFORM4IVARBPROC glUniform4ivARB; +extern PFNGLUNIFORMMATRIX2FVARBPROC glUniformMatrix2fvARB; +extern PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fvARB; +extern PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fvARB; +extern PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB; +extern PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; +extern PFNGLGETINFOLOGARBPROC glGetInfoLogARB; +extern PFNGLGETATTACHEDOBJECTSARBPROC glGetAttachedObjectsARB; +extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; +extern PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniformARB; +extern PFNGLGETUNIFORMFVARBPROC glGetUniformfvARB; +extern PFNGLGETUNIFORMIVARBPROC glGetUniformivARB; +extern PFNGLGETSHADERSOURCEARBPROC glGetShaderSourceARB; + +// GL_ARB;_vertex_shader +extern PFNGLVERTEXATTRIB1DARBPROC glVertexAttrib1dARB; +extern PFNGLVERTEXATTRIB1DVARBPROC glVertexAttrib1dvARB; +extern PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB; +extern PFNGLVERTEXATTRIB1FVARBPROC glVertexAttrib1fvARB; +extern PFNGLVERTEXATTRIB1SARBPROC glVertexAttrib1sARB; +extern PFNGLVERTEXATTRIB1SVARBPROC glVertexAttrib1svARB; +extern PFNGLVERTEXATTRIB2DARBPROC glVertexAttrib2dARB; +extern PFNGLVERTEXATTRIB2DVARBPROC glVertexAttrib2dvARB; +extern PFNGLVERTEXATTRIB2FARBPROC glVertexAttrib2fARB; +extern PFNGLVERTEXATTRIB2FVARBPROC glVertexAttrib2fvARB; +extern PFNGLVERTEXATTRIB2SARBPROC glVertexAttrib2sARB; +extern PFNGLVERTEXATTRIB2SVARBPROC glVertexAttrib2svARB; +extern PFNGLVERTEXATTRIB3DARBPROC glVertexAttrib3dARB; +extern PFNGLVERTEXATTRIB3DVARBPROC glVertexAttrib3dvARB; +extern PFNGLVERTEXATTRIB3FARBPROC glVertexAttrib3fARB; +extern PFNGLVERTEXATTRIB3FVARBPROC glVertexAttrib3fvARB; +extern PFNGLVERTEXATTRIB3SARBPROC glVertexAttrib3sARB; +extern PFNGLVERTEXATTRIB3SVARBPROC glVertexAttrib3svARB; +extern PFNGLVERTEXATTRIB4NBVARBPROC glVertexAttrib4nbvARB; +extern PFNGLVERTEXATTRIB4NIVARBPROC glVertexAttrib4nivARB; +extern PFNGLVERTEXATTRIB4NSVARBPROC glVertexAttrib4nsvARB; +extern PFNGLVERTEXATTRIB4NUBARBPROC glVertexAttrib4nubARB; +extern PFNGLVERTEXATTRIB4NUBVARBPROC glVertexAttrib4nubvARB; +extern PFNGLVERTEXATTRIB4NUIVARBPROC glVertexAttrib4nuivARB; +extern PFNGLVERTEXATTRIB4NUSVARBPROC glVertexAttrib4nusvARB; +extern PFNGLVERTEXATTRIB4BVARBPROC glVertexAttrib4bvARB; +extern PFNGLVERTEXATTRIB4DARBPROC glVertexAttrib4dARB; +extern PFNGLVERTEXATTRIB4DVARBPROC glVertexAttrib4dvARB; +extern PFNGLVERTEXATTRIB4FARBPROC glVertexAttrib4fARB; +extern PFNGLVERTEXATTRIB4FVARBPROC glVertexAttrib4fvARB; +extern PFNGLVERTEXATTRIB4IVARBPROC glVertexAttrib4ivARB; +extern PFNGLVERTEXATTRIB4SARBPROC glVertexAttrib4sARB; +extern PFNGLVERTEXATTRIB4SVARBPROC glVertexAttrib4svARB; +extern PFNGLVERTEXATTRIB4UBVARBPROC glVertexAttrib4ubvARB; +extern PFNGLVERTEXATTRIB4UIVARBPROC glVertexAttrib4uivARB; +extern PFNGLVERTEXATTRIB4USVARBPROC glVertexAttrib4usvARB; +extern PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB; +extern PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; +extern PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; +extern PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; +extern PFNGLBINDPROGRAMARBPROC glBindProgramARB; +extern PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; +extern PFNGLGENPROGRAMSARBPROC glGenProgramsARB; +extern PFNGLPROGRAMENVPARAMETER4DARBPROC glProgramEnvParameter4dARB; +extern PFNGLPROGRAMENVPARAMETER4DVARBPROC glProgramEnvParameter4dvARB; +extern PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB; +extern PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB; +extern PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB; +extern PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glProgramLocalParameter4dvARB; +extern PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB; +extern PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; +extern PFNGLGETPROGRAMENVPARAMETERDVARBPROC glGetProgramEnvParameterdvARB; +extern PFNGLGETPROGRAMENVPARAMETERFVARBPROC glGetProgramEnvParameterfvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glGetProgramLocalParameterfvARB; +extern PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; +extern PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; +extern PFNGLGETVERTEXATTRIBDVARBPROC glGetVertexAttribdvARB; +extern PFNGLGETVERTEXATTRIBFVARBPROC glGetVertexAttribfvARB; +extern PFNGLGETVERTEXATTRIBIVARBPROC glGetVertexAttribivARB; +extern PFNGLGETVERTEXATTRIBPOINTERVARBPROC glGetVertexAttribPointervARB; +extern PFNGLISPROGRAMARBPROC glIsProgramARB; +extern PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocationARB; +extern PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB; +extern PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB; + +extern PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glCompressedTexImage2DARB; +extern PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glGetCompressedTexImageARB; + +extern PFNGLCOLORTABLEEXTPROC glColorTableEXT; + +#elif LL_WINDOWS +//---------------------------------------------------------------------------- +#include +#include + +// quotes so we get libraries/.../GL/ version +#include "GL/glext.h" +#include "GL/glh_extensions.h" + + +// GL_ARB_vertex_buffer_object +extern PFNGLBINDBUFFERARBPROC glBindBufferARB; +extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB; +extern PFNGLGENBUFFERSARBPROC glGenBuffersARB; +extern PFNGLISBUFFERARBPROC glIsBufferARB; +extern PFNGLBUFFERDATAARBPROC glBufferDataARB; +extern PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB; +extern PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubDataARB; +extern PFNGLMAPBUFFERARBPROC glMapBufferARB; +extern PFNGLUNMAPBUFFERARBPROC glUnmapBufferARB; +extern PFNGLGETBUFFERPARAMETERIVARBPROC glGetBufferParameterivARB; +extern PFNGLGETBUFFERPOINTERVARBPROC glGetBufferPointervARB; + +// GL_ATI_vertex_array_object +extern PFNGLNEWOBJECTBUFFERATIPROC glNewObjectBufferATI; +extern PFNGLISOBJECTBUFFERATIPROC glIsObjectBufferATI; +extern PFNGLUPDATEOBJECTBUFFERATIPROC glUpdateObjectBufferATI; +extern PFNGLGETOBJECTBUFFERFVATIPROC glGetObjectBufferfvATI; +extern PFNGLGETOBJECTBUFFERIVATIPROC glGetObjectBufferivATI; +extern PFNGLFREEOBJECTBUFFERATIPROC glFreeObjectBufferATI; +extern PFNGLARRAYOBJECTATIPROC glArrayObjectATI; +extern PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glVertexAttribArrayObjectATI; +extern PFNGLGETARRAYOBJECTFVATIPROC glGetArrayObjectfvATI; +extern PFNGLGETARRAYOBJECTIVATIPROC glGetArrayObjectivATI; +extern PFNGLVARIANTARRAYOBJECTATIPROC glVariantObjectArrayATI; +extern PFNGLGETVARIANTARRAYOBJECTFVATIPROC glGetVariantArrayObjectfvATI; +extern PFNGLGETVARIANTARRAYOBJECTIVATIPROC glGetVariantArrayObjectivATI; + +extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; + +// GL_ARB_occlusion_query +extern PFNGLGENQUERIESARBPROC glGenQueriesARB; +extern PFNGLDELETEQUERIESARBPROC glDeleteQueriesARB; +extern PFNGLISQUERYARBPROC glIsQueryARB; +extern PFNGLBEGINQUERYARBPROC glBeginQueryARB; +extern PFNGLENDQUERYARBPROC glEndQueryARB; +extern PFNGLGETQUERYIVARBPROC glGetQueryivARB; +extern PFNGLGETQUERYOBJECTIVARBPROC glGetQueryObjectivARB; +extern PFNGLGETQUERYOBJECTUIVARBPROC glGetQueryObjectuivARB; + +// GL_ARB_shader_objects +extern PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; +extern PFNGLGETHANDLEARBPROC glGetHandleARB; +extern PFNGLDETACHOBJECTARBPROC glDetachObjectARB; +extern PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; +extern PFNGLSHADERSOURCEARBPROC glShaderSourceARB; +extern PFNGLCOMPILESHADERARBPROC glCompileShaderARB; +extern PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; +extern PFNGLATTACHOBJECTARBPROC glAttachObjectARB; +extern PFNGLLINKPROGRAMARBPROC glLinkProgramARB; +extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; +extern PFNGLVALIDATEPROGRAMARBPROC glValidateProgramARB; +extern PFNGLUNIFORM1FARBPROC glUniform1fARB; +extern PFNGLUNIFORM2FARBPROC glUniform2fARB; +extern PFNGLUNIFORM3FARBPROC glUniform3fARB; +extern PFNGLUNIFORM4FARBPROC glUniform4fARB; +extern PFNGLUNIFORM1IARBPROC glUniform1iARB; +extern PFNGLUNIFORM2IARBPROC glUniform2iARB; +extern PFNGLUNIFORM3IARBPROC glUniform3iARB; +extern PFNGLUNIFORM4IARBPROC glUniform4iARB; +extern PFNGLUNIFORM1FVARBPROC glUniform1fvARB; +extern PFNGLUNIFORM2FVARBPROC glUniform2fvARB; +extern PFNGLUNIFORM3FVARBPROC glUniform3fvARB; +extern PFNGLUNIFORM4FVARBPROC glUniform4fvARB; +extern PFNGLUNIFORM1IVARBPROC glUniform1ivARB; +extern PFNGLUNIFORM2IVARBPROC glUniform2ivARB; +extern PFNGLUNIFORM3IVARBPROC glUniform3ivARB; +extern PFNGLUNIFORM4IVARBPROC glUniform4ivARB; +extern PFNGLUNIFORMMATRIX2FVARBPROC glUniformMatrix2fvARB; +extern PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fvARB; +extern PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fvARB; +extern PFNGLGETOBJECTPARAMETERFVARBPROC glGetObjectParameterfvARB; +extern PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; +extern PFNGLGETINFOLOGARBPROC glGetInfoLogARB; +extern PFNGLGETATTACHEDOBJECTSARBPROC glGetAttachedObjectsARB; +extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; +extern PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniformARB; +extern PFNGLGETUNIFORMFVARBPROC glGetUniformfvARB; +extern PFNGLGETUNIFORMIVARBPROC glGetUniformivARB; +extern PFNGLGETSHADERSOURCEARBPROC glGetShaderSourceARB; + +// GL_ARB;_vertex_shader +extern PFNGLVERTEXATTRIB1DARBPROC glVertexAttrib1dARB; +extern PFNGLVERTEXATTRIB1DVARBPROC glVertexAttrib1dvARB; +extern PFNGLVERTEXATTRIB1FARBPROC glVertexAttrib1fARB; +extern PFNGLVERTEXATTRIB1FVARBPROC glVertexAttrib1fvARB; +extern PFNGLVERTEXATTRIB1SARBPROC glVertexAttrib1sARB; +extern PFNGLVERTEXATTRIB1SVARBPROC glVertexAttrib1svARB; +extern PFNGLVERTEXATTRIB2DARBPROC glVertexAttrib2dARB; +extern PFNGLVERTEXATTRIB2DVARBPROC glVertexAttrib2dvARB; +extern PFNGLVERTEXATTRIB2FARBPROC glVertexAttrib2fARB; +extern PFNGLVERTEXATTRIB2FVARBPROC glVertexAttrib2fvARB; +extern PFNGLVERTEXATTRIB2SARBPROC glVertexAttrib2sARB; +extern PFNGLVERTEXATTRIB2SVARBPROC glVertexAttrib2svARB; +extern PFNGLVERTEXATTRIB3DARBPROC glVertexAttrib3dARB; +extern PFNGLVERTEXATTRIB3DVARBPROC glVertexAttrib3dvARB; +extern PFNGLVERTEXATTRIB3FARBPROC glVertexAttrib3fARB; +extern PFNGLVERTEXATTRIB3FVARBPROC glVertexAttrib3fvARB; +extern PFNGLVERTEXATTRIB3SARBPROC glVertexAttrib3sARB; +extern PFNGLVERTEXATTRIB3SVARBPROC glVertexAttrib3svARB; +extern PFNGLVERTEXATTRIB4NBVARBPROC glVertexAttrib4nbvARB; +extern PFNGLVERTEXATTRIB4NIVARBPROC glVertexAttrib4nivARB; +extern PFNGLVERTEXATTRIB4NSVARBPROC glVertexAttrib4nsvARB; +extern PFNGLVERTEXATTRIB4NUBARBPROC glVertexAttrib4nubARB; +extern PFNGLVERTEXATTRIB4NUBVARBPROC glVertexAttrib4nubvARB; +extern PFNGLVERTEXATTRIB4NUIVARBPROC glVertexAttrib4nuivARB; +extern PFNGLVERTEXATTRIB4NUSVARBPROC glVertexAttrib4nusvARB; +extern PFNGLVERTEXATTRIB4BVARBPROC glVertexAttrib4bvARB; +extern PFNGLVERTEXATTRIB4DARBPROC glVertexAttrib4dARB; +extern PFNGLVERTEXATTRIB4DVARBPROC glVertexAttrib4dvARB; +extern PFNGLVERTEXATTRIB4FARBPROC glVertexAttrib4fARB; +extern PFNGLVERTEXATTRIB4FVARBPROC glVertexAttrib4fvARB; +extern PFNGLVERTEXATTRIB4IVARBPROC glVertexAttrib4ivARB; +extern PFNGLVERTEXATTRIB4SARBPROC glVertexAttrib4sARB; +extern PFNGLVERTEXATTRIB4SVARBPROC glVertexAttrib4svARB; +extern PFNGLVERTEXATTRIB4UBVARBPROC glVertexAttrib4ubvARB; +extern PFNGLVERTEXATTRIB4UIVARBPROC glVertexAttrib4uivARB; +extern PFNGLVERTEXATTRIB4USVARBPROC glVertexAttrib4usvARB; +extern PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB; +extern PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; +extern PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; +extern PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; +extern PFNGLBINDPROGRAMARBPROC glBindProgramARB; +extern PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; +extern PFNGLGENPROGRAMSARBPROC glGenProgramsARB; +extern PFNGLPROGRAMENVPARAMETER4DARBPROC glProgramEnvParameter4dARB; +extern PFNGLPROGRAMENVPARAMETER4DVARBPROC glProgramEnvParameter4dvARB; +extern PFNGLPROGRAMENVPARAMETER4FARBPROC glProgramEnvParameter4fARB; +extern PFNGLPROGRAMENVPARAMETER4FVARBPROC glProgramEnvParameter4fvARB; +extern PFNGLPROGRAMLOCALPARAMETER4DARBPROC glProgramLocalParameter4dARB; +extern PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glProgramLocalParameter4dvARB; +extern PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB; +extern PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; +extern PFNGLGETPROGRAMENVPARAMETERDVARBPROC glGetProgramEnvParameterdvARB; +extern PFNGLGETPROGRAMENVPARAMETERFVARBPROC glGetProgramEnvParameterfvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glGetProgramLocalParameterdvARB; +extern PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glGetProgramLocalParameterfvARB; +extern PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; +extern PFNGLGETPROGRAMSTRINGARBPROC glGetProgramStringARB; +extern PFNGLGETVERTEXATTRIBDVARBPROC glGetVertexAttribdvARB; +extern PFNGLGETVERTEXATTRIBFVARBPROC glGetVertexAttribfvARB; +extern PFNGLGETVERTEXATTRIBIVARBPROC glGetVertexAttribivARB; +extern PFNGLGETVERTEXATTRIBPOINTERVARBPROC glGetVertexAttribPointervARB; +extern PFNGLISPROGRAMARBPROC glIsProgramARB; +extern PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocationARB; +extern PFNGLGETACTIVEATTRIBARBPROC glGetActiveAttribARB; +extern PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB; + + +#elif LL_DARWIN +//---------------------------------------------------------------------------- +// LL_DARWIN + +#include +#include + +#define GL_EXT_separate_specular_color 1 +#include + +#include "GL/glh_extensions.h" + +#ifdef __cplusplus +extern "C" { +#endif +// +// Define vertex buffer object headers on Mac +// +#ifndef GL_ARB_vertex_buffer_object +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +#endif + + + +#ifndef GL_ARB_vertex_buffer_object +/* GL types for handling large vertex buffer objects */ +typedef intptr_t GLintptrARB; +typedef intptr_t GLsizeiptrARB; +#endif + + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +#ifdef GL_GLEXT_FUNCTION_POINTERS +typedef void (* glBindBufferARBProcPtr) (GLenum target, GLuint buffer); +typedef void (* glDeleteBufferARBProcPtr) (GLsizei n, const GLuint *buffers); +typedef void (* glGenBuffersARBProcPtr) (GLsizei n, GLuint *buffers); +typedef GLboolean (* glIsBufferARBProcPtr) (GLuint buffer); +typedef void (* glBufferDataARBProcPtr) (GLenum target, GLsizeiptrARB size, const GLvoid *data, GLenum usage); +typedef void (* glBufferSubDataARBProcPtr) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid *data); +typedef void (* glGetBufferSubDataARBProcPtr) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid *data); +typedef GLvoid* (* glMapBufferARBProcPtr) (GLenum target, GLenum access); +typedef GLboolean (* glUnmapBufferARBProcPtr) (GLenum target); +typedef void (* glGetBufferParameterivARBProcPtr) (GLenum target, GLenum pname, GLint *params); +typedef void (* glGetBufferPointervARBProcPtr) (GLenum target, GLenum pname, GLvoid* *params); +#else +extern void glBindBufferARB (GLenum, GLuint); +extern void glDeleteBuffersARB (GLsizei, const GLuint *); +extern void glGenBuffersARB (GLsizei, GLuint *); +extern GLboolean glIsBufferARB (GLuint); +extern void glBufferDataARB (GLenum, GLsizeiptrARB, const GLvoid *, GLenum); +extern void glBufferSubDataARB (GLenum, GLintptrARB, GLsizeiptrARB, const GLvoid *); +extern void glGetBufferSubDataARB (GLenum, GLintptrARB, GLsizeiptrARB, GLvoid *); +extern GLvoid* glMapBufferARB (GLenum, GLenum); +extern GLboolean glUnmapBufferARB (GLenum); +extern void glGetBufferParameterivARB (GLenum, GLenum, GLint *); +extern void glGetBufferPointervARB (GLenum, GLenum, GLvoid* *); +#endif /* GL_GLEXT_FUNCTION_POINTERS */ +#endif + +// May be needed for DARWIN... +// #ifndef GL_ARB_compressed_tex_image +// #define GL_ARB_compressed_tex_image 1 +// #ifdef GL_GLEXT_FUNCTION_POINTERS +// typedef void (* glCompressedTexImage1D) (GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexImage2D) (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexImage3D) (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexSubImage1D) (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexSubImage2D) (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*); +// typedef void (* glCompressedTexSubImage3D) (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*); +// typedef void (* glGetCompressedTexImage) (GLenum, GLint, GLvoid*); +// #else +// extern void glCompressedTexImage1D (GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const GLvoid*); +// extern void glCompressedTexImage2D (GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*); +// extern void glCompressedTexImage3D (GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const GLvoid*); +// extern void glCompressedTexSubImage1D (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid*); +// extern void glCompressedTexSubImage2D (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*); +// extern void glCompressedTexSubImage3D (GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const GLvoid*); +// extern void glGetCompressedTexImage (GLenum, GLint, GLvoid*); +// #endif /* GL_GLEXT_FUNCTION_POINTERS */ +// #endif + +#ifdef __cplusplus +} +#endif + +#endif // LL_MESA / LL_WINDOWS / LL_DARWIN + + +#endif // LL_LLGLHEADERS_H diff --git a/linden/indra/llwindow/llglstates.h b/linden/indra/llwindow/llglstates.h new file mode 100644 index 0000000..8e67e33 --- /dev/null +++ b/linden/indra/llwindow/llglstates.h @@ -0,0 +1,339 @@ +/** + * @file llglstates.h + * @brief LLGL states definitions + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +//THIS HEADER SHOULD ONLY BE INCLUDED FROM llgl.h +#ifndef LL_LLGLSTATES_H +#define LL_LLGLSTATES_H + +#ifdef WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# include +#endif + +#if LL_DARWIN +#include +#else +#include "llglheaders.h" +#endif + +//---------------------------------------------------------------------------- + +class LLGLDepthTest +{ + // Enabled by default +public: + LLGLDepthTest(GLboolean depth_enabled, GLboolean write_enabled = GL_TRUE, GLenum depth_func = GL_LEQUAL) + : mPrevDepthEnabled(sDepthEnabled), mPrevDepthFunc(sDepthFunc), mPrevWriteEnabled(sWriteEnabled) + { + if (depth_enabled != sDepthEnabled) + { + if (depth_enabled) glEnable(GL_DEPTH_TEST); + else glDisable(GL_DEPTH_TEST); + sDepthEnabled = depth_enabled; + } + if (depth_func != sDepthFunc) + { + glDepthFunc(depth_func); + sDepthFunc = depth_func; + } + if (write_enabled != sWriteEnabled) + { + glDepthMask(write_enabled); + sWriteEnabled = write_enabled; + } + } + ~LLGLDepthTest() + { + if (sDepthEnabled != mPrevDepthEnabled ) + { + if (mPrevDepthEnabled) glEnable(GL_DEPTH_TEST); + else glDisable(GL_DEPTH_TEST); + sDepthEnabled = mPrevDepthEnabled; + } + if (sDepthFunc != mPrevDepthFunc) + { + glDepthFunc(mPrevDepthFunc); + sDepthFunc = mPrevDepthFunc; + } + if (sWriteEnabled != mPrevWriteEnabled ) + { + glDepthMask(mPrevWriteEnabled); + sWriteEnabled = mPrevWriteEnabled; + } + } + GLboolean mPrevDepthEnabled; + GLenum mPrevDepthFunc; + GLboolean mPrevWriteEnabled; +private: + static GLboolean sDepthEnabled; // defaults to GL_FALSE + static GLenum sDepthFunc; // defaults to GL_LESS + static GLboolean sWriteEnabled; // defaults to GL_TRUE +}; + +//---------------------------------------------------------------------------- + +class LLGLSDefault +{ +protected: + LLGLEnable mTexture2D, mColorMaterial; + LLGLDisable mAlphaTest, mBlend, mCullFace, mDither, mFog, + mLineSmooth, mLineStipple, mNormalize, mPolygonSmooth, + mTextureGenQ, mTextureGenR, mTextureGenS, mTextureGenT; +public: + LLGLSDefault() + : + // Enable + mTexture2D(GL_TEXTURE_2D), + mColorMaterial(GL_COLOR_MATERIAL), + // Disable + mAlphaTest(GL_ALPHA_TEST), + mBlend(GL_BLEND), + mCullFace(GL_CULL_FACE), + mDither(GL_DITHER), + mFog(GL_FOG), + mLineSmooth(GL_LINE_SMOOTH), + mLineStipple(GL_LINE_STIPPLE), + mNormalize(GL_NORMALIZE), + mPolygonSmooth(GL_POLYGON_SMOOTH), + mTextureGenQ(GL_TEXTURE_GEN_Q), + mTextureGenR(GL_TEXTURE_GEN_R), + mTextureGenS(GL_TEXTURE_GEN_S), + mTextureGenT(GL_TEXTURE_GEN_T) + { } +}; + +class LLGLSTexture +{ +protected: + LLGLEnable mTexture2D; +public: + LLGLSTexture() + : mTexture2D(GL_TEXTURE_2D) + {} +}; + + +class LLGLSNoTexture +{ +protected: + LLGLDisable mTexture2D; +public: + LLGLSNoTexture() + : mTexture2D(GL_TEXTURE_2D) + {} +}; + +class LLGLSObjectSelect // : public LLGLSDefault +{ +protected: + LLGLDisable mBlend, mFog, mTexture2D, mAlphaTest; + LLGLEnable mCullFace; +public: + LLGLSObjectSelect() + : mBlend(GL_BLEND), mFog(GL_FOG), mTexture2D(GL_TEXTURE_2D), + mAlphaTest(GL_ALPHA_TEST), + mCullFace(GL_CULL_FACE) + {} +}; + +class LLGLSObjectSelectAlpha // : public LLGLSObjectSelect +{ +protected: + LLGLEnable mTexture2D, mAlphaTest; +public: + LLGLSObjectSelectAlpha() + : mTexture2D(GL_TEXTURE_2D), mAlphaTest(GL_ALPHA_TEST) + {} +}; + +//---------------------------------------------------------------------------- + +class LLGLSUIDefault // : public LLGLSDefault +{ +protected: + LLGLEnable mBlend, mAlphaTest, mTexture2D; + LLGLDisable mCullFace; + LLGLDepthTest mDepthTest; +public: + LLGLSUIDefault() + : mBlend(GL_BLEND), mAlphaTest(GL_ALPHA_TEST), + mTexture2D(GL_TEXTURE_2D), + mCullFace(GL_CULL_FACE), + mDepthTest(GL_FALSE, GL_TRUE, GL_LEQUAL) + {} +}; + +class LLGLSNoAlphaTest // : public LLGLSUIDefault +{ +protected: + LLGLDisable mAlphaTest; +public: + LLGLSNoAlphaTest() + : mAlphaTest(GL_ALPHA_TEST) + {} +}; + +class LLGLSNoTextureNoAlphaTest // : public LLGLSUIDefault +{ +protected: + LLGLDisable mAlphaTest; + LLGLDisable mTexture2D; +public: + LLGLSNoTextureNoAlphaTest() + : mAlphaTest(GL_ALPHA_TEST), + mTexture2D(GL_TEXTURE_2D) + {} +}; + +//---------------------------------------------------------------------------- + +class LLGLSFog +{ +protected: + LLGLEnable mFog; +public: + LLGLSFog() + : mFog(GL_FOG) + {} +}; + +class LLGLSNoFog +{ +protected: + LLGLDisable mFog; +public: + LLGLSNoFog() + : mFog(GL_FOG) + {} +}; + +//---------------------------------------------------------------------------- + +class LLGLSPipeline // : public LLGLSDefault +{ +protected: + LLGLEnable mCullFace; + LLGLDepthTest mDepthTest; +public: + LLGLSPipeline() + : mCullFace(GL_CULL_FACE), + mDepthTest(GL_TRUE, GL_TRUE, GL_LEQUAL) + { } +}; + +class LLGLSPipelineAlpha // : public LLGLSPipeline +{ +protected: + LLGLEnable mBlend, mAlphaTest; +public: + LLGLSPipelineAlpha() + : mBlend(GL_BLEND), + mAlphaTest(GL_ALPHA_TEST) + { } +}; + +class LLGLSPipelineEmbossBump // : public LLGLSPipelineAlpha +{ +protected: + LLGLDisable mFog; +public: + LLGLSPipelineEmbossBump() + : mFog(GL_FOG) + { } +}; + +class LLGLSPipelineSelection // : public LLGLSPipelineAlpha +{ +protected: + LLGLDisable mCullFace; +public: + LLGLSPipelineSelection() + : mCullFace(GL_CULL_FACE) + {} +}; + +class LLGLSPipelineAvatar // : public LLGLSPipeline +{ +protected: + LLGLEnable mNormalize; +public: + LLGLSPipelineAvatar() + : mNormalize(GL_NORMALIZE) + {} +}; + +class LLGLSPipelineSkyBox // : public LLGLSPipeline +{ +protected: + LLGLDisable mAlphaTest, mCullFace, mFog; +public: + LLGLSPipelineSkyBox() + : mAlphaTest(GL_ALPHA_TEST), mCullFace(GL_CULL_FACE), mFog(GL_FOG) + { } +}; + +class LLGLSTracker // : public LLGLSDefault +{ +protected: + LLGLEnable mCullFace, mBlend, mAlphaTest; + LLGLDisable mTexture2D; +public: + LLGLSTracker() : + mCullFace(GL_CULL_FACE), + mBlend(GL_BLEND), + mAlphaTest(GL_ALPHA_TEST), + mTexture2D(GL_TEXTURE_2D) + {} +}; + +//---------------------------------------------------------------------------- + +class LLGLSSpecular +{ +public: + LLGLSSpecular(const LLColor4& color, F32 shininess) + { + if (shininess > 0.0f) + { + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color.mV); + S32 shiny = (S32)(shininess*128.f); + shiny = llclamp(shiny,0,128); + glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, shiny); + } + } + ~LLGLSSpecular() + { + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, LLColor4(0.f,0.f,0.f,0.f).mV); + glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 0); + } +}; + +//---------------------------------------------------------------------------- + + +#endif diff --git a/linden/indra/llwindow/llglstubs.h b/linden/indra/llwindow/llglstubs.h new file mode 100644 index 0000000..d7f0d94 --- /dev/null +++ b/linden/indra/llwindow/llglstubs.h @@ -0,0 +1,232 @@ +/** + * @file llglstubs.h + * @brief LLGL stubs header file + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + + +// bunch of macros that get #included multiple times with GL_FUNC defined +// various ways to make stubs for OpenGL entry points. These are statically +// linked to the app, and call into the real GL, which is dynamically +// loaded at runtime. See llwindowsdl.cpp for current implementation. + +#if 1 +GL_FUNC(void,glAlphaFunc,(GLenum f,GLclampf x),(f,x),) +GL_FUNC(void,glBegin,(GLenum e),(e),) +GL_FUNC(void,glBindTexture,(GLenum target,GLuint name),(target,name),) +GL_FUNC(void,glBlendFunc,(GLenum f,GLenum x),(f,x),) +GL_FUNC(void,glCallLists,(GLsizei a,GLenum b,const GLvoid* c),(a,b,c),) +GL_FUNC(void,glClear,(GLbitfield a),(a),) +GL_FUNC(void,glClearColor,(GLclampf r,GLclampf g,GLclampf b,GLclampf a),(r,g,b,a),) +GL_FUNC(void,glClearDepth,(GLclampd x),(x),) +GL_FUNC(void,glColor3f,(GLfloat r,GLfloat g,GLfloat b),(r,g,b),) +GL_FUNC(void,glColor4f,(GLfloat r,GLfloat g,GLfloat b,GLfloat a),(r,g,b,a),) +GL_FUNC(void,glColorMask,(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha),(red,green,blue,alpha),) +GL_FUNC(void,glColorPointer,(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer),(size, type, stride, pointer),) +GL_FUNC(void,glCopyTexImage2D,(GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border),(target, level, internalFormat, x, y, width, height, border),) +GL_FUNC(void,glCopyTexSubImage2D,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height),(target, level, xoffset, yoffset, x, y, width, height),) +GL_FUNC(void,glCullFace,(GLenum mode),(mode),) +GL_FUNC(void,glDeleteLists,(GLuint list, GLsizei range),(list,range),) +GL_FUNC(void,glDeleteTextures,(GLsizei n, const GLuint *textures),(n,textures),) +GL_FUNC(void,glDepthFunc,(GLenum func),(func),) +GL_FUNC(void,glDepthMask,(GLboolean flag),(flag),) +GL_FUNC(void,glDisable,(GLenum cap),(cap),) +GL_FUNC(void,glDisableClientState,(GLenum array),(array),) +GL_FUNC(void,glDrawArrays,(GLenum mode, GLint first, GLsizei count),(mode,first,count),) +GL_FUNC(void,glDrawBuffer,(GLenum mode),(mode),) +GL_FUNC(void,glEnable,(GLenum cap),(cap),) +GL_FUNC(void,glEnableClientState,(GLenum array),(array),) +GL_FUNC(void,glEnd,(void),(),) +GL_FUNC(void,glEndList,(void),(),) +GL_FUNC(GLuint,glGenLists,(GLsizei range),(range),return) +GL_FUNC(void,glGenTextures,(GLsizei n, GLuint *textures),(n,textures),) +GL_FUNC(GLenum,glGetError,(void),(),return) +GL_FUNC(void,glGetFloatv,(GLenum pname, GLfloat *params),(pname,params),) +GL_FUNC(void,glHint,(GLenum target, GLenum mode),(target,mode),) +GL_FUNC(void,glInterleavedArrays,(GLenum format, GLsizei stride, const GLvoid *pointer),(format,stride,pointer),) +GL_FUNC(GLboolean,glIsTexture,(GLuint texture),(texture),return) +GL_FUNC(void,glLightfv,(GLenum light, GLenum pname, const GLfloat *params),(light,pname,params),) +GL_FUNC(void,glListBase,(GLuint base),(base),) +GL_FUNC(void,glLoadIdentity,(void),(),) +GL_FUNC(void,glLoadMatrixf,(const GLfloat *m),(m),) +GL_FUNC(void,glMatrixMode,(GLenum mode),(mode),) +GL_FUNC(void,glNewList,(GLuint list, GLenum mode),(list,mode),) +GL_FUNC(void,glNormal3f,(GLfloat nx, GLfloat ny, GLfloat nz),(nx,ny,nz),) +GL_FUNC(void,glOrtho,(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar),(left,right,bottom,top,zNear,zFar),) +GL_FUNC(void,glPixelStorei,(GLenum pname, GLint param),(pname,param),) +GL_FUNC(void,glPixelTransferi,(GLenum pname, GLint param),(pname,param),) +GL_FUNC(void,glPointSize,(GLfloat size),(size),) +GL_FUNC(void,glPopMatrix,(void),(),) +GL_FUNC(void,glPushMatrix,(void),(),) +GL_FUNC(void,glReadBuffer,(GLenum mode),(mode),) +GL_FUNC(void,glReadPixels,(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels),(x,y,width,height,format,type,pixels),) +GL_FUNC(void,glRotatef,(GLfloat angle, GLfloat x, GLfloat y, GLfloat z),(angle,x,y,z),) +GL_FUNC(void,glScalef,(GLfloat x, GLfloat y, GLfloat z),(x,y,z),) +GL_FUNC(void,glShadeModel,(GLenum mode),(mode),) +GL_FUNC(void,glTexCoord2f,(GLfloat s, GLfloat t),(s,t),) +GL_FUNC(void,glTexCoordPointer,(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer),(size,type,stride,pointer),) +GL_FUNC(void,glTexEnvf,(GLenum target, GLenum pname, GLfloat param),(target,pname,param),) +GL_FUNC(void,glTexEnvfv,(GLenum target, GLenum pname, const GLfloat *params),(target,pname,params),) +GL_FUNC(void,glTexEnvi,(GLenum target, GLenum pname, GLint param),(target,pname,param),) +GL_FUNC(void,glTexParameterf,(GLenum target, GLenum pname, GLfloat param),(target,pname,param),) +GL_FUNC(void,glTexParameteri,(GLenum target, GLenum pname, GLint param),(target,pname,param),) +GL_FUNC(void,glTexSubImage2D,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels),(target,level,xoffset,yoffset,width,height,format,type,pixels),) +GL_FUNC(void,glTranslated,(GLdouble x, GLdouble y, GLdouble z),(x,y,z),) +GL_FUNC(void,glTranslatef,(GLfloat x, GLfloat y, GLfloat z),(x,y,z),) +GL_FUNC(void,glVertex2i,(GLint x, GLint y),(x,y),) +GL_FUNC(void,glVertex2f,(GLfloat x, GLfloat y),(x,y),) +GL_FUNC(void,glVertex3f,(GLfloat x, GLfloat y, GLfloat z),(x,y,z),) +GL_FUNC(void,glVertexPointer,(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer),(size,type,stride,pointer),) +GL_FUNC(void,glViewport,(GLint x, GLint y, GLsizei width, GLsizei height),(x,y,width,height),) +GL_FUNC(void,glLockArraysEXT,(GLint first, GLsizei count),(first,count),) +GL_FUNC(void,glUnlockArraysEXT,(void),(),) +GL_FUNC(void,glGetIntegerv,(GLenum pname, GLint *params),(pname,params),) +GL_FUNC(const GLubyte *,glGetString,(GLenum name),(name),return) +GL_FUNC(void,glGetTexLevelParameteriv,(GLenum target, GLint level, GLenum pname, GLint *params),(target,level,pname,params),) +GL_FUNC(void,glMultMatrixd,(const GLdouble *m),(m),) +GL_FUNC(void,glMultMatrixf,(const GLfloat *m),(m),) +GL_FUNC(void,glGetTexImage,(GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels),(target,level,format,type,pixels),) +GL_FUNC(void,glTexImage1D,(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels),(target,level,internalFormat,width,border,format,type,pixels),) +GL_FUNC(void,glTexImage2D,(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels),(target,level,internalFormat,width,height,border,format,type,pixels),) +GL_FUNC(void,glTexImage3D,(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels),(target,level,internalformat,width,height,depth,border,format,type,pixels),) +GL_FUNC(void,glDepthRange,(GLclampd near_val, GLclampd far_val),(near_val,far_val),) +GL_FUNC(void,glCallList,(GLuint list),(list),) +GL_FUNC(void,glClearStencil,(GLint s),(s),) +GL_FUNC(void,glColor3d,(GLdouble red, GLdouble green, GLdouble blue),(red,green,blue),) +GL_FUNC(void,glColor3dv,(const GLdouble *v),(v),) +GL_FUNC(void,glColor3fv,(const GLfloat *v),(v),) +GL_FUNC(void,glColor4dv,(const GLdouble *v),(v),) +GL_FUNC(void,glColor4fv,(const GLfloat *v),(v),) +GL_FUNC(void,glColor4ub,(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha),(red,green,blue,alpha),) +GL_FUNC(void,glColor4ubv,(const GLubyte *v),(v),) +GL_FUNC(void,glColorMaterial,(GLenum face, GLenum mode),(face,mode),) +GL_FUNC(void,glClientActiveTextureARB,(GLenum x),(x),) +GL_FUNC(void,glActiveTextureARB,(GLenum texture),(texture),) +GL_FUNC(GLboolean,glAreTexturesResident,(GLsizei n, const GLuint *textures, GLboolean *residences),(n,textures,residences),return) +GL_FUNC(void,glClipPlane,(GLenum plane, const GLdouble *equation),(plane,equation),) +GL_FUNC(void,glBindBufferARB,(GLenum x, GLuint y),(x,y),) +GL_FUNC(void,glDeleteBuffersARB,(GLsizei x, const GLuint *y),(x,y),) +GL_FUNC(void,glGenBuffersARB,(GLsizei x, GLuint *y),(x,y),) +GL_FUNC(void,glBufferDataARB,(GLenum a, GLsizeiptrARB b, const GLvoid *c, GLenum d),(a,b,c,d),) +GL_FUNC(void,glBufferSubDataARB,(GLenum a, GLintptr b, GLsizeiptr c, const GLvoid *d),(a,b,c,d),) +GL_FUNC(void,glProgramStringARB,(GLenum a, GLenum b, GLsizei c, const GLvoid *d),(a,b,c,d),) +GL_FUNC(void,glBindProgramARB,(GLenum a, GLuint b),(a,b),) +GL_FUNC(void,glDeleteProgramsARB,(GLsizei a, const GLuint *b),(a,b),) +GL_FUNC(void,glGenProgramsARB,(GLsizei a, GLuint *b),(a,b),) +GL_FUNC(void,glProgramEnvParameter4dARB,(GLenum a, GLuint b, GLdouble c, GLdouble d, GLdouble e, GLdouble f),(a,b,c,d,e,f),) +GL_FUNC(void,glProgramEnvParameter4dvARB,(GLenum a, GLuint b, const GLdouble *c),(a,b,c),) +GL_FUNC(void,glProgramEnvParameter4fARB,(GLenum a, GLuint b, GLfloat c, GLfloat d, GLfloat e, GLfloat f),(a,b,c,d,e,f),) +GL_FUNC(void,glProgramEnvParameter4fvARB,(GLenum a, GLuint b, const GLfloat *c),(a,b,c),) +GL_FUNC(void,glProgramLocalParameter4dARB,(GLenum a, GLuint b, GLdouble c, GLdouble d, GLdouble e, GLdouble f),(a,b,c,d,e,f),) +GL_FUNC(void,glProgramLocalParameter4dvARB,(GLenum a, GLuint b, const GLdouble *c),(a,b,c),) +GL_FUNC(void,glProgramLocalParameter4fARB,(GLenum a, GLuint b, GLfloat c, GLfloat d, GLfloat e, GLfloat f),(a,b,c,d,e,f),) +GL_FUNC(void,glProgramLocalParameter4fvARB,(GLenum a, GLuint b, const GLfloat *c),(a,b,c),) +GL_FUNC(void,glGetProgramEnvParameterdvARB,(GLenum a, GLuint b, GLdouble *c),(a,b,c),) +GL_FUNC(void,glGetProgramEnvParameterfvARB,(GLenum a, GLuint b, GLfloat *c),(a,b,c),) +GL_FUNC(void,glGetProgramLocalParameterdvARB,(GLenum a, GLuint b, GLdouble *c),(a,b,c),) +GL_FUNC(void,glGetProgramLocalParameterfvARB,(GLenum a, GLuint b, GLfloat *c),(a,b,c),) +GL_FUNC(void,glGetProgramivARB,(GLenum a, GLenum b, GLint *c),(a,b,c),) +GL_FUNC(void,glGetProgramStringARB,(GLenum a, GLenum b, GLvoid *c),(a,b,c),) +GL_FUNC(GLboolean,glIsProgramARB,(GLuint a),(a),return) +GL_FUNC(void,glColorTableEXT,(GLenum a, GLenum b, GLsizei c, GLenum d, GLenum e, const GLvoid *f),(a,b,c,d,e,f),) +GL_FUNC(void,glCompressedTexImage2DARB,(GLenum a, GLint b, GLenum c, GLsizei d, GLsizei e, GLint f, GLsizei g, const GLvoid *h),(a,b,c,d,e,f,g,h),) +GL_FUNC(void,glEnableVertexAttribArrayARB,(GLuint a),(a),) +GL_FUNC(void,glDisableVertexAttribArrayARB,(GLuint a),(a),) +GL_FUNC(void,glDrawElements,(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices),(mode,count,type,indices),) +GL_FUNC(void,glDrawPixels,(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels),(width,height,format,type,pixels),) +GL_FUNC(void,glDrawRangeElements,(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices),(mode,start,end,count,type,indices),) +GL_FUNC(void,glFlush,(void),(),) +GL_FUNC(void,glFogf,(GLenum pname, GLfloat param),(pname,param),) +GL_FUNC(void,glFogfv,(GLenum pname, const GLfloat *params),(pname,params),) +GL_FUNC(void,glFogi,(GLenum pname, GLint param),(pname,param),) +GL_FUNC(void,glFrontFace,(GLenum mode),(mode),) +GL_FUNC(void,glGetBooleanv,(GLenum pname, GLboolean *params),(pname,params),) +GL_FUNC(void,glGetDoublev,(GLenum pname, GLdouble *params),(pname,params),) +GL_FUNC(void,glGetMaterialfv,(GLenum face, GLenum pname, GLfloat *params),(face,pname,params),) +GL_FUNC(void,glGetLightfv,(GLenum light, GLenum pname, GLfloat *params),(light,pname,params),) +GL_FUNC(GLboolean,glIsEnabled,(GLenum cap),(cap),return) +GL_FUNC(void,glGetCompressedTexImageARB,(GLenum a, GLint b, GLvoid *c),(a,b,c),) +GL_FUNC(void,glLightf,(GLenum light, GLenum pname, GLfloat param),(light,pname,param),) +GL_FUNC(void,glLightModelfv,(GLenum pname, const GLfloat *params),(pname,params),) +GL_FUNC(void,glLightModeli,(GLenum pname, GLint param),(pname,param),) +GL_FUNC(void,glLightModeliv,(GLenum pname, const GLint *params),(pname,params),) +GL_FUNC(void,glLineStipple,(GLint factor, GLushort pattern),(factor,pattern),) +GL_FUNC(void,glLineWidth,(GLfloat width),(width),) +GL_FUNC(void,glPushAttrib,(GLbitfield mask),(mask),) +GL_FUNC(void,glPopAttrib,(void),(),) +GL_FUNC(void,glLogicOp,(GLenum opcode),(opcode),) +GL_FUNC(void,glMaterialf,(GLenum face, GLenum pname, GLfloat param),(face,pname,param),) +GL_FUNC(void,glMateriali,(GLenum face, GLenum pname, GLint param),(face,pname,param),) +GL_FUNC(void,glMaterialfv,(GLenum face, GLenum pname, const GLfloat *params),(face,pname,params),) +GL_FUNC(void,glNormal3d,(GLdouble nx, GLdouble ny, GLdouble nz),(nx,ny,nz),) +GL_FUNC(void,glNormal3dv,(const GLdouble *v),(v),) +GL_FUNC(void,glNormal3fv,(const GLfloat *v),(v),) +GL_FUNC(void,glNormalPointer,(GLenum type, GLsizei stride, const GLvoid *ptr),(type,stride,ptr),) +GL_FUNC(void,glPolygonMode,(GLenum face, GLenum mode),(face,mode),) +GL_FUNC(void,glPolygonOffset,(GLfloat factor, GLfloat units),(factor,units),) +GL_FUNC(void,glPolygonStipple,(const GLubyte *mask),(mask),) +GL_FUNC(void,glRotated,(GLdouble angle, GLdouble x, GLdouble y, GLdouble z),(angle,x,y,z),) +GL_FUNC(void,glStencilFunc,(GLenum func, GLint ref, GLuint mask),(func,ref,mask),) +GL_FUNC(void,glStencilMask,(GLuint mask),(mask),) +GL_FUNC(void,glScissor,(GLint x, GLint y, GLsizei width, GLsizei height),(x,y,width,height),) +GL_FUNC(void,glStencilOp,(GLenum fail, GLenum zfail, GLenum zpass),(fail,zfail,zpass),) +GL_FUNC(void,glTexCoord2i,(GLint s, GLint t),(s,t),) +GL_FUNC(void,glTexCoord2fv,(const GLfloat *v),(v),) +GL_FUNC(void,glTexGenfv,(GLenum coord, GLenum pname, const GLfloat *params),(coord,pname,params),) +GL_FUNC(void,glTexGeni,(GLenum coord, GLenum pname, GLint param),(coord,pname,param),) +GL_FUNC(void,glTexParameterfv,(GLenum target, GLenum pname, const GLfloat *params),(target,pname,params),) +GL_FUNC(void,glVertex2d,(GLdouble x, GLdouble y),(x,y),) +GL_FUNC(void,glVertex2dv,(const GLdouble *v),(v),) +GL_FUNC(void,glVertex2fv,(const GLfloat *v),(v),) +GL_FUNC(void,glVertex3dv,(const GLdouble *v),(v),) +GL_FUNC(void,glVertex3fv,(const GLfloat *v),(v),) +GL_FUNC(void,glVertex4dv,(const GLdouble *v),(v),) +GL_FUNC(void,glEvalPoint1,(GLint i),(i),) +GL_FUNC(void,glEvalPoint2,(GLint i, GLint j),(i,j),) +GL_FUNC(void,glEvalCoord1f,(GLfloat u),(u),) +GL_FUNC(void,glEvalCoord2f,(GLfloat u, GLfloat v),(u,v),) +GL_FUNC(void,glEvalMesh1,(GLenum mode, GLint i1, GLint i2),(mode,i1,i2),) +GL_FUNC(void,glEvalMesh2,(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2),(mode,i1,i2,j1,j2),) +GL_FUNC(void,glMapGrid1f,(GLint un, GLfloat u1, GLfloat u2),(un,u1,u2),) +GL_FUNC(void,glMapGrid2d,(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2),(un,u1,u2,vn,v1,v2),) +GL_FUNC(void,glMapGrid2f,(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2),(un,u1,u2,vn,v1,v2),) +GL_FUNC(void,glMap1f,(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points),(target,u1,u2,stride,order,points),) +GL_FUNC(void,glMap2f,(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points),(target,u1,u2,ustride,uorder,v1,v2,vstride,vorder,points),) +GL_FUNC(void,glVertexAttribPointerARB,(GLuint a, GLint b, GLenum c, GLboolean d, GLsizei e, const GLvoid *f),(a,b,c,d,e,f),) +GL_FUNC(GLuint,glNewObjectBufferATI,(GLsizei a, const GLvoid *b, GLenum c),(a,b,c),return) +GL_FUNC(void,glUpdateObjectBufferATI,(GLuint a, GLuint b, GLsizei c, const GLvoid *d, GLenum e),(a,b,c,d,e),) +GL_FUNC(void,glFreeObjectBufferATI,(GLuint a),(a),) +GL_FUNC(void,glArrayObjectATI,(GLenum a, GLint b, GLenum c, GLsizei d, GLuint e, GLuint f),(a,b,c,d,e,f),) +GL_FUNC(void,glVertexAttribArrayObjectATI,(GLuint a, GLint b, GLenum c, GLboolean d, GLsizei e, GLuint f, GLuint g),(a,b,c,d,e,f,g),) + +// CgGL needs these on Linux... +#if LL_LINUX +GL_FUNC(void*,glXGetCurrentDisplay,(void),(),return) +GL_FUNC(const char *,glXQueryExtensionsString,(void *dpy, int screen),(dpy,screen),return) +GL_FUNC(void*,glXGetProcAddressARB,(const GLubyte *fn),(fn),return) +#endif +#endif + +// end of llglstubs.h ... + diff --git a/linden/indra/llwindow/llgltypes.h b/linden/indra/llwindow/llgltypes.h new file mode 100644 index 0000000..52a58b5 --- /dev/null +++ b/linden/indra/llwindow/llgltypes.h @@ -0,0 +1,40 @@ +/** + * @file llgltypes.h + * @brief LLGL definition + * + * Copyright (c) 2006-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLGLTYPES_H +#define LLGLTYPES_H + +#define MAX_GL_TEXTURE_UNITS 16 + +typedef U32 LLGLenum; +typedef U32 LLGLuint; +typedef S32 LLGLint; +typedef F32 LLGLfloat; +typedef F64 LLGLdouble; +typedef U8 LLGLboolean; + +#endif diff --git a/linden/indra/llwindow/llkeyboard.cpp b/linden/indra/llwindow/llkeyboard.cpp new file mode 100644 index 0000000..fd6fcdc --- /dev/null +++ b/linden/indra/llwindow/llkeyboard.cpp @@ -0,0 +1,392 @@ +/** + * @file llkeyboard.cpp + * @brief Handler for assignable key bindings + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" +#include "indra_constants.h" +#include "llkeyboard.h" + +#include "llwindow.h" + + +// +// Globals +// + +LLKeyboard *gKeyboard = NULL; + +//static +std::map LLKeyboard::sKeysToNames; +std::map 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::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::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(); + mKeyLevelFrameCount[translated_key] = 0; + mKeyRepeated[translated_key] = FALSE; + } + 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; + + // 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 in chat to move agents forward. JC + mKeyUp[translated_key] = TRUE; + 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) +{ + return mKeyLevelTimer[key].getElapsedTimeF32(); +} + +// Returns time in frames since key was pressed. +S32 LLKeyboard::getKeyElapsedFrameCount(KEY key) +{ + return mKeyLevelFrameCount[key]; +} + +// 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/linden/indra/llwindow/llkeyboard.h b/linden/indra/llwindow/llkeyboard.h new file mode 100644 index 0000000..e262ab6 --- /dev/null +++ b/linden/indra/llwindow/llkeyboard.h @@ -0,0 +1,144 @@ +/** + * @file llkeyboard.h + * @brief Handler for assignable key bindings + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLKEYBOARD_H +#define LL_LLKEYBOARD_H + +#include + +#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 getKeyDown(mCurScanKey) ? getKeyElapsedTime( mCurScanKey ) : 0.f; } + F32 getCurKeyElapsedFrameCount() { return getKeyDown(mCurScanKey) ? (F32)getKeyElapsedFrameCount( mCurScanKey ) : 0.f; } + 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; } + F32 getKeyElapsedTime( KEY key ); // Returns time in seconds since key was pressed. + S32 getKeyElapsedFrameCount( KEY key ); // Returns time in frames since key was pressed. + +protected: + void addKeyName(KEY key, const LLString& name); + +protected: + std::map mTranslateKeyMap; // Map of translations from OS keys to Linden KEYs + std::map 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 sKeysToNames; + static std::map sNamesToKeys; +}; + +extern LLKeyboard *gKeyboard; + +#endif diff --git a/linden/indra/llwindow/llkeyboardmacosx.cpp b/linden/indra/llwindow/llkeyboardmacosx.cpp new file mode 100644 index 0000000..961bb66 --- /dev/null +++ b/linden/indra/llwindow/llkeyboardmacosx.cpp @@ -0,0 +1,328 @@ +/** + * @file llkeyboardmacosx.cpp + * @brief Handler for assignable key bindings + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#if LL_DARWIN + +#include "linden_common.h" +#include "llkeyboardmacosx.h" +#include "llwindow.h" + +#include + +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::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::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::iterator iter= mInvTranslateNumpadMap.find(translated_key); + if(iter != mInvTranslateNumpadMap.end()) + { + return iter->second; + } + } + return inverseTranslateKey(translated_key); +} + +#endif // LL_DARWIN diff --git a/linden/indra/llwindow/llkeyboardmacosx.h b/linden/indra/llwindow/llkeyboardmacosx.h new file mode 100644 index 0000000..a4d9115 --- /dev/null +++ b/linden/indra/llwindow/llkeyboardmacosx.h @@ -0,0 +1,55 @@ +/** + * @file llkeyboardmacosx.h + * @brief Handler for assignable key bindings + * + * Copyright (c) 2004-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 mTranslateNumpadMap; // special map for translating OS keys to numpad keys + std::map mInvTranslateNumpadMap; // inverse of the above +}; + +#endif diff --git a/linden/indra/llwindow/llkeyboardsdl.cpp b/linden/indra/llwindow/llkeyboardsdl.cpp new file mode 100644 index 0000000..07db986 --- /dev/null +++ b/linden/indra/llwindow/llkeyboardsdl.cpp @@ -0,0 +1,343 @@ +/** + * @file llkeyboardsdl.cpp + * @brief Handler for assignable key bindings + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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::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::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::iterator iter= mInvTranslateNumpadMap.find(translated_key); + if(iter != mInvTranslateNumpadMap.end()) + { + return iter->second; + } + } + return inverseTranslateKey(translated_key); +} + +#endif + diff --git a/linden/indra/llwindow/llkeyboardsdl.h b/linden/indra/llwindow/llkeyboardsdl.h new file mode 100644 index 0000000..b582e21 --- /dev/null +++ b/linden/indra/llwindow/llkeyboardsdl.h @@ -0,0 +1,56 @@ +/** + * @file llkeyboardsdl.h + * @brief Handler for assignable key bindings + * + * Copyright (c) 2004-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 mTranslateNumpadMap; // special map for translating OS keys to numpad keys + std::map mInvTranslateNumpadMap; // inverse of the above +}; + +#endif diff --git a/linden/indra/llwindow/llkeyboardwin32.cpp b/linden/indra/llwindow/llkeyboardwin32.cpp new file mode 100644 index 0000000..531ad87 --- /dev/null +++ b/linden/indra/llwindow/llkeyboardwin32.cpp @@ -0,0 +1,401 @@ +/** + * @file llkeyboardwin32.cpp + * @brief Handler for assignable key bindings + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#if LL_WINDOWS + +#include "linden_common.h" + +#include "llkeyboardwin32.h" + +#include "llwindow.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include + +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::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() +{ + //RN: this seems redundant, as we should have already received the appropriate + // messages for the modifier keys + + // 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]) + { + // *TODO: 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::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::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/linden/indra/llwindow/llkeyboardwin32.h b/linden/indra/llwindow/llkeyboardwin32.h new file mode 100644 index 0000000..1501e8f --- /dev/null +++ b/linden/indra/llwindow/llkeyboardwin32.h @@ -0,0 +1,59 @@ +/** + * @file llkeyboardwin32.h + * @brief Handler for assignable key bindings + * + * Copyright (c) 2004-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 mTranslateNumpadMap; + std::map mInvTranslateNumpadMap; +}; + +#endif diff --git a/linden/indra/llwindow/llmousehandler.h b/linden/indra/llwindow/llmousehandler.h new file mode 100644 index 0000000..b907b55 --- /dev/null +++ b/linden/indra/llwindow/llmousehandler.h @@ -0,0 +1,58 @@ +/** + * @file llmousehandler.h + * @brief LLMouseHandler class definition + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llwindow/llwindow.cpp b/linden/indra/llwindow/llwindow.cpp new file mode 100644 index 0000000..20fc84e --- /dev/null +++ b/linden/indra/llwindow/llwindow.cpp @@ -0,0 +1,420 @@ +/** + * @file llwindow.cpp + * @brief Basic graphical window class + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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" +#include "linked_lists.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 // !!! *FIX: (???) + 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 // !!! *FIX: (???) + 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 +// + +// TODO: replace with std::set +static LLLinkedList 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/linden/indra/llwindow/llwindow.h b/linden/indra/llwindow/llwindow.h new file mode 100644 index 0000000..ac427f1 --- /dev/null +++ b/linden/indra/llwindow/llwindow.h @@ -0,0 +1,334 @@ +/** + * @file llwindow.h + * @brief Basic graphical window class + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLWINDOW_H +#define LL_LLWINDOW_H + +#include + +#include "llrect.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 +{ +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 ); + +#endif // _LL_window_h_ diff --git a/linden/indra/llwindow/llwindow.vcproj b/linden/indra/llwindow/llwindow.vcproj new file mode 100644 index 0000000..f2f4e32 --- /dev/null +++ b/linden/indra/llwindow/llwindow.vcproj @@ -0,0 +1,270 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linden/indra/llwindow/llwindowheadless.cpp b/linden/indra/llwindow/llwindowheadless.cpp new file mode 100644 index 0000000..821632d --- /dev/null +++ b/linden/indra/llwindow/llwindowheadless.cpp @@ -0,0 +1,51 @@ +/** + * @file llwindowheadless.cpp + * @brief Headless implementation of LLWindow class + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llwindow/llwindowheadless.h b/linden/indra/llwindow/llwindowheadless.h new file mode 100644 index 0000000..cc7fa06 --- /dev/null +++ b/linden/indra/llwindow/llwindowheadless.h @@ -0,0 +1,117 @@ +/** + * @file llwindowheadless.h + * @brief Headless definition of LLWindow class + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llwindow/llwindowlinux.cpp b/linden/indra/llwindow/llwindowlinux.cpp new file mode 100644 index 0000000..e266efc --- /dev/null +++ b/linden/indra/llwindow/llwindowlinux.cpp @@ -0,0 +1,57 @@ +/** + * @file llwindowlinux.cpp + * @brief Platform-dependent implementation of llwindow + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#if LL_LINUX + +#include "linden_common.h" +#include "indra_constants.h" + +#include "llwindowlinux.h" +#include "llgl.h" +#include "llglheaders.h" + +// +// LLWindowLinux +// +LLWindowLinux::LLWindowLinux(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) +{ + llerrs << "Linux window not yet supported" << llendl; +} + + +LLWindowLinux::~LLWindowLinux() +{ +} + +void LLWindowLinux::swapBuffers() +{ +} + +#endif // LL_LINUX diff --git a/linden/indra/llwindow/llwindowlinux.h b/linden/indra/llwindow/llwindowlinux.h new file mode 100644 index 0000000..9550cdd --- /dev/null +++ b/linden/indra/llwindow/llwindowlinux.h @@ -0,0 +1,115 @@ +/** + * @file llwindowlinux.h + * @brief Linux implementation of LLWindow class + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLWINDOWWIN32_H +#define LL_LLWINDOWWIN32_H + +#include "llwindow.h" + +class LLWindowLinux : 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 BOOL dialog_color_picker (F32 *r, F32 *g, F32 *b ); + + /*virtual*/ void *getPlatformWindow() { return NULL; } + + LLWindowLinux(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); + ~LLWindowLinux(); +}; + +class LLSplashScreenLinux : public LLSplashScreen +{ +public: + LLSplashScreenLinux() {}; + virtual ~LLSplashScreenLinux() {}; + + /*virtual*/ void showImpl() {}; + /*virtual*/ void updateImpl(const char* mesg) {}; + /*virtual*/ void hideImpl() {}; + +}; + +#endif //LL_LLWINDOWWIN32_H diff --git a/linden/indra/llwindow/llwindowmacosx-objc.h b/linden/indra/llwindow/llwindowmacosx-objc.h new file mode 100644 index 0000000..d055729 --- /dev/null +++ b/linden/indra/llwindow/llwindowmacosx-objc.h @@ -0,0 +1,38 @@ +/** + * @file llwindowmacosx-objc.h + * @brief Prototypes for functions shared between llwindowmacosx.cpp + * and llwindowmacosx-objc.mm. + * + * Copyright (c) 2006-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + + +// 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/linden/indra/llwindow/llwindowmacosx-objc.mm b/linden/indra/llwindow/llwindowmacosx-objc.mm new file mode 100644 index 0000000..bde3841 --- /dev/null +++ b/linden/indra/llwindow/llwindowmacosx-objc.mm @@ -0,0 +1,106 @@ +/** + * @file llwindowmacosx-objc.mm + * @brief Definition of functions shared between llwindowmacosx.cpp + * and llwindowmacosx-objc.mm. + * + * Copyright (c) 2006-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include + +/* + * 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/linden/indra/llwindow/llwindowmacosx.cpp b/linden/indra/llwindow/llwindowmacosx.cpp new file mode 100644 index 0000000..d990bb5 --- /dev/null +++ b/linden/indra/llwindow/llwindowmacosx.cpp @@ -0,0 +1,2923 @@ +/** + * @file llwindowmacosx.cpp + * @brief Platform-dependent implementation of llwindow + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#if LL_DARWIN + +#include "linden_common.h" + +#include + +#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, ¤tBounds); + 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), ¤tBounds); + } + } + 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 /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, + ¶ms, + &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/linden/indra/llwindow/llwindowmacosx.h b/linden/indra/llwindow/llwindowmacosx.h new file mode 100644 index 0000000..7425077 --- /dev/null +++ b/linden/indra/llwindow/llwindowmacosx.h @@ -0,0 +1,208 @@ +/** + * @file llwindowmacosx.h + * @brief Mac implementation of LLWindow class + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLWINDOWMACOSX_H +#define LL_LLWINDOWMACOSX_H + +#include "llwindow.h" + +#include +#include + +// 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/linden/indra/llwindow/llwindowmesaheadless.cpp b/linden/indra/llwindow/llwindowmesaheadless.cpp new file mode 100644 index 0000000..7e5f4b0 --- /dev/null +++ b/linden/indra/llwindow/llwindowmesaheadless.cpp @@ -0,0 +1,83 @@ +/** + * @file llwindowmesaheadless.cpp + * @brief Platform-dependent implementation of llwindow + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llwindow/llwindowmesaheadless.h b/linden/indra/llwindow/llwindowmesaheadless.h new file mode 100644 index 0000000..f8599c6 --- /dev/null +++ b/linden/indra/llwindow/llwindowmesaheadless.h @@ -0,0 +1,123 @@ +/** + * @file llwindowmesaheadless.h + * @brief Windows implementation of LLWindow class + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llwindow/llwindowsdl.cpp b/linden/indra/llwindow/llwindowsdl.cpp new file mode 100644 index 0000000..a94284e --- /dev/null +++ b/linden/indra/llwindow/llwindowsdl.cpp @@ -0,0 +1,2506 @@ +/** + * @file llwindowsdl.cpp + * @brief Platform-dependent implementation of llwindow + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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 +# include +# include +#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 +// 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"; // *FIX: (???) + + // 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; + + 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) + { + 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); + + // *FIX: 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 // *FIX: 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 // *FIX: 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() <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) + { + // *FIX: (???) + //MacMoveWindow(mWindow, position.mX, position.mY, false); + } + + return TRUE; +} + +BOOL LLWindowSDL::setSize(const LLCoordScreen size) +{ + if(mWindow) + { + // *FIX: (???) + //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) + { + // *FIX: NOT YET WORKING (see below) + //switchContext(TRUE, old_size, TRUE); + } + // *FIX: 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." <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." <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; iw; + 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: // *FIX: handle this? + llinfos << "Handling a resize event: " << event.resize.w << + "x" << event.resize.h << llendl; + + // *FIX: I'm not sure this is necessary! + mWindow = SDL_SetVideoMode(event.resize.w, event.resize.h, 32, mSDLFlags); + if (!mWindow) + { + // *FIX: 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; ih; ++i) { + for (j=0; jw; ++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; ibeforeDialog(); + + 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 ) +{ + // *FIX: (???) + 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() +{ + // *FIX: (???) + fprintf(stderr, "bringToFront\n"); +} + +#endif // LL_SDL diff --git a/linden/indra/llwindow/llwindowsdl.h b/linden/indra/llwindow/llwindowsdl.h new file mode 100644 index 0000000..0528b84 --- /dev/null +++ b/linden/indra/llwindow/llwindowsdl.h @@ -0,0 +1,217 @@ +/** + * @file llwindowsdl.h + * @brief SDL implementation of LLWindow class + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#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/linden/indra/llwindow/llwindowwin32.cpp b/linden/indra/llwindow/llwindowwin32.cpp new file mode 100644 index 0000000..b405bdb --- /dev/null +++ b/linden/indra/llwindow/llwindowwin32.cpp @@ -0,0 +1,3255 @@ +/** + * @file llwindowwin32.cpp + * @brief Platform-dependent implementation of llwindow + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "linden_common.h" + +#if LL_WINDOWS && !LL_MESA_HEADLESS + +#include "llwindowwin32.h" + +#include +#include +#include +#include // for _spawn +#include + +// Require DirectInput version 8 +#define DIRECTINPUT_VERSION 0x0800 +#include + + +#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<handleTranslatedKeyDown(KEY_BUTTON1+i, 0); + mJoyButtonState |= (1<handleTranslatedKeyUp(KEY_BUTTON1+i, 0); + mJoyButtonState &= ~(1< 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/linden/indra/llwindow/llwindowwin32.h b/linden/indra/llwindow/llwindowwin32.h new file mode 100644 index 0000000..77696e6 --- /dev/null +++ b/linden/indra/llwindow/llwindowwin32.h @@ -0,0 +1,209 @@ +/** + * @file llwindowwin32.h + * @brief Windows implementation of LLWindow class + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LL_LLWINDOWWIN32_H +#define LL_LLWINDOWWIN32_H + +// Limit Windows API to small and manageable set. +#define WIN32_LEAN_AND_MEAN +#include +#include + +#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 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 -- cgit v1.1