diff options
Diffstat (limited to 'indra/llwindow')
31 files changed, 16697 insertions, 16697 deletions
diff --git a/indra/llwindow/lldragdropwin32.cpp b/indra/llwindow/lldragdropwin32.cpp index e08a2c2fc6..15490ef953 100644 --- a/indra/llwindow/lldragdropwin32.cpp +++ b/indra/llwindow/lldragdropwin32.cpp @@ -1,361 +1,361 @@ -/**
- * @file lldragdrop32.cpp
- * @brief Handler for Windows specific drag and drop (OS to client) code
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#if LL_WINDOWS
-
-#if LL_OS_DRAGDROP_ENABLED
-
-#include "linden_common.h"
-
-#include "llwindowwin32.h"
-#include "llkeyboardwin32.h"
-#include "llwindowcallbacks.h"
-#include "lldragdropwin32.h"
-
-class LLDragDropWin32Target:
- public IDropTarget
-{
- public:
- ////////////////////////////////////////////////////////////////////////////////
- //
- LLDragDropWin32Target( HWND hWnd ) :
- mRefCount( 1 ),
- mAppWindowHandle( hWnd ),
- mAllowDrop(false),
- mIsSlurl(false)
- {
- };
-
- virtual ~LLDragDropWin32Target()
- {
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- ULONG __stdcall AddRef( void )
- {
- return InterlockedIncrement( &mRefCount );
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- ULONG __stdcall Release( void )
- {
- LONG count = InterlockedDecrement( &mRefCount );
-
- if ( count == 0 )
- {
- delete this;
- return 0;
- }
- else
- {
- return count;
- };
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- HRESULT __stdcall QueryInterface( REFIID iid, void** ppvObject )
- {
- if ( iid == IID_IUnknown || iid == IID_IDropTarget )
- {
- AddRef();
- *ppvObject = this;
- return S_OK;
- }
- else
- {
- *ppvObject = 0;
- return E_NOINTERFACE;
- };
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- HRESULT __stdcall DragEnter( IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect )
- {
- FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
-
- // support CF_TEXT using a HGLOBAL?
- if ( S_OK == pDataObject->QueryGetData( &fmtetc ) )
- {
- mAllowDrop = true;
- mDropUrl = std::string();
- mIsSlurl = false;
-
- STGMEDIUM stgmed;
- if( S_OK == pDataObject->GetData( &fmtetc, &stgmed ) )
- {
- PVOID data = GlobalLock( stgmed.hGlobal );
- mDropUrl = std::string( (char*)data );
- // XXX MAJOR MAJOR HACK!
- LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA );
- if (NULL != window_imp)
- {
- LLCoordGL gl_coord( 0, 0 );
-
- POINT pt2;
- pt2.x = pt.x;
- pt2.y = pt.y;
- ScreenToClient( mAppWindowHandle, &pt2 );
-
- LLCoordWindow cursor_coord_window( pt2.x, pt2.y );
- MASK mask = gKeyboard->currentMask(true);
-
- LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask,
- LLWindowCallbacks::DNDA_START_TRACKING, mDropUrl );
-
- switch (result)
- {
- case LLWindowCallbacks::DND_COPY:
- *pdwEffect = DROPEFFECT_COPY;
- break;
- case LLWindowCallbacks::DND_LINK:
- *pdwEffect = DROPEFFECT_LINK;
- break;
- case LLWindowCallbacks::DND_MOVE:
- *pdwEffect = DROPEFFECT_MOVE;
- break;
- case LLWindowCallbacks::DND_NONE:
- default:
- *pdwEffect = DROPEFFECT_NONE;
- break;
- }
- };
-
- GlobalUnlock( stgmed.hGlobal );
- ReleaseStgMedium( &stgmed );
- };
- SetFocus( mAppWindowHandle );
- }
- else
- {
- mAllowDrop = false;
- *pdwEffect = DROPEFFECT_NONE;
- };
-
- return S_OK;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- HRESULT __stdcall DragOver( DWORD grfKeyState, POINTL pt, DWORD* pdwEffect )
- {
- if ( mAllowDrop )
- {
- // XXX MAJOR MAJOR HACK!
- LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA );
- if (NULL != window_imp)
- {
- LLCoordGL gl_coord( 0, 0 );
-
- POINT pt2;
- pt2.x = pt.x;
- pt2.y = pt.y;
- ScreenToClient( mAppWindowHandle, &pt2 );
-
- LLCoordWindow cursor_coord_window( pt2.x, pt2.y );
- MASK mask = gKeyboard->currentMask(true);
-
- LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask,
- LLWindowCallbacks::DNDA_TRACK, mDropUrl );
-
- switch (result)
- {
- case LLWindowCallbacks::DND_COPY:
- *pdwEffect = DROPEFFECT_COPY;
- break;
- case LLWindowCallbacks::DND_LINK:
- *pdwEffect = DROPEFFECT_LINK;
- break;
- case LLWindowCallbacks::DND_MOVE:
- *pdwEffect = DROPEFFECT_MOVE;
- break;
- case LLWindowCallbacks::DND_NONE:
- default:
- *pdwEffect = DROPEFFECT_NONE;
- break;
- }
- };
- }
- else
- {
- *pdwEffect = DROPEFFECT_NONE;
- };
-
- return S_OK;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- HRESULT __stdcall DragLeave( void )
- {
- // XXX MAJOR MAJOR HACK!
- LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA );
- if (NULL != window_imp)
- {
- LLCoordGL gl_coord( 0, 0 );
- MASK mask = gKeyboard->currentMask(true);
- window_imp->completeDragNDropRequest( gl_coord, mask, LLWindowCallbacks::DNDA_STOP_TRACKING, mDropUrl );
- };
- return S_OK;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- HRESULT __stdcall Drop( IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect )
- {
- if ( mAllowDrop )
- {
- // window impl stored in Window data (neat!)
- LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA );
- if ( NULL != window_imp )
- {
- POINT pt_client;
- pt_client.x = pt.x;
- pt_client.y = pt.y;
- ScreenToClient( mAppWindowHandle, &pt_client );
-
- LLCoordWindow cursor_coord_window( pt_client.x, pt_client.y );
- LLCoordGL gl_coord(cursor_coord_window.convert());
- LL_INFOS() << "### (Drop) URL is: " << mDropUrl << LL_ENDL;
- LL_INFOS() << "### raw coords are: " << pt.x << " x " << pt.y << LL_ENDL;
- LL_INFOS() << "### client coords are: " << pt_client.x << " x " << pt_client.y << LL_ENDL;
- LL_INFOS() << "### GL coords are: " << gl_coord.mX << " x " << gl_coord.mY << LL_ENDL;
- LL_INFOS() << LL_ENDL;
-
- // no keyboard modifier option yet but we could one day
- MASK mask = gKeyboard->currentMask( true );
-
- // actually do the drop
- LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, mask,
- LLWindowCallbacks::DNDA_DROPPED, mDropUrl );
-
- switch (result)
- {
- case LLWindowCallbacks::DND_COPY:
- *pdwEffect = DROPEFFECT_COPY;
- break;
- case LLWindowCallbacks::DND_LINK:
- *pdwEffect = DROPEFFECT_LINK;
- break;
- case LLWindowCallbacks::DND_MOVE:
- *pdwEffect = DROPEFFECT_MOVE;
- break;
- case LLWindowCallbacks::DND_NONE:
- default:
- *pdwEffect = DROPEFFECT_NONE;
- break;
- }
- };
- }
- else
- {
- *pdwEffect = DROPEFFECT_NONE;
- };
-
- return S_OK;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- private:
- LONG mRefCount;
- HWND mAppWindowHandle;
- bool mAllowDrop;
- std::string mDropUrl;
- bool mIsSlurl;
- friend class LLWindowWin32;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-//
-LLDragDropWin32::LLDragDropWin32() :
- mDropTarget( NULL ),
- mDropWindowHandle( NULL )
-
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-LLDragDropWin32::~LLDragDropWin32()
-{
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-bool LLDragDropWin32::init( HWND hWnd )
-{
- if ( NOERROR != OleInitialize( NULL ) )
- return false;
-
- mDropTarget = new LLDragDropWin32Target( hWnd );
- if ( mDropTarget )
- {
- HRESULT result = CoLockObjectExternal( mDropTarget, TRUE, FALSE );
- if ( S_OK == result )
- {
- result = RegisterDragDrop( hWnd, mDropTarget );
- if ( S_OK != result )
- {
- // RegisterDragDrop failed
- return false;
- };
-
- // all ok
- mDropWindowHandle = hWnd;
- }
- else
- {
- // Unable to lock OLE object
- return false;
- };
- };
-
- // success
- return true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//
-void LLDragDropWin32::reset()
-{
- if ( mDropTarget )
- {
- RevokeDragDrop( mDropWindowHandle );
- CoLockObjectExternal( mDropTarget, FALSE, TRUE );
- mDropTarget->Release();
- };
-
- OleUninitialize();
-}
-
-#endif // LL_OS_DRAGDROP_ENABLED
-
-#endif // LL_WINDOWS
-
+/** + * @file lldragdrop32.cpp + * @brief Handler for Windows specific drag and drop (OS to client) code + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if LL_WINDOWS + +#if LL_OS_DRAGDROP_ENABLED + +#include "linden_common.h" + +#include "llwindowwin32.h" +#include "llkeyboardwin32.h" +#include "llwindowcallbacks.h" +#include "lldragdropwin32.h" + +class LLDragDropWin32Target: + public IDropTarget +{ + public: + //////////////////////////////////////////////////////////////////////////////// + // + LLDragDropWin32Target( HWND hWnd ) : + mRefCount( 1 ), + mAppWindowHandle( hWnd ), + mAllowDrop(false), + mIsSlurl(false) + { + }; + + virtual ~LLDragDropWin32Target() + { + }; + + //////////////////////////////////////////////////////////////////////////////// + // + ULONG __stdcall AddRef( void ) + { + return InterlockedIncrement( &mRefCount ); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + ULONG __stdcall Release( void ) + { + LONG count = InterlockedDecrement( &mRefCount ); + + if ( count == 0 ) + { + delete this; + return 0; + } + else + { + return count; + }; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + HRESULT __stdcall QueryInterface( REFIID iid, void** ppvObject ) + { + if ( iid == IID_IUnknown || iid == IID_IDropTarget ) + { + AddRef(); + *ppvObject = this; + return S_OK; + } + else + { + *ppvObject = 0; + return E_NOINTERFACE; + }; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + HRESULT __stdcall DragEnter( IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect ) + { + FORMATETC fmtetc = { CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + + // support CF_TEXT using a HGLOBAL? + if ( S_OK == pDataObject->QueryGetData( &fmtetc ) ) + { + mAllowDrop = true; + mDropUrl = std::string(); + mIsSlurl = false; + + STGMEDIUM stgmed; + if( S_OK == pDataObject->GetData( &fmtetc, &stgmed ) ) + { + PVOID data = GlobalLock( stgmed.hGlobal ); + mDropUrl = std::string( (char*)data ); + // XXX MAJOR MAJOR HACK! + LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA ); + if (NULL != window_imp) + { + LLCoordGL gl_coord( 0, 0 ); + + POINT pt2; + pt2.x = pt.x; + pt2.y = pt.y; + ScreenToClient( mAppWindowHandle, &pt2 ); + + LLCoordWindow cursor_coord_window( pt2.x, pt2.y ); + MASK mask = gKeyboard->currentMask(true); + + LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, + LLWindowCallbacks::DNDA_START_TRACKING, mDropUrl ); + + switch (result) + { + case LLWindowCallbacks::DND_COPY: + *pdwEffect = DROPEFFECT_COPY; + break; + case LLWindowCallbacks::DND_LINK: + *pdwEffect = DROPEFFECT_LINK; + break; + case LLWindowCallbacks::DND_MOVE: + *pdwEffect = DROPEFFECT_MOVE; + break; + case LLWindowCallbacks::DND_NONE: + default: + *pdwEffect = DROPEFFECT_NONE; + break; + } + }; + + GlobalUnlock( stgmed.hGlobal ); + ReleaseStgMedium( &stgmed ); + }; + SetFocus( mAppWindowHandle ); + } + else + { + mAllowDrop = false; + *pdwEffect = DROPEFFECT_NONE; + }; + + return S_OK; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + HRESULT __stdcall DragOver( DWORD grfKeyState, POINTL pt, DWORD* pdwEffect ) + { + if ( mAllowDrop ) + { + // XXX MAJOR MAJOR HACK! + LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA ); + if (NULL != window_imp) + { + LLCoordGL gl_coord( 0, 0 ); + + POINT pt2; + pt2.x = pt.x; + pt2.y = pt.y; + ScreenToClient( mAppWindowHandle, &pt2 ); + + LLCoordWindow cursor_coord_window( pt2.x, pt2.y ); + MASK mask = gKeyboard->currentMask(true); + + LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, + LLWindowCallbacks::DNDA_TRACK, mDropUrl ); + + switch (result) + { + case LLWindowCallbacks::DND_COPY: + *pdwEffect = DROPEFFECT_COPY; + break; + case LLWindowCallbacks::DND_LINK: + *pdwEffect = DROPEFFECT_LINK; + break; + case LLWindowCallbacks::DND_MOVE: + *pdwEffect = DROPEFFECT_MOVE; + break; + case LLWindowCallbacks::DND_NONE: + default: + *pdwEffect = DROPEFFECT_NONE; + break; + } + }; + } + else + { + *pdwEffect = DROPEFFECT_NONE; + }; + + return S_OK; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + HRESULT __stdcall DragLeave( void ) + { + // XXX MAJOR MAJOR HACK! + LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA ); + if (NULL != window_imp) + { + LLCoordGL gl_coord( 0, 0 ); + MASK mask = gKeyboard->currentMask(true); + window_imp->completeDragNDropRequest( gl_coord, mask, LLWindowCallbacks::DNDA_STOP_TRACKING, mDropUrl ); + }; + return S_OK; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + HRESULT __stdcall Drop( IDataObject* pDataObject, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect ) + { + if ( mAllowDrop ) + { + // window impl stored in Window data (neat!) + LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLongPtr( mAppWindowHandle, GWLP_USERDATA ); + if ( NULL != window_imp ) + { + POINT pt_client; + pt_client.x = pt.x; + pt_client.y = pt.y; + ScreenToClient( mAppWindowHandle, &pt_client ); + + LLCoordWindow cursor_coord_window( pt_client.x, pt_client.y ); + LLCoordGL gl_coord(cursor_coord_window.convert()); + LL_INFOS() << "### (Drop) URL is: " << mDropUrl << LL_ENDL; + LL_INFOS() << "### raw coords are: " << pt.x << " x " << pt.y << LL_ENDL; + LL_INFOS() << "### client coords are: " << pt_client.x << " x " << pt_client.y << LL_ENDL; + LL_INFOS() << "### GL coords are: " << gl_coord.mX << " x " << gl_coord.mY << LL_ENDL; + LL_INFOS() << LL_ENDL; + + // no keyboard modifier option yet but we could one day + MASK mask = gKeyboard->currentMask( true ); + + // actually do the drop + LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, mask, + LLWindowCallbacks::DNDA_DROPPED, mDropUrl ); + + switch (result) + { + case LLWindowCallbacks::DND_COPY: + *pdwEffect = DROPEFFECT_COPY; + break; + case LLWindowCallbacks::DND_LINK: + *pdwEffect = DROPEFFECT_LINK; + break; + case LLWindowCallbacks::DND_MOVE: + *pdwEffect = DROPEFFECT_MOVE; + break; + case LLWindowCallbacks::DND_NONE: + default: + *pdwEffect = DROPEFFECT_NONE; + break; + } + }; + } + else + { + *pdwEffect = DROPEFFECT_NONE; + }; + + return S_OK; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + private: + LONG mRefCount; + HWND mAppWindowHandle; + bool mAllowDrop; + std::string mDropUrl; + bool mIsSlurl; + friend class LLWindowWin32; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +LLDragDropWin32::LLDragDropWin32() : + mDropTarget( NULL ), + mDropWindowHandle( NULL ) + +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLDragDropWin32::~LLDragDropWin32() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLDragDropWin32::init( HWND hWnd ) +{ + if ( NOERROR != OleInitialize( NULL ) ) + return false; + + mDropTarget = new LLDragDropWin32Target( hWnd ); + if ( mDropTarget ) + { + HRESULT result = CoLockObjectExternal( mDropTarget, TRUE, FALSE ); + if ( S_OK == result ) + { + result = RegisterDragDrop( hWnd, mDropTarget ); + if ( S_OK != result ) + { + // RegisterDragDrop failed + return false; + }; + + // all ok + mDropWindowHandle = hWnd; + } + else + { + // Unable to lock OLE object + return false; + }; + }; + + // success + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLDragDropWin32::reset() +{ + if ( mDropTarget ) + { + RevokeDragDrop( mDropWindowHandle ); + CoLockObjectExternal( mDropTarget, FALSE, TRUE ); + mDropTarget->Release(); + }; + + OleUninitialize(); +} + +#endif // LL_OS_DRAGDROP_ENABLED + +#endif // LL_WINDOWS + diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp index 5d0e15d9f4..0830dc9528 100644 --- a/indra/llwindow/lldxhardware.cpp +++ b/indra/llwindow/lldxhardware.cpp @@ -1,1119 +1,1119 @@ -/**
- * @file lldxhardware.cpp
- * @brief LLDXHardware implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifdef LL_WINDOWS
-
-// Culled from some Microsoft sample code
-
-#include "linden_common.h"
-
-#define INITGUID
-#include <dxdiag.h>
-#undef INITGUID
-
-#include <wbemidl.h>
-#include <comdef.h>
-
-#include <boost/tokenizer.hpp>
-
-#include "lldxhardware.h"
-
-#include "llerror.h"
-
-#include "llstring.h"
-#include "llstl.h"
-#include "lltimer.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; } }
-
-typedef BOOL ( WINAPI* PfnCoSetProxyBlanket )( IUnknown* pProxy, DWORD dwAuthnSvc, DWORD dwAuthzSvc,
- OLECHAR* pServerPrincName, DWORD dwAuthnLevel, DWORD dwImpLevel,
- RPC_AUTH_IDENTITY_HANDLE pAuthInfo, DWORD dwCapabilities );
-
-HRESULT GetVideoMemoryViaWMI(WCHAR* strInputDeviceID, DWORD* pdwAdapterRam)
-{
- HRESULT hr;
- bool bGotMemory = false;
- IWbemLocator* pIWbemLocator = nullptr;
- IWbemServices* pIWbemServices = nullptr;
- BSTR pNamespace = nullptr;
-
- *pdwAdapterRam = 0;
- CoInitializeEx(0, COINIT_APARTMENTTHREADED);
-
- hr = CoCreateInstance( CLSID_WbemLocator,
- nullptr,
- CLSCTX_INPROC_SERVER,
- IID_IWbemLocator,
- ( LPVOID* )&pIWbemLocator );
-#ifdef PRINTF_DEBUGGING
- if( FAILED( hr ) ) wprintf( L"WMI: CoCreateInstance failed: 0x%0.8x\n", hr );
-#endif
-
- if( SUCCEEDED( hr ) && pIWbemLocator )
- {
- // Using the locator, connect to WMI in the given namespace.
- pNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );
-
- hr = pIWbemLocator->ConnectServer( pNamespace, nullptr, nullptr, 0L,
- 0L, nullptr, nullptr, &pIWbemServices );
-#ifdef PRINTF_DEBUGGING
- if( FAILED( hr ) ) wprintf( L"WMI: pIWbemLocator->ConnectServer failed: 0x%0.8x\n", hr );
-#endif
- if( SUCCEEDED( hr ) && pIWbemServices != 0 )
- {
- HINSTANCE hinstOle32 = nullptr;
-
- hinstOle32 = LoadLibraryW( L"ole32.dll" );
- if( hinstOle32 )
- {
- PfnCoSetProxyBlanket pfnCoSetProxyBlanket = nullptr;
-
- pfnCoSetProxyBlanket = ( PfnCoSetProxyBlanket )GetProcAddress( hinstOle32, "CoSetProxyBlanket" );
- if( pfnCoSetProxyBlanket != 0 )
- {
- // Switch security level to IMPERSONATE.
- pfnCoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
- RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, 0 );
- }
-
- FreeLibrary( hinstOle32 );
- }
-
- IEnumWbemClassObject* pEnumVideoControllers = nullptr;
- BSTR pClassName = nullptr;
-
- pClassName = SysAllocString( L"Win32_VideoController" );
-
- hr = pIWbemServices->CreateInstanceEnum( pClassName, 0,
- nullptr, &pEnumVideoControllers );
-#ifdef PRINTF_DEBUGGING
- if( FAILED( hr ) ) wprintf( L"WMI: pIWbemServices->CreateInstanceEnum failed: 0x%0.8x\n", hr );
-#endif
-
- if( SUCCEEDED( hr ) && pEnumVideoControllers )
- {
- IWbemClassObject* pVideoControllers[10] = {0};
- DWORD uReturned = 0;
- BSTR pPropName = nullptr;
-
- // Get the first one in the list
- pEnumVideoControllers->Reset();
- hr = pEnumVideoControllers->Next( 5000, // timeout in 5 seconds
- 10, // return the first 10
- pVideoControllers,
- &uReturned );
-#ifdef PRINTF_DEBUGGING
- if( FAILED( hr ) ) wprintf( L"WMI: pEnumVideoControllers->Next failed: 0x%0.8x\n", hr );
- if( uReturned == 0 ) wprintf( L"WMI: pEnumVideoControllers uReturned == 0\n" );
-#endif
-
- VARIANT var;
- if( SUCCEEDED( hr ) )
- {
- bool bFound = false;
- for( UINT iController = 0; iController < uReturned; iController++ )
- {
- if ( !pVideoControllers[iController] )
- continue;
-
- // if strInputDeviceID is set find this specific device and return memory or specific device
- // if strInputDeviceID is not set return the best device
- if (strInputDeviceID)
- {
- pPropName = SysAllocString( L"PNPDeviceID" );
- hr = pVideoControllers[iController]->Get( pPropName, 0L, &var, nullptr, nullptr );
-#ifdef PRINTF_DEBUGGING
- if( FAILED( hr ) )
- wprintf( L"WMI: pVideoControllers[iController]->Get PNPDeviceID failed: 0x%0.8x\n", hr );
-#endif
- if( SUCCEEDED( hr ) && strInputDeviceID)
- {
- if( wcsstr( var.bstrVal, strInputDeviceID ) != 0 )
- bFound = true;
- }
- VariantClear( &var );
- if( pPropName ) SysFreeString( pPropName );
- }
-
- if( bFound || !strInputDeviceID )
- {
- pPropName = SysAllocString( L"AdapterRAM" );
- hr = pVideoControllers[iController]->Get( pPropName, 0L, &var, nullptr, nullptr );
-#ifdef PRINTF_DEBUGGING
- if( FAILED( hr ) )
- wprintf( L"WMI: pVideoControllers[iController]->Get AdapterRAM failed: 0x%0.8x\n",
- hr );
-#endif
- if( SUCCEEDED( hr ) )
- {
- bGotMemory = true;
- *pdwAdapterRam = llmax(var.ulVal, *pdwAdapterRam);
- }
- VariantClear( &var );
- if( pPropName ) SysFreeString( pPropName );
- }
-
- SAFE_RELEASE( pVideoControllers[iController] );
-
- if (bFound)
- {
- break;
- }
- }
- }
- }
-
- if( pClassName )
- SysFreeString( pClassName );
- SAFE_RELEASE( pEnumVideoControllers );
- }
-
- if( pNamespace )
- SysFreeString( pNamespace );
- SAFE_RELEASE( pIWbemServices );
- }
-
- SAFE_RELEASE( pIWbemLocator );
-
- CoUninitialize();
-
- if( bGotMemory )
- return S_OK;
- else
- return E_FAIL;
-}
-
-//static
-S32 LLDXHardware::getMBVideoMemoryViaWMI()
-{
- DWORD vram = 0;
- if (SUCCEEDED(GetVideoMemoryViaWMI(NULL, &vram)))
- {
- return vram / (1024 * 1024);;
- }
- return 0;
-}
-
-//Getting the version of graphics controller driver via WMI
-std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor)
-{
- std::string mDriverVersion;
- HRESULT hres;
- CoInitializeEx(0, COINIT_APARTMENTTHREADED);
- IWbemLocator *pLoc = NULL;
-
- hres = CoCreateInstance(
- CLSID_WbemLocator,
- 0,
- CLSCTX_INPROC_SERVER,
- IID_IWbemLocator, (LPVOID *)&pLoc);
-
- if (FAILED(hres))
- {
- LL_DEBUGS("AppInit") << "Failed to initialize COM library. Error code = 0x" << hres << LL_ENDL;
- return std::string(); // Program has failed.
- }
-
- IWbemServices *pSvc = NULL;
-
- // Connect to the root\cimv2 namespace with
- // the current user and obtain pointer pSvc
- // to make IWbemServices calls.
- hres = pLoc->ConnectServer(
- _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
- NULL, // User name. NULL = current user
- NULL, // User password. NULL = current
- 0, // Locale. NULL indicates current
- NULL, // Security flags.
- 0, // Authority (e.g. Kerberos)
- 0, // Context object
- &pSvc // pointer to IWbemServices proxy
- );
-
- if (FAILED(hres))
- {
- LL_WARNS("AppInit") << "Could not connect. Error code = 0x" << hres << LL_ENDL;
- pLoc->Release();
- CoUninitialize();
- return std::string(); // Program has failed.
- }
-
- LL_DEBUGS("AppInit") << "Connected to ROOT\\CIMV2 WMI namespace" << LL_ENDL;
-
- // Set security levels on the proxy -------------------------
- hres = CoSetProxyBlanket(
- pSvc, // Indicates the proxy to set
- RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
- RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
- NULL, // Server principal name
- RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
- RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
- NULL, // client identity
- EOAC_NONE // proxy capabilities
- );
-
- if (FAILED(hres))
- {
- LL_WARNS("AppInit") << "Could not set proxy blanket. Error code = 0x" << hres << LL_ENDL;
- pSvc->Release();
- pLoc->Release();
- CoUninitialize();
- return std::string(); // Program has failed.
- }
- IEnumWbemClassObject* pEnumerator = NULL;
-
- // Get the data from the query
- ULONG uReturn = 0;
- hres = pSvc->ExecQuery(
- bstr_t("WQL"),
- bstr_t("SELECT * FROM Win32_VideoController"), //Consider using Availability to filter out disabled controllers
- WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
- NULL,
- &pEnumerator);
-
- if (FAILED(hres))
- {
- LL_WARNS("AppInit") << "Query for operating system name failed." << " Error code = 0x" << hres << LL_ENDL;
- pSvc->Release();
- pLoc->Release();
- CoUninitialize();
- return std::string(); // Program has failed.
- }
-
- while (pEnumerator)
- {
- IWbemClassObject *pclsObj = NULL;
- HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
- &pclsObj, &uReturn);
-
- if (0 == uReturn)
- {
- break; // If quantity less then 1.
- }
-
- if (vendor != GPU_ANY)
- {
- VARIANT vtCaptionProp;
- // Might be preferable to check "AdapterCompatibility" here instead of caption.
- hr = pclsObj->Get(L"Caption", 0, &vtCaptionProp, 0, 0);
-
- if (FAILED(hr))
- {
- LL_WARNS("AppInit") << "Query for Caption property failed." << " Error code = 0x" << hr << LL_ENDL;
- pSvc->Release();
- pLoc->Release();
- CoUninitialize();
- return std::string(); // Program has failed.
- }
-
- // use characters in the returned driver version
- BSTR caption(vtCaptionProp.bstrVal);
-
- //convert BSTR to std::string
- std::wstring ws(caption, SysStringLen(caption));
- std::string caption_str(ws.begin(), ws.end());
- LLStringUtil::toLower(caption_str);
-
- bool found = false;
- switch (vendor)
- {
- case GPU_INTEL:
- found = caption_str.find("intel") != std::string::npos;
- break;
- case GPU_NVIDIA:
- found = caption_str.find("nvidia") != std::string::npos;
- break;
- case GPU_AMD:
- found = caption_str.find("amd") != std::string::npos
- || caption_str.find("ati ") != std::string::npos
- || caption_str.find("radeon") != std::string::npos;
- break;
- default:
- break;
- }
-
- if (found)
- {
- VariantClear(&vtCaptionProp);
- }
- else
- {
- VariantClear(&vtCaptionProp);
- pclsObj->Release();
- continue;
- }
- }
-
- VARIANT vtVersionProp;
-
- // Get the value of the DriverVersion property
- hr = pclsObj->Get(L"DriverVersion", 0, &vtVersionProp, 0, 0);
-
- if (FAILED(hr))
- {
- LL_WARNS("AppInit") << "Query for DriverVersion property failed." << " Error code = 0x" << hr << LL_ENDL;
- pSvc->Release();
- pLoc->Release();
- CoUninitialize();
- return std::string(); // Program has failed.
- }
-
- // use characters in the returned driver version
- BSTR driverVersion(vtVersionProp.bstrVal);
-
- //convert BSTR to std::string
- std::wstring ws(driverVersion, SysStringLen(driverVersion));
- std::string str(ws.begin(), ws.end());
- LL_INFOS("AppInit") << " DriverVersion : " << str << LL_ENDL;
-
- if (mDriverVersion.empty())
- {
- mDriverVersion = str;
- }
- else if (mDriverVersion != str)
- {
- if (vendor == GPU_ANY)
- {
- // Expected from systems with gpus from different vendors
- LL_INFOS("DriverVersion") << "Multiple video drivers detected. Version of second driver: " << str << LL_ENDL;
- }
- else
- {
- // Not Expected!
- LL_WARNS("DriverVersion") << "Multiple video drivers detected from same vendor. Version of second driver : " << str << LL_ENDL;
- }
- }
-
- VariantClear(&vtVersionProp);
- pclsObj->Release();
- }
-
- // Cleanup
- // ========
- if (pSvc)
- {
- pSvc->Release();
- }
- if (pLoc)
- {
- pLoc->Release();
- }
- if (pEnumerator)
- {
- pEnumerator->Release();
- }
-
- // supposed to always call CoUninitialize even if init returned false
- CoUninitialize();
-
- return mDriverVersion;
-}
-
-void get_wstring(IDxDiagContainer* containerp, WCHAR* wszPropName, WCHAR* wszPropValue, int outputSize)
-{
- HRESULT hr;
- VARIANT var;
-
- 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 ); /* Flawfinder: ignore */
- break;
- case VT_I4:
- swprintf( wszPropValue, L"%d", var.lVal ); /* Flawfinder: ignore */
- break;
- case VT_BOOL:
- wcscpy( wszPropValue, (var.boolVal) ? L"true" : L"false" ); /* Flawfinder: ignore */
- break;
- case VT_BSTR:
- wcsncpy( wszPropValue, var.bstrVal, outputSize-1 ); /* Flawfinder: ignore */
- wszPropValue[outputSize-1] = 0;
- break;
- }
- }
- // Clear the variant (this is needed to free BSTR memory)
- VariantClear( &var );
-}
-
-std::string get_string(IDxDiagContainer *containerp, WCHAR *wszPropName)
-{
- WCHAR wszPropValue[256];
- get_wstring(containerp, wszPropName, wszPropValue, 256);
-
- return utf16str_to_utf8str(wszPropValue);
-}
-
-
-LLVersion::LLVersion()
-{
- mValid = false;
- S32 i;
- for (i = 0; i < 4; i++)
- {
- mFields[i] = 0;
- }
-}
-
-bool LLVersion::set(const std::string &version_string)
-{
- S32 i;
- for (i = 0; i < 4; i++)
- {
- mFields[i] = 0;
- }
- // Split the version string.
- std::string str(version_string);
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep(".", "", boost::keep_empty_tokens);
- tokenizer tokens(str, sep);
-
- tokenizer::iterator iter = tokens.begin();
- S32 count = 0;
- for (;(iter != tokens.end()) && (count < 4);++iter)
- {
- mFields[count] = atoi(iter->c_str());
- count++;
- }
- if (count < 4)
- {
- //LL_WARNS() << "Potentially bogus version string!" << version_string << LL_ENDL;
- 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];
- }
-}
-
-std::string 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");
- }
- LL_INFOS() << mFilepath << LL_ENDL;
- LL_INFOS() << mName << LL_ENDL;
- LL_INFOS() << mVersionString << LL_ENDL;
- LL_INFOS() << mDateString << LL_ENDL;
-
- return "";
-}
-
-LLDXDevice::~LLDXDevice()
-{
- for_each(mDriverFiles.begin(), mDriverFiles.end(), DeletePairedPointer());
- mDriverFiles.clear();
-}
-
-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");
- }
- LL_INFOS() << LL_ENDL;
- LL_INFOS() << "DeviceName:" << mName << LL_ENDL;
- LL_INFOS() << "PCIString:" << mPCIString << LL_ENDL;
- LL_INFOS() << "Drivers" << LL_ENDL;
- LL_INFOS() << "-------" << LL_ENDL;
- 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());
- // mDevices.clear();
-}
-
-/*
-std::string LLDXHardware::dumpDevices()
-{
- if (gWriteDebug)
- {
- gWriteDebug("\n");
- gWriteDebug("StartAllDevices\n");
- }
- for (device_map_t::iterator iter = mDevices.begin(),
- end = mDevices.end();
- iter != end; iter++)
- {
- LLDXDevice *devicep = iter->second;
- devicep->dump();
- }
- if (gWriteDebug)
- {
- gWriteDebug("EndAllDevices\n\n");
- }
- return "";
-}
-
-LLDXDevice *LLDXHardware::findDevice(const std::string &vendor, const std::string &devices)
-{
- // Iterate through different devices tokenized in devices string
- std::string str(devices);
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
- tokenizer tokens(str, sep);
-
- tokenizer::iterator iter = tokens.begin();
- for (;iter != tokens.end();++iter)
- {
- std::string dev_str = *iter;
- for (device_map_t::iterator iter = mDevices.begin(),
- end = mDevices.end();
- iter != end; iter++)
- {
- LLDXDevice *devicep = iter->second;
- if ((devicep->mVendorID == vendor)
- && (devicep->mDeviceID == dev_str))
- {
- return devicep;
- }
- }
- }
-
- return NULL;
-}
-*/
-
-bool LLDXHardware::getInfo(bool vram_only)
-{
- LLTimer hw_timer;
- bool ok = false;
- HRESULT hr;
-
- // CLSID_DxDiagProvider does not work with Multithreaded?
- CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
-
- 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;
- DWORD dw_device_count;
-
- mVRAM = 0;
-
- // CoCreate a IDxDiagProvider*
- LL_DEBUGS("AppInit") << "CoCreateInstance IID_IDxDiagProvider" << LL_ENDL;
- hr = CoCreateInstance(CLSID_DxDiagProvider,
- NULL,
- CLSCTX_INPROC_SERVER,
- IID_IDxDiagProvider,
- (LPVOID*) &dx_diag_providerp);
-
- if (FAILED(hr))
- {
- LL_WARNS("AppInit") << "No DXDiag provider found! DirectX 9 not installed!" << LL_ENDL;
- 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;
-
- LL_DEBUGS("AppInit") << "dx_diag_providerp->Initialize" << LL_ENDL;
- hr = dx_diag_providerp->Initialize(&dx_diag_init_params);
- if(FAILED(hr))
- {
- goto LCleanup;
- }
-
- LL_DEBUGS("AppInit") << "dx_diag_providerp->GetRootContainer" << LL_ENDL;
- hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp );
- if(FAILED(hr) || !dx_diag_rootp)
- {
- goto LCleanup;
- }
-
- HRESULT hr;
-
- // Get display driver information
- LL_DEBUGS("AppInit") << "dx_diag_rootp->GetChildContainer" << LL_ENDL;
- hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp);
- if(FAILED(hr) || !devices_containerp)
- {
- // do not release 'dirty' devices_containerp at this stage, only dx_diag_rootp
- devices_containerp = NULL;
- goto LCleanup;
- }
-
- // make sure there is something inside
- hr = devices_containerp->GetNumberOfChildContainers(&dw_device_count);
- if (FAILED(hr) || dw_device_count == 0)
- {
- goto LCleanup;
- }
-
- // Get device 0
- // By default 0 device is the primary one, howhever in case of various hybrid graphics
- // like itegrated AMD and PCI AMD GPUs system might switch.
- LL_DEBUGS("AppInit") << "devices_containerp->GetChildContainer" << LL_ENDL;
- hr = devices_containerp->GetChildContainer(L"0", &device_containerp);
- if(FAILED(hr) || !device_containerp)
- {
- goto LCleanup;
- }
-
- DWORD vram = 0;
-
- WCHAR deviceID[512];
-
- get_wstring(device_containerp, L"szDeviceID", deviceID, 512);
- // Example: searches id like 1F06 in pnp string (aka VEN_10DE&DEV_1F06)
- // doesn't seem to work on some systems since format is unrecognizable
- // but in such case keyDeviceID works
- if (SUCCEEDED(GetVideoMemoryViaWMI(deviceID, &vram)))
- {
- mVRAM = vram/(1024*1024);
- }
- else
- {
- get_wstring(device_containerp, L"szKeyDeviceID", deviceID, 512);
- LL_WARNS() << "szDeviceID" << deviceID << LL_ENDL;
- // '+9' to avoid ENUM\\PCI\\ prefix
- // Returns string like Enum\\PCI\\VEN_10DE&DEV_1F06&SUBSYS...
- // and since GetVideoMemoryViaWMI searches by PNPDeviceID it is sufficient
- if (SUCCEEDED(GetVideoMemoryViaWMI(deviceID + 9, &vram)))
- {
- mVRAM = vram / (1024 * 1024);
- }
- }
-
- if (mVRAM == 0)
- { // 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);
- LL_INFOS("AppInit") << "VRAM Detected: " << mVRAM << " DX9 string: " << ram_str << LL_ENDL;
- }
-
- if (vram_only)
- {
- ok = true;
- goto LCleanup;
- }
-
-
- /* for now, we ONLY do vram_only the rest of this
- is commented out, to ensure no-one is tempted
- to use it
-
- // 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];
- LL_DEBUGS("AppInit") << "dx_diag_rootp->GetChildContainer DxDiag_SystemDevices" << LL_ENDL;
- 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;
- }
-
- LL_DEBUGS("AppInit") << "DX9 iterating over devices" << LL_ENDL;
- S32 device_num = 0;
- for (device_num = 0; device_num < (S32)num_devices; device_num++)
- {
- hr = system_device_containerp->EnumChildContainerNames(device_num, wszContainer, 256);
- if (FAILED(hr))
- {
- goto LCleanup;
- }
-
- hr = system_device_containerp->GetChildContainer(wszContainer, &device_containerp);
- if (FAILED(hr) || device_containerp == NULL)
- {
- goto LCleanup;
- }
-
- std::string device_name = get_string(device_containerp, L"szDescription");
-
- std::string device_id = get_string(device_containerp, L"szDeviceID");
-
- LLDXDevice *dxdevicep = new LLDXDevice;
- dxdevicep->mName = device_name;
- dxdevicep->mPCIString = device_id;
- mDevices[dxdevicep->mPCIString] = dxdevicep;
-
- // Split the PCI string based on vendor, device, subsys, rev.
- std::string str(device_id);
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep("&\\", "", boost::keep_empty_tokens);
- tokenizer tokens(str, sep);
-
- tokenizer::iterator iter = tokens.begin();
- S32 count = 0;
- bool valid = true;
- for (;(iter != tokens.end()) && (count < 3);++iter)
- {
- switch (count)
- {
- case 0:
- if (strcmp(iter->c_str(), "PCI"))
- {
- valid = false;
- }
- break;
- case 1:
- dxdevicep->mVendorID = iter->c_str();
- break;
- case 2:
- dxdevicep->mDeviceID = iter->c_str();
- break;
- default:
- // Ignore it
- break;
- }
- count++;
- }
-
-
-
-
- // Now, iterate through the related drivers
- hr = device_containerp->GetChildContainer(L"Drivers", &driver_containerp);
- if (FAILED(hr) || !driver_containerp)
- {
- goto LCleanup;
- }
-
- DWORD num_files = 0;
- hr = driver_containerp->GetNumberOfChildContainers(&num_files);
- if (FAILED(hr))
- {
- goto LCleanup;
- }
-
- S32 file_num = 0;
- for (file_num = 0; file_num < (S32)num_files; file_num++ )
- {
-
- hr = driver_containerp->EnumChildContainerNames(file_num, wszContainer, 256);
- if (FAILED(hr))
- {
- goto LCleanup;
- }
-
- hr = driver_containerp->GetChildContainer(wszContainer, &file_containerp);
- if (FAILED(hr) || file_containerp == NULL)
- {
- goto LCleanup;
- }
-
- std::string driver_path = get_string(file_containerp, L"szPath");
- std::string driver_name = get_string(file_containerp, L"szName");
- std::string driver_version = get_string(file_containerp, L"szVersion");
- std::string driver_date = get_string(file_containerp, L"szDatestampEnglish");
-
- LLDXDriverFile *dxdriverfilep = new LLDXDriverFile;
- dxdriverfilep->mName = driver_name;
- dxdriverfilep->mFilepath= driver_path;
- dxdriverfilep->mVersionString = driver_version;
- dxdriverfilep->mVersion.set(driver_version);
- dxdriverfilep->mDateString = driver_date;
-
- dxdevicep->mDriverFiles[driver_name] = dxdriverfilep;
-
- SAFE_RELEASE(file_containerp);
- }
- SAFE_RELEASE(device_containerp);
- }
- */
- }
-
- // dumpDevices();
- ok = true;
-
-LCleanup:
- if (!ok)
- {
- LL_WARNS("AppInit") << "DX9 probe failed" << LL_ENDL;
- 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;
- }
-
-LLSD LLDXHardware::getDisplayInfo()
-{
- LLTimer hw_timer;
- HRESULT hr;
- LLSD ret;
- CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
-
- IDxDiagProvider *dx_diag_providerp = NULL;
- IDxDiagContainer *dx_diag_rootp = NULL;
- IDxDiagContainer *devices_containerp = NULL;
- IDxDiagContainer *device_containerp = NULL;
- IDxDiagContainer *file_containerp = NULL;
- IDxDiagContainer *driver_containerp = NULL;
- DWORD dw_device_count;
-
- // CoCreate a IDxDiagProvider*
- LL_INFOS() << "CoCreateInstance IID_IDxDiagProvider" << LL_ENDL;
- hr = CoCreateInstance(CLSID_DxDiagProvider,
- NULL,
- CLSCTX_INPROC_SERVER,
- IID_IDxDiagProvider,
- (LPVOID*) &dx_diag_providerp);
-
- if (FAILED(hr))
- {
- LL_WARNS() << "No DXDiag provider found! DirectX 9 not installed!" << LL_ENDL;
- 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;
-
- LL_INFOS() << "dx_diag_providerp->Initialize" << LL_ENDL;
- hr = dx_diag_providerp->Initialize(&dx_diag_init_params);
- if(FAILED(hr))
- {
- goto LCleanup;
- }
-
- LL_INFOS() << "dx_diag_providerp->GetRootContainer" << LL_ENDL;
- hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp );
- if(FAILED(hr) || !dx_diag_rootp)
- {
- goto LCleanup;
- }
-
- HRESULT hr;
-
- // Get display driver information
- LL_INFOS() << "dx_diag_rootp->GetChildContainer" << LL_ENDL;
- hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp);
- if(FAILED(hr) || !devices_containerp)
- {
- // do not release 'dirty' devices_containerp at this stage, only dx_diag_rootp
- devices_containerp = NULL;
- goto LCleanup;
- }
-
- // make sure there is something inside
- hr = devices_containerp->GetNumberOfChildContainers(&dw_device_count);
- if (FAILED(hr) || dw_device_count == 0)
- {
- goto LCleanup;
- }
-
- // Get device 0
- LL_INFOS() << "devices_containerp->GetChildContainer" << LL_ENDL;
- 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");
-
-
- // Dump the string as an int into the structure
- char *stopstring;
- ret["VRAM"] = strtol(ram_str.c_str(), &stopstring, 10);
- std::string device_name = get_string(device_containerp, L"szDescription");
- ret["DeviceName"] = device_name;
- std::string device_driver= get_string(device_containerp, L"szDriverVersion");
- ret["DriverVersion"] = device_driver;
-
- // ATI has a slightly different version string
- if(device_name.length() >= 4 && device_name.substr(0,4) == "ATI ")
- {
- // get the key
- HKEY hKey;
- const DWORD RV_SIZE = 100;
- WCHAR release_version[RV_SIZE];
-
- // Hard coded registry entry. Using this since it's simpler for now.
- // And using EnumDisplayDevices to get a registry key also requires
- // a hard coded Query value.
- if(ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\ATI Technologies\\CBT"), &hKey))
- {
- // get the value
- DWORD dwType = REG_SZ;
- DWORD dwSize = sizeof(WCHAR) * RV_SIZE;
- if(ERROR_SUCCESS == RegQueryValueEx(hKey, TEXT("ReleaseVersion"),
- NULL, &dwType, (LPBYTE)release_version, &dwSize))
- {
- // print the value
- // windows doesn't guarantee to be null terminated
- release_version[RV_SIZE - 1] = NULL;
- ret["DriverVersion"] = utf16str_to_utf8str(release_version);
-
- }
- RegCloseKey(hKey);
- }
- }
- }
-
-LCleanup:
- if (!ret.isMap() || (ret.size() == 0))
- {
- LL_INFOS() << "Failed to get data, cleaning up" << LL_ENDL;
- }
- 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 ret;
-}
-
-void LLDXHardware::setWriteDebugFunc(void (*func)(const char*))
-{
- gWriteDebug = func;
-}
-
-#endif
+/** + * @file lldxhardware.cpp + * @brief LLDXHardware implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifdef LL_WINDOWS + +// Culled from some Microsoft sample code + +#include "linden_common.h" + +#define INITGUID +#include <dxdiag.h> +#undef INITGUID + +#include <wbemidl.h> +#include <comdef.h> + +#include <boost/tokenizer.hpp> + +#include "lldxhardware.h" + +#include "llerror.h" + +#include "llstring.h" +#include "llstl.h" +#include "lltimer.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; } } + +typedef BOOL ( WINAPI* PfnCoSetProxyBlanket )( IUnknown* pProxy, DWORD dwAuthnSvc, DWORD dwAuthzSvc, + OLECHAR* pServerPrincName, DWORD dwAuthnLevel, DWORD dwImpLevel, + RPC_AUTH_IDENTITY_HANDLE pAuthInfo, DWORD dwCapabilities ); + +HRESULT GetVideoMemoryViaWMI(WCHAR* strInputDeviceID, DWORD* pdwAdapterRam) +{ + HRESULT hr; + bool bGotMemory = false; + IWbemLocator* pIWbemLocator = nullptr; + IWbemServices* pIWbemServices = nullptr; + BSTR pNamespace = nullptr; + + *pdwAdapterRam = 0; + CoInitializeEx(0, COINIT_APARTMENTTHREADED); + + hr = CoCreateInstance( CLSID_WbemLocator, + nullptr, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + ( LPVOID* )&pIWbemLocator ); +#ifdef PRINTF_DEBUGGING + if( FAILED( hr ) ) wprintf( L"WMI: CoCreateInstance failed: 0x%0.8x\n", hr ); +#endif + + if( SUCCEEDED( hr ) && pIWbemLocator ) + { + // Using the locator, connect to WMI in the given namespace. + pNamespace = SysAllocString( L"\\\\.\\root\\cimv2" ); + + hr = pIWbemLocator->ConnectServer( pNamespace, nullptr, nullptr, 0L, + 0L, nullptr, nullptr, &pIWbemServices ); +#ifdef PRINTF_DEBUGGING + if( FAILED( hr ) ) wprintf( L"WMI: pIWbemLocator->ConnectServer failed: 0x%0.8x\n", hr ); +#endif + if( SUCCEEDED( hr ) && pIWbemServices != 0 ) + { + HINSTANCE hinstOle32 = nullptr; + + hinstOle32 = LoadLibraryW( L"ole32.dll" ); + if( hinstOle32 ) + { + PfnCoSetProxyBlanket pfnCoSetProxyBlanket = nullptr; + + pfnCoSetProxyBlanket = ( PfnCoSetProxyBlanket )GetProcAddress( hinstOle32, "CoSetProxyBlanket" ); + if( pfnCoSetProxyBlanket != 0 ) + { + // Switch security level to IMPERSONATE. + pfnCoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, 0 ); + } + + FreeLibrary( hinstOle32 ); + } + + IEnumWbemClassObject* pEnumVideoControllers = nullptr; + BSTR pClassName = nullptr; + + pClassName = SysAllocString( L"Win32_VideoController" ); + + hr = pIWbemServices->CreateInstanceEnum( pClassName, 0, + nullptr, &pEnumVideoControllers ); +#ifdef PRINTF_DEBUGGING + if( FAILED( hr ) ) wprintf( L"WMI: pIWbemServices->CreateInstanceEnum failed: 0x%0.8x\n", hr ); +#endif + + if( SUCCEEDED( hr ) && pEnumVideoControllers ) + { + IWbemClassObject* pVideoControllers[10] = {0}; + DWORD uReturned = 0; + BSTR pPropName = nullptr; + + // Get the first one in the list + pEnumVideoControllers->Reset(); + hr = pEnumVideoControllers->Next( 5000, // timeout in 5 seconds + 10, // return the first 10 + pVideoControllers, + &uReturned ); +#ifdef PRINTF_DEBUGGING + if( FAILED( hr ) ) wprintf( L"WMI: pEnumVideoControllers->Next failed: 0x%0.8x\n", hr ); + if( uReturned == 0 ) wprintf( L"WMI: pEnumVideoControllers uReturned == 0\n" ); +#endif + + VARIANT var; + if( SUCCEEDED( hr ) ) + { + bool bFound = false; + for( UINT iController = 0; iController < uReturned; iController++ ) + { + if ( !pVideoControllers[iController] ) + continue; + + // if strInputDeviceID is set find this specific device and return memory or specific device + // if strInputDeviceID is not set return the best device + if (strInputDeviceID) + { + pPropName = SysAllocString( L"PNPDeviceID" ); + hr = pVideoControllers[iController]->Get( pPropName, 0L, &var, nullptr, nullptr ); +#ifdef PRINTF_DEBUGGING + if( FAILED( hr ) ) + wprintf( L"WMI: pVideoControllers[iController]->Get PNPDeviceID failed: 0x%0.8x\n", hr ); +#endif + if( SUCCEEDED( hr ) && strInputDeviceID) + { + if( wcsstr( var.bstrVal, strInputDeviceID ) != 0 ) + bFound = true; + } + VariantClear( &var ); + if( pPropName ) SysFreeString( pPropName ); + } + + if( bFound || !strInputDeviceID ) + { + pPropName = SysAllocString( L"AdapterRAM" ); + hr = pVideoControllers[iController]->Get( pPropName, 0L, &var, nullptr, nullptr ); +#ifdef PRINTF_DEBUGGING + if( FAILED( hr ) ) + wprintf( L"WMI: pVideoControllers[iController]->Get AdapterRAM failed: 0x%0.8x\n", + hr ); +#endif + if( SUCCEEDED( hr ) ) + { + bGotMemory = true; + *pdwAdapterRam = llmax(var.ulVal, *pdwAdapterRam); + } + VariantClear( &var ); + if( pPropName ) SysFreeString( pPropName ); + } + + SAFE_RELEASE( pVideoControllers[iController] ); + + if (bFound) + { + break; + } + } + } + } + + if( pClassName ) + SysFreeString( pClassName ); + SAFE_RELEASE( pEnumVideoControllers ); + } + + if( pNamespace ) + SysFreeString( pNamespace ); + SAFE_RELEASE( pIWbemServices ); + } + + SAFE_RELEASE( pIWbemLocator ); + + CoUninitialize(); + + if( bGotMemory ) + return S_OK; + else + return E_FAIL; +} + +//static +S32 LLDXHardware::getMBVideoMemoryViaWMI() +{ + DWORD vram = 0; + if (SUCCEEDED(GetVideoMemoryViaWMI(NULL, &vram))) + { + return vram / (1024 * 1024);; + } + return 0; +} + +//Getting the version of graphics controller driver via WMI +std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor) +{ + std::string mDriverVersion; + HRESULT hres; + CoInitializeEx(0, COINIT_APARTMENTTHREADED); + IWbemLocator *pLoc = NULL; + + hres = CoCreateInstance( + CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID *)&pLoc); + + if (FAILED(hres)) + { + LL_DEBUGS("AppInit") << "Failed to initialize COM library. Error code = 0x" << hres << LL_ENDL; + return std::string(); // Program has failed. + } + + IWbemServices *pSvc = NULL; + + // Connect to the root\cimv2 namespace with + // the current user and obtain pointer pSvc + // to make IWbemServices calls. + hres = pLoc->ConnectServer( + _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace + NULL, // User name. NULL = current user + NULL, // User password. NULL = current + 0, // Locale. NULL indicates current + NULL, // Security flags. + 0, // Authority (e.g. Kerberos) + 0, // Context object + &pSvc // pointer to IWbemServices proxy + ); + + if (FAILED(hres)) + { + LL_WARNS("AppInit") << "Could not connect. Error code = 0x" << hres << LL_ENDL; + pLoc->Release(); + CoUninitialize(); + return std::string(); // Program has failed. + } + + LL_DEBUGS("AppInit") << "Connected to ROOT\\CIMV2 WMI namespace" << LL_ENDL; + + // Set security levels on the proxy ------------------------- + hres = CoSetProxyBlanket( + pSvc, // Indicates the proxy to set + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + NULL, // Server principal name + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + NULL, // client identity + EOAC_NONE // proxy capabilities + ); + + if (FAILED(hres)) + { + LL_WARNS("AppInit") << "Could not set proxy blanket. Error code = 0x" << hres << LL_ENDL; + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return std::string(); // Program has failed. + } + IEnumWbemClassObject* pEnumerator = NULL; + + // Get the data from the query + ULONG uReturn = 0; + hres = pSvc->ExecQuery( + bstr_t("WQL"), + bstr_t("SELECT * FROM Win32_VideoController"), //Consider using Availability to filter out disabled controllers + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &pEnumerator); + + if (FAILED(hres)) + { + LL_WARNS("AppInit") << "Query for operating system name failed." << " Error code = 0x" << hres << LL_ENDL; + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return std::string(); // Program has failed. + } + + while (pEnumerator) + { + IWbemClassObject *pclsObj = NULL; + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, + &pclsObj, &uReturn); + + if (0 == uReturn) + { + break; // If quantity less then 1. + } + + if (vendor != GPU_ANY) + { + VARIANT vtCaptionProp; + // Might be preferable to check "AdapterCompatibility" here instead of caption. + hr = pclsObj->Get(L"Caption", 0, &vtCaptionProp, 0, 0); + + if (FAILED(hr)) + { + LL_WARNS("AppInit") << "Query for Caption property failed." << " Error code = 0x" << hr << LL_ENDL; + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return std::string(); // Program has failed. + } + + // use characters in the returned driver version + BSTR caption(vtCaptionProp.bstrVal); + + //convert BSTR to std::string + std::wstring ws(caption, SysStringLen(caption)); + std::string caption_str(ws.begin(), ws.end()); + LLStringUtil::toLower(caption_str); + + bool found = false; + switch (vendor) + { + case GPU_INTEL: + found = caption_str.find("intel") != std::string::npos; + break; + case GPU_NVIDIA: + found = caption_str.find("nvidia") != std::string::npos; + break; + case GPU_AMD: + found = caption_str.find("amd") != std::string::npos + || caption_str.find("ati ") != std::string::npos + || caption_str.find("radeon") != std::string::npos; + break; + default: + break; + } + + if (found) + { + VariantClear(&vtCaptionProp); + } + else + { + VariantClear(&vtCaptionProp); + pclsObj->Release(); + continue; + } + } + + VARIANT vtVersionProp; + + // Get the value of the DriverVersion property + hr = pclsObj->Get(L"DriverVersion", 0, &vtVersionProp, 0, 0); + + if (FAILED(hr)) + { + LL_WARNS("AppInit") << "Query for DriverVersion property failed." << " Error code = 0x" << hr << LL_ENDL; + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return std::string(); // Program has failed. + } + + // use characters in the returned driver version + BSTR driverVersion(vtVersionProp.bstrVal); + + //convert BSTR to std::string + std::wstring ws(driverVersion, SysStringLen(driverVersion)); + std::string str(ws.begin(), ws.end()); + LL_INFOS("AppInit") << " DriverVersion : " << str << LL_ENDL; + + if (mDriverVersion.empty()) + { + mDriverVersion = str; + } + else if (mDriverVersion != str) + { + if (vendor == GPU_ANY) + { + // Expected from systems with gpus from different vendors + LL_INFOS("DriverVersion") << "Multiple video drivers detected. Version of second driver: " << str << LL_ENDL; + } + else + { + // Not Expected! + LL_WARNS("DriverVersion") << "Multiple video drivers detected from same vendor. Version of second driver : " << str << LL_ENDL; + } + } + + VariantClear(&vtVersionProp); + pclsObj->Release(); + } + + // Cleanup + // ======== + if (pSvc) + { + pSvc->Release(); + } + if (pLoc) + { + pLoc->Release(); + } + if (pEnumerator) + { + pEnumerator->Release(); + } + + // supposed to always call CoUninitialize even if init returned false + CoUninitialize(); + + return mDriverVersion; +} + +void get_wstring(IDxDiagContainer* containerp, WCHAR* wszPropName, WCHAR* wszPropValue, int outputSize) +{ + HRESULT hr; + VARIANT var; + + 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 ); /* Flawfinder: ignore */ + break; + case VT_I4: + swprintf( wszPropValue, L"%d", var.lVal ); /* Flawfinder: ignore */ + break; + case VT_BOOL: + wcscpy( wszPropValue, (var.boolVal) ? L"true" : L"false" ); /* Flawfinder: ignore */ + break; + case VT_BSTR: + wcsncpy( wszPropValue, var.bstrVal, outputSize-1 ); /* Flawfinder: ignore */ + wszPropValue[outputSize-1] = 0; + break; + } + } + // Clear the variant (this is needed to free BSTR memory) + VariantClear( &var ); +} + +std::string get_string(IDxDiagContainer *containerp, WCHAR *wszPropName) +{ + WCHAR wszPropValue[256]; + get_wstring(containerp, wszPropName, wszPropValue, 256); + + return utf16str_to_utf8str(wszPropValue); +} + + +LLVersion::LLVersion() +{ + mValid = false; + S32 i; + for (i = 0; i < 4; i++) + { + mFields[i] = 0; + } +} + +bool LLVersion::set(const std::string &version_string) +{ + S32 i; + for (i = 0; i < 4; i++) + { + mFields[i] = 0; + } + // Split the version string. + std::string str(version_string); + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep(".", "", boost::keep_empty_tokens); + tokenizer tokens(str, sep); + + tokenizer::iterator iter = tokens.begin(); + S32 count = 0; + for (;(iter != tokens.end()) && (count < 4);++iter) + { + mFields[count] = atoi(iter->c_str()); + count++; + } + if (count < 4) + { + //LL_WARNS() << "Potentially bogus version string!" << version_string << LL_ENDL; + 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]; + } +} + +std::string 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"); + } + LL_INFOS() << mFilepath << LL_ENDL; + LL_INFOS() << mName << LL_ENDL; + LL_INFOS() << mVersionString << LL_ENDL; + LL_INFOS() << mDateString << LL_ENDL; + + return ""; +} + +LLDXDevice::~LLDXDevice() +{ + for_each(mDriverFiles.begin(), mDriverFiles.end(), DeletePairedPointer()); + mDriverFiles.clear(); +} + +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"); + } + LL_INFOS() << LL_ENDL; + LL_INFOS() << "DeviceName:" << mName << LL_ENDL; + LL_INFOS() << "PCIString:" << mPCIString << LL_ENDL; + LL_INFOS() << "Drivers" << LL_ENDL; + LL_INFOS() << "-------" << LL_ENDL; + 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()); + // mDevices.clear(); +} + +/* +std::string LLDXHardware::dumpDevices() +{ + if (gWriteDebug) + { + gWriteDebug("\n"); + gWriteDebug("StartAllDevices\n"); + } + for (device_map_t::iterator iter = mDevices.begin(), + end = mDevices.end(); + iter != end; iter++) + { + LLDXDevice *devicep = iter->second; + devicep->dump(); + } + if (gWriteDebug) + { + gWriteDebug("EndAllDevices\n\n"); + } + return ""; +} + +LLDXDevice *LLDXHardware::findDevice(const std::string &vendor, const std::string &devices) +{ + // Iterate through different devices tokenized in devices string + std::string str(devices); + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("|", "", boost::keep_empty_tokens); + tokenizer tokens(str, sep); + + tokenizer::iterator iter = tokens.begin(); + for (;iter != tokens.end();++iter) + { + std::string dev_str = *iter; + for (device_map_t::iterator iter = mDevices.begin(), + end = mDevices.end(); + iter != end; iter++) + { + LLDXDevice *devicep = iter->second; + if ((devicep->mVendorID == vendor) + && (devicep->mDeviceID == dev_str)) + { + return devicep; + } + } + } + + return NULL; +} +*/ + +bool LLDXHardware::getInfo(bool vram_only) +{ + LLTimer hw_timer; + bool ok = false; + HRESULT hr; + + // CLSID_DxDiagProvider does not work with Multithreaded? + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + 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; + DWORD dw_device_count; + + mVRAM = 0; + + // CoCreate a IDxDiagProvider* + LL_DEBUGS("AppInit") << "CoCreateInstance IID_IDxDiagProvider" << LL_ENDL; + hr = CoCreateInstance(CLSID_DxDiagProvider, + NULL, + CLSCTX_INPROC_SERVER, + IID_IDxDiagProvider, + (LPVOID*) &dx_diag_providerp); + + if (FAILED(hr)) + { + LL_WARNS("AppInit") << "No DXDiag provider found! DirectX 9 not installed!" << LL_ENDL; + 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; + + LL_DEBUGS("AppInit") << "dx_diag_providerp->Initialize" << LL_ENDL; + hr = dx_diag_providerp->Initialize(&dx_diag_init_params); + if(FAILED(hr)) + { + goto LCleanup; + } + + LL_DEBUGS("AppInit") << "dx_diag_providerp->GetRootContainer" << LL_ENDL; + hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp ); + if(FAILED(hr) || !dx_diag_rootp) + { + goto LCleanup; + } + + HRESULT hr; + + // Get display driver information + LL_DEBUGS("AppInit") << "dx_diag_rootp->GetChildContainer" << LL_ENDL; + hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp); + if(FAILED(hr) || !devices_containerp) + { + // do not release 'dirty' devices_containerp at this stage, only dx_diag_rootp + devices_containerp = NULL; + goto LCleanup; + } + + // make sure there is something inside + hr = devices_containerp->GetNumberOfChildContainers(&dw_device_count); + if (FAILED(hr) || dw_device_count == 0) + { + goto LCleanup; + } + + // Get device 0 + // By default 0 device is the primary one, howhever in case of various hybrid graphics + // like itegrated AMD and PCI AMD GPUs system might switch. + LL_DEBUGS("AppInit") << "devices_containerp->GetChildContainer" << LL_ENDL; + hr = devices_containerp->GetChildContainer(L"0", &device_containerp); + if(FAILED(hr) || !device_containerp) + { + goto LCleanup; + } + + DWORD vram = 0; + + WCHAR deviceID[512]; + + get_wstring(device_containerp, L"szDeviceID", deviceID, 512); + // Example: searches id like 1F06 in pnp string (aka VEN_10DE&DEV_1F06) + // doesn't seem to work on some systems since format is unrecognizable + // but in such case keyDeviceID works + if (SUCCEEDED(GetVideoMemoryViaWMI(deviceID, &vram))) + { + mVRAM = vram/(1024*1024); + } + else + { + get_wstring(device_containerp, L"szKeyDeviceID", deviceID, 512); + LL_WARNS() << "szDeviceID" << deviceID << LL_ENDL; + // '+9' to avoid ENUM\\PCI\\ prefix + // Returns string like Enum\\PCI\\VEN_10DE&DEV_1F06&SUBSYS... + // and since GetVideoMemoryViaWMI searches by PNPDeviceID it is sufficient + if (SUCCEEDED(GetVideoMemoryViaWMI(deviceID + 9, &vram))) + { + mVRAM = vram / (1024 * 1024); + } + } + + if (mVRAM == 0) + { // 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); + LL_INFOS("AppInit") << "VRAM Detected: " << mVRAM << " DX9 string: " << ram_str << LL_ENDL; + } + + if (vram_only) + { + ok = true; + goto LCleanup; + } + + + /* for now, we ONLY do vram_only the rest of this + is commented out, to ensure no-one is tempted + to use it + + // 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]; + LL_DEBUGS("AppInit") << "dx_diag_rootp->GetChildContainer DxDiag_SystemDevices" << LL_ENDL; + 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; + } + + LL_DEBUGS("AppInit") << "DX9 iterating over devices" << LL_ENDL; + S32 device_num = 0; + for (device_num = 0; device_num < (S32)num_devices; device_num++) + { + hr = system_device_containerp->EnumChildContainerNames(device_num, wszContainer, 256); + if (FAILED(hr)) + { + goto LCleanup; + } + + hr = system_device_containerp->GetChildContainer(wszContainer, &device_containerp); + if (FAILED(hr) || device_containerp == NULL) + { + goto LCleanup; + } + + std::string device_name = get_string(device_containerp, L"szDescription"); + + std::string device_id = get_string(device_containerp, L"szDeviceID"); + + LLDXDevice *dxdevicep = new LLDXDevice; + dxdevicep->mName = device_name; + dxdevicep->mPCIString = device_id; + mDevices[dxdevicep->mPCIString] = dxdevicep; + + // Split the PCI string based on vendor, device, subsys, rev. + std::string str(device_id); + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep("&\\", "", boost::keep_empty_tokens); + tokenizer tokens(str, sep); + + tokenizer::iterator iter = tokens.begin(); + S32 count = 0; + bool valid = true; + for (;(iter != tokens.end()) && (count < 3);++iter) + { + switch (count) + { + case 0: + if (strcmp(iter->c_str(), "PCI")) + { + valid = false; + } + break; + case 1: + dxdevicep->mVendorID = iter->c_str(); + break; + case 2: + dxdevicep->mDeviceID = iter->c_str(); + break; + default: + // Ignore it + break; + } + count++; + } + + + + + // Now, iterate through the related drivers + hr = device_containerp->GetChildContainer(L"Drivers", &driver_containerp); + if (FAILED(hr) || !driver_containerp) + { + goto LCleanup; + } + + DWORD num_files = 0; + hr = driver_containerp->GetNumberOfChildContainers(&num_files); + if (FAILED(hr)) + { + goto LCleanup; + } + + S32 file_num = 0; + for (file_num = 0; file_num < (S32)num_files; file_num++ ) + { + + hr = driver_containerp->EnumChildContainerNames(file_num, wszContainer, 256); + if (FAILED(hr)) + { + goto LCleanup; + } + + hr = driver_containerp->GetChildContainer(wszContainer, &file_containerp); + if (FAILED(hr) || file_containerp == NULL) + { + goto LCleanup; + } + + std::string driver_path = get_string(file_containerp, L"szPath"); + std::string driver_name = get_string(file_containerp, L"szName"); + std::string driver_version = get_string(file_containerp, L"szVersion"); + std::string driver_date = get_string(file_containerp, L"szDatestampEnglish"); + + LLDXDriverFile *dxdriverfilep = new LLDXDriverFile; + dxdriverfilep->mName = driver_name; + dxdriverfilep->mFilepath= driver_path; + dxdriverfilep->mVersionString = driver_version; + dxdriverfilep->mVersion.set(driver_version); + dxdriverfilep->mDateString = driver_date; + + dxdevicep->mDriverFiles[driver_name] = dxdriverfilep; + + SAFE_RELEASE(file_containerp); + } + SAFE_RELEASE(device_containerp); + } + */ + } + + // dumpDevices(); + ok = true; + +LCleanup: + if (!ok) + { + LL_WARNS("AppInit") << "DX9 probe failed" << LL_ENDL; + 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; + } + +LLSD LLDXHardware::getDisplayInfo() +{ + LLTimer hw_timer; + HRESULT hr; + LLSD ret; + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + IDxDiagProvider *dx_diag_providerp = NULL; + IDxDiagContainer *dx_diag_rootp = NULL; + IDxDiagContainer *devices_containerp = NULL; + IDxDiagContainer *device_containerp = NULL; + IDxDiagContainer *file_containerp = NULL; + IDxDiagContainer *driver_containerp = NULL; + DWORD dw_device_count; + + // CoCreate a IDxDiagProvider* + LL_INFOS() << "CoCreateInstance IID_IDxDiagProvider" << LL_ENDL; + hr = CoCreateInstance(CLSID_DxDiagProvider, + NULL, + CLSCTX_INPROC_SERVER, + IID_IDxDiagProvider, + (LPVOID*) &dx_diag_providerp); + + if (FAILED(hr)) + { + LL_WARNS() << "No DXDiag provider found! DirectX 9 not installed!" << LL_ENDL; + 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; + + LL_INFOS() << "dx_diag_providerp->Initialize" << LL_ENDL; + hr = dx_diag_providerp->Initialize(&dx_diag_init_params); + if(FAILED(hr)) + { + goto LCleanup; + } + + LL_INFOS() << "dx_diag_providerp->GetRootContainer" << LL_ENDL; + hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp ); + if(FAILED(hr) || !dx_diag_rootp) + { + goto LCleanup; + } + + HRESULT hr; + + // Get display driver information + LL_INFOS() << "dx_diag_rootp->GetChildContainer" << LL_ENDL; + hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp); + if(FAILED(hr) || !devices_containerp) + { + // do not release 'dirty' devices_containerp at this stage, only dx_diag_rootp + devices_containerp = NULL; + goto LCleanup; + } + + // make sure there is something inside + hr = devices_containerp->GetNumberOfChildContainers(&dw_device_count); + if (FAILED(hr) || dw_device_count == 0) + { + goto LCleanup; + } + + // Get device 0 + LL_INFOS() << "devices_containerp->GetChildContainer" << LL_ENDL; + 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"); + + + // Dump the string as an int into the structure + char *stopstring; + ret["VRAM"] = strtol(ram_str.c_str(), &stopstring, 10); + std::string device_name = get_string(device_containerp, L"szDescription"); + ret["DeviceName"] = device_name; + std::string device_driver= get_string(device_containerp, L"szDriverVersion"); + ret["DriverVersion"] = device_driver; + + // ATI has a slightly different version string + if(device_name.length() >= 4 && device_name.substr(0,4) == "ATI ") + { + // get the key + HKEY hKey; + const DWORD RV_SIZE = 100; + WCHAR release_version[RV_SIZE]; + + // Hard coded registry entry. Using this since it's simpler for now. + // And using EnumDisplayDevices to get a registry key also requires + // a hard coded Query value. + if(ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\ATI Technologies\\CBT"), &hKey)) + { + // get the value + DWORD dwType = REG_SZ; + DWORD dwSize = sizeof(WCHAR) * RV_SIZE; + if(ERROR_SUCCESS == RegQueryValueEx(hKey, TEXT("ReleaseVersion"), + NULL, &dwType, (LPBYTE)release_version, &dwSize)) + { + // print the value + // windows doesn't guarantee to be null terminated + release_version[RV_SIZE - 1] = NULL; + ret["DriverVersion"] = utf16str_to_utf8str(release_version); + + } + RegCloseKey(hKey); + } + } + } + +LCleanup: + if (!ret.isMap() || (ret.size() == 0)) + { + LL_INFOS() << "Failed to get data, cleaning up" << LL_ENDL; + } + 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 ret; +} + +void LLDXHardware::setWriteDebugFunc(void (*func)(const char*)) +{ + gWriteDebug = func; +} + +#endif diff --git a/indra/llwindow/lldxhardware.h b/indra/llwindow/lldxhardware.h index 54447a87b3..aaefc6ae8c 100644 --- a/indra/llwindow/lldxhardware.h +++ b/indra/llwindow/lldxhardware.h @@ -1,126 +1,126 @@ -/**
- * @file lldxhardware.h
- * @brief LLDXHardware definition
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLDXHARDWARE_H
-#define LL_LLDXHARDWARE_H
-
-#include <map>
-
-#include "stdtypes.h"
-#include "llstring.h"
-#include "llsd.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:
- std::string dump();
-
-public:
- std::string mFilepath;
- std::string mName;
- std::string mVersionString;
- LLVersion mVersion;
- std::string mDateString;
-};
-
-class LLDXDevice
-{
-public:
- ~LLDXDevice();
- std::string dump();
-
- LLDXDriverFile *findDriver(const std::string &driver);
-public:
- std::string mName;
- std::string mPCIString;
- std::string mVendorID;
- std::string mDeviceID;
-
- typedef std::map<std::string, LLDXDriverFile *> driver_file_map_t;
- driver_file_map_t mDriverFiles;
-};
-
-
-class LLDXHardware
-{
-public:
- LLDXHardware();
-
- void setWriteDebugFunc(void (*func)(const char*));
- void cleanup();
-
- // Returns true on success.
- // vram_only true does a "light" probe.
- bool getInfo(bool vram_only);
-
- // WMI can return multiple GPU drivers
- // specify which one to output
- typedef enum {
- GPU_INTEL,
- GPU_NVIDIA,
- GPU_AMD,
- GPU_ANY
- } EGPUVendor;
- std::string getDriverVersionWMI(EGPUVendor vendor);
-
- S32 getVRAM() const { return mVRAM; }
-
- LLSD getDisplayInfo();
-
- // Will get memory of best GPU in MB, return memory on sucsess, 0 on failure
- // Note: WMI is not accurate in some cases
- static S32 getMBVideoMemoryViaWMI();
-
- // 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);
-
- // std::string dumpDevices();
-public:
- typedef std::map<std::string, LLDXDevice *> device_map_t;
- // device_map_t mDevices;
-protected:
- S32 mVRAM;
-};
-
-extern void (*gWriteDebug)(const char* msg);
-extern LLDXHardware gDXHardware;
-
-#endif // LL_LLDXHARDWARE_H
+/** + * @file lldxhardware.h + * @brief LLDXHardware definition + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLDXHARDWARE_H +#define LL_LLDXHARDWARE_H + +#include <map> + +#include "stdtypes.h" +#include "llstring.h" +#include "llsd.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: + std::string dump(); + +public: + std::string mFilepath; + std::string mName; + std::string mVersionString; + LLVersion mVersion; + std::string mDateString; +}; + +class LLDXDevice +{ +public: + ~LLDXDevice(); + std::string dump(); + + LLDXDriverFile *findDriver(const std::string &driver); +public: + std::string mName; + std::string mPCIString; + std::string mVendorID; + std::string mDeviceID; + + typedef std::map<std::string, LLDXDriverFile *> driver_file_map_t; + driver_file_map_t mDriverFiles; +}; + + +class LLDXHardware +{ +public: + LLDXHardware(); + + void setWriteDebugFunc(void (*func)(const char*)); + void cleanup(); + + // Returns true on success. + // vram_only true does a "light" probe. + bool getInfo(bool vram_only); + + // WMI can return multiple GPU drivers + // specify which one to output + typedef enum { + GPU_INTEL, + GPU_NVIDIA, + GPU_AMD, + GPU_ANY + } EGPUVendor; + std::string getDriverVersionWMI(EGPUVendor vendor); + + S32 getVRAM() const { return mVRAM; } + + LLSD getDisplayInfo(); + + // Will get memory of best GPU in MB, return memory on sucsess, 0 on failure + // Note: WMI is not accurate in some cases + static S32 getMBVideoMemoryViaWMI(); + + // 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); + + // std::string dumpDevices(); +public: + typedef std::map<std::string, LLDXDevice *> device_map_t; + // device_map_t mDevices; +protected: + S32 mVRAM; +}; + +extern void (*gWriteDebug)(const char* msg); +extern LLDXHardware gDXHardware; + +#endif // LL_LLDXHARDWARE_H diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp index 361666d1c5..4f29fb0b0a 100644 --- a/indra/llwindow/llkeyboard.cpp +++ b/indra/llwindow/llkeyboard.cpp @@ -1,543 +1,543 @@ -/**
- * @file llkeyboard.cpp
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "indra_constants.h"
-#include "llkeyboard.h"
-
-#include "llwindowcallbacks.h"
-
-//
-// Globals
-//
-
-LLKeyboard *gKeyboard = NULL;
-
-//static
-std::map<KEY,std::string> LLKeyboard::sKeysToNames;
-std::map<std::string,KEY> LLKeyboard::sNamesToKeys;
-LLKeyStringTranslatorFunc* LLKeyboard::mStringTranslator = NULL; // Used for l10n + PC/Mac/Linux accelerator labeling
-
-
-//
-// Class Implementation
-//
-
-LLKeyboard::LLKeyboard() : mCallbacks(NULL)
-{
- 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;
- mCurScanKey = 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_DIVIDE, "PAD_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 std::string& name)
-{
- sKeysToNames[key] = name;
- std::string nameuc = name;
- LLStringUtil::toUpper(nameuc);
- sNamesToKeys[nameuc] = key;
-}
-
-void LLKeyboard::resetKeyDownAndHandle()
-{
- MASK mask = currentMask(false);
- for (S32 i = 0; i < KEY_COUNT; i++)
- {
- if (mKeyLevel[i])
- {
- mKeyDown[i] = false;
- mKeyLevel[i] = false;
- mKeyUp[i] = true;
- mCurTranslatedKey = (KEY)i;
- mCallbacks->handleTranslatedKeyUp(i, mask);
- }
- }
-}
-
-// 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;
- }
- }
-
- for (i = 0; i < KEY_COUNT; i++)
- {
- mKeyUp[i] = false;
- }
-
- for (i = 0; i < KEY_COUNT; i++)
- {
- mKeyDown[i] = false;
- }
-
- for (i = 0; i < KEY_COUNT; i++)
- {
- mKeyRepeated[i] = false;
- }
-}
-
-
-bool LLKeyboard::translateKey(const U16 os_key, KEY *out_key)
-{
- std::map<U16, KEY>::iterator iter;
-
- // Only translate keys in the map, ignore all other keys for now
- iter = mTranslateKeyMap.find(os_key);
- if (iter == mTranslateKeyMap.end())
- {
- //LL_WARNS() << "Unknown virtual key " << os_key << LL_ENDL;
- *out_key = 0;
- return false;
- }
- else
- {
- *out_key = iter->second;
- return true;
- }
-}
-
-
-U16 LLKeyboard::inverseTranslateKey(const KEY translated_key)
-{
- std::map<KEY, U16>::iterator iter;
- iter = mInvTranslateKeyMap.find(translated_key);
- if (iter == mInvTranslateKeyMap.end())
- {
- return 0;
- }
- else
- {
- return iter->second;
- }
-}
-
-
-bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask)
-{
- bool handled = false;
- bool repeated = false;
-
- // is this the first time the key went down?
- // if so, generate "character" message
- if( !mKeyLevel[translated_key] )
- {
- mKeyLevel[translated_key] = true;
- mKeyLevelTimer[translated_key].reset();
- 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<return> in chat to move agents forward. JC
- mKeyUp[translated_key] = true;
- handled = mCallbacks->handleTranslatedKeyUp(translated_key, translated_mask);
- }
-
- LL_DEBUGS("UserInput") << "keyup -" << translated_key << "-" << LL_ENDL;
-
- 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 std::string& str, KEY *key)
-{
- std::string 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;
- }
- }
-
- LLStringUtil::toUpper(instring);
- KEY res = get_if_there(sNamesToKeys, instring, (KEY)0);
- if (res != 0)
- {
- *key = res;
- return true;
- }
- LL_WARNS() << "keyFromString failed: " << str << LL_ENDL;
- return false;
-}
-
-
-// static
-std::string LLKeyboard::stringFromKey(KEY key, bool translate)
-{
- std::string res = get_if_there(sKeysToNames, key, std::string());
- if (res.empty())
- {
- char buffer[2]; /* Flawfinder: ignore */
- buffer[0] = key;
- buffer[1] = '\0';
- res = std::string(buffer);
- }
-
- if (translate)
- {
- LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator;
- if (trans != NULL)
- {
- res = trans(res.c_str());
- }
- }
-
- return res;
-}
-
-//static
-std::string LLKeyboard::stringFromMouse(EMouseClickType click, bool translate)
-{
- std::string res;
- switch (click)
- {
- case CLICK_LEFT:
- res = "LMB";
- break;
- case CLICK_MIDDLE:
- res = "MMB";
- break;
- case CLICK_RIGHT:
- res = "RMB";
- break;
- case CLICK_BUTTON4:
- res = "MB4";
- break;
- case CLICK_BUTTON5:
- res = "MB5";
- break;
- case CLICK_DOUBLELEFT:
- res = "Double LMB";
- break;
- default:
- break;
- }
-
- if (translate && !res.empty())
- {
- LLKeyStringTranslatorFunc* trans = gKeyboard->mStringTranslator;
- if (trans != NULL)
- {
- res = trans(res.c_str());
- }
- }
- return res;
-}
-
-//static
-std::string LLKeyboard::stringFromAccelerator(MASK accel_mask)
-{
- std::string res;
-
- LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator;
-
- if (trans == NULL)
- {
- LL_ERRS() << "No mKeyStringTranslator" << LL_ENDL;
- return res;
- }
-
- // Append any masks
-#ifdef LL_DARWIN
- // Standard Mac names for modifier keys in menu equivalents
- // We could use the symbol characters, but they only exist in certain fonts.
- if (accel_mask & MASK_CONTROL)
- {
- if (accel_mask & MASK_MAC_CONTROL)
- {
- res.append(trans("accel-mac-control"));
- }
- else
- {
- res.append(trans("accel-mac-command")); // Symbol would be "\xE2\x8C\x98"
- }
- }
- if (accel_mask & MASK_ALT)
- res.append(trans("accel-mac-option")); // Symbol would be "\xE2\x8C\xA5"
- if (accel_mask & MASK_SHIFT)
- res.append(trans("accel-mac-shift")); // Symbol would be "\xE2\x8C\xA7"
-#else
- if (accel_mask & MASK_CONTROL)
- res.append(trans("accel-win-control"));
- if (accel_mask & MASK_ALT)
- res.append(trans("accel-win-alt"));
- if (accel_mask & MASK_SHIFT)
- res.append(trans("accel-win-shift"));
-#endif
- return res;
-}
-//static
-std::string LLKeyboard::stringFromAccelerator( MASK accel_mask, KEY key )
-{
- std::string res;
-
- // break early if this is a silly thing to do.
- if( KEY_NONE == key )
- {
- return res;
- }
-
- res.append(stringFromAccelerator(accel_mask));
- std::string key_string = LLKeyboard::stringFromKey(key);
- if ((accel_mask & MASK_NORMALKEYS) &&
- (key_string[0] == '-' || key_string[0] == '=' || key_string[0] == '+'))
- {
- res.append( " " );
- }
-
- std::string keystr = stringFromKey( key );
- res.append( keystr );
-
- return res;
-}
-
-//static
-std::string LLKeyboard::stringFromAccelerator(MASK accel_mask, EMouseClickType click)
-{
- std::string res;
- if (CLICK_NONE == click)
- {
- return res;
- }
- res.append(stringFromAccelerator(accel_mask));
- res.append(stringFromMouse(click));
- return res;
-}
-
-//static
-bool LLKeyboard::maskFromString(const std::string& str, MASK *mask)
-{
- std::string 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;
- }
-}
-
-
-//static
-void LLKeyboard::setStringTranslatorFunc( LLKeyStringTranslatorFunc *trans_func )
-{
- mStringTranslator = trans_func;
-}
+/** + * @file llkeyboard.cpp + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "indra_constants.h" +#include "llkeyboard.h" + +#include "llwindowcallbacks.h" + +// +// Globals +// + +LLKeyboard *gKeyboard = NULL; + +//static +std::map<KEY,std::string> LLKeyboard::sKeysToNames; +std::map<std::string,KEY> LLKeyboard::sNamesToKeys; +LLKeyStringTranslatorFunc* LLKeyboard::mStringTranslator = NULL; // Used for l10n + PC/Mac/Linux accelerator labeling + + +// +// Class Implementation +// + +LLKeyboard::LLKeyboard() : mCallbacks(NULL) +{ + 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; + mCurScanKey = 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_DIVIDE, "PAD_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 std::string& name) +{ + sKeysToNames[key] = name; + std::string nameuc = name; + LLStringUtil::toUpper(nameuc); + sNamesToKeys[nameuc] = key; +} + +void LLKeyboard::resetKeyDownAndHandle() +{ + MASK mask = currentMask(false); + for (S32 i = 0; i < KEY_COUNT; i++) + { + if (mKeyLevel[i]) + { + mKeyDown[i] = false; + mKeyLevel[i] = false; + mKeyUp[i] = true; + mCurTranslatedKey = (KEY)i; + mCallbacks->handleTranslatedKeyUp(i, mask); + } + } +} + +// 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; + } + } + + for (i = 0; i < KEY_COUNT; i++) + { + mKeyUp[i] = false; + } + + for (i = 0; i < KEY_COUNT; i++) + { + mKeyDown[i] = false; + } + + for (i = 0; i < KEY_COUNT; i++) + { + mKeyRepeated[i] = false; + } +} + + +bool LLKeyboard::translateKey(const U16 os_key, KEY *out_key) +{ + std::map<U16, KEY>::iterator iter; + + // Only translate keys in the map, ignore all other keys for now + iter = mTranslateKeyMap.find(os_key); + if (iter == mTranslateKeyMap.end()) + { + //LL_WARNS() << "Unknown virtual key " << os_key << LL_ENDL; + *out_key = 0; + return false; + } + else + { + *out_key = iter->second; + return true; + } +} + + +U16 LLKeyboard::inverseTranslateKey(const KEY translated_key) +{ + std::map<KEY, U16>::iterator iter; + iter = mInvTranslateKeyMap.find(translated_key); + if (iter == mInvTranslateKeyMap.end()) + { + return 0; + } + else + { + return iter->second; + } +} + + +bool LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask) +{ + bool handled = false; + bool repeated = false; + + // is this the first time the key went down? + // if so, generate "character" message + if( !mKeyLevel[translated_key] ) + { + mKeyLevel[translated_key] = true; + mKeyLevelTimer[translated_key].reset(); + 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<return> in chat to move agents forward. JC + mKeyUp[translated_key] = true; + handled = mCallbacks->handleTranslatedKeyUp(translated_key, translated_mask); + } + + LL_DEBUGS("UserInput") << "keyup -" << translated_key << "-" << LL_ENDL; + + 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 std::string& str, KEY *key) +{ + std::string 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; + } + } + + LLStringUtil::toUpper(instring); + KEY res = get_if_there(sNamesToKeys, instring, (KEY)0); + if (res != 0) + { + *key = res; + return true; + } + LL_WARNS() << "keyFromString failed: " << str << LL_ENDL; + return false; +} + + +// static +std::string LLKeyboard::stringFromKey(KEY key, bool translate) +{ + std::string res = get_if_there(sKeysToNames, key, std::string()); + if (res.empty()) + { + char buffer[2]; /* Flawfinder: ignore */ + buffer[0] = key; + buffer[1] = '\0'; + res = std::string(buffer); + } + + if (translate) + { + LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; + if (trans != NULL) + { + res = trans(res.c_str()); + } + } + + return res; +} + +//static +std::string LLKeyboard::stringFromMouse(EMouseClickType click, bool translate) +{ + std::string res; + switch (click) + { + case CLICK_LEFT: + res = "LMB"; + break; + case CLICK_MIDDLE: + res = "MMB"; + break; + case CLICK_RIGHT: + res = "RMB"; + break; + case CLICK_BUTTON4: + res = "MB4"; + break; + case CLICK_BUTTON5: + res = "MB5"; + break; + case CLICK_DOUBLELEFT: + res = "Double LMB"; + break; + default: + break; + } + + if (translate && !res.empty()) + { + LLKeyStringTranslatorFunc* trans = gKeyboard->mStringTranslator; + if (trans != NULL) + { + res = trans(res.c_str()); + } + } + return res; +} + +//static +std::string LLKeyboard::stringFromAccelerator(MASK accel_mask) +{ + std::string res; + + LLKeyStringTranslatorFunc *trans = gKeyboard->mStringTranslator; + + if (trans == NULL) + { + LL_ERRS() << "No mKeyStringTranslator" << LL_ENDL; + return res; + } + + // Append any masks +#ifdef LL_DARWIN + // Standard Mac names for modifier keys in menu equivalents + // We could use the symbol characters, but they only exist in certain fonts. + if (accel_mask & MASK_CONTROL) + { + if (accel_mask & MASK_MAC_CONTROL) + { + res.append(trans("accel-mac-control")); + } + else + { + res.append(trans("accel-mac-command")); // Symbol would be "\xE2\x8C\x98" + } + } + if (accel_mask & MASK_ALT) + res.append(trans("accel-mac-option")); // Symbol would be "\xE2\x8C\xA5" + if (accel_mask & MASK_SHIFT) + res.append(trans("accel-mac-shift")); // Symbol would be "\xE2\x8C\xA7" +#else + if (accel_mask & MASK_CONTROL) + res.append(trans("accel-win-control")); + if (accel_mask & MASK_ALT) + res.append(trans("accel-win-alt")); + if (accel_mask & MASK_SHIFT) + res.append(trans("accel-win-shift")); +#endif + return res; +} +//static +std::string LLKeyboard::stringFromAccelerator( MASK accel_mask, KEY key ) +{ + std::string res; + + // break early if this is a silly thing to do. + if( KEY_NONE == key ) + { + return res; + } + + res.append(stringFromAccelerator(accel_mask)); + std::string key_string = LLKeyboard::stringFromKey(key); + if ((accel_mask & MASK_NORMALKEYS) && + (key_string[0] == '-' || key_string[0] == '=' || key_string[0] == '+')) + { + res.append( " " ); + } + + std::string keystr = stringFromKey( key ); + res.append( keystr ); + + return res; +} + +//static +std::string LLKeyboard::stringFromAccelerator(MASK accel_mask, EMouseClickType click) +{ + std::string res; + if (CLICK_NONE == click) + { + return res; + } + res.append(stringFromAccelerator(accel_mask)); + res.append(stringFromMouse(click)); + return res; +} + +//static +bool LLKeyboard::maskFromString(const std::string& str, MASK *mask) +{ + std::string 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; + } +} + + +//static +void LLKeyboard::setStringTranslatorFunc( LLKeyStringTranslatorFunc *trans_func ) +{ + mStringTranslator = trans_func; +} diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index 4278ffb12f..7f8d0af155 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -1,144 +1,144 @@ -/**
- * @file llkeyboard.h
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLKEYBOARD_H
-#define LL_LLKEYBOARD_H
-
-#include <map>
-#include <boost/function.hpp>
-
-#include "llstringtable.h"
-#include "lltimer.h"
-#include "indra_constants.h"
-
-enum EKeystate
-{
- KEYSTATE_DOWN,
- KEYSTATE_LEVEL,
- KEYSTATE_UP
-};
-
-typedef boost::function<bool(EKeystate keystate)> LLKeyFunc;
-typedef std::string (LLKeyStringTranslatorFunc)(const char *label);
-
-enum EKeyboardInsertMode
-{
- LL_KIM_INSERT,
- LL_KIM_OVERWRITE
-};
-
-class LLWindowCallbacks;
-
-class LLKeyboard
-{
-public:
- LLKeyboard();
- virtual ~LLKeyboard();
-
- void resetKeyDownAndHandle();
- 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;
-
-#ifdef LL_DARWIN
- // We only actually use this for macOS.
- virtual void handleModifier(MASK mask) = 0;
-#endif // LL_DARWIN
-
- // 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 std::string& str, MASK *mask); // False on failure
- static bool keyFromString(const std::string& str, KEY *key); // False on failure
- static std::string stringFromKey(KEY key, bool translate = true);
- static std::string stringFromMouse(EMouseClickType click, bool translate = true);
- static std::string stringFromAccelerator( MASK accel_mask ); // separated for convinience, returns with "+": "Shift+" or "Shift+Alt+"...
- static std::string stringFromAccelerator( MASK accel_mask, KEY key );
- static std::string stringFromAccelerator(MASK accel_mask, EMouseClickType click);
-
- 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.
-
- static void setStringTranslatorFunc( LLKeyStringTranslatorFunc *trans_func );
-
-protected:
- void addKeyName(KEY key, const std::string& name);
-
-protected:
- std::map<U16, KEY> mTranslateKeyMap; // Map of translations from OS keys to Linden KEYs
- std::map<KEY, U16> mInvTranslateKeyMap; // Map of translations from Linden KEYs to OS keys
- LLWindowCallbacks *mCallbacks;
-
- LLTimer mKeyLevelTimer[KEY_COUNT]; // Time since level was set
- S32 mKeyLevelFrameCount[KEY_COUNT]; // Frames since level was set
- bool mKeyLevel[KEY_COUNT]; // Levels
- bool mKeyRepeated[KEY_COUNT]; // Key was repeated
- bool mKeyUp[KEY_COUNT]; // Up edge
- bool mKeyDown[KEY_COUNT]; // Down edge
- KEY mCurTranslatedKey;
- KEY mCurScanKey; // Used during the scanKeyboard()
-
- static LLKeyStringTranslatorFunc* mStringTranslator; // Used for l10n + PC/Mac/Linux accelerator labeling
-
- EKeyboardInsertMode mInsertMode;
-
- static std::map<KEY,std::string> sKeysToNames;
- static std::map<std::string,KEY> sNamesToKeys;
-};
-
-// Interface to get key from assigned command
-class LLKeyBindingToStringHandler
-{
-public:
- virtual std::string getKeyBindingAsString(const std::string& mode, const std::string& control) const = 0;
-};
-
-extern LLKeyboard *gKeyboard;
-
-#endif
+/** + * @file llkeyboard.h + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLKEYBOARD_H +#define LL_LLKEYBOARD_H + +#include <map> +#include <boost/function.hpp> + +#include "llstringtable.h" +#include "lltimer.h" +#include "indra_constants.h" + +enum EKeystate +{ + KEYSTATE_DOWN, + KEYSTATE_LEVEL, + KEYSTATE_UP +}; + +typedef boost::function<bool(EKeystate keystate)> LLKeyFunc; +typedef std::string (LLKeyStringTranslatorFunc)(const char *label); + +enum EKeyboardInsertMode +{ + LL_KIM_INSERT, + LL_KIM_OVERWRITE +}; + +class LLWindowCallbacks; + +class LLKeyboard +{ +public: + LLKeyboard(); + virtual ~LLKeyboard(); + + void resetKeyDownAndHandle(); + 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; + +#ifdef LL_DARWIN + // We only actually use this for macOS. + virtual void handleModifier(MASK mask) = 0; +#endif // LL_DARWIN + + // 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 std::string& str, MASK *mask); // False on failure + static bool keyFromString(const std::string& str, KEY *key); // False on failure + static std::string stringFromKey(KEY key, bool translate = true); + static std::string stringFromMouse(EMouseClickType click, bool translate = true); + static std::string stringFromAccelerator( MASK accel_mask ); // separated for convinience, returns with "+": "Shift+" or "Shift+Alt+"... + static std::string stringFromAccelerator( MASK accel_mask, KEY key ); + static std::string stringFromAccelerator(MASK accel_mask, EMouseClickType click); + + 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. + + static void setStringTranslatorFunc( LLKeyStringTranslatorFunc *trans_func ); + +protected: + void addKeyName(KEY key, const std::string& name); + +protected: + std::map<U16, KEY> mTranslateKeyMap; // Map of translations from OS keys to Linden KEYs + std::map<KEY, U16> mInvTranslateKeyMap; // Map of translations from Linden KEYs to OS keys + LLWindowCallbacks *mCallbacks; + + LLTimer mKeyLevelTimer[KEY_COUNT]; // Time since level was set + S32 mKeyLevelFrameCount[KEY_COUNT]; // Frames since level was set + bool mKeyLevel[KEY_COUNT]; // Levels + bool mKeyRepeated[KEY_COUNT]; // Key was repeated + bool mKeyUp[KEY_COUNT]; // Up edge + bool mKeyDown[KEY_COUNT]; // Down edge + KEY mCurTranslatedKey; + KEY mCurScanKey; // Used during the scanKeyboard() + + static LLKeyStringTranslatorFunc* mStringTranslator; // Used for l10n + PC/Mac/Linux accelerator labeling + + EKeyboardInsertMode mInsertMode; + + static std::map<KEY,std::string> sKeysToNames; + static std::map<std::string,KEY> sNamesToKeys; +}; + +// Interface to get key from assigned command +class LLKeyBindingToStringHandler +{ +public: + virtual std::string getKeyBindingAsString(const std::string& mode, const std::string& control) const = 0; +}; + +extern LLKeyboard *gKeyboard; + +#endif diff --git a/indra/llwindow/llkeyboardheadless.cpp b/indra/llwindow/llkeyboardheadless.cpp index 098d9b62c7..8669a5b41a 100644 --- a/indra/llwindow/llkeyboardheadless.cpp +++ b/indra/llwindow/llkeyboardheadless.cpp @@ -1,80 +1,80 @@ -/**
- * @file llkeyboardheadless.cpp
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llkeyboardheadless.h"
-#include "llwindowcallbacks.h"
-
-LLKeyboardHeadless::LLKeyboardHeadless()
-{ }
-
-void LLKeyboardHeadless::resetMaskKeys()
-{ }
-
-
-bool LLKeyboardHeadless::handleKeyDown(const U16 key, const U32 mask)
-{ return false; }
-
-
-bool LLKeyboardHeadless::handleKeyUp(const U16 key, const U32 mask)
-{ return false; }
-
-MASK LLKeyboardHeadless::currentMask(bool for_mouse_event)
-{ return MASK_NONE; }
-
-#ifdef LL_DARWIN
-void LLKeyboardHeadless::handleModifier(MASK mask)
-{
-
-}
-#endif
-
-void LLKeyboardHeadless::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]++;
- }
- }
-}
-
+/** + * @file llkeyboardheadless.cpp + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "llkeyboardheadless.h" +#include "llwindowcallbacks.h" + +LLKeyboardHeadless::LLKeyboardHeadless() +{ } + +void LLKeyboardHeadless::resetMaskKeys() +{ } + + +bool LLKeyboardHeadless::handleKeyDown(const U16 key, const U32 mask) +{ return false; } + + +bool LLKeyboardHeadless::handleKeyUp(const U16 key, const U32 mask) +{ return false; } + +MASK LLKeyboardHeadless::currentMask(bool for_mouse_event) +{ return MASK_NONE; } + +#ifdef LL_DARWIN +void LLKeyboardHeadless::handleModifier(MASK mask) +{ + +} +#endif + +void LLKeyboardHeadless::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]++; + } + } +} + diff --git a/indra/llwindow/llkeyboardheadless.h b/indra/llwindow/llkeyboardheadless.h index df7b015e8d..2528f0e3f1 100644 --- a/indra/llwindow/llkeyboardheadless.h +++ b/indra/llwindow/llkeyboardheadless.h @@ -1,48 +1,48 @@ -/**
- * @file llkeyboardheadless.h
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLKEYBOARDHEADLESS_H
-#define LL_LLKEYBOARDHEADLESS_H
-
-#include "llkeyboard.h"
-
-class LLKeyboardHeadless : public LLKeyboard
-{
-public:
- LLKeyboardHeadless();
- /*virtual*/ ~LLKeyboardHeadless() {};
-
- /*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();
-#ifdef LL_DARWIN
- /*virtual*/ void handleModifier(MASK mask);
-#endif
-};
-
-#endif
+/** + * @file llkeyboardheadless.h + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLKEYBOARDHEADLESS_H +#define LL_LLKEYBOARDHEADLESS_H + +#include "llkeyboard.h" + +class LLKeyboardHeadless : public LLKeyboard +{ +public: + LLKeyboardHeadless(); + /*virtual*/ ~LLKeyboardHeadless() {}; + + /*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(); +#ifdef LL_DARWIN + /*virtual*/ void handleModifier(MASK mask); +#endif +}; + +#endif diff --git a/indra/llwindow/llkeyboardmacosx.cpp b/indra/llwindow/llkeyboardmacosx.cpp index 4a415a2f54..89ff7c6d3f 100644 --- a/indra/llwindow/llkeyboardmacosx.cpp +++ b/indra/llwindow/llkeyboardmacosx.cpp @@ -1,317 +1,317 @@ -/**
- * @file llkeyboardmacosx.cpp
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#if LL_DARWIN
-
-#include "linden_common.h"
-#include "llkeyboardmacosx.h"
-#include "llwindowcallbacks.h"
-
-#include "llwindowmacosx-objc.h"
-
-LLKeyboardMacOSX::LLKeyboardMacOSX()
-{
- // Virtual keycode mapping table. Yes, this was as annoying to generate as it looks.
- mTranslateKeyMap[0x00] = 'A';
- mTranslateKeyMap[0x01] = 'S';
- mTranslateKeyMap[0x02] = 'D';
- mTranslateKeyMap[0x03] = 'F';
- mTranslateKeyMap[0x04] = 'H';
- mTranslateKeyMap[0x05] = 'G';
- mTranslateKeyMap[0x06] = 'Z';
- mTranslateKeyMap[0x07] = 'X';
- mTranslateKeyMap[0x08] = 'C';
- mTranslateKeyMap[0x09] = 'V';
- mTranslateKeyMap[0x0b] = 'B';
- mTranslateKeyMap[0x0c] = 'Q';
- mTranslateKeyMap[0x0d] = 'W';
- mTranslateKeyMap[0x0e] = 'E';
- mTranslateKeyMap[0x0f] = 'R';
- mTranslateKeyMap[0x10] = 'Y';
- mTranslateKeyMap[0x11] = 'T';
- mTranslateKeyMap[0x12] = '1';
- mTranslateKeyMap[0x13] = '2';
- mTranslateKeyMap[0x14] = '3';
- mTranslateKeyMap[0x15] = '4';
- mTranslateKeyMap[0x16] = '6';
- mTranslateKeyMap[0x17] = '5';
- mTranslateKeyMap[0x18] = '='; // KEY_EQUALS
- mTranslateKeyMap[0x19] = '9';
- mTranslateKeyMap[0x1a] = '7';
- mTranslateKeyMap[0x1b] = '-'; // KEY_HYPHEN
- mTranslateKeyMap[0x1c] = '8';
- mTranslateKeyMap[0x1d] = '0';
- mTranslateKeyMap[0x1e] = ']';
- mTranslateKeyMap[0x1f] = 'O';
- mTranslateKeyMap[0x20] = 'U';
- mTranslateKeyMap[0x21] = '[';
- mTranslateKeyMap[0x22] = 'I';
- mTranslateKeyMap[0x23] = 'P';
- mTranslateKeyMap[0x24] = KEY_RETURN,
- mTranslateKeyMap[0x25] = 'L';
- mTranslateKeyMap[0x26] = 'J';
- mTranslateKeyMap[0x27] = '\'';
- mTranslateKeyMap[0x28] = 'K';
- mTranslateKeyMap[0x29] = ';';
- mTranslateKeyMap[0x2a] = '\\';
- mTranslateKeyMap[0x2b] = ',';
- mTranslateKeyMap[0x2c] = KEY_DIVIDE;
- 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] = KEY_PAD_DIVIDE; // keypad
- mTranslateKeyMap[0x4c] = KEY_RETURN; // keypad enter
- mTranslateKeyMap[0x4e] = '-'; // keypad
- mTranslateKeyMap[0x51] = '='; // keypad
- mTranslateKeyMap[0x52] = '0'; // keypad
- mTranslateKeyMap[0x53] = '1'; // keypad
- mTranslateKeyMap[0x54] = '2'; // keypad
- mTranslateKeyMap[0x55] = '3'; // keypad
- mTranslateKeyMap[0x56] = '4'; // keypad
- mTranslateKeyMap[0x57] = '5'; // keypad
- mTranslateKeyMap[0x58] = '6'; // keypad
- mTranslateKeyMap[0x59] = '7'; // keypad
- mTranslateKeyMap[0x5b] = '8'; // keypad
- mTranslateKeyMap[0x5c] = '9'; // keypad
- mTranslateKeyMap[0x60] = KEY_F5;
- mTranslateKeyMap[0x61] = KEY_F6;
- mTranslateKeyMap[0x62] = KEY_F7;
- mTranslateKeyMap[0x63] = KEY_F3;
- mTranslateKeyMap[0x64] = KEY_F8;
- mTranslateKeyMap[0x65] = KEY_F9;
- mTranslateKeyMap[0x67] = KEY_F11;
- mTranslateKeyMap[0x6d] = KEY_F10;
- mTranslateKeyMap[0x6f] = KEY_F12;
- mTranslateKeyMap[0x72] = KEY_INSERT;
- mTranslateKeyMap[0x73] = KEY_HOME;
- mTranslateKeyMap[0x74] = KEY_PAGE_UP;
- mTranslateKeyMap[0x75] = KEY_DELETE;
- mTranslateKeyMap[0x76] = KEY_F4;
- mTranslateKeyMap[0x77] = KEY_END;
- mTranslateKeyMap[0x78] = KEY_F2;
- mTranslateKeyMap[0x79] = KEY_PAGE_DOWN;
- mTranslateKeyMap[0x7a] = KEY_F1;
- mTranslateKeyMap[0x7b] = KEY_LEFT;
- mTranslateKeyMap[0x7c] = KEY_RIGHT;
- mTranslateKeyMap[0x7d] = KEY_DOWN;
- mTranslateKeyMap[0x7e] = KEY_UP;
-
- // Build inverse map
- std::map<U16, KEY>::iterator iter;
- for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++)
- {
- mInvTranslateKeyMap[iter->second] = iter->first;
- }
-
- // build numpad maps
- mTranslateNumpadMap[0x52] = KEY_PAD_INS; // keypad 0
- mTranslateNumpadMap[0x53] = KEY_PAD_END; // keypad 1
- mTranslateNumpadMap[0x54] = KEY_PAD_DOWN; // keypad 2
- mTranslateNumpadMap[0x55] = KEY_PAD_PGDN; // keypad 3
- mTranslateNumpadMap[0x56] = KEY_PAD_LEFT; // keypad 4
- mTranslateNumpadMap[0x57] = KEY_PAD_CENTER; // keypad 5
- mTranslateNumpadMap[0x58] = KEY_PAD_RIGHT; // keypad 6
- mTranslateNumpadMap[0x59] = KEY_PAD_HOME; // keypad 7
- mTranslateNumpadMap[0x5b] = KEY_PAD_UP; // keypad 8
- mTranslateNumpadMap[0x5c] = KEY_PAD_PGUP; // keypad 9
- mTranslateNumpadMap[0x41] = KEY_PAD_DEL; // keypad .
- mTranslateNumpadMap[0x4c] = KEY_PAD_RETURN; // keypad enter
-
- // Build inverse numpad map
- for (iter = mTranslateNumpadMap.begin(); iter != mTranslateNumpadMap.end(); iter++)
- {
- mInvTranslateNumpadMap[iter->second] = iter->first;
- }
-}
-
-void LLKeyboardMacOSX::resetMaskKeys()
-{
- U32 mask = getModifiers();
-
- // MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys().
- // It looks a bit suspicious, as it won't correct for keys that have been released.
- // Is this the way it's supposed to work?
-
- // We apply the modifier masks directly within getModifiers. So check to see which masks we've applied.
-
- if(mask & MAC_SHIFT_KEY)
- {
- mKeyLevel[KEY_SHIFT] = true;
- }
-
- if(mask & MAC_CTRL_KEY)
- {
- mKeyLevel[KEY_CONTROL] = true;
- }
-
- if(mask & MAC_ALT_KEY)
- {
- 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);
-}
-*/
-
-void LLKeyboardMacOSX::handleModifier(MASK mask)
-{
- updateModifiers(mask);
-}
-
-MASK LLKeyboardMacOSX::updateModifiers(const U32 mask)
-{
- // translate the mask
- MASK out_mask = 0;
-
- if(mask & MAC_SHIFT_KEY)
- {
- out_mask |= MASK_SHIFT;
- }
-
- if(mask & (MAC_CTRL_KEY | MAC_CMD_KEY))
- {
- out_mask |= MASK_CONTROL;
- }
-
- if(mask & MAC_ALT_KEY)
- {
- 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 = getModifiers();
-
- if (mask & MAC_SHIFT_KEY) result |= MASK_SHIFT;
- if (mask & MAC_CTRL_KEY) result |= MASK_CONTROL;
- if (mask & MAC_ALT_KEY) result |= MASK_ALT;
-
- // For keyboard events, consider Command equivalent to Control
- if (!for_mouse_event)
- {
- if (mask & MAC_CMD_KEY) 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 )
-{
- return translateKey(os_key, translated_key);
-}
-
-U16 LLKeyboardMacOSX::inverseTranslateNumpadKey(const KEY translated_key)
-{
- return inverseTranslateKey(translated_key);
-}
-
-#endif // LL_DARWIN
+/** + * @file llkeyboardmacosx.cpp + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if LL_DARWIN + +#include "linden_common.h" +#include "llkeyboardmacosx.h" +#include "llwindowcallbacks.h" + +#include "llwindowmacosx-objc.h" + +LLKeyboardMacOSX::LLKeyboardMacOSX() +{ + // Virtual keycode mapping table. Yes, this was as annoying to generate as it looks. + mTranslateKeyMap[0x00] = 'A'; + mTranslateKeyMap[0x01] = 'S'; + mTranslateKeyMap[0x02] = 'D'; + mTranslateKeyMap[0x03] = 'F'; + mTranslateKeyMap[0x04] = 'H'; + mTranslateKeyMap[0x05] = 'G'; + mTranslateKeyMap[0x06] = 'Z'; + mTranslateKeyMap[0x07] = 'X'; + mTranslateKeyMap[0x08] = 'C'; + mTranslateKeyMap[0x09] = 'V'; + mTranslateKeyMap[0x0b] = 'B'; + mTranslateKeyMap[0x0c] = 'Q'; + mTranslateKeyMap[0x0d] = 'W'; + mTranslateKeyMap[0x0e] = 'E'; + mTranslateKeyMap[0x0f] = 'R'; + mTranslateKeyMap[0x10] = 'Y'; + mTranslateKeyMap[0x11] = 'T'; + mTranslateKeyMap[0x12] = '1'; + mTranslateKeyMap[0x13] = '2'; + mTranslateKeyMap[0x14] = '3'; + mTranslateKeyMap[0x15] = '4'; + mTranslateKeyMap[0x16] = '6'; + mTranslateKeyMap[0x17] = '5'; + mTranslateKeyMap[0x18] = '='; // KEY_EQUALS + mTranslateKeyMap[0x19] = '9'; + mTranslateKeyMap[0x1a] = '7'; + mTranslateKeyMap[0x1b] = '-'; // KEY_HYPHEN + mTranslateKeyMap[0x1c] = '8'; + mTranslateKeyMap[0x1d] = '0'; + mTranslateKeyMap[0x1e] = ']'; + mTranslateKeyMap[0x1f] = 'O'; + mTranslateKeyMap[0x20] = 'U'; + mTranslateKeyMap[0x21] = '['; + mTranslateKeyMap[0x22] = 'I'; + mTranslateKeyMap[0x23] = 'P'; + mTranslateKeyMap[0x24] = KEY_RETURN, + mTranslateKeyMap[0x25] = 'L'; + mTranslateKeyMap[0x26] = 'J'; + mTranslateKeyMap[0x27] = '\''; + mTranslateKeyMap[0x28] = 'K'; + mTranslateKeyMap[0x29] = ';'; + mTranslateKeyMap[0x2a] = '\\'; + mTranslateKeyMap[0x2b] = ','; + mTranslateKeyMap[0x2c] = KEY_DIVIDE; + 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] = KEY_PAD_DIVIDE; // keypad + mTranslateKeyMap[0x4c] = KEY_RETURN; // keypad enter + mTranslateKeyMap[0x4e] = '-'; // keypad + mTranslateKeyMap[0x51] = '='; // keypad + mTranslateKeyMap[0x52] = '0'; // keypad + mTranslateKeyMap[0x53] = '1'; // keypad + mTranslateKeyMap[0x54] = '2'; // keypad + mTranslateKeyMap[0x55] = '3'; // keypad + mTranslateKeyMap[0x56] = '4'; // keypad + mTranslateKeyMap[0x57] = '5'; // keypad + mTranslateKeyMap[0x58] = '6'; // keypad + mTranslateKeyMap[0x59] = '7'; // keypad + mTranslateKeyMap[0x5b] = '8'; // keypad + mTranslateKeyMap[0x5c] = '9'; // keypad + mTranslateKeyMap[0x60] = KEY_F5; + mTranslateKeyMap[0x61] = KEY_F6; + mTranslateKeyMap[0x62] = KEY_F7; + mTranslateKeyMap[0x63] = KEY_F3; + mTranslateKeyMap[0x64] = KEY_F8; + mTranslateKeyMap[0x65] = KEY_F9; + mTranslateKeyMap[0x67] = KEY_F11; + mTranslateKeyMap[0x6d] = KEY_F10; + mTranslateKeyMap[0x6f] = KEY_F12; + mTranslateKeyMap[0x72] = KEY_INSERT; + mTranslateKeyMap[0x73] = KEY_HOME; + mTranslateKeyMap[0x74] = KEY_PAGE_UP; + mTranslateKeyMap[0x75] = KEY_DELETE; + mTranslateKeyMap[0x76] = KEY_F4; + mTranslateKeyMap[0x77] = KEY_END; + mTranslateKeyMap[0x78] = KEY_F2; + mTranslateKeyMap[0x79] = KEY_PAGE_DOWN; + mTranslateKeyMap[0x7a] = KEY_F1; + mTranslateKeyMap[0x7b] = KEY_LEFT; + mTranslateKeyMap[0x7c] = KEY_RIGHT; + mTranslateKeyMap[0x7d] = KEY_DOWN; + mTranslateKeyMap[0x7e] = KEY_UP; + + // Build inverse map + std::map<U16, KEY>::iterator iter; + for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++) + { + mInvTranslateKeyMap[iter->second] = iter->first; + } + + // build numpad maps + mTranslateNumpadMap[0x52] = KEY_PAD_INS; // keypad 0 + mTranslateNumpadMap[0x53] = KEY_PAD_END; // keypad 1 + mTranslateNumpadMap[0x54] = KEY_PAD_DOWN; // keypad 2 + mTranslateNumpadMap[0x55] = KEY_PAD_PGDN; // keypad 3 + mTranslateNumpadMap[0x56] = KEY_PAD_LEFT; // keypad 4 + mTranslateNumpadMap[0x57] = KEY_PAD_CENTER; // keypad 5 + mTranslateNumpadMap[0x58] = KEY_PAD_RIGHT; // keypad 6 + mTranslateNumpadMap[0x59] = KEY_PAD_HOME; // keypad 7 + mTranslateNumpadMap[0x5b] = KEY_PAD_UP; // keypad 8 + mTranslateNumpadMap[0x5c] = KEY_PAD_PGUP; // keypad 9 + mTranslateNumpadMap[0x41] = KEY_PAD_DEL; // keypad . + mTranslateNumpadMap[0x4c] = KEY_PAD_RETURN; // keypad enter + + // Build inverse numpad map + for (iter = mTranslateNumpadMap.begin(); iter != mTranslateNumpadMap.end(); iter++) + { + mInvTranslateNumpadMap[iter->second] = iter->first; + } +} + +void LLKeyboardMacOSX::resetMaskKeys() +{ + U32 mask = getModifiers(); + + // MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys(). + // It looks a bit suspicious, as it won't correct for keys that have been released. + // Is this the way it's supposed to work? + + // We apply the modifier masks directly within getModifiers. So check to see which masks we've applied. + + if(mask & MAC_SHIFT_KEY) + { + mKeyLevel[KEY_SHIFT] = true; + } + + if(mask & MAC_CTRL_KEY) + { + mKeyLevel[KEY_CONTROL] = true; + } + + if(mask & MAC_ALT_KEY) + { + 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); +} +*/ + +void LLKeyboardMacOSX::handleModifier(MASK mask) +{ + updateModifiers(mask); +} + +MASK LLKeyboardMacOSX::updateModifiers(const U32 mask) +{ + // translate the mask + MASK out_mask = 0; + + if(mask & MAC_SHIFT_KEY) + { + out_mask |= MASK_SHIFT; + } + + if(mask & (MAC_CTRL_KEY | MAC_CMD_KEY)) + { + out_mask |= MASK_CONTROL; + } + + if(mask & MAC_ALT_KEY) + { + 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 = getModifiers(); + + if (mask & MAC_SHIFT_KEY) result |= MASK_SHIFT; + if (mask & MAC_CTRL_KEY) result |= MASK_CONTROL; + if (mask & MAC_ALT_KEY) result |= MASK_ALT; + + // For keyboard events, consider Command equivalent to Control + if (!for_mouse_event) + { + if (mask & MAC_CMD_KEY) 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 ) +{ + return translateKey(os_key, translated_key); +} + +U16 LLKeyboardMacOSX::inverseTranslateNumpadKey(const KEY translated_key) +{ + return inverseTranslateKey(translated_key); +} + +#endif // LL_DARWIN diff --git a/indra/llwindow/llkeyboardmacosx.h b/indra/llwindow/llkeyboardmacosx.h index 5c5328a156..92ab5c9a85 100644 --- a/indra/llwindow/llkeyboardmacosx.h +++ b/indra/llwindow/llkeyboardmacosx.h @@ -1,64 +1,64 @@ -/**
- * @file llkeyboardmacosx.h
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLKEYBOARDMACOSX_H
-#define LL_LLKEYBOARDMACOSX_H
-
-#include "llkeyboard.h"
-
-// These more or less mirror their equivalents in NSEvent.h.
-enum EMacEventKeys {
- MAC_SHIFT_KEY = 1 << 17,
- MAC_CTRL_KEY = 1 << 18,
- MAC_ALT_KEY = 1 << 19,
- MAC_CMD_KEY = 1 << 20,
- MAC_FN_KEY = 1 << 23
-};
-
-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();
- /*virtual*/ void handleModifier(MASK mask);
-
-protected:
- MASK updateModifiers(const U32 mask);
- void setModifierKeyLevel( KEY key, bool new_state );
- bool translateNumpadKey( const U16 os_key, KEY *translated_key );
- U16 inverseTranslateNumpadKey(const KEY translated_key);
-private:
- std::map<U16, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys
- std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above
-};
-
-#endif
+/** + * @file llkeyboardmacosx.h + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLKEYBOARDMACOSX_H +#define LL_LLKEYBOARDMACOSX_H + +#include "llkeyboard.h" + +// These more or less mirror their equivalents in NSEvent.h. +enum EMacEventKeys { + MAC_SHIFT_KEY = 1 << 17, + MAC_CTRL_KEY = 1 << 18, + MAC_ALT_KEY = 1 << 19, + MAC_CMD_KEY = 1 << 20, + MAC_FN_KEY = 1 << 23 +}; + +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(); + /*virtual*/ void handleModifier(MASK mask); + +protected: + MASK updateModifiers(const U32 mask); + void setModifierKeyLevel( KEY key, bool new_state ); + bool translateNumpadKey( const U16 os_key, KEY *translated_key ); + U16 inverseTranslateNumpadKey(const KEY translated_key); +private: + std::map<U16, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys + std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above +}; + +#endif diff --git a/indra/llwindow/llkeyboardsdl.cpp b/indra/llwindow/llkeyboardsdl.cpp index eaf577108a..97198f0cc0 100644 --- a/indra/llwindow/llkeyboardsdl.cpp +++ b/indra/llwindow/llkeyboardsdl.cpp @@ -1,324 +1,324 @@ -/**
- * @file llkeyboardsdl.cpp
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#if LL_SDL
-
-#include "linden_common.h"
-#include "llkeyboardsdl.h"
-#include "llwindowcallbacks.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_PAD_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] = KEY_DIVIDE;
- mTranslateKeyMap[SDLK_SEMICOLON] = ';';
- mTranslateKeyMap[SDLK_LEFTBRACKET] = '[';
- mTranslateKeyMap[SDLK_BACKSLASH] = '\\';
- mTranslateKeyMap[SDLK_RIGHTBRACKET] = ']';
- mTranslateKeyMap[SDLK_QUOTE] = '\'';
-
- // Build inverse map
- std::map<U16, KEY>::iterator iter;
- for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++)
- {
- mInvTranslateKeyMap[iter->second] = iter->first;
- }
-
- // numpad map
- mTranslateNumpadMap[SDLK_KP0] = KEY_PAD_INS;
- mTranslateNumpadMap[SDLK_KP1] = KEY_PAD_END;
- mTranslateNumpadMap[SDLK_KP2] = KEY_PAD_DOWN;
- mTranslateNumpadMap[SDLK_KP3] = KEY_PAD_PGDN;
- mTranslateNumpadMap[SDLK_KP4] = KEY_PAD_LEFT;
- mTranslateNumpadMap[SDLK_KP5] = KEY_PAD_CENTER;
- mTranslateNumpadMap[SDLK_KP6] = KEY_PAD_RIGHT;
- mTranslateNumpadMap[SDLK_KP7] = KEY_PAD_HOME;
- mTranslateNumpadMap[SDLK_KP8] = KEY_PAD_UP;
- mTranslateNumpadMap[SDLK_KP9] = KEY_PAD_PGUP;
- mTranslateNumpadMap[SDLK_KP_PERIOD] = KEY_PAD_DEL;
-
- // build inverse numpad map
- for (iter = mTranslateNumpadMap.begin();
- iter != mTranslateNumpadMap.end();
- iter++)
- {
- mInvTranslateNumpadMap[iter->second] = iter->first;
- }
-}
-
-void LLKeyboardSDL::resetMaskKeys()
-{
- SDLMod mask = SDL_GetModState();
-
- // MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys().
- // It looks a bit suspicious, as it won't correct for keys that have been released.
- // Is this the way it's supposed to work?
-
- if(mask & KMOD_SHIFT)
- {
- mKeyLevel[KEY_SHIFT] = true;
- }
-
- if(mask & KMOD_CTRL)
- {
- mKeyLevel[KEY_CONTROL] = true;
- }
-
- if(mask & KMOD_ALT)
- {
- mKeyLevel[KEY_ALT] = true;
- }
-}
-
-
-MASK LLKeyboardSDL::updateModifiers(const U32 mask)
-{
- // translate the mask
- MASK out_mask = MASK_NONE;
-
- if(mask & KMOD_SHIFT)
- {
- out_mask |= MASK_SHIFT;
- }
-
- if(mask & KMOD_CTRL)
- {
- out_mask |= MASK_CONTROL;
- }
-
- if(mask & KMOD_ALT)
- {
- out_mask |= MASK_ALT;
- }
-
- return out_mask;
-}
-
-
-static U16 adjustNativekeyFromUnhandledMask(const U16 key, const U32 mask)
-{
- // SDL doesn't automatically adjust the keysym according to
- // whether NUMLOCK is engaged, so we massage the keysym manually.
- U16 rtn = key;
- if (!(mask & KMOD_NUM))
- {
- switch (key)
- {
- case SDLK_KP_PERIOD: rtn = SDLK_DELETE; break;
- case SDLK_KP0: rtn = SDLK_INSERT; break;
- case SDLK_KP1: rtn = SDLK_END; break;
- case SDLK_KP2: rtn = SDLK_DOWN; break;
- case SDLK_KP3: rtn = SDLK_PAGEDOWN; break;
- case SDLK_KP4: rtn = SDLK_LEFT; break;
- case SDLK_KP6: rtn = SDLK_RIGHT; break;
- case SDLK_KP7: rtn = SDLK_HOME; break;
- case SDLK_KP8: rtn = SDLK_UP; break;
- case SDLK_KP9: rtn = SDLK_PAGEUP; break;
- }
- }
- return rtn;
-}
-
-
-bool LLKeyboardSDL::handleKeyDown(const U16 key, const U32 mask)
-{
- U16 adjusted_nativekey;
- KEY translated_key = 0;
- U32 translated_mask = MASK_NONE;
- bool handled = false;
-
- adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask);
-
- translated_mask = updateModifiers(mask);
-
- if(translateNumpadKey(adjusted_nativekey, &translated_key))
- {
- handled = handleTranslatedKeyDown(translated_key, translated_mask);
- }
-
- return handled;
-}
-
-
-bool LLKeyboardSDL::handleKeyUp(const U16 key, const U32 mask)
-{
- U16 adjusted_nativekey;
- KEY translated_key = 0;
- U32 translated_mask = MASK_NONE;
- bool handled = false;
-
- adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask);
-
- translated_mask = updateModifiers(mask);
-
- if(translateNumpadKey(adjusted_nativekey, &translated_key))
- {
- handled = handleTranslatedKeyUp(translated_key, translated_mask);
- }
-
- return handled;
-}
-
-MASK LLKeyboardSDL::currentMask(bool for_mouse_event)
-{
- MASK result = MASK_NONE;
- SDLMod mask = SDL_GetModState();
-
- if (mask & KMOD_SHIFT) result |= MASK_SHIFT;
- if (mask & KMOD_CTRL) result |= MASK_CONTROL;
- if (mask & KMOD_ALT) result |= MASK_ALT;
-
- // For keyboard events, consider Meta keys equivalent to Control
- if (!for_mouse_event)
- {
- if (mask & KMOD_META) result |= MASK_CONTROL;
- }
-
- return result;
-}
-
-void LLKeyboardSDL::scanKeyboard()
-{
- for (S32 key = 0; key < KEY_COUNT; key++)
- {
- // Generate callback if any event has occurred on this key this frame.
- // Can't just test mKeyLevel, because this could be a slow frame and
- // key might have gone down then up. JC
- if (mKeyLevel[key] || mKeyDown[key] || mKeyUp[key])
- {
- mCurScanKey = key;
- mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
- }
- }
-
- // Reset edges for next frame
- for (S32 key = 0; key < KEY_COUNT; key++)
- {
- mKeyUp[key] = false;
- mKeyDown[key] = false;
- if (mKeyLevel[key])
- {
- mKeyLevelFrameCount[key]++;
- }
- }
-}
-
-
-bool LLKeyboardSDL::translateNumpadKey( const U16 os_key, KEY *translated_key)
-{
- return translateKey(os_key, translated_key);
-}
-
-U16 LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key)
-{
- return inverseTranslateKey(translated_key);
-}
-
-#endif
-
+/** + * @file llkeyboardsdl.cpp + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if LL_SDL + +#include "linden_common.h" +#include "llkeyboardsdl.h" +#include "llwindowcallbacks.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_PAD_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] = KEY_DIVIDE; + mTranslateKeyMap[SDLK_SEMICOLON] = ';'; + mTranslateKeyMap[SDLK_LEFTBRACKET] = '['; + mTranslateKeyMap[SDLK_BACKSLASH] = '\\'; + mTranslateKeyMap[SDLK_RIGHTBRACKET] = ']'; + mTranslateKeyMap[SDLK_QUOTE] = '\''; + + // Build inverse map + std::map<U16, KEY>::iterator iter; + for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++) + { + mInvTranslateKeyMap[iter->second] = iter->first; + } + + // numpad map + mTranslateNumpadMap[SDLK_KP0] = KEY_PAD_INS; + mTranslateNumpadMap[SDLK_KP1] = KEY_PAD_END; + mTranslateNumpadMap[SDLK_KP2] = KEY_PAD_DOWN; + mTranslateNumpadMap[SDLK_KP3] = KEY_PAD_PGDN; + mTranslateNumpadMap[SDLK_KP4] = KEY_PAD_LEFT; + mTranslateNumpadMap[SDLK_KP5] = KEY_PAD_CENTER; + mTranslateNumpadMap[SDLK_KP6] = KEY_PAD_RIGHT; + mTranslateNumpadMap[SDLK_KP7] = KEY_PAD_HOME; + mTranslateNumpadMap[SDLK_KP8] = KEY_PAD_UP; + mTranslateNumpadMap[SDLK_KP9] = KEY_PAD_PGUP; + mTranslateNumpadMap[SDLK_KP_PERIOD] = KEY_PAD_DEL; + + // build inverse numpad map + for (iter = mTranslateNumpadMap.begin(); + iter != mTranslateNumpadMap.end(); + iter++) + { + mInvTranslateNumpadMap[iter->second] = iter->first; + } +} + +void LLKeyboardSDL::resetMaskKeys() +{ + SDLMod mask = SDL_GetModState(); + + // MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys(). + // It looks a bit suspicious, as it won't correct for keys that have been released. + // Is this the way it's supposed to work? + + if(mask & KMOD_SHIFT) + { + mKeyLevel[KEY_SHIFT] = true; + } + + if(mask & KMOD_CTRL) + { + mKeyLevel[KEY_CONTROL] = true; + } + + if(mask & KMOD_ALT) + { + mKeyLevel[KEY_ALT] = true; + } +} + + +MASK LLKeyboardSDL::updateModifiers(const U32 mask) +{ + // translate the mask + MASK out_mask = MASK_NONE; + + if(mask & KMOD_SHIFT) + { + out_mask |= MASK_SHIFT; + } + + if(mask & KMOD_CTRL) + { + out_mask |= MASK_CONTROL; + } + + if(mask & KMOD_ALT) + { + out_mask |= MASK_ALT; + } + + return out_mask; +} + + +static U16 adjustNativekeyFromUnhandledMask(const U16 key, const U32 mask) +{ + // SDL doesn't automatically adjust the keysym according to + // whether NUMLOCK is engaged, so we massage the keysym manually. + U16 rtn = key; + if (!(mask & KMOD_NUM)) + { + switch (key) + { + case SDLK_KP_PERIOD: rtn = SDLK_DELETE; break; + case SDLK_KP0: rtn = SDLK_INSERT; break; + case SDLK_KP1: rtn = SDLK_END; break; + case SDLK_KP2: rtn = SDLK_DOWN; break; + case SDLK_KP3: rtn = SDLK_PAGEDOWN; break; + case SDLK_KP4: rtn = SDLK_LEFT; break; + case SDLK_KP6: rtn = SDLK_RIGHT; break; + case SDLK_KP7: rtn = SDLK_HOME; break; + case SDLK_KP8: rtn = SDLK_UP; break; + case SDLK_KP9: rtn = SDLK_PAGEUP; break; + } + } + return rtn; +} + + +bool LLKeyboardSDL::handleKeyDown(const U16 key, const U32 mask) +{ + U16 adjusted_nativekey; + KEY translated_key = 0; + U32 translated_mask = MASK_NONE; + bool handled = false; + + adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask); + + translated_mask = updateModifiers(mask); + + if(translateNumpadKey(adjusted_nativekey, &translated_key)) + { + handled = handleTranslatedKeyDown(translated_key, translated_mask); + } + + return handled; +} + + +bool LLKeyboardSDL::handleKeyUp(const U16 key, const U32 mask) +{ + U16 adjusted_nativekey; + KEY translated_key = 0; + U32 translated_mask = MASK_NONE; + bool handled = false; + + adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask); + + translated_mask = updateModifiers(mask); + + if(translateNumpadKey(adjusted_nativekey, &translated_key)) + { + handled = handleTranslatedKeyUp(translated_key, translated_mask); + } + + return handled; +} + +MASK LLKeyboardSDL::currentMask(bool for_mouse_event) +{ + MASK result = MASK_NONE; + SDLMod mask = SDL_GetModState(); + + if (mask & KMOD_SHIFT) result |= MASK_SHIFT; + if (mask & KMOD_CTRL) result |= MASK_CONTROL; + if (mask & KMOD_ALT) result |= MASK_ALT; + + // For keyboard events, consider Meta keys equivalent to Control + if (!for_mouse_event) + { + if (mask & KMOD_META) result |= MASK_CONTROL; + } + + return result; +} + +void LLKeyboardSDL::scanKeyboard() +{ + for (S32 key = 0; key < KEY_COUNT; key++) + { + // Generate callback if any event has occurred on this key this frame. + // Can't just test mKeyLevel, because this could be a slow frame and + // key might have gone down then up. JC + if (mKeyLevel[key] || mKeyDown[key] || mKeyUp[key]) + { + mCurScanKey = key; + mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]); + } + } + + // Reset edges for next frame + for (S32 key = 0; key < KEY_COUNT; key++) + { + mKeyUp[key] = false; + mKeyDown[key] = false; + if (mKeyLevel[key]) + { + mKeyLevelFrameCount[key]++; + } + } +} + + +bool LLKeyboardSDL::translateNumpadKey( const U16 os_key, KEY *translated_key) +{ + return translateKey(os_key, translated_key); +} + +U16 LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key) +{ + return inverseTranslateKey(translated_key); +} + +#endif + diff --git a/indra/llwindow/llkeyboardsdl.h b/indra/llwindow/llkeyboardsdl.h index f1c069c242..fd348b28f2 100644 --- a/indra/llwindow/llkeyboardsdl.h +++ b/indra/llwindow/llkeyboardsdl.h @@ -1,55 +1,55 @@ -/**
- * @file llkeyboardsdl.h
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLKEYBOARDSDL_H
-#define LL_LLKEYBOARDSDL_H
-
-#include "llkeyboard.h"
-#include "SDL/SDL.h"
-
-class LLKeyboardSDL : public LLKeyboard
-{
-public:
- LLKeyboardSDL();
- /*virtual*/ ~LLKeyboardSDL() {};
-
- /*virtual*/ bool handleKeyUp(const U16 key, MASK mask);
- /*virtual*/ bool handleKeyDown(const U16 key, MASK mask);
- /*virtual*/ void resetMaskKeys();
- /*virtual*/ MASK currentMask(bool for_mouse_event);
- /*virtual*/ void scanKeyboard();
-
-protected:
- MASK updateModifiers(const U32 mask);
- void setModifierKeyLevel( KEY key, bool new_state );
- bool translateNumpadKey( const U16 os_key, KEY *translated_key );
- U16 inverseTranslateNumpadKey(const KEY translated_key);
-private:
- std::map<U16, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys
- std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above
-};
-
-#endif
+/** + * @file llkeyboardsdl.h + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLKEYBOARDSDL_H +#define LL_LLKEYBOARDSDL_H + +#include "llkeyboard.h" +#include "SDL/SDL.h" + +class LLKeyboardSDL : public LLKeyboard +{ +public: + LLKeyboardSDL(); + /*virtual*/ ~LLKeyboardSDL() {}; + + /*virtual*/ bool handleKeyUp(const U16 key, MASK mask); + /*virtual*/ bool handleKeyDown(const U16 key, MASK mask); + /*virtual*/ void resetMaskKeys(); + /*virtual*/ MASK currentMask(bool for_mouse_event); + /*virtual*/ void scanKeyboard(); + +protected: + MASK updateModifiers(const U32 mask); + void setModifierKeyLevel( KEY key, bool new_state ); + bool translateNumpadKey( const U16 os_key, KEY *translated_key ); + U16 inverseTranslateNumpadKey(const KEY translated_key); +private: + std::map<U16, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys + std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above +}; + +#endif diff --git a/indra/llwindow/llkeyboardwin32.cpp b/indra/llwindow/llkeyboardwin32.cpp index 510d680941..ec0055fe60 100644 --- a/indra/llwindow/llkeyboardwin32.cpp +++ b/indra/llwindow/llkeyboardwin32.cpp @@ -1,324 +1,324 @@ -/**
- * @file llkeyboardwin32.cpp
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#if LL_WINDOWS
-
-#include "linden_common.h"
-
-#include "llwin32headerslean.h"
-#include "llkeyboardwin32.h"
-
-#include "llwindowcallbacks.h"
-
-
-
-LLKeyboardWin32::LLKeyboardWin32()
-{
- // Set up key mapping for windows - eventually can read this from a file?
- // Anything not in the key map gets dropped
- // Add default A-Z
-
- // Virtual key mappings from WinUser.h
-
- KEY cur_char;
- for (cur_char = 'A'; cur_char <= 'Z'; cur_char++)
- {
- mTranslateKeyMap[cur_char] = (KEY)cur_char;
- }
-
- for (cur_char = '0'; cur_char <= '9'; cur_char++)
- {
- mTranslateKeyMap[cur_char] = (KEY)cur_char;
- }
- // numpad number keys
- for (cur_char = 0x60; cur_char <= 0x69; cur_char++)
- {
- mTranslateKeyMap[cur_char] = (KEY)('0' + (cur_char - 0x60));
- }
-
-
- 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] = '/';//This used to be KEY_PAD_DIVIDE, but that breaks typing into text fields in media prims
- mTranslateKeyMap[VK_OEM_3] = '`';
- mTranslateKeyMap[VK_OEM_4] = '[';
- mTranslateKeyMap[VK_OEM_5] = '\\';
- mTranslateKeyMap[VK_OEM_6] = ']';
- mTranslateKeyMap[VK_OEM_7] = '\'';
- mTranslateKeyMap[VK_ESCAPE] = KEY_ESCAPE;
- mTranslateKeyMap[VK_RETURN] = KEY_RETURN;
- mTranslateKeyMap[VK_LEFT] = KEY_LEFT;
- mTranslateKeyMap[VK_RIGHT] = KEY_RIGHT;
- mTranslateKeyMap[VK_UP] = KEY_UP;
- mTranslateKeyMap[VK_DOWN] = KEY_DOWN;
- mTranslateKeyMap[VK_BACK] = KEY_BACKSPACE;
- mTranslateKeyMap[VK_INSERT] = KEY_INSERT;
- mTranslateKeyMap[VK_DELETE] = KEY_DELETE;
- mTranslateKeyMap[VK_SHIFT] = KEY_SHIFT;
- mTranslateKeyMap[VK_CONTROL] = KEY_CONTROL;
- mTranslateKeyMap[VK_MENU] = KEY_ALT;
- mTranslateKeyMap[VK_CAPITAL] = KEY_CAPSLOCK;
- mTranslateKeyMap[VK_HOME] = KEY_HOME;
- mTranslateKeyMap[VK_END] = KEY_END;
- mTranslateKeyMap[VK_PRIOR] = KEY_PAGE_UP;
- mTranslateKeyMap[VK_NEXT] = KEY_PAGE_DOWN;
- mTranslateKeyMap[VK_TAB] = KEY_TAB;
- mTranslateKeyMap[VK_ADD] = KEY_ADD;
- mTranslateKeyMap[VK_SUBTRACT] = KEY_SUBTRACT;
- mTranslateKeyMap[VK_MULTIPLY] = KEY_MULTIPLY;
- mTranslateKeyMap[VK_DIVIDE] = KEY_DIVIDE;
- mTranslateKeyMap[VK_F1] = KEY_F1;
- mTranslateKeyMap[VK_F2] = KEY_F2;
- mTranslateKeyMap[VK_F3] = KEY_F3;
- mTranslateKeyMap[VK_F4] = KEY_F4;
- mTranslateKeyMap[VK_F5] = KEY_F5;
- mTranslateKeyMap[VK_F6] = KEY_F6;
- mTranslateKeyMap[VK_F7] = KEY_F7;
- mTranslateKeyMap[VK_F8] = KEY_F8;
- mTranslateKeyMap[VK_F9] = KEY_F9;
- mTranslateKeyMap[VK_F10] = KEY_F10;
- mTranslateKeyMap[VK_F11] = KEY_F11;
- mTranslateKeyMap[VK_F12] = KEY_F12;
- mTranslateKeyMap[VK_CLEAR] = KEY_PAD_CENTER;
-
- // Build inverse map
- std::map<U16, KEY>::iterator iter;
- for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++)
- {
- mInvTranslateKeyMap[iter->second] = iter->first;
- }
-
- // numpad map
- mTranslateNumpadMap[0x60] = KEY_PAD_INS; // keypad 0
- mTranslateNumpadMap[0x61] = KEY_PAD_END; // keypad 1
- mTranslateNumpadMap[0x62] = KEY_PAD_DOWN; // keypad 2
- mTranslateNumpadMap[0x63] = KEY_PAD_PGDN; // keypad 3
- mTranslateNumpadMap[0x64] = KEY_PAD_LEFT; // keypad 4
- mTranslateNumpadMap[0x65] = KEY_PAD_CENTER; // keypad 5
- mTranslateNumpadMap[0x66] = KEY_PAD_RIGHT; // keypad 6
- mTranslateNumpadMap[0x67] = KEY_PAD_HOME; // keypad 7
- mTranslateNumpadMap[0x68] = KEY_PAD_UP; // keypad 8
- mTranslateNumpadMap[0x69] = KEY_PAD_PGUP; // keypad 9
- mTranslateNumpadMap[0x6A] = KEY_PAD_MULTIPLY; // keypad *
- mTranslateNumpadMap[0x6B] = KEY_PAD_ADD; // keypad +
- mTranslateNumpadMap[0x6D] = KEY_PAD_SUBTRACT; // keypad -
- mTranslateNumpadMap[0x6E] = KEY_PAD_DEL; // keypad .
- mTranslateNumpadMap[0x6F] = KEY_PAD_DIVIDE; // 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)
- mKeyLevel[KEY_CAPSLOCK] = (GetKeyState(VK_CAPITAL) & 0x0001) != 0; // Low order bit carries the toggle state.
- // Get mask for keyboard events
- MASK mask = currentMask(false);
- return mask;
-}
-
-
-// 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;
- MSG msg;
- PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD);
- 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 LLKeyboardWin32::translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key)
-{
- return translateKey(os_key, translated_key);
-}
-
-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(GetKeyState(VK_NUMLOCK) & 1)
- {
- std::map<KEY, U16>::iterator iter = mInvTranslateNumpadMap.find(translated_key);
- if (iter != mInvTranslateNumpadMap.end())
- {
- return iter->second;
- }
- }
-
- // if numlock is off or we're not converting numbers to arrows, we map our keypad arrows
- // to regular arrows since Windows doesn't distinguish between them
- KEY converted_key = translated_key;
- switch (converted_key)
- {
- case KEY_PAD_LEFT:
- converted_key = KEY_LEFT; break;
- case KEY_PAD_RIGHT:
- converted_key = KEY_RIGHT; break;
- case KEY_PAD_UP:
- converted_key = KEY_UP; break;
- case KEY_PAD_DOWN:
- converted_key = KEY_DOWN; break;
- case KEY_PAD_HOME:
- converted_key = KEY_HOME; break;
- case KEY_PAD_END:
- converted_key = KEY_END; break;
- case KEY_PAD_PGUP:
- converted_key = KEY_PAGE_UP; break;
- case KEY_PAD_PGDN:
- converted_key = KEY_PAGE_DOWN; break;
- case KEY_PAD_INS:
- converted_key = KEY_INSERT; break;
- case KEY_PAD_DEL:
- converted_key = KEY_DELETE; break;
- case KEY_PAD_RETURN:
- converted_key = KEY_RETURN; break;
- }
- // convert our virtual keys to OS keys
- return inverseTranslateKey(converted_key);
-}
-
-#endif
+/** + * @file llkeyboardwin32.cpp + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if LL_WINDOWS + +#include "linden_common.h" + +#include "llwin32headerslean.h" +#include "llkeyboardwin32.h" + +#include "llwindowcallbacks.h" + + + +LLKeyboardWin32::LLKeyboardWin32() +{ + // Set up key mapping for windows - eventually can read this from a file? + // Anything not in the key map gets dropped + // Add default A-Z + + // Virtual key mappings from WinUser.h + + KEY cur_char; + for (cur_char = 'A'; cur_char <= 'Z'; cur_char++) + { + mTranslateKeyMap[cur_char] = (KEY)cur_char; + } + + for (cur_char = '0'; cur_char <= '9'; cur_char++) + { + mTranslateKeyMap[cur_char] = (KEY)cur_char; + } + // numpad number keys + for (cur_char = 0x60; cur_char <= 0x69; cur_char++) + { + mTranslateKeyMap[cur_char] = (KEY)('0' + (cur_char - 0x60)); + } + + + 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] = '/';//This used to be KEY_PAD_DIVIDE, but that breaks typing into text fields in media prims + mTranslateKeyMap[VK_OEM_3] = '`'; + mTranslateKeyMap[VK_OEM_4] = '['; + mTranslateKeyMap[VK_OEM_5] = '\\'; + mTranslateKeyMap[VK_OEM_6] = ']'; + mTranslateKeyMap[VK_OEM_7] = '\''; + mTranslateKeyMap[VK_ESCAPE] = KEY_ESCAPE; + mTranslateKeyMap[VK_RETURN] = KEY_RETURN; + mTranslateKeyMap[VK_LEFT] = KEY_LEFT; + mTranslateKeyMap[VK_RIGHT] = KEY_RIGHT; + mTranslateKeyMap[VK_UP] = KEY_UP; + mTranslateKeyMap[VK_DOWN] = KEY_DOWN; + mTranslateKeyMap[VK_BACK] = KEY_BACKSPACE; + mTranslateKeyMap[VK_INSERT] = KEY_INSERT; + mTranslateKeyMap[VK_DELETE] = KEY_DELETE; + mTranslateKeyMap[VK_SHIFT] = KEY_SHIFT; + mTranslateKeyMap[VK_CONTROL] = KEY_CONTROL; + mTranslateKeyMap[VK_MENU] = KEY_ALT; + mTranslateKeyMap[VK_CAPITAL] = KEY_CAPSLOCK; + mTranslateKeyMap[VK_HOME] = KEY_HOME; + mTranslateKeyMap[VK_END] = KEY_END; + mTranslateKeyMap[VK_PRIOR] = KEY_PAGE_UP; + mTranslateKeyMap[VK_NEXT] = KEY_PAGE_DOWN; + mTranslateKeyMap[VK_TAB] = KEY_TAB; + mTranslateKeyMap[VK_ADD] = KEY_ADD; + mTranslateKeyMap[VK_SUBTRACT] = KEY_SUBTRACT; + mTranslateKeyMap[VK_MULTIPLY] = KEY_MULTIPLY; + mTranslateKeyMap[VK_DIVIDE] = KEY_DIVIDE; + mTranslateKeyMap[VK_F1] = KEY_F1; + mTranslateKeyMap[VK_F2] = KEY_F2; + mTranslateKeyMap[VK_F3] = KEY_F3; + mTranslateKeyMap[VK_F4] = KEY_F4; + mTranslateKeyMap[VK_F5] = KEY_F5; + mTranslateKeyMap[VK_F6] = KEY_F6; + mTranslateKeyMap[VK_F7] = KEY_F7; + mTranslateKeyMap[VK_F8] = KEY_F8; + mTranslateKeyMap[VK_F9] = KEY_F9; + mTranslateKeyMap[VK_F10] = KEY_F10; + mTranslateKeyMap[VK_F11] = KEY_F11; + mTranslateKeyMap[VK_F12] = KEY_F12; + mTranslateKeyMap[VK_CLEAR] = KEY_PAD_CENTER; + + // Build inverse map + std::map<U16, KEY>::iterator iter; + for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++) + { + mInvTranslateKeyMap[iter->second] = iter->first; + } + + // numpad map + mTranslateNumpadMap[0x60] = KEY_PAD_INS; // keypad 0 + mTranslateNumpadMap[0x61] = KEY_PAD_END; // keypad 1 + mTranslateNumpadMap[0x62] = KEY_PAD_DOWN; // keypad 2 + mTranslateNumpadMap[0x63] = KEY_PAD_PGDN; // keypad 3 + mTranslateNumpadMap[0x64] = KEY_PAD_LEFT; // keypad 4 + mTranslateNumpadMap[0x65] = KEY_PAD_CENTER; // keypad 5 + mTranslateNumpadMap[0x66] = KEY_PAD_RIGHT; // keypad 6 + mTranslateNumpadMap[0x67] = KEY_PAD_HOME; // keypad 7 + mTranslateNumpadMap[0x68] = KEY_PAD_UP; // keypad 8 + mTranslateNumpadMap[0x69] = KEY_PAD_PGUP; // keypad 9 + mTranslateNumpadMap[0x6A] = KEY_PAD_MULTIPLY; // keypad * + mTranslateNumpadMap[0x6B] = KEY_PAD_ADD; // keypad + + mTranslateNumpadMap[0x6D] = KEY_PAD_SUBTRACT; // keypad - + mTranslateNumpadMap[0x6E] = KEY_PAD_DEL; // keypad . + mTranslateNumpadMap[0x6F] = KEY_PAD_DIVIDE; // 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) + mKeyLevel[KEY_CAPSLOCK] = (GetKeyState(VK_CAPITAL) & 0x0001) != 0; // Low order bit carries the toggle state. + // Get mask for keyboard events + MASK mask = currentMask(false); + return mask; +} + + +// 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; + MSG msg; + PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD); + 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 LLKeyboardWin32::translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key) +{ + return translateKey(os_key, translated_key); +} + +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(GetKeyState(VK_NUMLOCK) & 1) + { + std::map<KEY, U16>::iterator iter = mInvTranslateNumpadMap.find(translated_key); + if (iter != mInvTranslateNumpadMap.end()) + { + return iter->second; + } + } + + // if numlock is off or we're not converting numbers to arrows, we map our keypad arrows + // to regular arrows since Windows doesn't distinguish between them + KEY converted_key = translated_key; + switch (converted_key) + { + case KEY_PAD_LEFT: + converted_key = KEY_LEFT; break; + case KEY_PAD_RIGHT: + converted_key = KEY_RIGHT; break; + case KEY_PAD_UP: + converted_key = KEY_UP; break; + case KEY_PAD_DOWN: + converted_key = KEY_DOWN; break; + case KEY_PAD_HOME: + converted_key = KEY_HOME; break; + case KEY_PAD_END: + converted_key = KEY_END; break; + case KEY_PAD_PGUP: + converted_key = KEY_PAGE_UP; break; + case KEY_PAD_PGDN: + converted_key = KEY_PAGE_DOWN; break; + case KEY_PAD_INS: + converted_key = KEY_INSERT; break; + case KEY_PAD_DEL: + converted_key = KEY_DELETE; break; + case KEY_PAD_RETURN: + converted_key = KEY_RETURN; break; + } + // convert our virtual keys to OS keys + return inverseTranslateKey(converted_key); +} + +#endif diff --git a/indra/llwindow/llkeyboardwin32.h b/indra/llwindow/llkeyboardwin32.h index 7788af8011..d0dfc5cfdd 100644 --- a/indra/llwindow/llkeyboardwin32.h +++ b/indra/llwindow/llkeyboardwin32.h @@ -1,58 +1,58 @@ -/**
- * @file llkeyboardwin32.h
- * @brief Handler for assignable key bindings
- *
- * $LicenseInfo:firstyear=2004&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLKEYBOARDWIN32_H
-#define LL_LLKEYBOARDWIN32_H
-
-#include "llkeyboard.h"
-
-// this mask distinguishes extended keys, which include non-numpad arrow keys
-// (and, curiously, the num lock and numpad '/')
-const MASK MASK_EXTENDED = 0x0100;
-
-class LLKeyboardWin32 : public LLKeyboard
-{
-public:
- LLKeyboardWin32();
- /*virtual*/ ~LLKeyboardWin32() {};
-
- /*virtual*/ bool handleKeyUp(const U16 key, MASK mask);
- /*virtual*/ bool handleKeyDown(const U16 key, MASK mask);
- /*virtual*/ void resetMaskKeys();
- /*virtual*/ MASK currentMask(bool for_mouse_event);
- /*virtual*/ void scanKeyboard();
- bool translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key);
- U16 inverseTranslateExtendedKey(const KEY translated_key);
-
-protected:
- MASK updateModifiers();
- //void setModifierKeyLevel( KEY key, bool new_state );
-private:
- std::map<U16, KEY> mTranslateNumpadMap;
- std::map<KEY, U16> mInvTranslateNumpadMap;
-};
-
-#endif
+/** + * @file llkeyboardwin32.h + * @brief Handler for assignable key bindings + * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLKEYBOARDWIN32_H +#define LL_LLKEYBOARDWIN32_H + +#include "llkeyboard.h" + +// this mask distinguishes extended keys, which include non-numpad arrow keys +// (and, curiously, the num lock and numpad '/') +const MASK MASK_EXTENDED = 0x0100; + +class LLKeyboardWin32 : public LLKeyboard +{ +public: + LLKeyboardWin32(); + /*virtual*/ ~LLKeyboardWin32() {}; + + /*virtual*/ bool handleKeyUp(const U16 key, MASK mask); + /*virtual*/ bool handleKeyDown(const U16 key, MASK mask); + /*virtual*/ void resetMaskKeys(); + /*virtual*/ MASK currentMask(bool for_mouse_event); + /*virtual*/ void scanKeyboard(); + bool translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key); + U16 inverseTranslateExtendedKey(const KEY translated_key); + +protected: + MASK updateModifiers(); + //void setModifierKeyLevel( KEY key, bool new_state ); +private: + std::map<U16, KEY> mTranslateNumpadMap; + std::map<KEY, U16> mInvTranslateNumpadMap; +}; + +#endif diff --git a/indra/llwindow/llmousehandler.cpp b/indra/llwindow/llmousehandler.cpp index 6498d42642..eeceab502a 100644 --- a/indra/llwindow/llmousehandler.cpp +++ b/indra/llwindow/llmousehandler.cpp @@ -1,66 +1,66 @@ -/**
- * @file llmousehandler.cpp
- * @brief LLMouseHandler class implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "llmousehandler.h"
-
-//virtual
-bool LLMouseHandler::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, bool down)
-{
- bool handled = false;
- if (down)
- {
- switch (clicktype)
- {
- case CLICK_LEFT: handled = handleMouseDown(x, y, mask); break;
- case CLICK_RIGHT: handled = handleRightMouseDown(x, y, mask); break;
- case CLICK_MIDDLE: handled = handleMiddleMouseDown(x, y, mask); break;
- case CLICK_DOUBLELEFT: handled = handleDoubleClick(x, y, mask); break;
- case CLICK_BUTTON4:
- case CLICK_BUTTON5:
- LL_INFOS() << "Handle mouse button " << clicktype + 1 << " down." << LL_ENDL;
- break;
- default:
- LL_WARNS() << "Unhandled enum." << LL_ENDL;
- }
- }
- else
- {
- switch (clicktype)
- {
- case CLICK_LEFT: handled = handleMouseUp(x, y, mask); break;
- case CLICK_RIGHT: handled = handleRightMouseUp(x, y, mask); break;
- case CLICK_MIDDLE: handled = handleMiddleMouseUp(x, y, mask); break;
- case CLICK_DOUBLELEFT: handled = handleDoubleClick(x, y, mask); break;
- case CLICK_BUTTON4:
- case CLICK_BUTTON5:
- LL_INFOS() << "Handle mouse button " << clicktype + 1 << " up." << LL_ENDL;
- break;
- default:
- LL_WARNS() << "Unhandled enum." << LL_ENDL;
- }
- }
- return handled;
-}
+/** + * @file llmousehandler.cpp + * @brief LLMouseHandler class implementation + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llmousehandler.h" + +//virtual +bool LLMouseHandler::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, bool down) +{ + bool handled = false; + if (down) + { + switch (clicktype) + { + case CLICK_LEFT: handled = handleMouseDown(x, y, mask); break; + case CLICK_RIGHT: handled = handleRightMouseDown(x, y, mask); break; + case CLICK_MIDDLE: handled = handleMiddleMouseDown(x, y, mask); break; + case CLICK_DOUBLELEFT: handled = handleDoubleClick(x, y, mask); break; + case CLICK_BUTTON4: + case CLICK_BUTTON5: + LL_INFOS() << "Handle mouse button " << clicktype + 1 << " down." << LL_ENDL; + break; + default: + LL_WARNS() << "Unhandled enum." << LL_ENDL; + } + } + else + { + switch (clicktype) + { + case CLICK_LEFT: handled = handleMouseUp(x, y, mask); break; + case CLICK_RIGHT: handled = handleRightMouseUp(x, y, mask); break; + case CLICK_MIDDLE: handled = handleMiddleMouseUp(x, y, mask); break; + case CLICK_DOUBLELEFT: handled = handleDoubleClick(x, y, mask); break; + case CLICK_BUTTON4: + case CLICK_BUTTON5: + LL_INFOS() << "Handle mouse button " << clicktype + 1 << " up." << LL_ENDL; + break; + default: + LL_WARNS() << "Unhandled enum." << LL_ENDL; + } + } + return handled; +} diff --git a/indra/llwindow/llmousehandler.h b/indra/llwindow/llmousehandler.h index fea750fa91..565d7bed70 100644 --- a/indra/llwindow/llmousehandler.h +++ b/indra/llwindow/llmousehandler.h @@ -1,73 +1,73 @@ -/**
- * @file llmousehandler.h
- * @brief LLMouseHandler class definition
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_MOUSEHANDLER_H
-#define LL_MOUSEHANDLER_H
-
-#include "linden_common.h"
-#include "llrect.h"
-#include "indra_constants.h"
-
-// Mostly-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() {}
-
- typedef enum {
- SHOW_NEVER,
- SHOW_IF_NOT_BLOCKED,
- SHOW_ALWAYS,
- } EShowToolTip;
-
- virtual bool handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, bool down);
- virtual bool handleMouseDown(S32 x, S32 y, MASK mask) = 0;
- virtual bool handleMouseUp(S32 x, S32 y, MASK mask) = 0;
- virtual bool handleMiddleMouseDown(S32 x, S32 y, MASK mask) = 0;
- virtual bool handleMiddleMouseUp(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 handleDoubleClick(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 handleScrollHWheel(S32 x, S32 y, S32 clicks) = 0;
- virtual bool handleToolTip(S32 x, S32 y, MASK mask) = 0;
- virtual const std::string& getName() const = 0;
-
- virtual void onMouseCaptureLost() = 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;
-
- virtual bool hasMouseCapture() = 0;
-};
-
-#endif
+/** + * @file llmousehandler.h + * @brief LLMouseHandler class definition + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_MOUSEHANDLER_H +#define LL_MOUSEHANDLER_H + +#include "linden_common.h" +#include "llrect.h" +#include "indra_constants.h" + +// Mostly-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() {} + + typedef enum { + SHOW_NEVER, + SHOW_IF_NOT_BLOCKED, + SHOW_ALWAYS, + } EShowToolTip; + + virtual bool handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, bool down); + virtual bool handleMouseDown(S32 x, S32 y, MASK mask) = 0; + virtual bool handleMouseUp(S32 x, S32 y, MASK mask) = 0; + virtual bool handleMiddleMouseDown(S32 x, S32 y, MASK mask) = 0; + virtual bool handleMiddleMouseUp(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 handleDoubleClick(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 handleScrollHWheel(S32 x, S32 y, S32 clicks) = 0; + virtual bool handleToolTip(S32 x, S32 y, MASK mask) = 0; + virtual const std::string& getName() const = 0; + + virtual void onMouseCaptureLost() = 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; + + virtual bool hasMouseCapture() = 0; +}; + +#endif diff --git a/indra/llwindow/llpreeditor.h b/indra/llwindow/llpreeditor.h index 915957e63a..492a3e9e88 100644 --- a/indra/llwindow/llpreeditor.h +++ b/indra/llwindow/llpreeditor.h @@ -1,101 +1,101 @@ -/**
- * @file llpreeditor.h
- * @brief I believe this is used for languages like Japanese that require
- * an "input method editor" to type Kanji.
- * @author Open source patch, incorporated by Dave Simmons
- *
- * $LicenseInfo:firstyear=2007&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_PREEDITOR
-#define LL_PREEDITOR
-
-class LLPreeditor
-{
-public:
-
- typedef std::vector<S32> segment_lengths_t;
- typedef std::deque<bool> standouts_t;
-
- // We don't delete against LLPreeditor, but compilers complain without this...
-
- virtual ~LLPreeditor() {};
-
- // Discard any preedit info. on this preeditor.
-
- virtual void resetPreedit() = 0;
-
- // Update the preedit feedback using specified details.
- // Existing preedit is discarded and replaced with the new one. (I.e., updatePreedit is not cumulative.)
- // All arguments are IN.
- // preedit_count is the number of elements in arrays preedit_list and preedit_standouts.
- // preedit list is an array of preedit texts (clauses.)
- // preedit_standouts indicates whether each preedit text should be shown as standout clause.
- // caret_position is the preedit-local position of text editing caret, in # of llwchar.
-
- virtual void updatePreedit(const LLWString &preedit_string,
- const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) = 0;
-
- // Turn the specified sub-contents into an active preedit.
- // Both position and length are IN and count with UTF-32 (llwchar) characters.
- // This method primarily facilitates reconversion.
-
- virtual void markAsPreedit(S32 position, S32 length) = 0;
-
- // Get the position and the length of the active preedit in the contents.
- // Both position and length are OUT and count with UTF-32 (llwchar) characters.
- // When this preeditor has no active preedit, position receives
- // the caret position, and length receives 0.
-
- virtual void getPreeditRange(S32 *position, S32 *length) const = 0;
-
- // Get the position and the length of the current selection in the contents.
- // Both position and length are OUT and count with UTF-32 (llwchar) characters.
- // When this preeditor has no selection, position receives
- // the caret position, and length receives 0.
-
- virtual void getSelectionRange(S32 *position, S32 *length) const = 0;
-
- // Get the locations where the preedit and related UI elements are displayed.
- // Locations are relative to the app window and measured in GL coordinate space (before scaling.)
- // query_position is IN argument, and other three are OUT.
-
- virtual bool getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const = 0;
-
- // Get the size (height) of the current font used in this preeditor.
-
- virtual S32 getPreeditFontSize() const = 0;
-
- // Get the contents of this preeditor as a LLWString. If there is an active preedit,
- // the returned LLWString contains it.
-
- virtual LLWString getPreeditString() const = 0;
-
- // Handle a UTF-32 char on this preeditor, i.e., add the character
- // to the contents.
- // This is a back door of the method of same name of LLWindowCallback.
- // called_from_parent should be set to false if calling through LLPreeditor.
-
- virtual bool handleUnicodeCharHere(llwchar uni_char) = 0;
-};
-
-#endif
+/** + * @file llpreeditor.h + * @brief I believe this is used for languages like Japanese that require + * an "input method editor" to type Kanji. + * @author Open source patch, incorporated by Dave Simmons + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_PREEDITOR +#define LL_PREEDITOR + +class LLPreeditor +{ +public: + + typedef std::vector<S32> segment_lengths_t; + typedef std::deque<bool> standouts_t; + + // We don't delete against LLPreeditor, but compilers complain without this... + + virtual ~LLPreeditor() {}; + + // Discard any preedit info. on this preeditor. + + virtual void resetPreedit() = 0; + + // Update the preedit feedback using specified details. + // Existing preedit is discarded and replaced with the new one. (I.e., updatePreedit is not cumulative.) + // All arguments are IN. + // preedit_count is the number of elements in arrays preedit_list and preedit_standouts. + // preedit list is an array of preedit texts (clauses.) + // preedit_standouts indicates whether each preedit text should be shown as standout clause. + // caret_position is the preedit-local position of text editing caret, in # of llwchar. + + virtual void updatePreedit(const LLWString &preedit_string, + const segment_lengths_t &preedit_segment_lengths, const standouts_t &preedit_standouts, S32 caret_position) = 0; + + // Turn the specified sub-contents into an active preedit. + // Both position and length are IN and count with UTF-32 (llwchar) characters. + // This method primarily facilitates reconversion. + + virtual void markAsPreedit(S32 position, S32 length) = 0; + + // Get the position and the length of the active preedit in the contents. + // Both position and length are OUT and count with UTF-32 (llwchar) characters. + // When this preeditor has no active preedit, position receives + // the caret position, and length receives 0. + + virtual void getPreeditRange(S32 *position, S32 *length) const = 0; + + // Get the position and the length of the current selection in the contents. + // Both position and length are OUT and count with UTF-32 (llwchar) characters. + // When this preeditor has no selection, position receives + // the caret position, and length receives 0. + + virtual void getSelectionRange(S32 *position, S32 *length) const = 0; + + // Get the locations where the preedit and related UI elements are displayed. + // Locations are relative to the app window and measured in GL coordinate space (before scaling.) + // query_position is IN argument, and other three are OUT. + + virtual bool getPreeditLocation(S32 query_position, LLCoordGL *coord, LLRect *bounds, LLRect *control) const = 0; + + // Get the size (height) of the current font used in this preeditor. + + virtual S32 getPreeditFontSize() const = 0; + + // Get the contents of this preeditor as a LLWString. If there is an active preedit, + // the returned LLWString contains it. + + virtual LLWString getPreeditString() const = 0; + + // Handle a UTF-32 char on this preeditor, i.e., add the character + // to the contents. + // This is a back door of the method of same name of LLWindowCallback. + // called_from_parent should be set to false if calling through LLPreeditor. + + virtual bool handleUnicodeCharHere(llwchar uni_char) = 0; +}; + +#endif diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index e8c1301230..964b173e74 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -1,510 +1,510 @@ -/**
- * @file llwindow.cpp
- * @brief Basic graphical window class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "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"
-#endif
-
-#include "llerror.h"
-#include "llkeyboard.h"
-#include "llwindowcallbacks.h"
-
-
-//
-// Globals
-//
-LLSplashScreen *gSplashScreenp = NULL;
-bool gDebugClicks = false;
-bool gDebugWindowProc = false;
-
-const S32 gURLProtocolWhitelistCount = 5;
-const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:", "mailto:" };
-
-// 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
-// Maestro: This list isn't referenced anywhere that I could find
-//const std::string gURLProtocolWhitelistHandler[] = { "http", "http", "https" };
-
-
-S32 OSMessageBox(const std::string& text, const std::string& 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: (?)
- LL_WARNS() << "OSMessageBox: " << text << LL_ENDL;
- 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(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags)
- : mCallbacks(callbacks),
- mPostQuit(true),
- mFullscreen(fullscreen),
- mFullscreenWidth(0),
- mFullscreenHeight(0),
- mFullscreenBits(0),
- mFullscreenRefresh(0),
- mSupportedResolutions(NULL),
- mNumSupportedResolutions(0),
- mCurrentCursor(UI_CURSOR_ARROW),
- mNextCursor(UI_CURSOR_ARROW),
- mCursorHidden(false),
- mBusyCount(0),
- mIsMouseClipping(false),
- mMinWindowWidth(0),
- mMinWindowHeight(0),
- mSwapMethod(SWAP_METHOD_UNDEFINED),
- mHideCursorPermanent(false),
- mFlags(flags),
- mHighSurrogate(0),
- mRefreshRate(0)
-{
-}
-
-LLWindow::~LLWindow()
-{
-}
-
-//virtual
-bool LLWindow::isValid()
-{
- return true;
-}
-
-//virtual
-bool LLWindow::canDelete()
-{
- return true;
-}
-
-//virtual
-void LLWindow::setTitle(const std::string title)
-{
- // the action happens in the platform specific impl
-}
-
-// virtual
-void LLWindow::incBusyCount()
-{
- ++mBusyCount;
-}
-
-// virtual
-void LLWindow::decBusyCount()
-{
- if (mBusyCount > 0)
- {
- --mBusyCount;
- }
-}
-
-//virtual
-void LLWindow::resetBusyCount()
-{
- mBusyCount = 0;
-}
-
-//virtual
-S32 LLWindow::getBusyCount() const
-{
- return mBusyCount;
-}
-
-//virtual
-ECursorType LLWindow::getCursor() const
-{
- return mCurrentCursor;
-}
-
-//virtual
-bool LLWindow::dialogColorPicker(F32 *r, F32 *g, F32 *b)
-{
- return false;
-}
-
-void *LLWindow::getMediaWindow()
-{
- // Default to returning the platform window.
- return getPlatformWindow();
-}
-
-bool LLWindow::setSize(LLCoordScreen size)
-{
- if (!getMaximized())
- {
- size.mX = llmax(size.mX, mMinWindowWidth);
- size.mY = llmax(size.mY, mMinWindowHeight);
- }
- return setSizeImpl(size);
-}
-
-bool LLWindow::setSize(LLCoordWindow size)
-{
- //HACK: we are inconsistently using minimum window dimensions
- // in this case, we are constraining the inner "client" rect and other times
- // we constrain the outer "window" rect
- // There doesn't seem to be a good way to do this consistently without a bunch of platform
- // specific code
- if (!getMaximized())
- {
- size.mX = llmax(size.mX, mMinWindowWidth);
- size.mY = llmax(size.mY, mMinWindowHeight);
- }
- return setSizeImpl(size);
-}
-
-
-// virtual
-void LLWindow::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately)
-{
- mMinWindowWidth = min_width;
- mMinWindowHeight = min_height;
-
- if (enforce_immediately)
- {
- LLCoordScreen cur_size;
- if (!getMaximized() && getSize(&cur_size))
- {
- if (cur_size.mX < mMinWindowWidth || cur_size.mY < mMinWindowHeight)
- {
- setSizeImpl(LLCoordScreen(llmin(cur_size.mX, mMinWindowWidth), llmin(cur_size.mY, mMinWindowHeight)));
- }
- }
- }
-}
-
-//virtual
-void LLWindow::processMiscNativeEvents()
-{
- // do nothing unless subclassed
-}
-
-//virtual
-bool LLWindow::isPrimaryTextAvailable()
-{
- return false; // no
-}
-//virtual
-bool LLWindow::pasteTextFromPrimary(LLWString &dst)
-{
- return false; // fail
-}
-// virtual
-bool LLWindow::copyTextToPrimary(const LLWString &src)
-{
- return false; // fail
-}
-
-// static
-std::vector<std::string> LLWindow::getDynamicFallbackFontList()
-{
-#if LL_WINDOWS
- return LLWindowWin32::getDynamicFallbackFontList();
-#elif LL_DARWIN
- return LLWindowMacOSX::getDynamicFallbackFontList();
-#elif LL_SDL
- return LLWindowSDL::getDynamicFallbackFontList();
-#else
- return std::vector<std::string>();
-#endif
-}
-
-// static
-std::vector<std::string> LLWindow::getDisplaysResolutionList()
-{
-#if LL_WINDOWS
- return LLWindowWin32::getDisplaysResolutionList();
-#elif LL_DARWIN
- return LLWindowMacOSX::getDisplaysResolutionList();
-#else
- return std::vector<std::string>();
-#endif
-}
-
-#define UTF16_IS_HIGH_SURROGATE(U) ((U16)((U) - 0xD800) < 0x0400)
-#define UTF16_IS_LOW_SURROGATE(U) ((U16)((U) - 0xDC00) < 0x0400)
-#define UTF16_SURROGATE_PAIR_TO_UTF32(H,L) (((H) << 10) + (L) - (0xD800 << 10) - 0xDC00 + 0x00010000)
-
-void LLWindow::handleUnicodeUTF16(U16 utf16, MASK mask)
-{
- // Note that we could discard unpaired surrogates, but I'm
- // following the Unicode Consortium's recommendation here;
- // that is, to preserve those unpaired surrogates in UTF-32
- // values. _To_preserve_ means to pass to the callback in our
- // context.
-
- if (mHighSurrogate == 0)
- {
- if (UTF16_IS_HIGH_SURROGATE(utf16))
- {
- mHighSurrogate = utf16;
- }
- else
- {
- mCallbacks->handleUnicodeChar(utf16, mask);
- }
- }
- else
- {
- if (UTF16_IS_LOW_SURROGATE(utf16))
- {
- /* A legal surrogate pair. */
- mCallbacks->handleUnicodeChar(UTF16_SURROGATE_PAIR_TO_UTF32(mHighSurrogate, utf16), mask);
- mHighSurrogate = 0;
- }
- else if (UTF16_IS_HIGH_SURROGATE(utf16))
- {
- /* Two consecutive high surrogates. */
- mCallbacks->handleUnicodeChar(mHighSurrogate, mask);
- mHighSurrogate = utf16;
- }
- else
- {
- /* A non-low-surrogate preceeded by a high surrogate. */
- mCallbacks->handleUnicodeChar(mHighSurrogate, mask);
- mHighSurrogate = 0;
- mCallbacks->handleUnicodeChar(utf16, mask);
- }
- }
-}
-
-//
-// LLSplashScreen
-//
-
-// static
-bool LLSplashScreen::isVisible()
-{
- return gSplashScreenp;
-}
-
-// 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 std::string& 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 std::set<LLWindow*> sWindowList;
-
-LLWindow* LLWindowManager::createWindow(
- LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags,
- bool fullscreen,
- bool clearBg,
- bool enable_vsync,
- bool use_gl,
- bool ignore_pixel_depth,
- U32 fsaa_samples,
- U32 max_cores,
- U32 max_vram,
- F32 max_gl_version)
-{
- LLWindow* new_window;
-
- if (use_gl)
- {
-#if LL_MESA_HEADLESS
- new_window = new LLWindowMesaHeadless(callbacks,
- title, name, x, y, width, height, flags,
- fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth);
-#elif LL_SDL
- new_window = new LLWindowSDL(callbacks,
- title, x, y, width, height, flags,
- fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples);
-#elif LL_WINDOWS
- new_window = new LLWindowWin32(callbacks,
- title, name, x, y, width, height, flags,
- fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples, max_cores, max_vram, max_gl_version);
-#elif LL_DARWIN
- new_window = new LLWindowMacOSX(callbacks,
- title, name, x, y, width, height, flags,
- fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples, max_vram);
-#endif
- }
- else
- {
- new_window = new LLWindowHeadless(callbacks,
- title, name, x, y, width, height, flags,
- fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth);
- }
-
- if (false == new_window->isValid())
- {
- delete new_window;
- LL_WARNS() << "LLWindowManager::create() : Error creating window." << LL_ENDL;
- return NULL;
- }
- sWindowList.insert(new_window);
- return new_window;
-}
-
-bool LLWindowManager::destroyWindow(LLWindow* window)
-{
- if (sWindowList.find(window) == sWindowList.end())
- {
- LL_ERRS() << "LLWindowManager::destroyWindow() : Window pointer not valid, this window doesn't exist!"
- << LL_ENDL;
- return false;
- }
-
- window->close();
-
- sWindowList.erase(window);
-
- delete window;
-
- return true;
-}
-
-bool LLWindowManager::isWindowValid(LLWindow *window)
-{
- return sWindowList.find(window) != sWindowList.end();
-}
-
-//coordinate conversion utility funcs that forward to llwindow
-LLCoordCommon LL_COORD_TYPE_WINDOW::convertToCommon() const
-{
- const LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this);
-
- LLCoordGL out;
- LLWindow::instance_snapshot().begin()->convertCoords(self, &out);
- return out.convert();
-}
-
-void LL_COORD_TYPE_WINDOW::convertFromCommon(const LLCoordCommon& from)
-{
- LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this);
-
- LLCoordGL from_gl(from);
- LLWindow::instance_snapshot().begin()->convertCoords(from_gl, &self);
-}
-
-LLCoordCommon LL_COORD_TYPE_SCREEN::convertToCommon() const
-{
- const LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this);
-
- LLCoordGL out;
- LLWindow::instance_snapshot().begin()->convertCoords(self, &out);
- return out.convert();
-}
-
-void LL_COORD_TYPE_SCREEN::convertFromCommon(const LLCoordCommon& from)
-{
- LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this);
-
- LLCoordGL from_gl(from);
- LLWindow::instance_snapshot().begin()->convertCoords(from_gl, &self);
-}
+/** + * @file llwindow.cpp + * @brief Basic graphical window class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "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" +#endif + +#include "llerror.h" +#include "llkeyboard.h" +#include "llwindowcallbacks.h" + + +// +// Globals +// +LLSplashScreen *gSplashScreenp = NULL; +bool gDebugClicks = false; +bool gDebugWindowProc = false; + +const S32 gURLProtocolWhitelistCount = 5; +const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:", "mailto:" }; + +// 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 +// Maestro: This list isn't referenced anywhere that I could find +//const std::string gURLProtocolWhitelistHandler[] = { "http", "http", "https" }; + + +S32 OSMessageBox(const std::string& text, const std::string& 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: (?) + LL_WARNS() << "OSMessageBox: " << text << LL_ENDL; + 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(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags) + : mCallbacks(callbacks), + mPostQuit(true), + mFullscreen(fullscreen), + mFullscreenWidth(0), + mFullscreenHeight(0), + mFullscreenBits(0), + mFullscreenRefresh(0), + mSupportedResolutions(NULL), + mNumSupportedResolutions(0), + mCurrentCursor(UI_CURSOR_ARROW), + mNextCursor(UI_CURSOR_ARROW), + mCursorHidden(false), + mBusyCount(0), + mIsMouseClipping(false), + mMinWindowWidth(0), + mMinWindowHeight(0), + mSwapMethod(SWAP_METHOD_UNDEFINED), + mHideCursorPermanent(false), + mFlags(flags), + mHighSurrogate(0), + mRefreshRate(0) +{ +} + +LLWindow::~LLWindow() +{ +} + +//virtual +bool LLWindow::isValid() +{ + return true; +} + +//virtual +bool LLWindow::canDelete() +{ + return true; +} + +//virtual +void LLWindow::setTitle(const std::string title) +{ + // the action happens in the platform specific impl +} + +// virtual +void LLWindow::incBusyCount() +{ + ++mBusyCount; +} + +// virtual +void LLWindow::decBusyCount() +{ + if (mBusyCount > 0) + { + --mBusyCount; + } +} + +//virtual +void LLWindow::resetBusyCount() +{ + mBusyCount = 0; +} + +//virtual +S32 LLWindow::getBusyCount() const +{ + return mBusyCount; +} + +//virtual +ECursorType LLWindow::getCursor() const +{ + return mCurrentCursor; +} + +//virtual +bool LLWindow::dialogColorPicker(F32 *r, F32 *g, F32 *b) +{ + return false; +} + +void *LLWindow::getMediaWindow() +{ + // Default to returning the platform window. + return getPlatformWindow(); +} + +bool LLWindow::setSize(LLCoordScreen size) +{ + if (!getMaximized()) + { + size.mX = llmax(size.mX, mMinWindowWidth); + size.mY = llmax(size.mY, mMinWindowHeight); + } + return setSizeImpl(size); +} + +bool LLWindow::setSize(LLCoordWindow size) +{ + //HACK: we are inconsistently using minimum window dimensions + // in this case, we are constraining the inner "client" rect and other times + // we constrain the outer "window" rect + // There doesn't seem to be a good way to do this consistently without a bunch of platform + // specific code + if (!getMaximized()) + { + size.mX = llmax(size.mX, mMinWindowWidth); + size.mY = llmax(size.mY, mMinWindowHeight); + } + return setSizeImpl(size); +} + + +// virtual +void LLWindow::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately) +{ + mMinWindowWidth = min_width; + mMinWindowHeight = min_height; + + if (enforce_immediately) + { + LLCoordScreen cur_size; + if (!getMaximized() && getSize(&cur_size)) + { + if (cur_size.mX < mMinWindowWidth || cur_size.mY < mMinWindowHeight) + { + setSizeImpl(LLCoordScreen(llmin(cur_size.mX, mMinWindowWidth), llmin(cur_size.mY, mMinWindowHeight))); + } + } + } +} + +//virtual +void LLWindow::processMiscNativeEvents() +{ + // do nothing unless subclassed +} + +//virtual +bool LLWindow::isPrimaryTextAvailable() +{ + return false; // no +} +//virtual +bool LLWindow::pasteTextFromPrimary(LLWString &dst) +{ + return false; // fail +} +// virtual +bool LLWindow::copyTextToPrimary(const LLWString &src) +{ + return false; // fail +} + +// static +std::vector<std::string> LLWindow::getDynamicFallbackFontList() +{ +#if LL_WINDOWS + return LLWindowWin32::getDynamicFallbackFontList(); +#elif LL_DARWIN + return LLWindowMacOSX::getDynamicFallbackFontList(); +#elif LL_SDL + return LLWindowSDL::getDynamicFallbackFontList(); +#else + return std::vector<std::string>(); +#endif +} + +// static +std::vector<std::string> LLWindow::getDisplaysResolutionList() +{ +#if LL_WINDOWS + return LLWindowWin32::getDisplaysResolutionList(); +#elif LL_DARWIN + return LLWindowMacOSX::getDisplaysResolutionList(); +#else + return std::vector<std::string>(); +#endif +} + +#define UTF16_IS_HIGH_SURROGATE(U) ((U16)((U) - 0xD800) < 0x0400) +#define UTF16_IS_LOW_SURROGATE(U) ((U16)((U) - 0xDC00) < 0x0400) +#define UTF16_SURROGATE_PAIR_TO_UTF32(H,L) (((H) << 10) + (L) - (0xD800 << 10) - 0xDC00 + 0x00010000) + +void LLWindow::handleUnicodeUTF16(U16 utf16, MASK mask) +{ + // Note that we could discard unpaired surrogates, but I'm + // following the Unicode Consortium's recommendation here; + // that is, to preserve those unpaired surrogates in UTF-32 + // values. _To_preserve_ means to pass to the callback in our + // context. + + if (mHighSurrogate == 0) + { + if (UTF16_IS_HIGH_SURROGATE(utf16)) + { + mHighSurrogate = utf16; + } + else + { + mCallbacks->handleUnicodeChar(utf16, mask); + } + } + else + { + if (UTF16_IS_LOW_SURROGATE(utf16)) + { + /* A legal surrogate pair. */ + mCallbacks->handleUnicodeChar(UTF16_SURROGATE_PAIR_TO_UTF32(mHighSurrogate, utf16), mask); + mHighSurrogate = 0; + } + else if (UTF16_IS_HIGH_SURROGATE(utf16)) + { + /* Two consecutive high surrogates. */ + mCallbacks->handleUnicodeChar(mHighSurrogate, mask); + mHighSurrogate = utf16; + } + else + { + /* A non-low-surrogate preceeded by a high surrogate. */ + mCallbacks->handleUnicodeChar(mHighSurrogate, mask); + mHighSurrogate = 0; + mCallbacks->handleUnicodeChar(utf16, mask); + } + } +} + +// +// LLSplashScreen +// + +// static +bool LLSplashScreen::isVisible() +{ + return gSplashScreenp; +} + +// 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 std::string& 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 std::set<LLWindow*> sWindowList; + +LLWindow* LLWindowManager::createWindow( + LLWindowCallbacks* callbacks, + const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, + bool fullscreen, + bool clearBg, + bool enable_vsync, + bool use_gl, + bool ignore_pixel_depth, + U32 fsaa_samples, + U32 max_cores, + U32 max_vram, + F32 max_gl_version) +{ + LLWindow* new_window; + + if (use_gl) + { +#if LL_MESA_HEADLESS + new_window = new LLWindowMesaHeadless(callbacks, + title, name, x, y, width, height, flags, + fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth); +#elif LL_SDL + new_window = new LLWindowSDL(callbacks, + title, x, y, width, height, flags, + fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); +#elif LL_WINDOWS + new_window = new LLWindowWin32(callbacks, + title, name, x, y, width, height, flags, + fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples, max_cores, max_vram, max_gl_version); +#elif LL_DARWIN + new_window = new LLWindowMacOSX(callbacks, + title, name, x, y, width, height, flags, + fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth, fsaa_samples, max_vram); +#endif + } + else + { + new_window = new LLWindowHeadless(callbacks, + title, name, x, y, width, height, flags, + fullscreen, clearBg, enable_vsync, use_gl, ignore_pixel_depth); + } + + if (false == new_window->isValid()) + { + delete new_window; + LL_WARNS() << "LLWindowManager::create() : Error creating window." << LL_ENDL; + return NULL; + } + sWindowList.insert(new_window); + return new_window; +} + +bool LLWindowManager::destroyWindow(LLWindow* window) +{ + if (sWindowList.find(window) == sWindowList.end()) + { + LL_ERRS() << "LLWindowManager::destroyWindow() : Window pointer not valid, this window doesn't exist!" + << LL_ENDL; + return false; + } + + window->close(); + + sWindowList.erase(window); + + delete window; + + return true; +} + +bool LLWindowManager::isWindowValid(LLWindow *window) +{ + return sWindowList.find(window) != sWindowList.end(); +} + +//coordinate conversion utility funcs that forward to llwindow +LLCoordCommon LL_COORD_TYPE_WINDOW::convertToCommon() const +{ + const LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this); + + LLCoordGL out; + LLWindow::instance_snapshot().begin()->convertCoords(self, &out); + return out.convert(); +} + +void LL_COORD_TYPE_WINDOW::convertFromCommon(const LLCoordCommon& from) +{ + LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this); + + LLCoordGL from_gl(from); + LLWindow::instance_snapshot().begin()->convertCoords(from_gl, &self); +} + +LLCoordCommon LL_COORD_TYPE_SCREEN::convertToCommon() const +{ + const LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this); + + LLCoordGL out; + LLWindow::instance_snapshot().begin()->convertCoords(self, &out); + return out.convert(); +} + +void LL_COORD_TYPE_SCREEN::convertFromCommon(const LLCoordCommon& from) +{ + LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this); + + LLCoordGL from_gl(from); + LLWindow::instance_snapshot().begin()->convertCoords(from_gl, &self); +} diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 5eca3e8939..b7bb426654 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -1,334 +1,334 @@ -/**
- * @file llwindow.h
- * @brief Basic graphical window class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLWINDOW_H
-#define LL_LLWINDOW_H
-
-#include "llrect.h"
-#include "llcoord.h"
-#include "llstring.h"
-#include "llcursortypes.h"
-#include "llinstancetracker.h"
-#include "llsd.h"
-
-class LLSplashScreen;
-class LLPreeditor;
-class LLWindowCallbacks;
-
-// Refer to llwindow_test in test/common/llwindow for usage example
-
-class LLWindow : public LLInstanceTracker<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;
- virtual void minimize() = 0;
- virtual void restore() = 0;
- bool getFullscreen() { return mFullscreen; };
- virtual bool getPosition(LLCoordScreen *position) = 0;
- virtual bool getSize(LLCoordScreen *size) = 0;
- virtual bool getSize(LLCoordWindow *size) = 0;
- virtual bool setPosition(LLCoordScreen position) = 0;
- bool setSize(LLCoordScreen size);
- bool setSize(LLCoordWindow size);
- virtual void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true);
- virtual bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) = 0;
-
- //create a new GL context that shares a namespace with this Window's main GL context and make it current on the current thread
- // returns a pointer to be handed back to destroySharedConext/makeContextCurrent
- virtual void* createSharedContext() = 0;
- //make the given context current on the current thread
- virtual void makeContextCurrent(void* context) = 0;
- //destroy the given context that was retrieved by createSharedContext()
- //Must be called on the same thread that called createSharedContext()
- virtual void destroySharedContext(void* context) = 0;
-
- virtual void toggleVSync(bool enable_vsync) = 0;
-
- virtual bool setCursorPosition(LLCoordWindow position) = 0;
- virtual bool getCursorPosition(LLCoordWindow *position) = 0;
-#if LL_WINDOWS
- virtual bool getCursorDelta(LLCoordCommon* delta) = 0;
-#endif
- virtual void showCursor() = 0;
- virtual void hideCursor() = 0;
- virtual bool isCursorHidden() = 0;
- virtual void showCursorFromMouseMove() = 0;
- virtual void hideCursorUntilMouseMove() = 0;
-
- // Provide a way to set the Viewer window title after the
- // windows has been created. The initial use case for this
- // is described in SL-16102 (update window title with agent
- // name, location etc. for non-interactive viewer) but it
- // may also be useful in other cases.
- virtual void setTitle(const std::string title);
-
- // 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();
- virtual S32 getBusyCount() const;
-
- // Sets cursor, may set to arrow+hourglass
- virtual void setCursor(ECursorType cursor) { mNextCursor = cursor; };
- virtual ECursorType getCursor() const;
- virtual ECursorType getNextCursor() const { return mNextCursor; };
- virtual void updateCursor() = 0;
-
- 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 bool isPrimaryTextAvailable();
- virtual bool pasteTextFromPrimary(LLWString &dst);
- virtual bool copyTextToPrimary(const LLWString &src);
-
- virtual void flashIcon(F32 seconds) = 0;
- virtual F32 getGamma() = 0;
- virtual bool setGamma(const F32 gamma) = 0; // Set the gamma
- virtual void setFSAASamples(const U32 fsaa_samples) = 0; //set number of FSAA samples
- virtual U32 getFSAASamples() = 0;
- virtual bool restoreGamma() = 0; // Restore original gamma table (before updating gamma)
- virtual ESwapMethod getSwapMethod() { return mSwapMethod; }
- virtual void processMiscNativeEvents();
- 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 void setOldResize(bool oldresize) { };
- // 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;
-
- virtual void setMaxVRAMMegabytes(U32 max_vram) = 0;
-
- 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, modally
- // Returns true if valid color selected
- virtual bool dialogColorPicker(F32 *r, F32 *g, F32 *b);
-
-// return a platform-specific window reference (HWND on Windows, WindowRef on the Mac, Gtk window on Linux)
- virtual void *getPlatformWindow() = 0;
-
-// return the platform-specific window reference we use to initialize llmozlib (HWND on Windows, WindowRef on the Mac, Gtk window on Linux)
- virtual void *getMediaWindow();
-
- // control platform's Language Text Input mechanisms.
- virtual void allowLanguageTextInput(LLPreeditor *preeditor, bool b) {}
- virtual void setLanguageTextInput( const LLCoordGL & pos ) {};
- virtual void updateLanguageTextInputArea() {}
- virtual void interruptLanguageTextInput() {}
- virtual void spawnWebBrowser(const std::string& escaped_url, bool async) {};
-
- static std::vector<std::string> getDynamicFallbackFontList();
-
- // Provide native key event data
- virtual LLSD getNativeKeyData() { return LLSD::emptyMap(); }
-
- // Get system UI size based on DPI (for 96 DPI UI size should be 1.0)
- virtual F32 getSystemUISize() { return 1.0; }
-
- static std::vector<std::string> getDisplaysResolutionList();
-
- // windows only DirectInput8 for joysticks
- virtual void* getDirectInput8() { return NULL; };
- virtual bool getInputDevices(U32 device_type_filter,
- std::function<bool(std::string&, LLSD&, void*)> osx_callback,
- void* win_callback,
- void* userdata)
- {
- return false;
- };
-
- virtual S32 getRefreshRate() { return mRefreshRate; }
-protected:
- LLWindow(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags);
- virtual ~LLWindow();
- // Defaults to true
- virtual bool isValid();
- // Defaults to true
- virtual bool canDelete();
-
- virtual bool setSizeImpl(LLCoordScreen size) = 0;
- virtual bool setSizeImpl(LLCoordWindow size) = 0;
-
-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;
- ECursorType mNextCursor;
- 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;
- U16 mHighSurrogate;
- S32 mMinWindowWidth;
- S32 mMinWindowHeight;
- S32 mRefreshRate;
-
- // Handle a UTF-16 encoding unit received from keyboard.
- // Converting the series of UTF-16 encoding units to UTF-32 data,
- // this method passes the resulting UTF-32 data to mCallback's
- // handleUnicodeChar. The mask should be that to be passed to the
- // callback. This method uses mHighSurrogate as a dedicated work
- // variable.
- void handleUnicodeUTF16(U16 utf16, MASK mask);
-
- 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 std::string& string);
-
- static bool isVisible();
-protected:
- // These are overridden by the platform implementation
- virtual void showImpl() = 0;
- virtual void updateImpl(const std::string& string) = 0;
- virtual void hideImpl() = 0;
-
- static bool sVisible;
-
-};
-
-// Platform-neutral for accessing the platform specific message box
-S32 OSMessageBox(const std::string& text, const std::string& 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(
- LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height,
- U32 flags = 0,
- bool fullscreen = false,
- bool clearBg = false,
- bool enable_vsync = false,
- bool use_gl = true,
- bool ignore_pixel_depth = false,
- U32 fsaa_samples = 0,
- U32 max_cores = 0,
- U32 max_vram = 0,
- F32 max_gl_version = 4.6f);
- static bool destroyWindow(LLWindow* window);
- static bool isWindowValid(LLWindow *window);
-};
-
-//
-// helper funcs
-//
-extern bool gDebugWindowProc;
-
-// Protocols, like "http" and "https" we support in URLs
-extern const S32 gURLProtocolWhitelistCount;
-extern const std::string gURLProtocolWhitelist[];
-//extern const std::string gURLProtocolWhitelistHandler[];
-
-void simpleEscapeString ( std::string& stringIn );
-
-#endif // _LL_window_h_
+/** + * @file llwindow.h + * @brief Basic graphical window class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLWINDOW_H +#define LL_LLWINDOW_H + +#include "llrect.h" +#include "llcoord.h" +#include "llstring.h" +#include "llcursortypes.h" +#include "llinstancetracker.h" +#include "llsd.h" + +class LLSplashScreen; +class LLPreeditor; +class LLWindowCallbacks; + +// Refer to llwindow_test in test/common/llwindow for usage example + +class LLWindow : public LLInstanceTracker<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; + virtual void minimize() = 0; + virtual void restore() = 0; + bool getFullscreen() { return mFullscreen; }; + virtual bool getPosition(LLCoordScreen *position) = 0; + virtual bool getSize(LLCoordScreen *size) = 0; + virtual bool getSize(LLCoordWindow *size) = 0; + virtual bool setPosition(LLCoordScreen position) = 0; + bool setSize(LLCoordScreen size); + bool setSize(LLCoordWindow size); + virtual void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true); + virtual bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) = 0; + + //create a new GL context that shares a namespace with this Window's main GL context and make it current on the current thread + // returns a pointer to be handed back to destroySharedConext/makeContextCurrent + virtual void* createSharedContext() = 0; + //make the given context current on the current thread + virtual void makeContextCurrent(void* context) = 0; + //destroy the given context that was retrieved by createSharedContext() + //Must be called on the same thread that called createSharedContext() + virtual void destroySharedContext(void* context) = 0; + + virtual void toggleVSync(bool enable_vsync) = 0; + + virtual bool setCursorPosition(LLCoordWindow position) = 0; + virtual bool getCursorPosition(LLCoordWindow *position) = 0; +#if LL_WINDOWS + virtual bool getCursorDelta(LLCoordCommon* delta) = 0; +#endif + virtual void showCursor() = 0; + virtual void hideCursor() = 0; + virtual bool isCursorHidden() = 0; + virtual void showCursorFromMouseMove() = 0; + virtual void hideCursorUntilMouseMove() = 0; + + // Provide a way to set the Viewer window title after the + // windows has been created. The initial use case for this + // is described in SL-16102 (update window title with agent + // name, location etc. for non-interactive viewer) but it + // may also be useful in other cases. + virtual void setTitle(const std::string title); + + // 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(); + virtual S32 getBusyCount() const; + + // Sets cursor, may set to arrow+hourglass + virtual void setCursor(ECursorType cursor) { mNextCursor = cursor; }; + virtual ECursorType getCursor() const; + virtual ECursorType getNextCursor() const { return mNextCursor; }; + virtual void updateCursor() = 0; + + 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 bool isPrimaryTextAvailable(); + virtual bool pasteTextFromPrimary(LLWString &dst); + virtual bool copyTextToPrimary(const LLWString &src); + + virtual void flashIcon(F32 seconds) = 0; + virtual F32 getGamma() = 0; + virtual bool setGamma(const F32 gamma) = 0; // Set the gamma + virtual void setFSAASamples(const U32 fsaa_samples) = 0; //set number of FSAA samples + virtual U32 getFSAASamples() = 0; + virtual bool restoreGamma() = 0; // Restore original gamma table (before updating gamma) + virtual ESwapMethod getSwapMethod() { return mSwapMethod; } + virtual void processMiscNativeEvents(); + 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 void setOldResize(bool oldresize) { }; + // 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; + + virtual void setMaxVRAMMegabytes(U32 max_vram) = 0; + + 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, modally + // Returns true if valid color selected + virtual bool dialogColorPicker(F32 *r, F32 *g, F32 *b); + +// return a platform-specific window reference (HWND on Windows, WindowRef on the Mac, Gtk window on Linux) + virtual void *getPlatformWindow() = 0; + +// return the platform-specific window reference we use to initialize llmozlib (HWND on Windows, WindowRef on the Mac, Gtk window on Linux) + virtual void *getMediaWindow(); + + // control platform's Language Text Input mechanisms. + virtual void allowLanguageTextInput(LLPreeditor *preeditor, bool b) {} + virtual void setLanguageTextInput( const LLCoordGL & pos ) {}; + virtual void updateLanguageTextInputArea() {} + virtual void interruptLanguageTextInput() {} + virtual void spawnWebBrowser(const std::string& escaped_url, bool async) {}; + + static std::vector<std::string> getDynamicFallbackFontList(); + + // Provide native key event data + virtual LLSD getNativeKeyData() { return LLSD::emptyMap(); } + + // Get system UI size based on DPI (for 96 DPI UI size should be 1.0) + virtual F32 getSystemUISize() { return 1.0; } + + static std::vector<std::string> getDisplaysResolutionList(); + + // windows only DirectInput8 for joysticks + virtual void* getDirectInput8() { return NULL; }; + virtual bool getInputDevices(U32 device_type_filter, + std::function<bool(std::string&, LLSD&, void*)> osx_callback, + void* win_callback, + void* userdata) + { + return false; + }; + + virtual S32 getRefreshRate() { return mRefreshRate; } +protected: + LLWindow(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags); + virtual ~LLWindow(); + // Defaults to true + virtual bool isValid(); + // Defaults to true + virtual bool canDelete(); + + virtual bool setSizeImpl(LLCoordScreen size) = 0; + virtual bool setSizeImpl(LLCoordWindow size) = 0; + +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; + ECursorType mNextCursor; + 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; + U16 mHighSurrogate; + S32 mMinWindowWidth; + S32 mMinWindowHeight; + S32 mRefreshRate; + + // Handle a UTF-16 encoding unit received from keyboard. + // Converting the series of UTF-16 encoding units to UTF-32 data, + // this method passes the resulting UTF-32 data to mCallback's + // handleUnicodeChar. The mask should be that to be passed to the + // callback. This method uses mHighSurrogate as a dedicated work + // variable. + void handleUnicodeUTF16(U16 utf16, MASK mask); + + 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 std::string& string); + + static bool isVisible(); +protected: + // These are overridden by the platform implementation + virtual void showImpl() = 0; + virtual void updateImpl(const std::string& string) = 0; + virtual void hideImpl() = 0; + + static bool sVisible; + +}; + +// Platform-neutral for accessing the platform specific message box +S32 OSMessageBox(const std::string& text, const std::string& 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( + LLWindowCallbacks* callbacks, + const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, + U32 flags = 0, + bool fullscreen = false, + bool clearBg = false, + bool enable_vsync = false, + bool use_gl = true, + bool ignore_pixel_depth = false, + U32 fsaa_samples = 0, + U32 max_cores = 0, + U32 max_vram = 0, + F32 max_gl_version = 4.6f); + static bool destroyWindow(LLWindow* window); + static bool isWindowValid(LLWindow *window); +}; + +// +// helper funcs +// +extern bool gDebugWindowProc; + +// Protocols, like "http" and "https" we support in URLs +extern const S32 gURLProtocolWhitelistCount; +extern const std::string gURLProtocolWhitelist[]; +//extern const std::string gURLProtocolWhitelistHandler[]; + +void simpleEscapeString ( std::string& stringIn ); + +#endif // _LL_window_h_ diff --git a/indra/llwindow/llwindowcallbacks.cpp b/indra/llwindow/llwindowcallbacks.cpp index 0c328c9140..c160382c17 100644 --- a/indra/llwindow/llwindowcallbacks.cpp +++ b/indra/llwindow/llwindowcallbacks.cpp @@ -1,227 +1,227 @@ -/**
- * @file llwindowcallbacks.cpp
- * @brief OS event callback class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llwindowcallbacks.h"
-
-//
-// LLWindowCallbacks
-//
-
-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)
-{
-}
-
-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::handleMiddleMouseDown(LLWindow *window, const LLCoordGL pos, MASK mask)
-{
- return false;
-}
-
-bool LLWindowCallbacks::handleMiddleMouseUp(LLWindow *window, const LLCoordGL pos, MASK mask)
-{
- return false;
-}
-
-bool LLWindowCallbacks::handleOtherMouseDown(LLWindow *window, const LLCoordGL pos, MASK mask, S32 button)
-{
- return false;
-}
-
-bool LLWindowCallbacks::handleOtherMouseUp(LLWindow *window, const LLCoordGL pos, MASK mask, S32 button)
-{
- return false;
-}
-
-bool LLWindowCallbacks::handleActivate(LLWindow *window, bool activated)
-{
- return false;
-}
-
-bool LLWindowCallbacks::handleActivateApp(LLWindow *window, bool activating)
-{
- return false;
-}
-
-void LLWindowCallbacks::handleMouseMove(LLWindow *window, const LLCoordGL pos, MASK mask)
-{
-}
-
-void LLWindowCallbacks::handleMouseDragged(LLWindow *window, const LLCoordGL pos, MASK mask)
-{
-}
-
-void LLWindowCallbacks::handleScrollWheel(LLWindow *window, S32 clicks)
-{
-}
-
-void LLWindowCallbacks::handleScrollHWheel(LLWindow *window, S32 clicks)
-{
-}
-
-void LLWindowCallbacks::handleResize(LLWindow *window, const S32 width, const S32 height)
-{
-}
-
-void LLWindowCallbacks::handleFocus(LLWindow *window)
-{
- LL_WARNS("COCOA") << "Called handleFocus proto" << LL_ENDL;
-}
-
-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)
-{
-}
-
-LLWindowCallbacks::DragNDropResult LLWindowCallbacks::handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, std::string data )
-{
- return LLWindowCallbacks::DND_NONE;
-}
-
-bool LLWindowCallbacks::handleTimerEvent(LLWindow *window)
-{
- return false;
-}
-
-bool LLWindowCallbacks::handleDeviceChange(LLWindow *window)
-{
- return false;
-}
-
-bool LLWindowCallbacks::handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height)
-{
- return false;
-}
-
-bool LLWindowCallbacks::handleWindowDidChangeScreen(LLWindow *window)
-{
- return false;
-}
-
-void LLWindowCallbacks::handlePingWatchdog(LLWindow *window, const char * msg)
-{
-
-}
-
-void LLWindowCallbacks::handlePauseWatchdog(LLWindow *window)
-{
-
-}
-
-void LLWindowCallbacks::handleResumeWatchdog(LLWindow *window)
-{
-
-}
-
-std::string LLWindowCallbacks::translateString(const char* tag)
-{
- return std::string();
-}
-
-//virtual
-std::string LLWindowCallbacks::translateString(const char* tag,
- const std::map<std::string, std::string>& args)
-{
- return std::string();
-}
+/** + * @file llwindowcallbacks.cpp + * @brief OS event callback class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llwindowcallbacks.h" + +// +// LLWindowCallbacks +// + +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) +{ +} + +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::handleMiddleMouseDown(LLWindow *window, const LLCoordGL pos, MASK mask) +{ + return false; +} + +bool LLWindowCallbacks::handleMiddleMouseUp(LLWindow *window, const LLCoordGL pos, MASK mask) +{ + return false; +} + +bool LLWindowCallbacks::handleOtherMouseDown(LLWindow *window, const LLCoordGL pos, MASK mask, S32 button) +{ + return false; +} + +bool LLWindowCallbacks::handleOtherMouseUp(LLWindow *window, const LLCoordGL pos, MASK mask, S32 button) +{ + return false; +} + +bool LLWindowCallbacks::handleActivate(LLWindow *window, bool activated) +{ + return false; +} + +bool LLWindowCallbacks::handleActivateApp(LLWindow *window, bool activating) +{ + return false; +} + +void LLWindowCallbacks::handleMouseMove(LLWindow *window, const LLCoordGL pos, MASK mask) +{ +} + +void LLWindowCallbacks::handleMouseDragged(LLWindow *window, const LLCoordGL pos, MASK mask) +{ +} + +void LLWindowCallbacks::handleScrollWheel(LLWindow *window, S32 clicks) +{ +} + +void LLWindowCallbacks::handleScrollHWheel(LLWindow *window, S32 clicks) +{ +} + +void LLWindowCallbacks::handleResize(LLWindow *window, const S32 width, const S32 height) +{ +} + +void LLWindowCallbacks::handleFocus(LLWindow *window) +{ + LL_WARNS("COCOA") << "Called handleFocus proto" << LL_ENDL; +} + +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) +{ +} + +LLWindowCallbacks::DragNDropResult LLWindowCallbacks::handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, std::string data ) +{ + return LLWindowCallbacks::DND_NONE; +} + +bool LLWindowCallbacks::handleTimerEvent(LLWindow *window) +{ + return false; +} + +bool LLWindowCallbacks::handleDeviceChange(LLWindow *window) +{ + return false; +} + +bool LLWindowCallbacks::handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height) +{ + return false; +} + +bool LLWindowCallbacks::handleWindowDidChangeScreen(LLWindow *window) +{ + return false; +} + +void LLWindowCallbacks::handlePingWatchdog(LLWindow *window, const char * msg) +{ + +} + +void LLWindowCallbacks::handlePauseWatchdog(LLWindow *window) +{ + +} + +void LLWindowCallbacks::handleResumeWatchdog(LLWindow *window) +{ + +} + +std::string LLWindowCallbacks::translateString(const char* tag) +{ + return std::string(); +} + +//virtual +std::string LLWindowCallbacks::translateString(const char* tag, + const std::map<std::string, std::string>& args) +{ + return std::string(); +} diff --git a/indra/llwindow/llwindowcallbacks.h b/indra/llwindow/llwindowcallbacks.h index c38d34c206..63b585231f 100644 --- a/indra/llwindow/llwindowcallbacks.h +++ b/indra/llwindow/llwindowcallbacks.h @@ -1,100 +1,100 @@ -/**
- * @file llwindowcallbacks.h
- * @brief OS event callback class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-#ifndef LLWINDOWCALLBACKS_H
-#define LLWINDOWCALLBACKS_H
-
-#include "llcoord.h"
-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 handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
- virtual bool handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
- virtual bool handleOtherMouseDown(LLWindow *window, LLCoordGL pos, MASK mask, S32 button);
- virtual bool handleOtherMouseUp(LLWindow *window, LLCoordGL pos, MASK mask, S32 button);
- virtual bool handleActivate(LLWindow *window, bool activated);
- virtual bool handleActivateApp(LLWindow *window, bool activating);
- virtual void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask);
- virtual void handleMouseDragged(LLWindow *window, LLCoordGL pos, MASK mask);
- virtual void handleScrollWheel(LLWindow *window, S32 clicks);
- virtual void handleScrollHWheel(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);
- virtual bool handleTimerEvent(LLWindow *window);
- virtual bool handleDeviceChange(LLWindow *window);
- virtual bool handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height);
- virtual bool handleWindowDidChangeScreen(LLWindow *window);
-
- enum DragNDropAction {
- DNDA_START_TRACKING = 0,// Start tracking an incoming drag
- DNDA_TRACK, // User is dragging an incoming drag around the window
- DNDA_STOP_TRACKING, // User is no longer dragging an incoming drag around the window (may have either cancelled or dropped on the window)
- DNDA_DROPPED // User dropped an incoming drag on the window (this is the "commit" event)
- };
-
- enum DragNDropResult {
- DND_NONE = 0, // No drop allowed
- DND_MOVE, // Drop accepted would result in a "move" operation
- DND_COPY, // Drop accepted would result in a "copy" operation
- DND_LINK // Drop accepted would result in a "link" operation
- };
- virtual DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, std::string data);
-
- virtual void handlePingWatchdog(LLWindow *window, const char * msg);
- virtual void handlePauseWatchdog(LLWindow *window);
- virtual void handleResumeWatchdog(LLWindow *window);
-
- // Look up a localized string, usually for an error message
- virtual std::string translateString(const char* tag);
- virtual std::string translateString(const char* tag,
- const std::map<std::string, std::string>& args);
-};
-
-
-#endif
+/** + * @file llwindowcallbacks.h + * @brief OS event callback class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LLWINDOWCALLBACKS_H +#define LLWINDOWCALLBACKS_H + +#include "llcoord.h" +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 handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask); + virtual bool handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask); + virtual bool handleOtherMouseDown(LLWindow *window, LLCoordGL pos, MASK mask, S32 button); + virtual bool handleOtherMouseUp(LLWindow *window, LLCoordGL pos, MASK mask, S32 button); + virtual bool handleActivate(LLWindow *window, bool activated); + virtual bool handleActivateApp(LLWindow *window, bool activating); + virtual void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask); + virtual void handleMouseDragged(LLWindow *window, LLCoordGL pos, MASK mask); + virtual void handleScrollWheel(LLWindow *window, S32 clicks); + virtual void handleScrollHWheel(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); + virtual bool handleTimerEvent(LLWindow *window); + virtual bool handleDeviceChange(LLWindow *window); + virtual bool handleDPIChanged(LLWindow *window, F32 ui_scale_factor, S32 window_width, S32 window_height); + virtual bool handleWindowDidChangeScreen(LLWindow *window); + + enum DragNDropAction { + DNDA_START_TRACKING = 0,// Start tracking an incoming drag + DNDA_TRACK, // User is dragging an incoming drag around the window + DNDA_STOP_TRACKING, // User is no longer dragging an incoming drag around the window (may have either cancelled or dropped on the window) + DNDA_DROPPED // User dropped an incoming drag on the window (this is the "commit" event) + }; + + enum DragNDropResult { + DND_NONE = 0, // No drop allowed + DND_MOVE, // Drop accepted would result in a "move" operation + DND_COPY, // Drop accepted would result in a "copy" operation + DND_LINK // Drop accepted would result in a "link" operation + }; + virtual DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, DragNDropAction action, std::string data); + + virtual void handlePingWatchdog(LLWindow *window, const char * msg); + virtual void handlePauseWatchdog(LLWindow *window); + virtual void handleResumeWatchdog(LLWindow *window); + + // Look up a localized string, usually for an error message + virtual std::string translateString(const char* tag); + virtual std::string translateString(const char* tag, + const std::map<std::string, std::string>& args); +}; + + +#endif diff --git a/indra/llwindow/llwindowheadless.cpp b/indra/llwindow/llwindowheadless.cpp index fdcd6ca8a9..55e5cfd51e 100644 --- a/indra/llwindow/llwindowheadless.cpp +++ b/indra/llwindow/llwindowheadless.cpp @@ -1,53 +1,53 @@ -/**
- * @file llwindowheadless.cpp
- * @brief Headless implementation of LLWindow class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "indra_constants.h"
-
-#include "llwindowheadless.h"
-#include "llkeyboardheadless.h"
-
-//
-// LLWindowHeadless
-//
-LLWindowHeadless::LLWindowHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height,
- U32 flags, bool fullscreen, bool clear_background,
- bool enable_vsync, bool use_gl, bool ignore_pixel_depth)
- : LLWindow(callbacks, fullscreen, flags)
-{
- // Initialize a headless keyboard.
- gKeyboard = new LLKeyboardHeadless();
- gKeyboard->setCallbacks(callbacks);
-}
-
-
-LLWindowHeadless::~LLWindowHeadless()
-{
-}
-
-void LLWindowHeadless::swapBuffers()
-{
-}
+/** + * @file llwindowheadless.cpp + * @brief Headless implementation of LLWindow class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "indra_constants.h" + +#include "llwindowheadless.h" +#include "llkeyboardheadless.h" + +// +// LLWindowHeadless +// +LLWindowHeadless::LLWindowHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, + U32 flags, bool fullscreen, bool clear_background, + bool enable_vsync, bool use_gl, bool ignore_pixel_depth) + : LLWindow(callbacks, fullscreen, flags) +{ + // Initialize a headless keyboard. + gKeyboard = new LLKeyboardHeadless(); + gKeyboard->setCallbacks(callbacks); +} + + +LLWindowHeadless::~LLWindowHeadless() +{ +} + +void LLWindowHeadless::swapBuffers() +{ +} diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h index 1a2363c13a..32c12b0d47 100644 --- a/indra/llwindow/llwindowheadless.h +++ b/indra/llwindow/llwindowheadless.h @@ -1,133 +1,133 @@ -/**
- * @file llwindowheadless.h
- * @brief Headless definition of LLWindow class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLWINDOWHEADLESS_H
-#define LL_LLWINDOWHEADLESS_H
-
-#include "llwindow.h"
-
-class LLWindowHeadless : public LLWindow
-{
-public:
- /*virtual*/ void show() override {}
- /*virtual*/ void hide() override {}
- /*virtual*/ void close() override {}
- /*virtual*/ bool getVisible() override {return false;}
- /*virtual*/ bool getMinimized() override {return false;}
- /*virtual*/ bool getMaximized() override {return false;}
- /*virtual*/ bool maximize() override {return false;}
- /*virtual*/ void minimize() override {}
- /*virtual*/ void restore() override {}
- // TODO: LLWindow::getFullscreen() is (intentionally?) NOT virtual.
- // Apparently the coder of LLWindowHeadless didn't realize that. Is it a
- // mistake to shadow the base-class method with an LLWindowHeadless
- // override when called on the subclass, yet call the base-class method
- // when indirecting through a polymorphic pointer or reference?
- bool getFullscreen() {return false;}
- /*virtual*/ bool getPosition(LLCoordScreen *position) override {return false;}
- /*virtual*/ bool getSize(LLCoordScreen *size) override {return false;}
- /*virtual*/ bool getSize(LLCoordWindow *size) override {return false;}
- /*virtual*/ bool setPosition(LLCoordScreen position) override {return false;}
- /*virtual*/ bool setSizeImpl(LLCoordScreen size) override {return false;}
- /*virtual*/ bool setSizeImpl(LLCoordWindow size) override {return false;}
- /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override {return false;}
- void* createSharedContext() override { return nullptr; }
- void makeContextCurrent(void*) override {}
- void destroySharedContext(void*) override {}
- /*virtual*/ void toggleVSync(bool enable_vsync) override { }
- /*virtual*/ bool setCursorPosition(LLCoordWindow position) override {return false;}
- /*virtual*/ bool getCursorPosition(LLCoordWindow *position) override {return false;}
-#if LL_WINDOWS
- /*virtual*/ bool getCursorDelta(LLCoordCommon* delta) override { return false; }
-#endif
- /*virtual*/ void showCursor() override {}
- /*virtual*/ void hideCursor() override {}
- /*virtual*/ void showCursorFromMouseMove() override {}
- /*virtual*/ void hideCursorUntilMouseMove() override {}
- /*virtual*/ bool isCursorHidden() override {return false;}
- /*virtual*/ void updateCursor() override {}
- //virtual ECursorType getCursor() override { return mCurrentCursor; }
- /*virtual*/ void captureMouse() override {}
- /*virtual*/ void releaseMouse() override {}
- /*virtual*/ void setMouseClipping( bool b ) override {}
- /*virtual*/ bool isClipboardTextAvailable() override {return false; }
- /*virtual*/ bool pasteTextFromClipboard(LLWString &dst) override {return false; }
- /*virtual*/ bool copyTextToClipboard(const LLWString &src) override {return false; }
- /*virtual*/ void flashIcon(F32 seconds) override {}
- /*virtual*/ F32 getGamma() override {return 1.0f; }
- /*virtual*/ bool setGamma(const F32 gamma) override {return false; } // Set the gamma
- /*virtual*/ void setFSAASamples(const U32 fsaa_samples) override { }
- /*virtual*/ U32 getFSAASamples() override { return 0; }
- /*virtual*/ bool restoreGamma() override {return false; } // Restore original gamma table (before updating gamma)
- //virtual ESwapMethod getSwapMethod() override { return mSwapMethod; }
- /*virtual*/ void gatherInput() override {}
- /*virtual*/ void delayInputProcessing() override {}
- /*virtual*/ void swapBuffers() override;
-
-
- // handy coordinate space conversion routines
- /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to) override { return false; }
- /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to) override { return false; }
-
- /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; }
- /*virtual*/ F32 getNativeAspectRatio() override { return 1.0f; }
- /*virtual*/ F32 getPixelAspectRatio() override { return 1.0f; }
- /*virtual*/ void setNativeAspectRatio(F32 ratio) override {}
-
- void setMaxVRAMMegabytes(U32 max_vram) override {}
-
- /*virtual*/ void *getPlatformWindow() override { return 0; }
- /*virtual*/ void bringToFront() override {}
-
- LLWindowHeadless(LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& name,
- S32 x, S32 y,
- S32 width, S32 height,
- U32 flags, bool fullscreen, bool clear_background,
- bool enable_vsync, bool use_gl, bool ignore_pixel_depth);
- virtual ~LLWindowHeadless();
-
-private:
-};
-
-class LLSplashScreenHeadless : public LLSplashScreen
-{
-public:
- LLSplashScreenHeadless() {}
- virtual ~LLSplashScreenHeadless() {}
-
- /*virtual*/ void showImpl() override {}
- /*virtual*/ void updateImpl(const std::string& mesg) override {}
- /*virtual*/ void hideImpl() override {}
-
-};
-
-#endif //LL_LLWINDOWHEADLESS_H
-
+/** + * @file llwindowheadless.h + * @brief Headless definition of LLWindow class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLWINDOWHEADLESS_H +#define LL_LLWINDOWHEADLESS_H + +#include "llwindow.h" + +class LLWindowHeadless : public LLWindow +{ +public: + /*virtual*/ void show() override {} + /*virtual*/ void hide() override {} + /*virtual*/ void close() override {} + /*virtual*/ bool getVisible() override {return false;} + /*virtual*/ bool getMinimized() override {return false;} + /*virtual*/ bool getMaximized() override {return false;} + /*virtual*/ bool maximize() override {return false;} + /*virtual*/ void minimize() override {} + /*virtual*/ void restore() override {} + // TODO: LLWindow::getFullscreen() is (intentionally?) NOT virtual. + // Apparently the coder of LLWindowHeadless didn't realize that. Is it a + // mistake to shadow the base-class method with an LLWindowHeadless + // override when called on the subclass, yet call the base-class method + // when indirecting through a polymorphic pointer or reference? + bool getFullscreen() {return false;} + /*virtual*/ bool getPosition(LLCoordScreen *position) override {return false;} + /*virtual*/ bool getSize(LLCoordScreen *size) override {return false;} + /*virtual*/ bool getSize(LLCoordWindow *size) override {return false;} + /*virtual*/ bool setPosition(LLCoordScreen position) override {return false;} + /*virtual*/ bool setSizeImpl(LLCoordScreen size) override {return false;} + /*virtual*/ bool setSizeImpl(LLCoordWindow size) override {return false;} + /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override {return false;} + void* createSharedContext() override { return nullptr; } + void makeContextCurrent(void*) override {} + void destroySharedContext(void*) override {} + /*virtual*/ void toggleVSync(bool enable_vsync) override { } + /*virtual*/ bool setCursorPosition(LLCoordWindow position) override {return false;} + /*virtual*/ bool getCursorPosition(LLCoordWindow *position) override {return false;} +#if LL_WINDOWS + /*virtual*/ bool getCursorDelta(LLCoordCommon* delta) override { return false; } +#endif + /*virtual*/ void showCursor() override {} + /*virtual*/ void hideCursor() override {} + /*virtual*/ void showCursorFromMouseMove() override {} + /*virtual*/ void hideCursorUntilMouseMove() override {} + /*virtual*/ bool isCursorHidden() override {return false;} + /*virtual*/ void updateCursor() override {} + //virtual ECursorType getCursor() override { return mCurrentCursor; } + /*virtual*/ void captureMouse() override {} + /*virtual*/ void releaseMouse() override {} + /*virtual*/ void setMouseClipping( bool b ) override {} + /*virtual*/ bool isClipboardTextAvailable() override {return false; } + /*virtual*/ bool pasteTextFromClipboard(LLWString &dst) override {return false; } + /*virtual*/ bool copyTextToClipboard(const LLWString &src) override {return false; } + /*virtual*/ void flashIcon(F32 seconds) override {} + /*virtual*/ F32 getGamma() override {return 1.0f; } + /*virtual*/ bool setGamma(const F32 gamma) override {return false; } // Set the gamma + /*virtual*/ void setFSAASamples(const U32 fsaa_samples) override { } + /*virtual*/ U32 getFSAASamples() override { return 0; } + /*virtual*/ bool restoreGamma() override {return false; } // Restore original gamma table (before updating gamma) + //virtual ESwapMethod getSwapMethod() override { return mSwapMethod; } + /*virtual*/ void gatherInput() override {} + /*virtual*/ void delayInputProcessing() override {} + /*virtual*/ void swapBuffers() override; + + + // handy coordinate space conversion routines + /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override { return false; } + /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override { return false; } + /*virtual*/ bool convertCoords(LLCoordWindow from, LLCoordGL *to) override { return false; } + /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordWindow *to) override { return false; } + /*virtual*/ bool convertCoords(LLCoordScreen from, LLCoordGL *to) override { return false; } + /*virtual*/ bool convertCoords(LLCoordGL from, LLCoordScreen *to) override { return false; } + + /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override { return NULL; } + /*virtual*/ F32 getNativeAspectRatio() override { return 1.0f; } + /*virtual*/ F32 getPixelAspectRatio() override { return 1.0f; } + /*virtual*/ void setNativeAspectRatio(F32 ratio) override {} + + void setMaxVRAMMegabytes(U32 max_vram) override {} + + /*virtual*/ void *getPlatformWindow() override { return 0; } + /*virtual*/ void bringToFront() override {} + + LLWindowHeadless(LLWindowCallbacks* callbacks, + const std::string& title, const std::string& name, + S32 x, S32 y, + S32 width, S32 height, + U32 flags, bool fullscreen, bool clear_background, + bool enable_vsync, bool use_gl, bool ignore_pixel_depth); + virtual ~LLWindowHeadless(); + +private: +}; + +class LLSplashScreenHeadless : public LLSplashScreen +{ +public: + LLSplashScreenHeadless() {} + virtual ~LLSplashScreenHeadless() {} + + /*virtual*/ void showImpl() override {} + /*virtual*/ void updateImpl(const std::string& mesg) override {} + /*virtual*/ void hideImpl() override {} + +}; + +#endif //LL_LLWINDOWHEADLESS_H + diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h index 609a441d66..d9d8bfce1f 100644 --- a/indra/llwindow/llwindowmacosx-objc.h +++ b/indra/llwindow/llwindowmacosx-objc.h @@ -1,186 +1,186 @@ -/**
- * @file llwindowmacosx-objc.h
- * @brief Prototypes for functions shared between llwindowmacosx.cpp
- * and llwindowmacosx-objc.mm.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLWINDOWMACOSX_OBJC_H
-#define LL_LLWINDOWMACOSX_OBJC_H
-
-#include <map>
-#include <vector>
-#include <deque>
-
-//fir CGSize
-#include <CoreGraphics/CGGeometry.h>
-
-typedef std::vector<std::pair<int, bool> > segment_t;
-
-typedef std::vector<int> segment_lengths;
-typedef std::deque<bool> segment_standouts;
-
-struct attributedStringInfo {
- segment_lengths seg_lengths;
- segment_standouts seg_standouts;
-};
-
-// This will actually hold an NSCursor*, but that type is only available in objective C.
-typedef void *CursorRef;
-typedef void *NSWindowRef;
-typedef void *GLViewRef;
-
-
-struct NativeKeyEventData {
- enum EventType {
- KEYUNKNOWN,
- KEYUP,
- KEYDOWN,
- KEYCHAR
- };
-
- EventType mKeyEvent = KEYUNKNOWN;
- uint32_t mEventType = 0;
- uint32_t mEventModifiers = 0;
- uint32_t mEventKeyCode = 0;
- uint32_t mEventChars = 0;
- uint32_t mEventUnmodChars = 0;
- bool mEventRepeat = false;
-};
-
-typedef const NativeKeyEventData * NSKeyEventRef;
-
-// These are defined in llappviewermacosx.cpp.
-bool initViewer();
-void handleQuit();
-bool pumpMainLoop();
-void initMainLoop();
-void cleanupViewer();
-void handleUrl(const char* url);
-void dispatchUrl(std::string url);
-
-/* Defined in llwindowmacosx-objc.mm: */
-int createNSApp(int argc, const char **argv);
-void setupCocoa();
-bool pasteBoardAvailable();
-bool copyToPBoard(const unsigned short *str, unsigned int len);
-unsigned short *copyFromPBoard();
-CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY);
-short releaseImageCursor(CursorRef ref);
-short setImageCursor(CursorRef ref);
-void setArrowCursor();
-void setIBeamCursor();
-void setPointingHandCursor();
-void setCopyCursor();
-void setCrossCursor();
-void setNotAllowedCursor();
-void hideNSCursor();
-void showNSCursor();
-bool isCGCursorVisible();
-void hideNSCursorTillMove(bool hide);
-void requestUserAttention();
-long showAlert(std::string title, std::string text, int type);
-void setResizeMode(bool oldresize, void* glview);
-
-NSWindowRef createNSWindow(int x, int y, int width, int height);
-
-#include <OpenGL/OpenGL.h>
-
-GLViewRef createOpenGLView(NSWindowRef window, unsigned int samples, bool vsync);
-void glSwapBuffers(void* context);
-CGLContextObj getCGLContextObj(GLViewRef view);
-unsigned long getVramSize(GLViewRef view);
-float getDeviceUnitSize(GLViewRef view);
-CGPoint getContentViewBoundsPosition(NSWindowRef window);
-CGSize getContentViewBoundsSize(NSWindowRef window);
-CGSize getDeviceContentViewSize(NSWindowRef window, GLViewRef view);
-void getWindowSize(NSWindowRef window, float* size);
-void setWindowSize(NSWindowRef window, int width, int height);
-void getCursorPos(NSWindowRef window, float* pos);
-void makeWindowOrderFront(NSWindowRef window);
-void convertScreenToWindow(NSWindowRef window, float *coord);
-void convertWindowToScreen(NSWindowRef window, float *coord);
-void convertScreenToView(NSWindowRef window, float *coord);
-void convertRectToScreen(NSWindowRef window, float *coord);
-void convertRectFromScreen(NSWindowRef window, float *coord);
-void setWindowPos(NSWindowRef window, float* pos);
-void closeWindow(NSWindowRef window);
-void removeGLView(GLViewRef view);
-void makeFirstResponder(NSWindowRef window, GLViewRef view);
-void setupInputWindow(NSWindowRef window, GLViewRef view);
-
-// These are all implemented in llwindowmacosx.cpp.
-// This is largely for easier interop between Obj-C and C++ (at least in the viewer's case due to the BOOL vs. BOOL conflict)
-bool callKeyUp(NSKeyEventRef event, unsigned short key, unsigned int mask);
-bool callKeyDown(NSKeyEventRef event, unsigned short key, unsigned int mask, wchar_t character);
-void callResetKeys();
-bool callUnicodeCallback(wchar_t character, unsigned int mask);
-void callRightMouseDown(float *pos, unsigned int mask);
-void callRightMouseUp(float *pos, unsigned int mask);
-void callLeftMouseDown(float *pos, unsigned int mask);
-void callLeftMouseUp(float *pos, unsigned int mask);
-void callDoubleClick(float *pos, unsigned int mask);
-void callResize(unsigned int width, unsigned int height);
-void callMouseMoved(float *pos, unsigned int mask);
-void callMouseDragged(float *pos, unsigned int mask);
-void callScrollMoved(float deltaX, float deltaY);
-void callMouseExit();
-void callWindowFocus();
-void callWindowUnfocus();
-void callWindowHide();
-void callWindowUnhide();
-void callWindowDidChangeScreen();
-void callDeltaUpdate(float *delta, unsigned int mask);
-void callOtherMouseDown(float *pos, unsigned int mask, int button);
-void callOtherMouseUp(float *pos, unsigned int mask, int button);
-void callFocus();
-void callFocusLost();
-void callModifier(unsigned int mask);
-void callQuitHandler();
-void commitCurrentPreedit(GLViewRef glView);
-
-#include <string>
-void callHandleDragEntered(std::string url);
-void callHandleDragExited(std::string url);
-void callHandleDragUpdated(std::string url);
-void callHandleDragDropped(std::string url);
-
-// LLPreeditor C bindings.
-std::basic_string<wchar_t> getPreeditString();
-void getPreeditSelectionRange(int *position, int *length);
-void getPreeditMarkedRange(int *position, int *length);
-bool handleUnicodeCharacter(wchar_t c);
-void updatePreeditor(unsigned short *str);
-void setPreeditMarkedRange(int position, int length);
-void resetPreedit();
-int wstring_length(const std::basic_string<wchar_t> & wstr, const int woffset, const int utf16_length, int *unaligned);
-void setMarkedText(unsigned short *text, unsigned int *selectedRange, unsigned int *replacementRange, long text_len, attributedStringInfo segments);
-void getPreeditLocation(float *location, unsigned int length);
-void allowDirectMarkedTextInput(bool allow, GLViewRef glView);
-
-NSWindowRef getMainAppWindow();
-GLViewRef getGLView();
-
-unsigned int getModifiers();
-
-#endif // LL_LLWINDOWMACOSX_OBJC_H
+/** + * @file llwindowmacosx-objc.h + * @brief Prototypes for functions shared between llwindowmacosx.cpp + * and llwindowmacosx-objc.mm. + * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLWINDOWMACOSX_OBJC_H +#define LL_LLWINDOWMACOSX_OBJC_H + +#include <map> +#include <vector> +#include <deque> + +//fir CGSize +#include <CoreGraphics/CGGeometry.h> + +typedef std::vector<std::pair<int, bool> > segment_t; + +typedef std::vector<int> segment_lengths; +typedef std::deque<bool> segment_standouts; + +struct attributedStringInfo { + segment_lengths seg_lengths; + segment_standouts seg_standouts; +}; + +// This will actually hold an NSCursor*, but that type is only available in objective C. +typedef void *CursorRef; +typedef void *NSWindowRef; +typedef void *GLViewRef; + + +struct NativeKeyEventData { + enum EventType { + KEYUNKNOWN, + KEYUP, + KEYDOWN, + KEYCHAR + }; + + EventType mKeyEvent = KEYUNKNOWN; + uint32_t mEventType = 0; + uint32_t mEventModifiers = 0; + uint32_t mEventKeyCode = 0; + uint32_t mEventChars = 0; + uint32_t mEventUnmodChars = 0; + bool mEventRepeat = false; +}; + +typedef const NativeKeyEventData * NSKeyEventRef; + +// These are defined in llappviewermacosx.cpp. +bool initViewer(); +void handleQuit(); +bool pumpMainLoop(); +void initMainLoop(); +void cleanupViewer(); +void handleUrl(const char* url); +void dispatchUrl(std::string url); + +/* Defined in llwindowmacosx-objc.mm: */ +int createNSApp(int argc, const char **argv); +void setupCocoa(); +bool pasteBoardAvailable(); +bool copyToPBoard(const unsigned short *str, unsigned int len); +unsigned short *copyFromPBoard(); +CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY); +short releaseImageCursor(CursorRef ref); +short setImageCursor(CursorRef ref); +void setArrowCursor(); +void setIBeamCursor(); +void setPointingHandCursor(); +void setCopyCursor(); +void setCrossCursor(); +void setNotAllowedCursor(); +void hideNSCursor(); +void showNSCursor(); +bool isCGCursorVisible(); +void hideNSCursorTillMove(bool hide); +void requestUserAttention(); +long showAlert(std::string title, std::string text, int type); +void setResizeMode(bool oldresize, void* glview); + +NSWindowRef createNSWindow(int x, int y, int width, int height); + +#include <OpenGL/OpenGL.h> + +GLViewRef createOpenGLView(NSWindowRef window, unsigned int samples, bool vsync); +void glSwapBuffers(void* context); +CGLContextObj getCGLContextObj(GLViewRef view); +unsigned long getVramSize(GLViewRef view); +float getDeviceUnitSize(GLViewRef view); +CGPoint getContentViewBoundsPosition(NSWindowRef window); +CGSize getContentViewBoundsSize(NSWindowRef window); +CGSize getDeviceContentViewSize(NSWindowRef window, GLViewRef view); +void getWindowSize(NSWindowRef window, float* size); +void setWindowSize(NSWindowRef window, int width, int height); +void getCursorPos(NSWindowRef window, float* pos); +void makeWindowOrderFront(NSWindowRef window); +void convertScreenToWindow(NSWindowRef window, float *coord); +void convertWindowToScreen(NSWindowRef window, float *coord); +void convertScreenToView(NSWindowRef window, float *coord); +void convertRectToScreen(NSWindowRef window, float *coord); +void convertRectFromScreen(NSWindowRef window, float *coord); +void setWindowPos(NSWindowRef window, float* pos); +void closeWindow(NSWindowRef window); +void removeGLView(GLViewRef view); +void makeFirstResponder(NSWindowRef window, GLViewRef view); +void setupInputWindow(NSWindowRef window, GLViewRef view); + +// These are all implemented in llwindowmacosx.cpp. +// This is largely for easier interop between Obj-C and C++ (at least in the viewer's case due to the BOOL vs. BOOL conflict) +bool callKeyUp(NSKeyEventRef event, unsigned short key, unsigned int mask); +bool callKeyDown(NSKeyEventRef event, unsigned short key, unsigned int mask, wchar_t character); +void callResetKeys(); +bool callUnicodeCallback(wchar_t character, unsigned int mask); +void callRightMouseDown(float *pos, unsigned int mask); +void callRightMouseUp(float *pos, unsigned int mask); +void callLeftMouseDown(float *pos, unsigned int mask); +void callLeftMouseUp(float *pos, unsigned int mask); +void callDoubleClick(float *pos, unsigned int mask); +void callResize(unsigned int width, unsigned int height); +void callMouseMoved(float *pos, unsigned int mask); +void callMouseDragged(float *pos, unsigned int mask); +void callScrollMoved(float deltaX, float deltaY); +void callMouseExit(); +void callWindowFocus(); +void callWindowUnfocus(); +void callWindowHide(); +void callWindowUnhide(); +void callWindowDidChangeScreen(); +void callDeltaUpdate(float *delta, unsigned int mask); +void callOtherMouseDown(float *pos, unsigned int mask, int button); +void callOtherMouseUp(float *pos, unsigned int mask, int button); +void callFocus(); +void callFocusLost(); +void callModifier(unsigned int mask); +void callQuitHandler(); +void commitCurrentPreedit(GLViewRef glView); + +#include <string> +void callHandleDragEntered(std::string url); +void callHandleDragExited(std::string url); +void callHandleDragUpdated(std::string url); +void callHandleDragDropped(std::string url); + +// LLPreeditor C bindings. +std::basic_string<wchar_t> getPreeditString(); +void getPreeditSelectionRange(int *position, int *length); +void getPreeditMarkedRange(int *position, int *length); +bool handleUnicodeCharacter(wchar_t c); +void updatePreeditor(unsigned short *str); +void setPreeditMarkedRange(int position, int length); +void resetPreedit(); +int wstring_length(const std::basic_string<wchar_t> & wstr, const int woffset, const int utf16_length, int *unaligned); +void setMarkedText(unsigned short *text, unsigned int *selectedRange, unsigned int *replacementRange, long text_len, attributedStringInfo segments); +void getPreeditLocation(float *location, unsigned int length); +void allowDirectMarkedTextInput(bool allow, GLViewRef glView); + +NSWindowRef getMainAppWindow(); +GLViewRef getGLView(); + +unsigned int getModifiers(); + +#endif // LL_LLWINDOWMACOSX_OBJC_H diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index fee74a9c25..cd00f4d33c 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1,2662 +1,2662 @@ -/**
- * @file llwindowmacosx.cpp
- * @brief Platform-dependent implementation of llwindow
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llwindowmacosx.h"
-
-#include "llkeyboardmacosx.h"
-#include "llwindowcallbacks.h"
-#include "llpreeditor.h"
-
-#include "llerror.h"
-#include "llgl.h"
-#include "llstring.h"
-#include "lldir.h"
-#include "indra_constants.h"
-
-#include <OpenGL/OpenGL.h>
-#include <Carbon/Carbon.h>
-#include <CoreServices/CoreServices.h>
-#include <CoreGraphics/CGDisplayConfiguration.h>
-
-#include <IOKit/IOCFPlugIn.h>
-#include <IOKit/IOKitLib.h>
-#include <IOKit/IOMessage.h>
-#include <IOKit/hid/IOHIDUsageTables.h>
-#include <IOKit/hid/IOHIDLib.h>
-#include <IOKit/usb/IOUSBLib.h>
-
-extern bool gDebugWindowProc;
-bool gHiDPISupport = true;
-
-const S32 BITS_PER_PIXEL = 32;
-const S32 MAX_NUM_RESOLUTIONS = 32;
-
-const S32 DEFAULT_REFRESH_RATE = 60;
-
-namespace
-{
- NSKeyEventRef mRawKeyEvent = NULL;
-}
-//
-// LLWindowMacOSX
-//
-
-bool LLWindowMacOSX::sUseMultGL = false;
-
-// Cross-platform bits:
-
-bool check_for_card(const char* RENDERER, const char* bad_card)
-{
- if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
- {
- std::string buffer = llformat(
- "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.c_str(), "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);
-
-// 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(LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& name, S32 x, S32 y, S32 width,
- S32 height, U32 flags,
- bool fullscreen, bool clearBg,
- bool enable_vsync, bool use_gl,
- bool ignore_pixel_depth,
- U32 fsaa_samples,
- U32 max_vram)
- : LLWindow(NULL, fullscreen, flags)
-{
- // *HACK: During window construction we get lots of OS events for window
- // reshape, activate, etc. that the viewer isn't ready to handle.
- // Route them to a dummy callback structure until the end of constructor.
- LLWindowCallbacks null_callbacks;
- mCallbacks = &null_callbacks;
-
- // Voodoo for calling cocoa from carbon (see llwindowmacosx-objc.mm).
- setupCocoa();
-
- // Initialize the keyboard
- gKeyboard = new LLKeyboardMacOSX();
- gKeyboard->setCallbacks(callbacks);
-
- // Ignore use_gl for now, only used for drones on PC
- mWindow = NULL;
- mContext = NULL;
- mPixelFormat = NULL;
- mDisplay = CGMainDisplayID();
- mSimulatedRightClick = false;
- mLastModifiers = 0;
- mHandsOffEvents = false;
- mCursorDecoupled = false;
- mCursorLastEventDeltaX = 0;
- mCursorLastEventDeltaY = 0;
- mCursorIgnoreNextDelta = false;
- mNeedsResize = false;
- mOverrideAspectRatio = 0.f;
- mMaximized = false;
- mMinimized = false;
- mLanguageTextInputAllowed = false;
- mPreeditor = NULL;
- mFSAASamples = fsaa_samples;
- mForceRebuild = false;
-
- // Get the original aspect ratio of the main device.
- mOriginalAspectRatio = (double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
-
- // Stash the window title
- mWindowTitle = title;
- //mWindowTitle[0] = title.length();
-
- mDragOverrideCursor = -1;
-
- // 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, enable_vsync))
- {
- if(mWindow != NULL)
- {
- makeWindowOrderFront(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 );
-
- allowLanguageTextInput(NULL, false);
- }
-
- mCallbacks = callbacks;
- stop_glerror();
-
-
-}
-
-// These functions are used as wrappers for our internal event handling callbacks.
-// It's a good idea to wrap these to avoid reworking more code than we need to within LLWindow.
-
-bool callKeyUp(NSKeyEventRef event, unsigned short key, unsigned int mask)
-{
- mRawKeyEvent = event;
- bool retVal = gKeyboard->handleKeyUp(key, mask);
- mRawKeyEvent = NULL;
- return retVal;
-}
-
-bool callKeyDown(NSKeyEventRef event, unsigned short key, unsigned int mask, wchar_t character)
-{
- //if (mask!=MASK_NONE)
- {
- if((key == gKeyboard->inverseTranslateKey('Z')) && (character == 'y'))
- {
- key = gKeyboard->inverseTranslateKey('Y');
- }
- else if ((key == gKeyboard->inverseTranslateKey('Y')) && (character == 'z'))
- {
- key = gKeyboard->inverseTranslateKey('Z');
- }
- }
-
- mRawKeyEvent = event;
- bool retVal = gKeyboard->handleKeyDown(key, mask);
- mRawKeyEvent = NULL;
- return retVal;
-}
-
-void callResetKeys()
-{
- gKeyboard->resetKeys();
-}
-
-bool callUnicodeCallback(wchar_t character, unsigned int mask)
-{
- NativeKeyEventData eventData;
-
- memset(&eventData, 0, sizeof(NativeKeyEventData));
-
- eventData.mKeyEvent = NativeKeyEventData::KEYCHAR;
- eventData.mEventType = 0;
- eventData.mEventModifiers = mask;
- eventData.mEventKeyCode = 0;
- eventData.mEventChars = character;
- eventData.mEventUnmodChars = character;
- eventData.mEventRepeat = false;
-
- mRawKeyEvent = &eventData;
-
- bool result = gWindowImplementation->getCallbacks()->handleUnicodeChar(character, mask);
- mRawKeyEvent = NULL;
- return result;
-}
-
-void callFocus()
-{
- if (gWindowImplementation)
- {
- gWindowImplementation->getCallbacks()->handleFocus(gWindowImplementation);
- }
-}
-
-void callFocusLost()
-{
- if (gWindowImplementation)
- {
- gWindowImplementation->getCallbacks()->handleFocusLost(gWindowImplementation);
- }
-}
-
-void callRightMouseDown(float *pos, MASK mask)
-{
- if (gWindowImplementation->allowsLanguageInput())
- {
- gWindowImplementation->interruptLanguageTextInput();
- }
-
- LLCoordGL outCoords;
- outCoords.mX = ll_round(pos[0]);
- outCoords.mY = ll_round(pos[1]);
- gWindowImplementation->getCallbacks()->handleRightMouseDown(gWindowImplementation, outCoords, gKeyboard->currentMask(true));
-}
-
-void callRightMouseUp(float *pos, MASK mask)
-{
- if (gWindowImplementation->allowsLanguageInput())
- {
- gWindowImplementation->interruptLanguageTextInput();
- }
-
- LLCoordGL outCoords;
- outCoords.mX = ll_round(pos[0]);
- outCoords.mY = ll_round(pos[1]);
- gWindowImplementation->getCallbacks()->handleRightMouseUp(gWindowImplementation, outCoords, gKeyboard->currentMask(true));
-}
-
-void callLeftMouseDown(float *pos, MASK mask)
-{
- if (gWindowImplementation->allowsLanguageInput())
- {
- gWindowImplementation->interruptLanguageTextInput();
- }
-
- LLCoordGL outCoords;
- outCoords.mX = ll_round(pos[0]);
- outCoords.mY = ll_round(pos[1]);
- gWindowImplementation->getCallbacks()->handleMouseDown(gWindowImplementation, outCoords, gKeyboard->currentMask(true));
-}
-
-void callLeftMouseUp(float *pos, MASK mask)
-{
- if (gWindowImplementation->allowsLanguageInput())
- {
- gWindowImplementation->interruptLanguageTextInput();
- }
-
- LLCoordGL outCoords;
- outCoords.mX = ll_round(pos[0]);
- outCoords.mY = ll_round(pos[1]);
- gWindowImplementation->getCallbacks()->handleMouseUp(gWindowImplementation, outCoords, gKeyboard->currentMask(true));
-
-}
-
-void callDoubleClick(float *pos, MASK mask)
-{
- if (gWindowImplementation->allowsLanguageInput())
- {
- gWindowImplementation->interruptLanguageTextInput();
- }
-
- LLCoordGL outCoords;
- outCoords.mX = ll_round(pos[0]);
- outCoords.mY = ll_round(pos[1]);
- gWindowImplementation->getCallbacks()->handleDoubleClick(gWindowImplementation, outCoords, gKeyboard->currentMask(true));
-}
-
-void callResize(unsigned int width, unsigned int height)
-{
- if (gWindowImplementation != NULL)
- {
- gWindowImplementation->getCallbacks()->handleResize(gWindowImplementation, width, height);
- }
-}
-
-void callMouseMoved(float *pos, MASK mask)
-{
- LLCoordGL outCoords;
- outCoords.mX = ll_round(pos[0]);
- outCoords.mY = ll_round(pos[1]);
- float deltas[2];
- gWindowImplementation->getMouseDeltas(deltas);
- outCoords.mX += deltas[0];
- outCoords.mY += deltas[1];
- gWindowImplementation->getCallbacks()->handleMouseMove(gWindowImplementation, outCoords, gKeyboard->currentMask(true));
- //gWindowImplementation->getCallbacks()->handleScrollWheel(gWindowImplementation, 0);
-}
-
-void callMouseDragged(float *pos, MASK mask)
-{
- LLCoordGL outCoords;
- outCoords.mX = ll_round(pos[0]);
- outCoords.mY = ll_round(pos[1]);
- float deltas[2];
- gWindowImplementation->getMouseDeltas(deltas);
- outCoords.mX += deltas[0];
- outCoords.mY += deltas[1];
- gWindowImplementation->getCallbacks()->handleMouseDragged(gWindowImplementation, outCoords, gKeyboard->currentMask(true));
-}
-
-void callScrollMoved(float deltaX, float deltaY)
-{
- if ( gWindowImplementation && gWindowImplementation->getCallbacks() )
- {
- gWindowImplementation->getCallbacks()->handleScrollHWheel(gWindowImplementation, deltaX);
- gWindowImplementation->getCallbacks()->handleScrollWheel(gWindowImplementation, deltaY);
- }
-}
-
-void callMouseExit()
-{
- gWindowImplementation->getCallbacks()->handleMouseLeave(gWindowImplementation);
-}
-
-void callWindowFocus()
-{
- if ( gWindowImplementation && gWindowImplementation->getCallbacks() )
- {
- gWindowImplementation->getCallbacks()->handleFocus (gWindowImplementation);
- }
- else
- {
- LL_WARNS("COCOA") << "Window Implementation or callbacks not yet initialized." << LL_ENDL;
- }
-
-
-}
-
-void callWindowUnfocus()
-{
- if ( gWindowImplementation && gWindowImplementation->getCallbacks() )
- {
- gWindowImplementation->getCallbacks()->handleFocusLost(gWindowImplementation);
- }
-}
-
-void callWindowHide()
-{
- if ( gWindowImplementation && gWindowImplementation->getCallbacks() )
- {
- gWindowImplementation->getCallbacks()->handleActivate(gWindowImplementation, false);
- }
-}
-
-void callWindowUnhide()
-{
- if ( gWindowImplementation && gWindowImplementation->getCallbacks() )
- {
- gWindowImplementation->getCallbacks()->handleActivate(gWindowImplementation, true);
- }
-}
-
-void callWindowDidChangeScreen()
-{
- if ( gWindowImplementation && gWindowImplementation->getCallbacks() )
- {
- gWindowImplementation->getCallbacks()->handleWindowDidChangeScreen(gWindowImplementation);
- }
-}
-
-void callDeltaUpdate(float *delta, MASK mask)
-{
- gWindowImplementation->updateMouseDeltas(delta);
-}
-
-void callOtherMouseDown(float *pos, MASK mask, int button)
-{
- LLCoordGL outCoords;
- outCoords.mX = ll_round(pos[0]);
- outCoords.mY = ll_round(pos[1]);
- float deltas[2];
- gWindowImplementation->getMouseDeltas(deltas);
- outCoords.mX += deltas[0];
- outCoords.mY += deltas[1];
-
- if (button == 2)
- {
- gWindowImplementation->getCallbacks()->handleMiddleMouseDown(gWindowImplementation, outCoords, mask);
- }
- else
- {
- gWindowImplementation->getCallbacks()->handleOtherMouseDown(gWindowImplementation, outCoords, mask, button + 1);
- }
-}
-
-void callOtherMouseUp(float *pos, MASK mask, int button)
-{
- LLCoordGL outCoords;
- outCoords.mX = ll_round(pos[0]);
- outCoords.mY = ll_round(pos[1]);
- float deltas[2];
- gWindowImplementation->getMouseDeltas(deltas);
- outCoords.mX += deltas[0];
- outCoords.mY += deltas[1];
- if (button == 2)
- {
- gWindowImplementation->getCallbacks()->handleMiddleMouseUp(gWindowImplementation, outCoords, mask);
- }
- else
- {
- gWindowImplementation->getCallbacks()->handleOtherMouseUp(gWindowImplementation, outCoords, mask, button + 1);
- }
-}
-
-void callModifier(MASK mask)
-{
- gKeyboard->handleModifier(mask);
-}
-
-void callHandleDragEntered(std::string url)
-{
- gWindowImplementation->handleDragNDrop(url, LLWindowCallbacks::DNDA_START_TRACKING);
-}
-
-void callHandleDragExited(std::string url)
-{
- gWindowImplementation->handleDragNDrop(url, LLWindowCallbacks::DNDA_STOP_TRACKING);
-}
-
-void callHandleDragUpdated(std::string url)
-{
- gWindowImplementation->handleDragNDrop(url, LLWindowCallbacks::DNDA_TRACK);
-}
-
-void callHandleDragDropped(std::string url)
-{
- gWindowImplementation->handleDragNDrop(url, LLWindowCallbacks::DNDA_DROPPED);
-}
-
-void callQuitHandler()
-{
- if (gWindowImplementation)
- {
- if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation))
- {
- gWindowImplementation->getCallbacks()->handleQuit(gWindowImplementation);
- }
- }
-}
-
-void getPreeditSelectionRange(int *position, int *length)
-{
- if (gWindowImplementation->getPreeditor())
- {
- gWindowImplementation->getPreeditor()->getSelectionRange(position, length);
- }
-}
-
-void getPreeditMarkedRange(int *position, int *length)
-{
- if (gWindowImplementation->getPreeditor())
- {
- gWindowImplementation->getPreeditor()->getPreeditRange(position, length);
- }
-}
-
-void setPreeditMarkedRange(int position, int length)
-{
- if (gWindowImplementation->getPreeditor())
- {
- gWindowImplementation->getPreeditor()->markAsPreedit(position, length);
- }
-}
-
-bool handleUnicodeCharacter(wchar_t c)
-{
- bool success = false;
- if (gWindowImplementation->getPreeditor())
- {
- success = gWindowImplementation->getPreeditor()->handleUnicodeCharHere(c);
- }
-
- return success;
-}
-
-void resetPreedit()
-{
- if (gWindowImplementation->getPreeditor())
- {
- gWindowImplementation->getPreeditor()->resetPreedit();
- }
-}
-
-// For reasons of convenience, handle IME updates here.
-// This largely mirrors the old implementation, only sans the carbon parameters.
-void setMarkedText(unsigned short *unitext, unsigned int *selectedRange, unsigned int *replacementRange, long text_len, attributedStringInfo segments)
-{
- if (gWindowImplementation->getPreeditor())
- {
- LLPreeditor *preeditor = gWindowImplementation->getPreeditor();
- preeditor->resetPreedit();
- // This should be a viable replacement for the kEventParamTextInputSendReplaceRange parameter.
- if (replacementRange[0] < replacementRange[1])
- {
- const LLWString& text = preeditor->getPreeditString();
- const S32 location = wstring_wstring_length_from_utf16_length(text, 0, replacementRange[0]);
- const S32 length = wstring_wstring_length_from_utf16_length(text, location, replacementRange[1]);
- preeditor->markAsPreedit(location, length);
- }
-
- LLWString fix_str = utf16str_to_wstring(llutf16string(unitext, text_len));
-
- S32 caret_position = fix_str.length();
-
- preeditor->updatePreedit(fix_str, segments.seg_lengths, segments.seg_standouts, caret_position);
- }
-}
-
-void getPreeditLocation(float *location, unsigned int length)
-{
- if (gWindowImplementation->getPreeditor())
- {
- LLPreeditor *preeditor = gWindowImplementation->getPreeditor();
- LLCoordGL coord;
- LLCoordScreen screen;
- LLRect rect;
-
- preeditor->getPreeditLocation(length, &coord, &rect, NULL);
-
- float c[4] = {float(coord.mX), float(coord.mY), 0, 0};
-
- convertRectToScreen(gWindowImplementation->getWindow(), c);
-
- location[0] = c[0];
- location[1] = c[1];
- }
-}
-
-void LLWindowMacOSX::updateMouseDeltas(float* deltas)
-{
- if (mCursorDecoupled)
- {
- mCursorLastEventDeltaX = ll_round(deltas[0]);
- mCursorLastEventDeltaY = ll_round(-deltas[1]);
-
- if (mCursorIgnoreNextDelta)
- {
- mCursorLastEventDeltaX = 0;
- mCursorLastEventDeltaY = 0;
- mCursorIgnoreNextDelta = false;
- }
- } else {
- mCursorLastEventDeltaX = 0;
- mCursorLastEventDeltaY = 0;
- }
-}
-
-void LLWindowMacOSX::getMouseDeltas(float* delta)
-{
- delta[0] = mCursorLastEventDeltaX;
- delta[1] = mCursorLastEventDeltaY;
-}
-
-bool LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync)
-{
- mFullscreen = fullscreen;
-
- if (mWindow == NULL)
- {
- mWindow = getMainAppWindow();
- }
-
- if(mContext == NULL)
- {
- // Our OpenGL view is already defined within SecondLife.xib.
- // Get the view instead.
- mGLView = createOpenGLView(mWindow, mFSAASamples, enable_vsync);
- mContext = getCGLContextObj(mGLView);
- gGLManager.mVRAM = getVramSize(mGLView);
-
- if(!mPixelFormat)
- {
- CGLPixelFormatAttribute attribs[] =
- {
- kCGLPFANoRecovery,
- kCGLPFADoubleBuffer,
- kCGLPFAClosestPolicy,
- kCGLPFAAccelerated,
- kCGLPFAMultisample,
- kCGLPFASampleBuffers, static_cast<CGLPixelFormatAttribute>((mFSAASamples > 0 ? 1 : 0)),
- kCGLPFASamples, static_cast<CGLPixelFormatAttribute>(mFSAASamples),
- kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8),
- kCGLPFADepthSize, static_cast<CGLPixelFormatAttribute>(24),
- kCGLPFAAlphaSize, static_cast<CGLPixelFormatAttribute>(8),
- kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24),
- kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_GL4_Core),
- static_cast<CGLPixelFormatAttribute>(0)
- };
-
- GLint numPixelFormats;
- CGLChoosePixelFormat (attribs, &mPixelFormat, &numPixelFormats);
-
- if(mPixelFormat == NULL) {
- CGLChoosePixelFormat (attribs, &mPixelFormat, &numPixelFormats);
- }
- }
-
- }
-
- // This sets up our view to recieve text from our non-inline text input window.
- setupInputWindow(mWindow, mGLView);
-
- // Hook up the context to a drawable
-
- if(mContext != NULL)
- {
-
-
- U32 err = CGLSetCurrentContext(mContext);
- if (err != kCGLNoError)
- {
- setupFailure("Can't activate GL rendering context", "Error", OSMB_OK);
- return false;
- }
- }
-
- mRefreshRate = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(mDisplay));
- if(mRefreshRate == 0)
- {
- //consider adding more appropriate fallback later
- mRefreshRate = DEFAULT_REFRESH_RATE;
- }
-
- // Disable vertical sync for swap
- toggleVSync(enable_vsync);
-
- //enable multi-threaded OpenGL
- if (sUseMultGL)
- {
- CGLError cgl_err;
- CGLContextObj ctx = CGLGetCurrentContext();
-
- cgl_err = CGLEnable( ctx, kCGLCEMPEngine);
-
- if (cgl_err != kCGLNoError )
- {
- LL_INFOS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL;
- }
- else
- {
- LL_INFOS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
- }
- }
- makeFirstResponder(mWindow, mGLView);
-
- return true;
-}
-
-
-// We only support OS X 10.7's fullscreen app mode which is literally a full screen window that fills a virtual desktop.
-// This makes this method obsolete.
-bool LLWindowMacOSX::switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp)
-{
- return false;
-}
-
-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)
- {
- LL_DEBUGS("Window") << "destroyContext: unhooking drawable " << LL_ENDL;
- CGLSetCurrentContext(NULL);
- }
-
- // Clean up remaining GL state before blowing away window
- gGLManager.shutdownGL();
-
- // Clean up the pixel format
- if(mPixelFormat != NULL)
- {
- CGLDestroyPixelFormat(mPixelFormat);
- mPixelFormat = NULL;
- }
-
- // Clean up the GL context
- if(mContext != NULL)
- {
- CGLDestroyContext(mContext);
- }
-
- // Destroy our LLOpenGLView
- if(mGLView != NULL)
- {
- removeGLView(mGLView);
- mGLView = NULL;
- }
-
- // Close the window
- if(mWindow != NULL)
- {
- NSWindowRef dead_window = mWindow;
- mWindow = NULL;
- closeWindow(dead_window);
- }
-
-}
-
-LLWindowMacOSX::~LLWindowMacOSX()
-{
- destroyContext();
-
- if(mSupportedResolutions != NULL)
- {
- delete []mSupportedResolutions;
- }
-
- gWindowImplementation = NULL;
-
-}
-
-
-void LLWindowMacOSX::show()
-{
-}
-
-void LLWindowMacOSX::hide()
-{
- setMouseClipping(false);
-}
-
-//virtual
-void LLWindowMacOSX::minimize()
-{
- setMouseClipping(false);
- showCursor();
-}
-
-//virtual
-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)
- {
- result = true;
- }
-
- return(result);
-}
-
-bool LLWindowMacOSX::getMinimized()
-{
- return mMinimized;
-}
-
-bool LLWindowMacOSX::getMaximized()
-{
- return mMaximized;
-}
-
-bool LLWindowMacOSX::maximize()
-{
- if (mWindow && !mMaximized)
- {
- }
-
- return mMaximized;
-}
-
-bool LLWindowMacOSX::getFullscreen()
-{
- return mFullscreen;
-}
-
-void LLWindowMacOSX::gatherInput()
-{
- updateCursor();
-}
-
-bool LLWindowMacOSX::getPosition(LLCoordScreen *position)
-{
- S32 err = -1;
-
- if(mFullscreen)
- {
- position->mX = 0;
- position->mY = 0;
- err = noErr;
- }
- else if(mWindow)
- {
- const CGPoint & pos = getContentViewBoundsPosition(mWindow);
-
- position->mX = pos.x;
- position->mY = pos.y;
-
- err = noErr;
- }
- else
- {
- LL_ERRS() << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << LL_ENDL;
- }
-
- return (err == noErr);
-}
-
-bool LLWindowMacOSX::getSize(LLCoordScreen *size)
-{
- S32 err = -1;
-
- if(mFullscreen)
- {
- size->mX = mFullscreenWidth;
- size->mY = mFullscreenHeight;
- err = noErr;
- }
- else if(mWindow)
- {
- const CGSize & sz = gHiDPISupport ? getDeviceContentViewSize(mWindow, mGLView) : getContentViewBoundsSize(mWindow);
-
- size->mX = sz.width;
- size->mY = sz.height;
- err = noErr;
- }
- else
- {
- LL_ERRS() << "LLWindowMacOSX::getSize(): no window and not fullscreen!" << LL_ENDL;
- }
-
- return (err == noErr);
-}
-
-bool LLWindowMacOSX::getSize(LLCoordWindow *size)
-{
- S32 err = -1;
-
- if(mFullscreen)
- {
- size->mX = mFullscreenWidth;
- size->mY = mFullscreenHeight;
- err = noErr;
- }
- else if(mWindow)
- {
- const CGSize & sz = gHiDPISupport ? getDeviceContentViewSize(mWindow, mGLView) : getContentViewBoundsSize(mWindow);
-
- size->mX = sz.width;
- size->mY = sz.height;
- err = noErr;
-
-
- }
- else
- {
- LL_ERRS() << "LLWindowMacOSX::getSize(): no window and not fullscreen!" << LL_ENDL;
- }
-
- return (err == noErr);
-}
-
-bool LLWindowMacOSX::setPosition(const LLCoordScreen position)
-{
- if(mWindow)
- {
- float pos[2] = {float(position.mX), float(position.mY)};
- setWindowPos(mWindow, pos);
- }
-
- return true;
-}
-
-bool LLWindowMacOSX::setSizeImpl(const LLCoordScreen size)
-{
- if(mWindow)
- {
- LLCoordWindow to;
- convertCoords(size, &to);
- setWindowSize(mWindow, to.mX, to.mY);
- return true;
- }
-
- return false;
-}
-
-bool LLWindowMacOSX::setSizeImpl(const LLCoordWindow size)
-{
- if (mWindow)
- {
- const int titlePadding = 22;
- setWindowSize(mWindow, size.mX, size.mY + titlePadding);
- return true;
- }
-
- return false;
-}
-
-void LLWindowMacOSX::swapBuffers()
-{
- CGLFlushDrawable(mContext);
-}
-
-void LLWindowMacOSX::restoreGLContext()
-{
- CGLSetCurrentContext(mContext);
-}
-
-F32 LLWindowMacOSX::getGamma()
-{
- F32 result = 2.2; // 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;
-}
-
-U32 LLWindowMacOSX::getFSAASamples()
-{
- return mFSAASamples;
-}
-
-void LLWindowMacOSX::setFSAASamples(const U32 samples)
-{
- mFSAASamples = samples;
- mForceRebuild = true;
-}
-
-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)
- {
- // LL_INFOS() << "setMouseClipping(true)" << LL_ENDL;
- }
- else
- {
- // LL_INFOS() << "setMouseClipping(false)" << LL_ENDL;
- }
-
- adjustCursorDecouple();
-}
-
-bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
-{
- bool result = false;
- LLCoordScreen screen_pos;
-
- if (!convertCoords(position, &screen_pos))
- {
- return false;
- }
-
- CGPoint newPosition;
-
- // LL_INFOS() << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << LL_ENDL;
-
- 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);
-
- // trigger mouse move callback
- LLCoordGL gl_pos;
- convertCoords(position, &gl_pos);
- float scale = getSystemUISize();
- gl_pos.mX *= scale;
- gl_pos.mY *= scale;
- mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
-
- return result;
-}
-
-bool LLWindowMacOSX::getCursorPosition(LLCoordWindow *position)
-{
- float cursor_point[2];
- LLCoordScreen screen_pos;
-
- if(mWindow == NULL)
- return false;
-
- getCursorPos(mWindow, cursor_point);
-
- 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[0] += mCursorLastEventDeltaX;
- cursor_point[1] += mCursorLastEventDeltaY;
- }
-
- float scale = getSystemUISize();
- position->mX = cursor_point[0] * scale;
- position->mY = cursor_point[1] * scale;
-
- return true;
-}
-
-void LLWindowMacOSX::adjustCursorDecouple(bool warpingMouse)
-{
- if(mIsMouseClipping && mCursorHidden)
- {
- if(warpingMouse)
- {
- // The cursor should be decoupled. Make sure it is.
- if(!mCursorDecoupled)
- {
- // LL_INFOS() << "adjustCursorDecouple: decoupling cursor" << LL_ENDL;
- CGAssociateMouseAndMouseCursorPosition(false);
- mCursorDecoupled = true;
- mCursorIgnoreNextDelta = true;
- }
- }
- }
- else
- {
- // The cursor should not be decoupled. Make sure it isn't.
- if(mCursorDecoupled)
- {
- // LL_INFOS() << "adjustCursorDecouple: recoupling cursor" << LL_ENDL;
- 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()
-{
- //macOS 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...
-
-// Since we're no longer supporting the "typical" fullscreen mode with CGL or NSOpenGL anymore, these are unnecessary. -Geenz
-void LLWindowMacOSX::beforeDialog()
-{
-}
-
-void LLWindowMacOSX::afterDialog()
-{
- //For fix problem with Core Flow view on OSX
- restoreGLContext();
-}
-
-
-void LLWindowMacOSX::flashIcon(F32 seconds)
-{
- // For consistency with macOS conventions, the number of seconds given is ignored and
- // left up to the OS (which will actually bounce it for one second).
- requestUserAttention();
-}
-
-bool LLWindowMacOSX::isClipboardTextAvailable()
-{
- return pasteBoardAvailable();
-}
-
-bool LLWindowMacOSX::pasteTextFromClipboard(LLWString &dst)
-{
- unsigned short* pboard_data = copyFromPBoard(); // must free returned data
- llutf16string str(pboard_data);
- free(pboard_data);
-
- dst = utf16str_to_wstring(str);
- if (dst != L"")
- {
- return true;
- } else {
- return false;
- }
-}
-
-bool LLWindowMacOSX::copyTextToClipboard(const LLWString &s)
-{
- bool result = false;
- llutf16string utf16str = wstring_to_utf16str(s);
-
- result = copyToPBoard(utf16str.data(), utf16str.length());
-
- return result;
-}
-
-
-// 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 = CGDisplayCopyAllDisplayModes(mDisplay, nullptr);
-
- 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++;
- }
- }
- }
- CFRelease(modes);
- }
- }
-
- num_resolutions = mNumSupportedResolutions;
- return mSupportedResolutions;
-}
-
-bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to)
-{
- to->mX = from.mX;
- to->mY = from.mY;
- return true;
-}
-
-bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to)
-{
- to->mX = from.mX;
- to->mY = from.mY;
- return true;
-}
-
-bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
-{
- if(mWindow)
- {
- float mouse_point[2];
-
- mouse_point[0] = from.mX;
- mouse_point[1] = from.mY;
-
- convertScreenToWindow(mWindow, mouse_point);
-
- to->mX = mouse_point[0];
- to->mY = mouse_point[1];
-
- return true;
- }
- return false;
-}
-
-bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to)
-{
- if(mWindow)
- {
- float mouse_point[2];
-
- mouse_point[0] = from.mX;
- mouse_point[1] = from.mY;
-
- convertWindowToScreen(mWindow, mouse_point);
-
- to->mX = mouse_point[0];
- to->mY = mouse_point[1];
-
- 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 std::string& text, const std::string& caption, U32 type)
-{
- destroyContext();
-
- OSMessageBox(text, caption, type);
-}
-
- // Note on event recording - QUIT is a known special case and we are choosing NOT to record it for the record and playback feature
- // it is handled at a very low-level
-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_SIZEALL: return "UI_CURSOR_SIZEALL";
- 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_TOOLZOOMOUT: return "UI_CURSOR_TOOLZOOMOUT";
- case UI_CURSOR_TOOLPICKOBJECT3: return "UI_CURSOR_TOOLPICKOBJECT3";
- case UI_CURSOR_TOOLPLAY: return "UI_CURSOR_TOOLPLAY";
- case UI_CURSOR_TOOLPAUSE: return "UI_CURSOR_TOOLPAUSE";
- case UI_CURSOR_TOOLMEDIAOPEN: return "UI_CURSOR_TOOLMEDIAOPEN";
- case UI_CURSOR_PIPETTE: return "UI_CURSOR_PIPETTE";
- case UI_CURSOR_TOOLSIT: return "UI_CURSOR_TOOLSIT";
- case UI_CURSOR_TOOLBUY: return "UI_CURSOR_TOOLBUY";
- case UI_CURSOR_TOOLOPEN: return "UI_CURSOR_TOOLOPEN";
- case UI_CURSOR_TOOLPATHFINDING: return "UI_CURSOR_PATHFINDING";
- case UI_CURSOR_TOOLPATHFINDING_PATH_START: return "UI_CURSOR_PATHFINDING_START";
- case UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD: return "UI_CURSOR_PATHFINDING_START_ADD";
- case UI_CURSOR_TOOLPATHFINDING_PATH_END: return "UI_CURSOR_PATHFINDING_END";
- case UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD: return "UI_CURSOR_PATHFINDING_END_ADD";
- case UI_CURSOR_TOOLNO: return "UI_CURSOR_NO";
- }
-
- LL_ERRS() << "cursorIDToName: unknown cursor id" << id << LL_ENDL;
-
- return "UI_CURSOR_ARROW";
-}
-
-static CursorRef gCursors[UI_CURSOR_COUNT];
-
-
-static void initPixmapCursor(int cursorid, int hotspotX, int hotspotY)
-{
- // cursors are in <Application Bundle>/Contents/Resources/cursors_mac/UI_CURSOR_FOO.tif
- std::string fullpath = gDirUtilp->add(
- gDirUtilp->getAppRODataDir(),
- "cursors_mac",
- cursorIDToName(cursorid) + std::string(".tif"));
-
- gCursors[cursorid] = createImageCursor(fullpath.c_str(), hotspotX, hotspotY);
-}
-
-void LLWindowMacOSX::updateCursor()
-{
- S32 result = 0;
-
- if (mDragOverrideCursor != -1)
- {
- // A drag is in progress...remember the requested cursor and we'll
- // restore it when it is done
- mCurrentCursor = mNextCursor;
- return;
- }
-
- if (mNextCursor == UI_CURSOR_ARROW
- && mBusyCount > 0)
- {
- mNextCursor = UI_CURSOR_WORKING;
- }
-
- if(mCurrentCursor == mNextCursor)
- {
- if(mCursorHidden && mHideCursorPermanent && isCGCursorVisible())
- {
- hideNSCursor();
- adjustCursorDecouple();
- }
- return;
- }
-
- // RN: replace multi-drag cursors with single versions
- if (mNextCursor == UI_CURSOR_ARROWDRAGMULTI)
- {
- mNextCursor = UI_CURSOR_ARROWDRAG;
- }
- else if (mNextCursor == UI_CURSOR_ARROWCOPYMULTI)
- {
- mNextCursor = UI_CURSOR_ARROWCOPY;
- }
-
- switch(mNextCursor)
- {
- default:
- case UI_CURSOR_ARROW:
- setArrowCursor();
- if(mCursorHidden)
- {
- // Since InitCursor resets the hide level, correct for it here.
- hideNSCursor();
- }
- 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: /* Apple purposely doesn't allow us to set the beachball cursor manually. Let NSApp figure out when to do this. */ break;
- case UI_CURSOR_IBEAM: setIBeamCursor(); break;
- case UI_CURSOR_CROSS: setCrossCursor(); break;
- case UI_CURSOR_HAND: setPointingHandCursor(); break;
- // case UI_CURSOR_NO: SetThemeCursor(kThemeNotAllowedCursor); break;
- case UI_CURSOR_ARROWCOPY: setCopyCursor(); 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_PIPETTE:
- 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_TOOLPLAY:
- case UI_CURSOR_TOOLPAUSE:
- case UI_CURSOR_TOOLMEDIAOPEN:
- case UI_CURSOR_TOOLSIT:
- case UI_CURSOR_TOOLBUY:
- case UI_CURSOR_TOOLOPEN:
- case UI_CURSOR_TOOLPATHFINDING:
- case UI_CURSOR_TOOLPATHFINDING_PATH_START:
- case UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD:
- case UI_CURSOR_TOOLPATHFINDING_PATH_END:
- case UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD:
- case UI_CURSOR_TOOLNO:
- result = setImageCursor(gCursors[mNextCursor]);
- break;
-
- }
-
- if(result != noErr)
- {
- setArrowCursor();
- }
-
- mCurrentCursor = mNextCursor;
-}
-
-ECursorType LLWindowMacOSX::getCursor() const
-{
- 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_PIPETTE, 3, 29);
- 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_TOOLZOOMOUT, 7, 6);
- initPixmapCursor(UI_CURSOR_TOOLPICKOBJECT3, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLPLAY, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLPAUSE, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLMEDIAOPEN, 1, 1);
- initPixmapCursor(UI_CURSOR_TOOLSIT, 20, 15);
- initPixmapCursor(UI_CURSOR_TOOLBUY, 20, 15);
- initPixmapCursor(UI_CURSOR_TOOLOPEN, 20, 15);
- initPixmapCursor(UI_CURSOR_TOOLPATHFINDING, 16, 16);
- initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_START, 16, 16);
- initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD, 16, 16);
- initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_END, 16, 16);
- initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD, 16, 16);
- initPixmapCursor(UI_CURSOR_TOOLNO, 8, 8);
-
- initPixmapCursor(UI_CURSOR_SIZENWSE, 10, 10);
- initPixmapCursor(UI_CURSOR_SIZENESW, 10, 10);
- initPixmapCursor(UI_CURSOR_SIZEWE, 10, 10);
- initPixmapCursor(UI_CURSOR_SIZENS, 10, 10);
- initPixmapCursor(UI_CURSOR_SIZEALL, 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)
- {
- // LL_INFOS() << "hideCursor: hiding" << LL_ENDL;
- mCursorHidden = true;
- mHideCursorPermanent = true;
- hideNSCursor();
- }
- else
- {
- // LL_INFOS() << "hideCursor: already hidden" << LL_ENDL;
- }
-
- adjustCursorDecouple();
-}
-
-void LLWindowMacOSX::showCursor()
-{
- if(mCursorHidden || !isCGCursorVisible())
- {
- // LL_INFOS() << "showCursor: showing" << LL_ENDL;
- mCursorHidden = false;
- mHideCursorPermanent = false;
- showNSCursor();
- }
- else
- {
- // LL_INFOS() << "showCursor: already visible" << LL_ENDL;
- }
-
- 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...
-}
-
-void LLSplashScreenMacOSX::updateImpl(const std::string& mesg)
-{
- if(mWindow != NULL)
- {
- CFStringCreateWithCString(NULL, mesg.c_str(), kCFStringEncodingUTF8);
- }
-}
-
-
-void LLSplashScreenMacOSX::hideImpl()
-{
- if(mWindow != NULL)
- {
- mWindow = NULL;
- }
-}
-
-S32 OSMessageBoxMacOSX(const std::string& text, const std::string& caption, U32 type)
-{
- return showAlert(text, caption, type);
-}
-
-// Open a URL with the user's default web browser.
-// Must begin with protocol identifier.
-void LLWindowMacOSX::spawnWebBrowser(const std::string& escaped_url, bool async)
-{
- // I'm fairly certain that this is all legitimate under Apple's currently supported APIs.
-
- bool found = false;
- S32 i;
- for (i = 0; i < gURLProtocolWhitelistCount; i++)
- {
- if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos)
- {
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- LL_WARNS() << "spawn_web_browser called for url with protocol not on whitelist: " << escaped_url << LL_ENDL;
- return;
- }
-
- S32 result = 0;
- CFURLRef urlRef = NULL;
-
- LL_INFOS() << "Opening URL " << escaped_url << LL_ENDL;
-
- CFStringRef stringRef = CFStringCreateWithCString(NULL, escaped_url.c_str(), 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)
- {
- LL_INFOS() << "Error " << result << " on open." << LL_ENDL;
- }
-
- CFRelease(urlRef);
- }
- else
- {
- LL_INFOS() << "Error: couldn't create URL." << LL_ENDL;
- }
-}
-
-// String should match ndof, so string mapping code was copied as is
-static char mapChar( char c )
-{
- unsigned char uc = ( unsigned char ) c;
-
- switch( uc )
- {
- case '/': return '-'; // use dash instead of slash
-
- case 0x7F: return ' ';
- case 0x80: return 'A';
- case 0x81: return 'A';
- case 0x82: return 'C';
- case 0x83: return 'E';
- case 0x84: return 'N';
- case 0x85: return 'O';
- case 0x86: return 'U';
- case 0x87: return 'a';
- case 0x88: return 'a';
- case 0x89: return 'a';
- case 0x8A: return 'a';
- case 0x8B: return 'a';
- case 0x8C: return 'a';
- case 0x8D: return 'c';
- case 0x8E: return 'e';
- case 0x8F: return 'e';
- case 0x90: return ' ';
- case 0x91: return ' '; // ? '
- case 0x92: return ' '; // ? '
- case 0x93: return ' '; // ? "
- case 0x94: return ' '; // ? "
- case 0x95: return ' ';
- case 0x96: return ' ';
- case 0x97: return ' ';
- case 0x98: return ' ';
- case 0x99: return ' ';
- case 0x9A: return ' ';
- case 0x9B: return 0x27;
- case 0x9C: return 0x22;
- case 0x9D: return ' ';
- case 0x9E: return ' ';
- case 0x9F: return ' ';
- case 0xA0: return ' ';
- case 0xA1: return ' ';
- case 0xA2: return ' ';
- case 0xA3: return ' ';
- case 0xA4: return ' ';
- case 0xA5: return ' ';
- case 0xA6: return ' ';
- case 0xA7: return ' ';
- case 0xA8: return ' ';
- case 0xA9: return ' ';
- case 0xAA: return ' ';
- case 0xAB: return ' ';
- case 0xAC: return ' ';
- case 0xAD: return ' ';
- case 0xAE: return ' ';
- case 0xAF: return ' ';
- case 0xB0: return ' ';
- case 0xB1: return ' ';
- case 0xB2: return ' ';
- case 0xB3: return ' ';
- case 0xB4: return ' ';
- case 0xB5: return ' ';
- case 0xB6: return ' ';
- case 0xB7: return ' ';
- case 0xB8: return ' ';
- case 0xB9: return ' ';
- case 0xBA: return ' ';
- case 0xBB: return ' ';
- case 0xBC: return ' ';
- case 0xBD: return ' ';
- case 0xBE: return ' ';
- case 0xBF: return ' ';
- case 0xC0: return ' ';
- case 0xC1: return ' ';
- case 0xC2: return ' ';
- case 0xC3: return ' ';
- case 0xC4: return ' ';
- case 0xC5: return ' ';
- case 0xC6: return ' ';
- case 0xC7: return ' ';
- case 0xC8: return ' ';
- case 0xC9: return ' ';
- case 0xCA: return ' ';
- case 0xCB: return 'A';
- case 0xCC: return 'A';
- case 0xCD: return 'O';
- case 0xCE: return ' ';
- case 0xCF: return ' ';
- case 0xD0: return '-';
- case 0xD1: return '-';
- case 0xD2: return 0x22;
- case 0xD3: return 0x22;
- case 0xD4: return 0x27;
- case 0xD5: return 0x27;
- case 0xD6: return '-'; // use dash instead of slash
- case 0xD7: return ' ';
- case 0xD8: return 'y';
- case 0xD9: return 'Y';
- case 0xDA: return '-'; // use dash instead of slash
- case 0xDB: return ' ';
- case 0xDC: return '<';
- case 0xDD: return '>';
- case 0xDE: return ' ';
- case 0xDF: return ' ';
- case 0xE0: return ' ';
- case 0xE1: return ' ';
- case 0xE2: return ',';
- case 0xE3: return ',';
- case 0xE4: return ' ';
- case 0xE5: return 'A';
- case 0xE6: return 'E';
- case 0xE7: return 'A';
- case 0xE8: return 'E';
- case 0xE9: return 'E';
- case 0xEA: return 'I';
- case 0xEB: return 'I';
- case 0xEC: return 'I';
- case 0xED: return 'I';
- case 0xEE: return 'O';
- case 0xEF: return 'O';
- case 0xF0: return ' ';
- case 0xF1: return 'O';
- case 0xF2: return 'U';
- case 0xF3: return 'U';
- case 0xF4: return 'U';
- case 0xF5: return '|';
- case 0xF6: return ' ';
- case 0xF7: return ' ';
- case 0xF8: return ' ';
- case 0xF9: return ' ';
- case 0xFA: return '.';
- case 0xFB: return ' ';
- case 0xFC: return ' ';
- case 0xFD: return 0x22;
- case 0xFE: return ' ';
- case 0xFF: return ' ';
- }
- return c;
-}
-
-// String should match ndof for manufacturer based search to work
-static void sanitizeString( char* inCStr )
-{
- char* charIt = inCStr;
- while ( *charIt )
- {
- *charIt = mapChar( *charIt );
- charIt++;
- }
-}
-
-struct HidDevice
-{
- long mAxis;
- long mLocalID;
- char mProduct[256];
- char mManufacturer[256];
- long mUsage;
- long mUsagePage;
-};
-
-static void populate_device_info( io_object_t io_obj_p, CFDictionaryRef device_dic, HidDevice* devicep )
-{
- CFMutableDictionaryRef io_properties = nil;
- io_registry_entry_t entry1;
- io_registry_entry_t entry2;
- kern_return_t rc;
-
- // Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
- // get dictionary for usb properties: step up two levels and get CF dictionary for USB properties
- // try to get parent1
- rc = IORegistryEntryGetParentEntry( io_obj_p, kIOServicePlane, &entry1 );
- if ( KERN_SUCCESS == rc )
- {
- rc = IORegistryEntryGetParentEntry( entry1, kIOServicePlane, &entry2 );
-
- IOObjectRelease( entry1 );
-
- if ( KERN_SUCCESS == rc )
- {
- rc = IORegistryEntryCreateCFProperties( entry2, &io_properties, kCFAllocatorDefault, kNilOptions );
- // either way, release parent2
- IOObjectRelease( entry2 );
- }
- }
- if ( KERN_SUCCESS == rc )
- {
- // IORegistryEntryCreateCFProperties() succeeded
- if ( io_properties != nil )
- {
- CFTypeRef dict_element = 0;
- // get device info
- // try hid dictionary first, if fail then go to usb dictionary
-
-
- dict_element = CFDictionaryGetValue( device_dic, CFSTR(kIOHIDProductKey) );
- if ( !dict_element )
- {
- dict_element = CFDictionaryGetValue( io_properties, CFSTR( "USB Product Name" ) );
- }
- if ( dict_element )
- {
- bool res = CFStringGetCString((CFStringRef)dict_element, devicep->mProduct, 256, kCFStringEncodingUTF8);
- sanitizeString(devicep->mProduct);
- if ( !res )
- {
- LL_WARNS("Joystick") << "Failed to populate mProduct" << LL_ENDL;
- }
- }
-
- dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDManufacturerKey ) );
- if ( !dict_element )
- {
- dict_element = CFDictionaryGetValue( io_properties, CFSTR( "USB Vendor Name" ) );
- }
- if ( dict_element )
- {
- bool res = CFStringGetCString( (CFStringRef)dict_element, devicep->mManufacturer, 256, kCFStringEncodingUTF8 );
- sanitizeString(devicep->mManufacturer);
- if ( !res )
- {
- LL_WARNS("Joystick") << "Failed to populate mManufacturer" << LL_ENDL;
- }
- }
-
- dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDLocationIDKey ) );
- if ( !dict_element )
- {
- dict_element = CFDictionaryGetValue( io_properties, CFSTR( "locationID" ) );
- }
- if ( dict_element )
- {
- bool res = CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mLocalID );
- if ( !res )
- {
- LL_WARNS("Joystick") << "Failed to populate mLocalID" << LL_ENDL;
- }
- }
-
- dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDPrimaryUsagePageKey ) );
- if ( dict_element )
- {
- bool res = CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mUsagePage );
- if ( !res )
- {
- LL_WARNS("Joystick") << "Failed to populate mUsagePage" << LL_ENDL;
- }
- dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDPrimaryUsageKey ) );
- if ( dict_element )
- {
- if ( !CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mUsage ) )
- {
- LL_WARNS("Joystick") << "Failed to populate mUsage" << LL_ENDL;
- }
- }
- }
-
- //Add axis, because ndof lib checks sutability by axises as well as other elements
- devicep->mAxis = 0;
- CFTypeRef hid_elements = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDElementKey ) );
- if ( hid_elements && CFGetTypeID( hid_elements ) == CFArrayGetTypeID( ) )
- {
- long count = CFArrayGetCount( (CFArrayRef) hid_elements );
- for (int i = 0; i < count; ++i)
- {
- CFTypeRef element = CFArrayGetValueAtIndex((CFArrayRef) hid_elements, i);
- if (element && CFGetTypeID( element ) == CFDictionaryGetTypeID( ))
- {
- long type = 0, usage_page = 0, usage = 0;
-
- CFTypeRef ref_value = CFDictionaryGetValue( (CFDictionaryRef) element, CFSTR( kIOHIDElementTypeKey ) );
- if ( ref_value )
- {
- CFNumberGetValue( (CFNumberRef)ref_value, kCFNumberLongType, &type );
- }
-
- ref_value = CFDictionaryGetValue( (CFDictionaryRef) element, CFSTR( kIOHIDElementUsagePageKey ) );
- if ( ref_value )
- {
- CFNumberGetValue( (CFNumberRef)ref_value, kCFNumberLongType, &usage_page );
- }
-
- ref_value = CFDictionaryGetValue( (CFDictionaryRef) element, CFSTR( kIOHIDElementUsageKey ) );
- if ( ref_value )
- {
- CFNumberGetValue( (CFNumberRef)ref_value, kCFNumberLongType, &usage );
- }
- if ( type != 0
- && type != kIOHIDElementTypeCollection
- && usage_page == kHIDPage_GenericDesktop)
- {
- switch( usage )
- {
- case kHIDUsage_GD_X:
- case kHIDUsage_GD_Y:
- case kHIDUsage_GD_Z:
- case kHIDUsage_GD_Rx:
- case kHIDUsage_GD_Ry:
- case kHIDUsage_GD_Rz:
- devicep->mAxis++;
- break;
- default:
- break;
- }
- }
- }
- }
- }
-
- CFRelease(io_properties);
- }
- else
- {
- LL_WARNS("Joystick") << "Failed to populate fields" << LL_ENDL;
- }
- }
-}
-
-HidDevice populate_device( io_object_t io_obj )
-{
- void* interfacep = nullptr;
- HidDevice device;
- memset( &device, 0, sizeof( HidDevice ) );
- CFMutableDictionaryRef device_dic = 0;
- kern_return_t result = IORegistryEntryCreateCFProperties( io_obj, &device_dic, kCFAllocatorDefault, kNilOptions );
-
- if ( KERN_SUCCESS == result
- && device_dic )
- {
- IOReturn io_result = kIOReturnSuccess;
- HRESULT query_result = S_OK;
- SInt32 the_score = 0;
- IOCFPlugInInterface **the_interface = NULL;
-
-
- io_result = IOCreatePlugInInterfaceForService( io_obj, kIOHIDDeviceUserClientTypeID,
- kIOCFPlugInInterfaceID, &the_interface, &the_score );
- if ( io_result == kIOReturnSuccess )
- {
- query_result = ( *the_interface )->QueryInterface( the_interface, CFUUIDGetUUIDBytes( kIOHIDDeviceInterfaceID ), ( LPVOID * ) & ( interfacep ) );
- if ( query_result != S_OK )
- {
- LL_WARNS("Joystick") << "QueryInterface failed" << LL_ENDL;
- }
- IODestroyPlugInInterface( the_interface );
- }
- else
- {
- LL_WARNS("Joystick") << "IOCreatePlugInInterfaceForService failed" << LL_ENDL;
- }
-
- if ( interfacep )
- {
- result = ( *( IOHIDDeviceInterface** )interfacep )->open( interfacep, 0 );
-
- if ( result != kIOReturnSuccess)
- {
- LL_WARNS("Joystick") << "open failed" << LL_ENDL;
- }
- }
- // extract needed fields
- populate_device_info( io_obj, device_dic, &device );
-
- // Release interface
- if ( interfacep )
- {
- ( *( IOHIDDeviceInterface** ) interfacep )->close( interfacep );
-
- ( *( IOHIDDeviceInterface** ) interfacep )->Release( interfacep );
-
- interfacep = NULL;
- }
-
- CFRelease( device_dic );
- }
- else
- {
- LL_WARNS("Joystick") << "populate_device failed" << LL_ENDL;
- }
-
- return device;
-}
-
-static void get_devices(std::list<HidDevice> &list_of_devices,
- io_iterator_t inIODeviceIterator)
-{
- IOReturn result = kIOReturnSuccess; // assume success( optimist! )
- io_object_t io_obj = 0;
-
- while ( 0 != (io_obj = IOIteratorNext( inIODeviceIterator ) ) )
- {
- HidDevice device = populate_device( io_obj );
-
- // Should match ndof
- if (device.mAxis >= 3
- || (device.mUsagePage == kHIDPage_GenericDesktop
- && (device.mUsage == kHIDUsage_GD_MultiAxisController
- || device.mUsage == kHIDUsage_GD_GamePad
- || device.mUsage == kHIDUsage_GD_Joystick))
- || (device.mUsagePage == kHIDPage_Game
- && device.mUsage == kHIDUsage_Game_3DGameController)
- || strstr(device.mManufacturer, "3Dconnexion"))
- {
- list_of_devices.push_back(device);
- }
- else
- {
- LL_DEBUGS("Joystick");
- list_of_devices.push_back(device);
- LL_CONT << "Device axes: " << (S32)device.mAxis
- << " Device HIDUsepage: " << (S32)device.mUsagePage
- << " Device HIDUsage: " << (S32)device.mUsage;
- LL_ENDL;
- }
-
-
- // release the device object, it is no longer needed
- result = IOObjectRelease( io_obj );
- if ( KERN_SUCCESS != result )
- {
- LL_WARNS("Joystick") << "IOObjectRelease failed" << LL_ENDL;
- }
- }
-}
-
-bool LLWindowMacOSX::getInputDevices(U32 device_type_filter,
- std::function<bool(std::string&, LLSD&, void*)> osx_callback,
- void* win_callback,
- void* userdata)
-{
- bool return_value = false;
- CFMutableDictionaryRef device_dict_ref;
- IOReturn result = kIOReturnSuccess; // assume success( optimist! )
-
- // Set up matching dictionary to search the I/O Registry for HID devices we are interested in. Dictionary reference is NULL if error.
-
- // A dictionary to match devices to?
- device_dict_ref = IOServiceMatching( kIOHIDDeviceKey );
-
- // BUG FIX! one reference is consumed by IOServiceGetMatchingServices
- CFRetain( device_dict_ref );
- io_iterator_t io_iter = 0;
-
- // create an IO object iterator
- result = IOServiceGetMatchingServices( kIOMasterPortDefault, device_dict_ref, &io_iter );
- if ( kIOReturnSuccess != result )
- {
- LL_WARNS("Joystick") << "IOServiceGetMatchingServices failed" << LL_ENDL;
- }
-
- if ( io_iter )
- {
- // add all existing devices
- std::list<HidDevice> device_list;
-
- get_devices(device_list, io_iter);
-
- std::list<HidDevice>::iterator iter;
-
- for (iter = device_list.begin(); iter != device_list.end(); ++iter)
- {
- std::string label(iter->mProduct);
- LLSD data;
- data["manufacturer"] = std::string(iter->mManufacturer);
- data["product"] = label;
-
- if (osx_callback(label, data, userdata))
- {
- break; //found device
- }
- }
- return_value = true;
- }
-
- CFRelease( device_dict_ref );
- return return_value;
-}
-
-LLSD LLWindowMacOSX::getNativeKeyData()
-{
- LLSD result = LLSD::emptyMap();
-
- if(mRawKeyEvent)
- {
- result["event_type"] = LLSD::Integer(mRawKeyEvent->mEventType);
- result["event_modifiers"] = LLSD::Integer(mRawKeyEvent->mEventModifiers);
- result["event_keycode"] = LLSD::Integer(mRawKeyEvent->mEventKeyCode);
- result["event_chars"] = (mRawKeyEvent->mEventChars) ? LLSD(LLSD::Integer(mRawKeyEvent->mEventChars)) : LLSD();
- result["event_umodchars"] = (mRawKeyEvent->mEventUnmodChars) ? LLSD(LLSD::Integer(mRawKeyEvent->mEventUnmodChars)) : LLSD();
- result["event_isrepeat"] = LLSD::Boolean(mRawKeyEvent->mEventRepeat);
- }
-
- LL_DEBUGS() << "native key data is: " << result << LL_ENDL;
-
- return result;
-}
-
-bool LLWindowMacOSX::dialogColorPicker( 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;
-
- error = NPickColor(&info);
-
- 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);
-}
-
-void *LLWindowMacOSX::getPlatformWindow()
-{
- // NOTE: this will be NULL in fullscreen mode. Plan accordingly.
- return (void*)mWindow;
-}
-
-// 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
-}
-
-void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor *preeditor, bool b)
-{
- if (preeditor != mPreeditor && !b)
- {
- // This condition may occur by a call to
- // setEnabled(bool) against LLTextEditor or LLLineEditor
- // when the control is not focused.
- // We need to silently ignore the case so that
- // the language input status of the focused control
- // is not disturbed.
- return;
- }
-
- // Take care of old and new preeditors.
- if (preeditor != mPreeditor || !b)
- {
- // We need to interrupt before updating mPreeditor,
- // so that the fix string from input method goes to
- // the old preeditor.
- if (mLanguageTextInputAllowed)
- {
- interruptLanguageTextInput();
- }
- mPreeditor = (b ? preeditor : NULL);
- }
-
- if (b == mLanguageTextInputAllowed)
- {
- return;
- }
- mLanguageTextInputAllowed = b;
- allowDirectMarkedTextInput(b, mGLView); // mLanguageTextInputAllowed and mMarkedTextAllowed should be updated at once (by Pell Smit
-}
-
-class sharedContext
-{
-public:
- CGLContextObj mContext;
-};
-
-void* LLWindowMacOSX::createSharedContext()
-{
- sharedContext* sc = new sharedContext();
- CGLCreateContext(mPixelFormat, mContext, &(sc->mContext));
-
- if (sUseMultGL)
- {
- CGLEnable(mContext, kCGLCEMPEngine);
- }
-
- return (void *)sc;
-}
-
-void LLWindowMacOSX::makeContextCurrent(void* context)
-{
- CGLSetCurrentContext(((sharedContext*)context)->mContext);
-
- //enable multi-threaded OpenGL
- if (sUseMultGL)
- {
- CGLError cgl_err;
- CGLContextObj ctx = CGLGetCurrentContext();
-
- cgl_err = CGLEnable( ctx, kCGLCEMPEngine);
-
- if (cgl_err != kCGLNoError )
- {
- LL_INFOS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL;
- }
- else
- {
- LL_INFOS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL;
- }
- }
-
-}
-
-void LLWindowMacOSX::destroySharedContext(void* context)
-{
- sharedContext* sc = (sharedContext*)context;
-
- CGLDestroyContext(sc->mContext);
-
- delete sc;
-}
-
-void LLWindowMacOSX::toggleVSync(bool enable_vsync)
-{
- GLint frames_per_swap = 0;
- if (!enable_vsync)
- {
- frames_per_swap = 0;
- }
- else
- {
- frames_per_swap = 1;
- }
-
- CGLSetParameter(mContext, kCGLCPSwapInterval, &frames_per_swap);
-}
-
-void LLWindowMacOSX::interruptLanguageTextInput()
-{
- commitCurrentPreedit(mGLView);
-}
-
-std::vector<std::string> LLWindowMacOSX::getDisplaysResolutionList()
-{
- std::vector<std::string> resolution_list;
-
- CGDirectDisplayID display_ids[10];
- uint32_t found_displays = 0;
- CGError err = CGGetActiveDisplayList(10, display_ids, &found_displays);
-
- if (kCGErrorSuccess != err)
- {
- LL_WARNS() << "Couldn't get a list of active displays" << LL_ENDL;
- return std::vector<std::string>();
- }
-
- for (uint32_t i = 0; i < found_displays; i++)
- {
- S32 monitor_width = CGDisplayPixelsWide(display_ids[i]);
- S32 monitor_height = CGDisplayPixelsHigh(display_ids[i]);
-
- std::ostringstream sstream;
- sstream << monitor_width << "x" << monitor_height;;
- std::string res = sstream.str();
-
- resolution_list.push_back(res);
- }
-
- return resolution_list;
-}
-
-//static
-std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList()
-{
- // Fonts previously in getFontListSans() have moved to fonts.xml.
- return std::vector<std::string>();
-}
-
-// static
-MASK LLWindowMacOSX::modifiersToMask(S16 modifiers)
-{
- MASK mask = 0;
- if(modifiers & MAC_SHIFT_KEY) { mask |= MASK_SHIFT; }
- if(modifiers & (MAC_CMD_KEY | MAC_CTRL_KEY)) { mask |= MASK_CONTROL; }
- if(modifiers & MAC_ALT_KEY) { mask |= MASK_ALT; }
- return mask;
-}
-
-F32 LLWindowMacOSX::getSystemUISize()
-{
- return gHiDPISupport ? ::getDeviceUnitSize(mGLView) : LLWindow::getSystemUISize();
-}
-
-#if LL_OS_DRAGDROP_ENABLED
-/*
-S16 LLWindowMacOSX::dragTrackingHandler(DragTrackingMessage message, WindowRef theWindow,
- void * handlerRefCon, DragRef drag)
-{
- S16 result = 0;
- LLWindowMacOSX *self = (LLWindowMacOSX*)handlerRefCon;
-
- LL_DEBUGS() << "drag tracking handler, message = " << message << LL_ENDL;
-
- switch(message)
- {
- case kDragTrackingInWindow:
- result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_TRACK);
- break;
-
- case kDragTrackingEnterHandler:
- result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_START_TRACKING);
- break;
-
- case kDragTrackingLeaveHandler:
- result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_STOP_TRACKING);
- break;
-
- default:
- break;
- }
-
- return result;
-}
-OSErr LLWindowMacOSX::dragReceiveHandler(WindowRef theWindow, void * handlerRefCon,
- DragRef drag)
-{
- LLWindowMacOSX *self = (LLWindowMacOSX*)handlerRefCon;
- return self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_DROPPED);
-
-}
-*/
-void LLWindowMacOSX::handleDragNDrop(std::string url, LLWindowCallbacks::DragNDropAction action)
-{
- MASK mask = LLWindowMacOSX::modifiersToMask(getModifiers());
-
- float mouse_point[2];
- // This will return the mouse point in window coords
- getCursorPos(mWindow, mouse_point);
- LLCoordWindow window_coords(mouse_point[0], mouse_point[1]);
- LLCoordGL gl_pos;
- convertCoords(window_coords, &gl_pos);
-
- if(!url.empty())
- {
- LLWindowCallbacks::DragNDropResult res =
- mCallbacks->handleDragNDrop(this, gl_pos, mask, action, url);
-
- switch (res) {
- case LLWindowCallbacks::DND_NONE: // No drop allowed
- if (action == LLWindowCallbacks::DNDA_TRACK)
- {
- mDragOverrideCursor = 0;
- }
- else {
- mDragOverrideCursor = -1;
- }
- break;
- case LLWindowCallbacks::DND_MOVE: // Drop accepted would result in a "move" operation
- mDragOverrideCursor = UI_CURSOR_NO;
- break;
- case LLWindowCallbacks::DND_COPY: // Drop accepted would result in a "copy" operation
- mDragOverrideCursor = UI_CURSOR_ARROWCOPY;
- break;
- default:
- mDragOverrideCursor = -1;
- break;
- }
- // This overrides the cursor being set by setCursor.
- // This is a bit of a hack workaround because lots of areas
- // within the viewer just blindly set the cursor.
- if (mDragOverrideCursor == -1)
- {
- // Restore the cursor
- ECursorType temp_cursor = mCurrentCursor;
- // get around the "setting the same cursor" code in setCursor()
- mCurrentCursor = UI_CURSOR_COUNT;
- setCursor(temp_cursor);
- }
- else {
- // Override the cursor
- switch (mDragOverrideCursor) {
- case 0:
- setArrowCursor();
- break;
- case UI_CURSOR_NO:
- setNotAllowedCursor();
- case UI_CURSOR_ARROWCOPY:
- setCopyCursor();
- default:
- break;
- };
- }
- }
-}
-
-#endif // LL_OS_DRAGDROP_ENABLED
+/** + * @file llwindowmacosx.cpp + * @brief Platform-dependent implementation of llwindow + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llwindowmacosx.h" + +#include "llkeyboardmacosx.h" +#include "llwindowcallbacks.h" +#include "llpreeditor.h" + +#include "llerror.h" +#include "llgl.h" +#include "llstring.h" +#include "lldir.h" +#include "indra_constants.h" + +#include <OpenGL/OpenGL.h> +#include <Carbon/Carbon.h> +#include <CoreServices/CoreServices.h> +#include <CoreGraphics/CGDisplayConfiguration.h> + +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOMessage.h> +#include <IOKit/hid/IOHIDUsageTables.h> +#include <IOKit/hid/IOHIDLib.h> +#include <IOKit/usb/IOUSBLib.h> + +extern bool gDebugWindowProc; +bool gHiDPISupport = true; + +const S32 BITS_PER_PIXEL = 32; +const S32 MAX_NUM_RESOLUTIONS = 32; + +const S32 DEFAULT_REFRESH_RATE = 60; + +namespace +{ + NSKeyEventRef mRawKeyEvent = NULL; +} +// +// LLWindowMacOSX +// + +bool LLWindowMacOSX::sUseMultGL = false; + +// Cross-platform bits: + +bool check_for_card(const char* RENDERER, const char* bad_card) +{ + if (!strnicmp(RENDERER, bad_card, strlen(bad_card))) + { + std::string buffer = llformat( + "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.c_str(), "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); + +// 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(LLWindowCallbacks* callbacks, + const std::string& title, const std::string& name, S32 x, S32 y, S32 width, + S32 height, U32 flags, + bool fullscreen, bool clearBg, + bool enable_vsync, bool use_gl, + bool ignore_pixel_depth, + U32 fsaa_samples, + U32 max_vram) + : LLWindow(NULL, fullscreen, flags) +{ + // *HACK: During window construction we get lots of OS events for window + // reshape, activate, etc. that the viewer isn't ready to handle. + // Route them to a dummy callback structure until the end of constructor. + LLWindowCallbacks null_callbacks; + mCallbacks = &null_callbacks; + + // Voodoo for calling cocoa from carbon (see llwindowmacosx-objc.mm). + setupCocoa(); + + // Initialize the keyboard + gKeyboard = new LLKeyboardMacOSX(); + gKeyboard->setCallbacks(callbacks); + + // Ignore use_gl for now, only used for drones on PC + mWindow = NULL; + mContext = NULL; + mPixelFormat = NULL; + mDisplay = CGMainDisplayID(); + mSimulatedRightClick = false; + mLastModifiers = 0; + mHandsOffEvents = false; + mCursorDecoupled = false; + mCursorLastEventDeltaX = 0; + mCursorLastEventDeltaY = 0; + mCursorIgnoreNextDelta = false; + mNeedsResize = false; + mOverrideAspectRatio = 0.f; + mMaximized = false; + mMinimized = false; + mLanguageTextInputAllowed = false; + mPreeditor = NULL; + mFSAASamples = fsaa_samples; + mForceRebuild = false; + + // Get the original aspect ratio of the main device. + mOriginalAspectRatio = (double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay); + + // Stash the window title + mWindowTitle = title; + //mWindowTitle[0] = title.length(); + + mDragOverrideCursor = -1; + + // 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, enable_vsync)) + { + if(mWindow != NULL) + { + makeWindowOrderFront(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 ); + + allowLanguageTextInput(NULL, false); + } + + mCallbacks = callbacks; + stop_glerror(); + + +} + +// These functions are used as wrappers for our internal event handling callbacks. +// It's a good idea to wrap these to avoid reworking more code than we need to within LLWindow. + +bool callKeyUp(NSKeyEventRef event, unsigned short key, unsigned int mask) +{ + mRawKeyEvent = event; + bool retVal = gKeyboard->handleKeyUp(key, mask); + mRawKeyEvent = NULL; + return retVal; +} + +bool callKeyDown(NSKeyEventRef event, unsigned short key, unsigned int mask, wchar_t character) +{ + //if (mask!=MASK_NONE) + { + if((key == gKeyboard->inverseTranslateKey('Z')) && (character == 'y')) + { + key = gKeyboard->inverseTranslateKey('Y'); + } + else if ((key == gKeyboard->inverseTranslateKey('Y')) && (character == 'z')) + { + key = gKeyboard->inverseTranslateKey('Z'); + } + } + + mRawKeyEvent = event; + bool retVal = gKeyboard->handleKeyDown(key, mask); + mRawKeyEvent = NULL; + return retVal; +} + +void callResetKeys() +{ + gKeyboard->resetKeys(); +} + +bool callUnicodeCallback(wchar_t character, unsigned int mask) +{ + NativeKeyEventData eventData; + + memset(&eventData, 0, sizeof(NativeKeyEventData)); + + eventData.mKeyEvent = NativeKeyEventData::KEYCHAR; + eventData.mEventType = 0; + eventData.mEventModifiers = mask; + eventData.mEventKeyCode = 0; + eventData.mEventChars = character; + eventData.mEventUnmodChars = character; + eventData.mEventRepeat = false; + + mRawKeyEvent = &eventData; + + bool result = gWindowImplementation->getCallbacks()->handleUnicodeChar(character, mask); + mRawKeyEvent = NULL; + return result; +} + +void callFocus() +{ + if (gWindowImplementation) + { + gWindowImplementation->getCallbacks()->handleFocus(gWindowImplementation); + } +} + +void callFocusLost() +{ + if (gWindowImplementation) + { + gWindowImplementation->getCallbacks()->handleFocusLost(gWindowImplementation); + } +} + +void callRightMouseDown(float *pos, MASK mask) +{ + if (gWindowImplementation->allowsLanguageInput()) + { + gWindowImplementation->interruptLanguageTextInput(); + } + + LLCoordGL outCoords; + outCoords.mX = ll_round(pos[0]); + outCoords.mY = ll_round(pos[1]); + gWindowImplementation->getCallbacks()->handleRightMouseDown(gWindowImplementation, outCoords, gKeyboard->currentMask(true)); +} + +void callRightMouseUp(float *pos, MASK mask) +{ + if (gWindowImplementation->allowsLanguageInput()) + { + gWindowImplementation->interruptLanguageTextInput(); + } + + LLCoordGL outCoords; + outCoords.mX = ll_round(pos[0]); + outCoords.mY = ll_round(pos[1]); + gWindowImplementation->getCallbacks()->handleRightMouseUp(gWindowImplementation, outCoords, gKeyboard->currentMask(true)); +} + +void callLeftMouseDown(float *pos, MASK mask) +{ + if (gWindowImplementation->allowsLanguageInput()) + { + gWindowImplementation->interruptLanguageTextInput(); + } + + LLCoordGL outCoords; + outCoords.mX = ll_round(pos[0]); + outCoords.mY = ll_round(pos[1]); + gWindowImplementation->getCallbacks()->handleMouseDown(gWindowImplementation, outCoords, gKeyboard->currentMask(true)); +} + +void callLeftMouseUp(float *pos, MASK mask) +{ + if (gWindowImplementation->allowsLanguageInput()) + { + gWindowImplementation->interruptLanguageTextInput(); + } + + LLCoordGL outCoords; + outCoords.mX = ll_round(pos[0]); + outCoords.mY = ll_round(pos[1]); + gWindowImplementation->getCallbacks()->handleMouseUp(gWindowImplementation, outCoords, gKeyboard->currentMask(true)); + +} + +void callDoubleClick(float *pos, MASK mask) +{ + if (gWindowImplementation->allowsLanguageInput()) + { + gWindowImplementation->interruptLanguageTextInput(); + } + + LLCoordGL outCoords; + outCoords.mX = ll_round(pos[0]); + outCoords.mY = ll_round(pos[1]); + gWindowImplementation->getCallbacks()->handleDoubleClick(gWindowImplementation, outCoords, gKeyboard->currentMask(true)); +} + +void callResize(unsigned int width, unsigned int height) +{ + if (gWindowImplementation != NULL) + { + gWindowImplementation->getCallbacks()->handleResize(gWindowImplementation, width, height); + } +} + +void callMouseMoved(float *pos, MASK mask) +{ + LLCoordGL outCoords; + outCoords.mX = ll_round(pos[0]); + outCoords.mY = ll_round(pos[1]); + float deltas[2]; + gWindowImplementation->getMouseDeltas(deltas); + outCoords.mX += deltas[0]; + outCoords.mY += deltas[1]; + gWindowImplementation->getCallbacks()->handleMouseMove(gWindowImplementation, outCoords, gKeyboard->currentMask(true)); + //gWindowImplementation->getCallbacks()->handleScrollWheel(gWindowImplementation, 0); +} + +void callMouseDragged(float *pos, MASK mask) +{ + LLCoordGL outCoords; + outCoords.mX = ll_round(pos[0]); + outCoords.mY = ll_round(pos[1]); + float deltas[2]; + gWindowImplementation->getMouseDeltas(deltas); + outCoords.mX += deltas[0]; + outCoords.mY += deltas[1]; + gWindowImplementation->getCallbacks()->handleMouseDragged(gWindowImplementation, outCoords, gKeyboard->currentMask(true)); +} + +void callScrollMoved(float deltaX, float deltaY) +{ + if ( gWindowImplementation && gWindowImplementation->getCallbacks() ) + { + gWindowImplementation->getCallbacks()->handleScrollHWheel(gWindowImplementation, deltaX); + gWindowImplementation->getCallbacks()->handleScrollWheel(gWindowImplementation, deltaY); + } +} + +void callMouseExit() +{ + gWindowImplementation->getCallbacks()->handleMouseLeave(gWindowImplementation); +} + +void callWindowFocus() +{ + if ( gWindowImplementation && gWindowImplementation->getCallbacks() ) + { + gWindowImplementation->getCallbacks()->handleFocus (gWindowImplementation); + } + else + { + LL_WARNS("COCOA") << "Window Implementation or callbacks not yet initialized." << LL_ENDL; + } + + +} + +void callWindowUnfocus() +{ + if ( gWindowImplementation && gWindowImplementation->getCallbacks() ) + { + gWindowImplementation->getCallbacks()->handleFocusLost(gWindowImplementation); + } +} + +void callWindowHide() +{ + if ( gWindowImplementation && gWindowImplementation->getCallbacks() ) + { + gWindowImplementation->getCallbacks()->handleActivate(gWindowImplementation, false); + } +} + +void callWindowUnhide() +{ + if ( gWindowImplementation && gWindowImplementation->getCallbacks() ) + { + gWindowImplementation->getCallbacks()->handleActivate(gWindowImplementation, true); + } +} + +void callWindowDidChangeScreen() +{ + if ( gWindowImplementation && gWindowImplementation->getCallbacks() ) + { + gWindowImplementation->getCallbacks()->handleWindowDidChangeScreen(gWindowImplementation); + } +} + +void callDeltaUpdate(float *delta, MASK mask) +{ + gWindowImplementation->updateMouseDeltas(delta); +} + +void callOtherMouseDown(float *pos, MASK mask, int button) +{ + LLCoordGL outCoords; + outCoords.mX = ll_round(pos[0]); + outCoords.mY = ll_round(pos[1]); + float deltas[2]; + gWindowImplementation->getMouseDeltas(deltas); + outCoords.mX += deltas[0]; + outCoords.mY += deltas[1]; + + if (button == 2) + { + gWindowImplementation->getCallbacks()->handleMiddleMouseDown(gWindowImplementation, outCoords, mask); + } + else + { + gWindowImplementation->getCallbacks()->handleOtherMouseDown(gWindowImplementation, outCoords, mask, button + 1); + } +} + +void callOtherMouseUp(float *pos, MASK mask, int button) +{ + LLCoordGL outCoords; + outCoords.mX = ll_round(pos[0]); + outCoords.mY = ll_round(pos[1]); + float deltas[2]; + gWindowImplementation->getMouseDeltas(deltas); + outCoords.mX += deltas[0]; + outCoords.mY += deltas[1]; + if (button == 2) + { + gWindowImplementation->getCallbacks()->handleMiddleMouseUp(gWindowImplementation, outCoords, mask); + } + else + { + gWindowImplementation->getCallbacks()->handleOtherMouseUp(gWindowImplementation, outCoords, mask, button + 1); + } +} + +void callModifier(MASK mask) +{ + gKeyboard->handleModifier(mask); +} + +void callHandleDragEntered(std::string url) +{ + gWindowImplementation->handleDragNDrop(url, LLWindowCallbacks::DNDA_START_TRACKING); +} + +void callHandleDragExited(std::string url) +{ + gWindowImplementation->handleDragNDrop(url, LLWindowCallbacks::DNDA_STOP_TRACKING); +} + +void callHandleDragUpdated(std::string url) +{ + gWindowImplementation->handleDragNDrop(url, LLWindowCallbacks::DNDA_TRACK); +} + +void callHandleDragDropped(std::string url) +{ + gWindowImplementation->handleDragNDrop(url, LLWindowCallbacks::DNDA_DROPPED); +} + +void callQuitHandler() +{ + if (gWindowImplementation) + { + if(gWindowImplementation->getCallbacks()->handleCloseRequest(gWindowImplementation)) + { + gWindowImplementation->getCallbacks()->handleQuit(gWindowImplementation); + } + } +} + +void getPreeditSelectionRange(int *position, int *length) +{ + if (gWindowImplementation->getPreeditor()) + { + gWindowImplementation->getPreeditor()->getSelectionRange(position, length); + } +} + +void getPreeditMarkedRange(int *position, int *length) +{ + if (gWindowImplementation->getPreeditor()) + { + gWindowImplementation->getPreeditor()->getPreeditRange(position, length); + } +} + +void setPreeditMarkedRange(int position, int length) +{ + if (gWindowImplementation->getPreeditor()) + { + gWindowImplementation->getPreeditor()->markAsPreedit(position, length); + } +} + +bool handleUnicodeCharacter(wchar_t c) +{ + bool success = false; + if (gWindowImplementation->getPreeditor()) + { + success = gWindowImplementation->getPreeditor()->handleUnicodeCharHere(c); + } + + return success; +} + +void resetPreedit() +{ + if (gWindowImplementation->getPreeditor()) + { + gWindowImplementation->getPreeditor()->resetPreedit(); + } +} + +// For reasons of convenience, handle IME updates here. +// This largely mirrors the old implementation, only sans the carbon parameters. +void setMarkedText(unsigned short *unitext, unsigned int *selectedRange, unsigned int *replacementRange, long text_len, attributedStringInfo segments) +{ + if (gWindowImplementation->getPreeditor()) + { + LLPreeditor *preeditor = gWindowImplementation->getPreeditor(); + preeditor->resetPreedit(); + // This should be a viable replacement for the kEventParamTextInputSendReplaceRange parameter. + if (replacementRange[0] < replacementRange[1]) + { + const LLWString& text = preeditor->getPreeditString(); + const S32 location = wstring_wstring_length_from_utf16_length(text, 0, replacementRange[0]); + const S32 length = wstring_wstring_length_from_utf16_length(text, location, replacementRange[1]); + preeditor->markAsPreedit(location, length); + } + + LLWString fix_str = utf16str_to_wstring(llutf16string(unitext, text_len)); + + S32 caret_position = fix_str.length(); + + preeditor->updatePreedit(fix_str, segments.seg_lengths, segments.seg_standouts, caret_position); + } +} + +void getPreeditLocation(float *location, unsigned int length) +{ + if (gWindowImplementation->getPreeditor()) + { + LLPreeditor *preeditor = gWindowImplementation->getPreeditor(); + LLCoordGL coord; + LLCoordScreen screen; + LLRect rect; + + preeditor->getPreeditLocation(length, &coord, &rect, NULL); + + float c[4] = {float(coord.mX), float(coord.mY), 0, 0}; + + convertRectToScreen(gWindowImplementation->getWindow(), c); + + location[0] = c[0]; + location[1] = c[1]; + } +} + +void LLWindowMacOSX::updateMouseDeltas(float* deltas) +{ + if (mCursorDecoupled) + { + mCursorLastEventDeltaX = ll_round(deltas[0]); + mCursorLastEventDeltaY = ll_round(-deltas[1]); + + if (mCursorIgnoreNextDelta) + { + mCursorLastEventDeltaX = 0; + mCursorLastEventDeltaY = 0; + mCursorIgnoreNextDelta = false; + } + } else { + mCursorLastEventDeltaX = 0; + mCursorLastEventDeltaY = 0; + } +} + +void LLWindowMacOSX::getMouseDeltas(float* delta) +{ + delta[0] = mCursorLastEventDeltaX; + delta[1] = mCursorLastEventDeltaY; +} + +bool LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool enable_vsync) +{ + mFullscreen = fullscreen; + + if (mWindow == NULL) + { + mWindow = getMainAppWindow(); + } + + if(mContext == NULL) + { + // Our OpenGL view is already defined within SecondLife.xib. + // Get the view instead. + mGLView = createOpenGLView(mWindow, mFSAASamples, enable_vsync); + mContext = getCGLContextObj(mGLView); + gGLManager.mVRAM = getVramSize(mGLView); + + if(!mPixelFormat) + { + CGLPixelFormatAttribute attribs[] = + { + kCGLPFANoRecovery, + kCGLPFADoubleBuffer, + kCGLPFAClosestPolicy, + kCGLPFAAccelerated, + kCGLPFAMultisample, + kCGLPFASampleBuffers, static_cast<CGLPixelFormatAttribute>((mFSAASamples > 0 ? 1 : 0)), + kCGLPFASamples, static_cast<CGLPixelFormatAttribute>(mFSAASamples), + kCGLPFAStencilSize, static_cast<CGLPixelFormatAttribute>(8), + kCGLPFADepthSize, static_cast<CGLPixelFormatAttribute>(24), + kCGLPFAAlphaSize, static_cast<CGLPixelFormatAttribute>(8), + kCGLPFAColorSize, static_cast<CGLPixelFormatAttribute>(24), + kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_GL4_Core), + static_cast<CGLPixelFormatAttribute>(0) + }; + + GLint numPixelFormats; + CGLChoosePixelFormat (attribs, &mPixelFormat, &numPixelFormats); + + if(mPixelFormat == NULL) { + CGLChoosePixelFormat (attribs, &mPixelFormat, &numPixelFormats); + } + } + + } + + // This sets up our view to recieve text from our non-inline text input window. + setupInputWindow(mWindow, mGLView); + + // Hook up the context to a drawable + + if(mContext != NULL) + { + + + U32 err = CGLSetCurrentContext(mContext); + if (err != kCGLNoError) + { + setupFailure("Can't activate GL rendering context", "Error", OSMB_OK); + return false; + } + } + + mRefreshRate = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(mDisplay)); + if(mRefreshRate == 0) + { + //consider adding more appropriate fallback later + mRefreshRate = DEFAULT_REFRESH_RATE; + } + + // Disable vertical sync for swap + toggleVSync(enable_vsync); + + //enable multi-threaded OpenGL + if (sUseMultGL) + { + CGLError cgl_err; + CGLContextObj ctx = CGLGetCurrentContext(); + + cgl_err = CGLEnable( ctx, kCGLCEMPEngine); + + if (cgl_err != kCGLNoError ) + { + LL_INFOS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL; + } + else + { + LL_INFOS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL; + } + } + makeFirstResponder(mWindow, mGLView); + + return true; +} + + +// We only support OS X 10.7's fullscreen app mode which is literally a full screen window that fills a virtual desktop. +// This makes this method obsolete. +bool LLWindowMacOSX::switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp) +{ + return false; +} + +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) + { + LL_DEBUGS("Window") << "destroyContext: unhooking drawable " << LL_ENDL; + CGLSetCurrentContext(NULL); + } + + // Clean up remaining GL state before blowing away window + gGLManager.shutdownGL(); + + // Clean up the pixel format + if(mPixelFormat != NULL) + { + CGLDestroyPixelFormat(mPixelFormat); + mPixelFormat = NULL; + } + + // Clean up the GL context + if(mContext != NULL) + { + CGLDestroyContext(mContext); + } + + // Destroy our LLOpenGLView + if(mGLView != NULL) + { + removeGLView(mGLView); + mGLView = NULL; + } + + // Close the window + if(mWindow != NULL) + { + NSWindowRef dead_window = mWindow; + mWindow = NULL; + closeWindow(dead_window); + } + +} + +LLWindowMacOSX::~LLWindowMacOSX() +{ + destroyContext(); + + if(mSupportedResolutions != NULL) + { + delete []mSupportedResolutions; + } + + gWindowImplementation = NULL; + +} + + +void LLWindowMacOSX::show() +{ +} + +void LLWindowMacOSX::hide() +{ + setMouseClipping(false); +} + +//virtual +void LLWindowMacOSX::minimize() +{ + setMouseClipping(false); + showCursor(); +} + +//virtual +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) + { + result = true; + } + + return(result); +} + +bool LLWindowMacOSX::getMinimized() +{ + return mMinimized; +} + +bool LLWindowMacOSX::getMaximized() +{ + return mMaximized; +} + +bool LLWindowMacOSX::maximize() +{ + if (mWindow && !mMaximized) + { + } + + return mMaximized; +} + +bool LLWindowMacOSX::getFullscreen() +{ + return mFullscreen; +} + +void LLWindowMacOSX::gatherInput() +{ + updateCursor(); +} + +bool LLWindowMacOSX::getPosition(LLCoordScreen *position) +{ + S32 err = -1; + + if(mFullscreen) + { + position->mX = 0; + position->mY = 0; + err = noErr; + } + else if(mWindow) + { + const CGPoint & pos = getContentViewBoundsPosition(mWindow); + + position->mX = pos.x; + position->mY = pos.y; + + err = noErr; + } + else + { + LL_ERRS() << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << LL_ENDL; + } + + return (err == noErr); +} + +bool LLWindowMacOSX::getSize(LLCoordScreen *size) +{ + S32 err = -1; + + if(mFullscreen) + { + size->mX = mFullscreenWidth; + size->mY = mFullscreenHeight; + err = noErr; + } + else if(mWindow) + { + const CGSize & sz = gHiDPISupport ? getDeviceContentViewSize(mWindow, mGLView) : getContentViewBoundsSize(mWindow); + + size->mX = sz.width; + size->mY = sz.height; + err = noErr; + } + else + { + LL_ERRS() << "LLWindowMacOSX::getSize(): no window and not fullscreen!" << LL_ENDL; + } + + return (err == noErr); +} + +bool LLWindowMacOSX::getSize(LLCoordWindow *size) +{ + S32 err = -1; + + if(mFullscreen) + { + size->mX = mFullscreenWidth; + size->mY = mFullscreenHeight; + err = noErr; + } + else if(mWindow) + { + const CGSize & sz = gHiDPISupport ? getDeviceContentViewSize(mWindow, mGLView) : getContentViewBoundsSize(mWindow); + + size->mX = sz.width; + size->mY = sz.height; + err = noErr; + + + } + else + { + LL_ERRS() << "LLWindowMacOSX::getSize(): no window and not fullscreen!" << LL_ENDL; + } + + return (err == noErr); +} + +bool LLWindowMacOSX::setPosition(const LLCoordScreen position) +{ + if(mWindow) + { + float pos[2] = {float(position.mX), float(position.mY)}; + setWindowPos(mWindow, pos); + } + + return true; +} + +bool LLWindowMacOSX::setSizeImpl(const LLCoordScreen size) +{ + if(mWindow) + { + LLCoordWindow to; + convertCoords(size, &to); + setWindowSize(mWindow, to.mX, to.mY); + return true; + } + + return false; +} + +bool LLWindowMacOSX::setSizeImpl(const LLCoordWindow size) +{ + if (mWindow) + { + const int titlePadding = 22; + setWindowSize(mWindow, size.mX, size.mY + titlePadding); + return true; + } + + return false; +} + +void LLWindowMacOSX::swapBuffers() +{ + CGLFlushDrawable(mContext); +} + +void LLWindowMacOSX::restoreGLContext() +{ + CGLSetCurrentContext(mContext); +} + +F32 LLWindowMacOSX::getGamma() +{ + F32 result = 2.2; // 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; +} + +U32 LLWindowMacOSX::getFSAASamples() +{ + return mFSAASamples; +} + +void LLWindowMacOSX::setFSAASamples(const U32 samples) +{ + mFSAASamples = samples; + mForceRebuild = true; +} + +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) + { + // LL_INFOS() << "setMouseClipping(true)" << LL_ENDL; + } + else + { + // LL_INFOS() << "setMouseClipping(false)" << LL_ENDL; + } + + adjustCursorDecouple(); +} + +bool LLWindowMacOSX::setCursorPosition(const LLCoordWindow position) +{ + bool result = false; + LLCoordScreen screen_pos; + + if (!convertCoords(position, &screen_pos)) + { + return false; + } + + CGPoint newPosition; + + // LL_INFOS() << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << LL_ENDL; + + 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); + + // trigger mouse move callback + LLCoordGL gl_pos; + convertCoords(position, &gl_pos); + float scale = getSystemUISize(); + gl_pos.mX *= scale; + gl_pos.mY *= scale; + mCallbacks->handleMouseMove(this, gl_pos, (MASK)0); + + return result; +} + +bool LLWindowMacOSX::getCursorPosition(LLCoordWindow *position) +{ + float cursor_point[2]; + LLCoordScreen screen_pos; + + if(mWindow == NULL) + return false; + + getCursorPos(mWindow, cursor_point); + + 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[0] += mCursorLastEventDeltaX; + cursor_point[1] += mCursorLastEventDeltaY; + } + + float scale = getSystemUISize(); + position->mX = cursor_point[0] * scale; + position->mY = cursor_point[1] * scale; + + return true; +} + +void LLWindowMacOSX::adjustCursorDecouple(bool warpingMouse) +{ + if(mIsMouseClipping && mCursorHidden) + { + if(warpingMouse) + { + // The cursor should be decoupled. Make sure it is. + if(!mCursorDecoupled) + { + // LL_INFOS() << "adjustCursorDecouple: decoupling cursor" << LL_ENDL; + CGAssociateMouseAndMouseCursorPosition(false); + mCursorDecoupled = true; + mCursorIgnoreNextDelta = true; + } + } + } + else + { + // The cursor should not be decoupled. Make sure it isn't. + if(mCursorDecoupled) + { + // LL_INFOS() << "adjustCursorDecouple: recoupling cursor" << LL_ENDL; + 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() +{ + //macOS 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... + +// Since we're no longer supporting the "typical" fullscreen mode with CGL or NSOpenGL anymore, these are unnecessary. -Geenz +void LLWindowMacOSX::beforeDialog() +{ +} + +void LLWindowMacOSX::afterDialog() +{ + //For fix problem with Core Flow view on OSX + restoreGLContext(); +} + + +void LLWindowMacOSX::flashIcon(F32 seconds) +{ + // For consistency with macOS conventions, the number of seconds given is ignored and + // left up to the OS (which will actually bounce it for one second). + requestUserAttention(); +} + +bool LLWindowMacOSX::isClipboardTextAvailable() +{ + return pasteBoardAvailable(); +} + +bool LLWindowMacOSX::pasteTextFromClipboard(LLWString &dst) +{ + unsigned short* pboard_data = copyFromPBoard(); // must free returned data + llutf16string str(pboard_data); + free(pboard_data); + + dst = utf16str_to_wstring(str); + if (dst != L"") + { + return true; + } else { + return false; + } +} + +bool LLWindowMacOSX::copyTextToClipboard(const LLWString &s) +{ + bool result = false; + llutf16string utf16str = wstring_to_utf16str(s); + + result = copyToPBoard(utf16str.data(), utf16str.length()); + + return result; +} + + +// 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 = CGDisplayCopyAllDisplayModes(mDisplay, nullptr); + + 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++; + } + } + } + CFRelease(modes); + } + } + + num_resolutions = mNumSupportedResolutions; + return mSupportedResolutions; +} + +bool LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to) +{ + to->mX = from.mX; + to->mY = from.mY; + return true; +} + +bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to) +{ + to->mX = from.mX; + to->mY = from.mY; + return true; +} + +bool LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to) +{ + if(mWindow) + { + float mouse_point[2]; + + mouse_point[0] = from.mX; + mouse_point[1] = from.mY; + + convertScreenToWindow(mWindow, mouse_point); + + to->mX = mouse_point[0]; + to->mY = mouse_point[1]; + + return true; + } + return false; +} + +bool LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to) +{ + if(mWindow) + { + float mouse_point[2]; + + mouse_point[0] = from.mX; + mouse_point[1] = from.mY; + + convertWindowToScreen(mWindow, mouse_point); + + to->mX = mouse_point[0]; + to->mY = mouse_point[1]; + + 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 std::string& text, const std::string& caption, U32 type) +{ + destroyContext(); + + OSMessageBox(text, caption, type); +} + + // Note on event recording - QUIT is a known special case and we are choosing NOT to record it for the record and playback feature + // it is handled at a very low-level +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_SIZEALL: return "UI_CURSOR_SIZEALL"; + 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_TOOLZOOMOUT: return "UI_CURSOR_TOOLZOOMOUT"; + case UI_CURSOR_TOOLPICKOBJECT3: return "UI_CURSOR_TOOLPICKOBJECT3"; + case UI_CURSOR_TOOLPLAY: return "UI_CURSOR_TOOLPLAY"; + case UI_CURSOR_TOOLPAUSE: return "UI_CURSOR_TOOLPAUSE"; + case UI_CURSOR_TOOLMEDIAOPEN: return "UI_CURSOR_TOOLMEDIAOPEN"; + case UI_CURSOR_PIPETTE: return "UI_CURSOR_PIPETTE"; + case UI_CURSOR_TOOLSIT: return "UI_CURSOR_TOOLSIT"; + case UI_CURSOR_TOOLBUY: return "UI_CURSOR_TOOLBUY"; + case UI_CURSOR_TOOLOPEN: return "UI_CURSOR_TOOLOPEN"; + case UI_CURSOR_TOOLPATHFINDING: return "UI_CURSOR_PATHFINDING"; + case UI_CURSOR_TOOLPATHFINDING_PATH_START: return "UI_CURSOR_PATHFINDING_START"; + case UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD: return "UI_CURSOR_PATHFINDING_START_ADD"; + case UI_CURSOR_TOOLPATHFINDING_PATH_END: return "UI_CURSOR_PATHFINDING_END"; + case UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD: return "UI_CURSOR_PATHFINDING_END_ADD"; + case UI_CURSOR_TOOLNO: return "UI_CURSOR_NO"; + } + + LL_ERRS() << "cursorIDToName: unknown cursor id" << id << LL_ENDL; + + return "UI_CURSOR_ARROW"; +} + +static CursorRef gCursors[UI_CURSOR_COUNT]; + + +static void initPixmapCursor(int cursorid, int hotspotX, int hotspotY) +{ + // cursors are in <Application Bundle>/Contents/Resources/cursors_mac/UI_CURSOR_FOO.tif + std::string fullpath = gDirUtilp->add( + gDirUtilp->getAppRODataDir(), + "cursors_mac", + cursorIDToName(cursorid) + std::string(".tif")); + + gCursors[cursorid] = createImageCursor(fullpath.c_str(), hotspotX, hotspotY); +} + +void LLWindowMacOSX::updateCursor() +{ + S32 result = 0; + + if (mDragOverrideCursor != -1) + { + // A drag is in progress...remember the requested cursor and we'll + // restore it when it is done + mCurrentCursor = mNextCursor; + return; + } + + if (mNextCursor == UI_CURSOR_ARROW + && mBusyCount > 0) + { + mNextCursor = UI_CURSOR_WORKING; + } + + if(mCurrentCursor == mNextCursor) + { + if(mCursorHidden && mHideCursorPermanent && isCGCursorVisible()) + { + hideNSCursor(); + adjustCursorDecouple(); + } + return; + } + + // RN: replace multi-drag cursors with single versions + if (mNextCursor == UI_CURSOR_ARROWDRAGMULTI) + { + mNextCursor = UI_CURSOR_ARROWDRAG; + } + else if (mNextCursor == UI_CURSOR_ARROWCOPYMULTI) + { + mNextCursor = UI_CURSOR_ARROWCOPY; + } + + switch(mNextCursor) + { + default: + case UI_CURSOR_ARROW: + setArrowCursor(); + if(mCursorHidden) + { + // Since InitCursor resets the hide level, correct for it here. + hideNSCursor(); + } + 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: /* Apple purposely doesn't allow us to set the beachball cursor manually. Let NSApp figure out when to do this. */ break; + case UI_CURSOR_IBEAM: setIBeamCursor(); break; + case UI_CURSOR_CROSS: setCrossCursor(); break; + case UI_CURSOR_HAND: setPointingHandCursor(); break; + // case UI_CURSOR_NO: SetThemeCursor(kThemeNotAllowedCursor); break; + case UI_CURSOR_ARROWCOPY: setCopyCursor(); 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_PIPETTE: + 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_TOOLPLAY: + case UI_CURSOR_TOOLPAUSE: + case UI_CURSOR_TOOLMEDIAOPEN: + case UI_CURSOR_TOOLSIT: + case UI_CURSOR_TOOLBUY: + case UI_CURSOR_TOOLOPEN: + case UI_CURSOR_TOOLPATHFINDING: + case UI_CURSOR_TOOLPATHFINDING_PATH_START: + case UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD: + case UI_CURSOR_TOOLPATHFINDING_PATH_END: + case UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD: + case UI_CURSOR_TOOLNO: + result = setImageCursor(gCursors[mNextCursor]); + break; + + } + + if(result != noErr) + { + setArrowCursor(); + } + + mCurrentCursor = mNextCursor; +} + +ECursorType LLWindowMacOSX::getCursor() const +{ + 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_PIPETTE, 3, 29); + 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_TOOLZOOMOUT, 7, 6); + initPixmapCursor(UI_CURSOR_TOOLPICKOBJECT3, 1, 1); + initPixmapCursor(UI_CURSOR_TOOLPLAY, 1, 1); + initPixmapCursor(UI_CURSOR_TOOLPAUSE, 1, 1); + initPixmapCursor(UI_CURSOR_TOOLMEDIAOPEN, 1, 1); + initPixmapCursor(UI_CURSOR_TOOLSIT, 20, 15); + initPixmapCursor(UI_CURSOR_TOOLBUY, 20, 15); + initPixmapCursor(UI_CURSOR_TOOLOPEN, 20, 15); + initPixmapCursor(UI_CURSOR_TOOLPATHFINDING, 16, 16); + initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_START, 16, 16); + initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD, 16, 16); + initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_END, 16, 16); + initPixmapCursor(UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD, 16, 16); + initPixmapCursor(UI_CURSOR_TOOLNO, 8, 8); + + initPixmapCursor(UI_CURSOR_SIZENWSE, 10, 10); + initPixmapCursor(UI_CURSOR_SIZENESW, 10, 10); + initPixmapCursor(UI_CURSOR_SIZEWE, 10, 10); + initPixmapCursor(UI_CURSOR_SIZENS, 10, 10); + initPixmapCursor(UI_CURSOR_SIZEALL, 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) + { + // LL_INFOS() << "hideCursor: hiding" << LL_ENDL; + mCursorHidden = true; + mHideCursorPermanent = true; + hideNSCursor(); + } + else + { + // LL_INFOS() << "hideCursor: already hidden" << LL_ENDL; + } + + adjustCursorDecouple(); +} + +void LLWindowMacOSX::showCursor() +{ + if(mCursorHidden || !isCGCursorVisible()) + { + // LL_INFOS() << "showCursor: showing" << LL_ENDL; + mCursorHidden = false; + mHideCursorPermanent = false; + showNSCursor(); + } + else + { + // LL_INFOS() << "showCursor: already visible" << LL_ENDL; + } + + 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... +} + +void LLSplashScreenMacOSX::updateImpl(const std::string& mesg) +{ + if(mWindow != NULL) + { + CFStringCreateWithCString(NULL, mesg.c_str(), kCFStringEncodingUTF8); + } +} + + +void LLSplashScreenMacOSX::hideImpl() +{ + if(mWindow != NULL) + { + mWindow = NULL; + } +} + +S32 OSMessageBoxMacOSX(const std::string& text, const std::string& caption, U32 type) +{ + return showAlert(text, caption, type); +} + +// Open a URL with the user's default web browser. +// Must begin with protocol identifier. +void LLWindowMacOSX::spawnWebBrowser(const std::string& escaped_url, bool async) +{ + // I'm fairly certain that this is all legitimate under Apple's currently supported APIs. + + bool found = false; + S32 i; + for (i = 0; i < gURLProtocolWhitelistCount; i++) + { + if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos) + { + found = true; + break; + } + } + + if (!found) + { + LL_WARNS() << "spawn_web_browser called for url with protocol not on whitelist: " << escaped_url << LL_ENDL; + return; + } + + S32 result = 0; + CFURLRef urlRef = NULL; + + LL_INFOS() << "Opening URL " << escaped_url << LL_ENDL; + + CFStringRef stringRef = CFStringCreateWithCString(NULL, escaped_url.c_str(), 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) + { + LL_INFOS() << "Error " << result << " on open." << LL_ENDL; + } + + CFRelease(urlRef); + } + else + { + LL_INFOS() << "Error: couldn't create URL." << LL_ENDL; + } +} + +// String should match ndof, so string mapping code was copied as is +static char mapChar( char c ) +{ + unsigned char uc = ( unsigned char ) c; + + switch( uc ) + { + case '/': return '-'; // use dash instead of slash + + case 0x7F: return ' '; + case 0x80: return 'A'; + case 0x81: return 'A'; + case 0x82: return 'C'; + case 0x83: return 'E'; + case 0x84: return 'N'; + case 0x85: return 'O'; + case 0x86: return 'U'; + case 0x87: return 'a'; + case 0x88: return 'a'; + case 0x89: return 'a'; + case 0x8A: return 'a'; + case 0x8B: return 'a'; + case 0x8C: return 'a'; + case 0x8D: return 'c'; + case 0x8E: return 'e'; + case 0x8F: return 'e'; + case 0x90: return ' '; + case 0x91: return ' '; // ? ' + case 0x92: return ' '; // ? ' + case 0x93: return ' '; // ? " + case 0x94: return ' '; // ? " + case 0x95: return ' '; + case 0x96: return ' '; + case 0x97: return ' '; + case 0x98: return ' '; + case 0x99: return ' '; + case 0x9A: return ' '; + case 0x9B: return 0x27; + case 0x9C: return 0x22; + case 0x9D: return ' '; + case 0x9E: return ' '; + case 0x9F: return ' '; + case 0xA0: return ' '; + case 0xA1: return ' '; + case 0xA2: return ' '; + case 0xA3: return ' '; + case 0xA4: return ' '; + case 0xA5: return ' '; + case 0xA6: return ' '; + case 0xA7: return ' '; + case 0xA8: return ' '; + case 0xA9: return ' '; + case 0xAA: return ' '; + case 0xAB: return ' '; + case 0xAC: return ' '; + case 0xAD: return ' '; + case 0xAE: return ' '; + case 0xAF: return ' '; + case 0xB0: return ' '; + case 0xB1: return ' '; + case 0xB2: return ' '; + case 0xB3: return ' '; + case 0xB4: return ' '; + case 0xB5: return ' '; + case 0xB6: return ' '; + case 0xB7: return ' '; + case 0xB8: return ' '; + case 0xB9: return ' '; + case 0xBA: return ' '; + case 0xBB: return ' '; + case 0xBC: return ' '; + case 0xBD: return ' '; + case 0xBE: return ' '; + case 0xBF: return ' '; + case 0xC0: return ' '; + case 0xC1: return ' '; + case 0xC2: return ' '; + case 0xC3: return ' '; + case 0xC4: return ' '; + case 0xC5: return ' '; + case 0xC6: return ' '; + case 0xC7: return ' '; + case 0xC8: return ' '; + case 0xC9: return ' '; + case 0xCA: return ' '; + case 0xCB: return 'A'; + case 0xCC: return 'A'; + case 0xCD: return 'O'; + case 0xCE: return ' '; + case 0xCF: return ' '; + case 0xD0: return '-'; + case 0xD1: return '-'; + case 0xD2: return 0x22; + case 0xD3: return 0x22; + case 0xD4: return 0x27; + case 0xD5: return 0x27; + case 0xD6: return '-'; // use dash instead of slash + case 0xD7: return ' '; + case 0xD8: return 'y'; + case 0xD9: return 'Y'; + case 0xDA: return '-'; // use dash instead of slash + case 0xDB: return ' '; + case 0xDC: return '<'; + case 0xDD: return '>'; + case 0xDE: return ' '; + case 0xDF: return ' '; + case 0xE0: return ' '; + case 0xE1: return ' '; + case 0xE2: return ','; + case 0xE3: return ','; + case 0xE4: return ' '; + case 0xE5: return 'A'; + case 0xE6: return 'E'; + case 0xE7: return 'A'; + case 0xE8: return 'E'; + case 0xE9: return 'E'; + case 0xEA: return 'I'; + case 0xEB: return 'I'; + case 0xEC: return 'I'; + case 0xED: return 'I'; + case 0xEE: return 'O'; + case 0xEF: return 'O'; + case 0xF0: return ' '; + case 0xF1: return 'O'; + case 0xF2: return 'U'; + case 0xF3: return 'U'; + case 0xF4: return 'U'; + case 0xF5: return '|'; + case 0xF6: return ' '; + case 0xF7: return ' '; + case 0xF8: return ' '; + case 0xF9: return ' '; + case 0xFA: return '.'; + case 0xFB: return ' '; + case 0xFC: return ' '; + case 0xFD: return 0x22; + case 0xFE: return ' '; + case 0xFF: return ' '; + } + return c; +} + +// String should match ndof for manufacturer based search to work +static void sanitizeString( char* inCStr ) +{ + char* charIt = inCStr; + while ( *charIt ) + { + *charIt = mapChar( *charIt ); + charIt++; + } +} + +struct HidDevice +{ + long mAxis; + long mLocalID; + char mProduct[256]; + char mManufacturer[256]; + long mUsage; + long mUsagePage; +}; + +static void populate_device_info( io_object_t io_obj_p, CFDictionaryRef device_dic, HidDevice* devicep ) +{ + CFMutableDictionaryRef io_properties = nil; + io_registry_entry_t entry1; + io_registry_entry_t entry2; + kern_return_t rc; + + // Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also + // get dictionary for usb properties: step up two levels and get CF dictionary for USB properties + // try to get parent1 + rc = IORegistryEntryGetParentEntry( io_obj_p, kIOServicePlane, &entry1 ); + if ( KERN_SUCCESS == rc ) + { + rc = IORegistryEntryGetParentEntry( entry1, kIOServicePlane, &entry2 ); + + IOObjectRelease( entry1 ); + + if ( KERN_SUCCESS == rc ) + { + rc = IORegistryEntryCreateCFProperties( entry2, &io_properties, kCFAllocatorDefault, kNilOptions ); + // either way, release parent2 + IOObjectRelease( entry2 ); + } + } + if ( KERN_SUCCESS == rc ) + { + // IORegistryEntryCreateCFProperties() succeeded + if ( io_properties != nil ) + { + CFTypeRef dict_element = 0; + // get device info + // try hid dictionary first, if fail then go to usb dictionary + + + dict_element = CFDictionaryGetValue( device_dic, CFSTR(kIOHIDProductKey) ); + if ( !dict_element ) + { + dict_element = CFDictionaryGetValue( io_properties, CFSTR( "USB Product Name" ) ); + } + if ( dict_element ) + { + bool res = CFStringGetCString((CFStringRef)dict_element, devicep->mProduct, 256, kCFStringEncodingUTF8); + sanitizeString(devicep->mProduct); + if ( !res ) + { + LL_WARNS("Joystick") << "Failed to populate mProduct" << LL_ENDL; + } + } + + dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDManufacturerKey ) ); + if ( !dict_element ) + { + dict_element = CFDictionaryGetValue( io_properties, CFSTR( "USB Vendor Name" ) ); + } + if ( dict_element ) + { + bool res = CFStringGetCString( (CFStringRef)dict_element, devicep->mManufacturer, 256, kCFStringEncodingUTF8 ); + sanitizeString(devicep->mManufacturer); + if ( !res ) + { + LL_WARNS("Joystick") << "Failed to populate mManufacturer" << LL_ENDL; + } + } + + dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDLocationIDKey ) ); + if ( !dict_element ) + { + dict_element = CFDictionaryGetValue( io_properties, CFSTR( "locationID" ) ); + } + if ( dict_element ) + { + bool res = CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mLocalID ); + if ( !res ) + { + LL_WARNS("Joystick") << "Failed to populate mLocalID" << LL_ENDL; + } + } + + dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDPrimaryUsagePageKey ) ); + if ( dict_element ) + { + bool res = CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mUsagePage ); + if ( !res ) + { + LL_WARNS("Joystick") << "Failed to populate mUsagePage" << LL_ENDL; + } + dict_element = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDPrimaryUsageKey ) ); + if ( dict_element ) + { + if ( !CFNumberGetValue( (CFNumberRef)dict_element, kCFNumberLongType, &devicep->mUsage ) ) + { + LL_WARNS("Joystick") << "Failed to populate mUsage" << LL_ENDL; + } + } + } + + //Add axis, because ndof lib checks sutability by axises as well as other elements + devicep->mAxis = 0; + CFTypeRef hid_elements = CFDictionaryGetValue( device_dic, CFSTR( kIOHIDElementKey ) ); + if ( hid_elements && CFGetTypeID( hid_elements ) == CFArrayGetTypeID( ) ) + { + long count = CFArrayGetCount( (CFArrayRef) hid_elements ); + for (int i = 0; i < count; ++i) + { + CFTypeRef element = CFArrayGetValueAtIndex((CFArrayRef) hid_elements, i); + if (element && CFGetTypeID( element ) == CFDictionaryGetTypeID( )) + { + long type = 0, usage_page = 0, usage = 0; + + CFTypeRef ref_value = CFDictionaryGetValue( (CFDictionaryRef) element, CFSTR( kIOHIDElementTypeKey ) ); + if ( ref_value ) + { + CFNumberGetValue( (CFNumberRef)ref_value, kCFNumberLongType, &type ); + } + + ref_value = CFDictionaryGetValue( (CFDictionaryRef) element, CFSTR( kIOHIDElementUsagePageKey ) ); + if ( ref_value ) + { + CFNumberGetValue( (CFNumberRef)ref_value, kCFNumberLongType, &usage_page ); + } + + ref_value = CFDictionaryGetValue( (CFDictionaryRef) element, CFSTR( kIOHIDElementUsageKey ) ); + if ( ref_value ) + { + CFNumberGetValue( (CFNumberRef)ref_value, kCFNumberLongType, &usage ); + } + if ( type != 0 + && type != kIOHIDElementTypeCollection + && usage_page == kHIDPage_GenericDesktop) + { + switch( usage ) + { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + devicep->mAxis++; + break; + default: + break; + } + } + } + } + } + + CFRelease(io_properties); + } + else + { + LL_WARNS("Joystick") << "Failed to populate fields" << LL_ENDL; + } + } +} + +HidDevice populate_device( io_object_t io_obj ) +{ + void* interfacep = nullptr; + HidDevice device; + memset( &device, 0, sizeof( HidDevice ) ); + CFMutableDictionaryRef device_dic = 0; + kern_return_t result = IORegistryEntryCreateCFProperties( io_obj, &device_dic, kCFAllocatorDefault, kNilOptions ); + + if ( KERN_SUCCESS == result + && device_dic ) + { + IOReturn io_result = kIOReturnSuccess; + HRESULT query_result = S_OK; + SInt32 the_score = 0; + IOCFPlugInInterface **the_interface = NULL; + + + io_result = IOCreatePlugInInterfaceForService( io_obj, kIOHIDDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &the_interface, &the_score ); + if ( io_result == kIOReturnSuccess ) + { + query_result = ( *the_interface )->QueryInterface( the_interface, CFUUIDGetUUIDBytes( kIOHIDDeviceInterfaceID ), ( LPVOID * ) & ( interfacep ) ); + if ( query_result != S_OK ) + { + LL_WARNS("Joystick") << "QueryInterface failed" << LL_ENDL; + } + IODestroyPlugInInterface( the_interface ); + } + else + { + LL_WARNS("Joystick") << "IOCreatePlugInInterfaceForService failed" << LL_ENDL; + } + + if ( interfacep ) + { + result = ( *( IOHIDDeviceInterface** )interfacep )->open( interfacep, 0 ); + + if ( result != kIOReturnSuccess) + { + LL_WARNS("Joystick") << "open failed" << LL_ENDL; + } + } + // extract needed fields + populate_device_info( io_obj, device_dic, &device ); + + // Release interface + if ( interfacep ) + { + ( *( IOHIDDeviceInterface** ) interfacep )->close( interfacep ); + + ( *( IOHIDDeviceInterface** ) interfacep )->Release( interfacep ); + + interfacep = NULL; + } + + CFRelease( device_dic ); + } + else + { + LL_WARNS("Joystick") << "populate_device failed" << LL_ENDL; + } + + return device; +} + +static void get_devices(std::list<HidDevice> &list_of_devices, + io_iterator_t inIODeviceIterator) +{ + IOReturn result = kIOReturnSuccess; // assume success( optimist! ) + io_object_t io_obj = 0; + + while ( 0 != (io_obj = IOIteratorNext( inIODeviceIterator ) ) ) + { + HidDevice device = populate_device( io_obj ); + + // Should match ndof + if (device.mAxis >= 3 + || (device.mUsagePage == kHIDPage_GenericDesktop + && (device.mUsage == kHIDUsage_GD_MultiAxisController + || device.mUsage == kHIDUsage_GD_GamePad + || device.mUsage == kHIDUsage_GD_Joystick)) + || (device.mUsagePage == kHIDPage_Game + && device.mUsage == kHIDUsage_Game_3DGameController) + || strstr(device.mManufacturer, "3Dconnexion")) + { + list_of_devices.push_back(device); + } + else + { + LL_DEBUGS("Joystick"); + list_of_devices.push_back(device); + LL_CONT << "Device axes: " << (S32)device.mAxis + << " Device HIDUsepage: " << (S32)device.mUsagePage + << " Device HIDUsage: " << (S32)device.mUsage; + LL_ENDL; + } + + + // release the device object, it is no longer needed + result = IOObjectRelease( io_obj ); + if ( KERN_SUCCESS != result ) + { + LL_WARNS("Joystick") << "IOObjectRelease failed" << LL_ENDL; + } + } +} + +bool LLWindowMacOSX::getInputDevices(U32 device_type_filter, + std::function<bool(std::string&, LLSD&, void*)> osx_callback, + void* win_callback, + void* userdata) +{ + bool return_value = false; + CFMutableDictionaryRef device_dict_ref; + IOReturn result = kIOReturnSuccess; // assume success( optimist! ) + + // Set up matching dictionary to search the I/O Registry for HID devices we are interested in. Dictionary reference is NULL if error. + + // A dictionary to match devices to? + device_dict_ref = IOServiceMatching( kIOHIDDeviceKey ); + + // BUG FIX! one reference is consumed by IOServiceGetMatchingServices + CFRetain( device_dict_ref ); + io_iterator_t io_iter = 0; + + // create an IO object iterator + result = IOServiceGetMatchingServices( kIOMasterPortDefault, device_dict_ref, &io_iter ); + if ( kIOReturnSuccess != result ) + { + LL_WARNS("Joystick") << "IOServiceGetMatchingServices failed" << LL_ENDL; + } + + if ( io_iter ) + { + // add all existing devices + std::list<HidDevice> device_list; + + get_devices(device_list, io_iter); + + std::list<HidDevice>::iterator iter; + + for (iter = device_list.begin(); iter != device_list.end(); ++iter) + { + std::string label(iter->mProduct); + LLSD data; + data["manufacturer"] = std::string(iter->mManufacturer); + data["product"] = label; + + if (osx_callback(label, data, userdata)) + { + break; //found device + } + } + return_value = true; + } + + CFRelease( device_dict_ref ); + return return_value; +} + +LLSD LLWindowMacOSX::getNativeKeyData() +{ + LLSD result = LLSD::emptyMap(); + + if(mRawKeyEvent) + { + result["event_type"] = LLSD::Integer(mRawKeyEvent->mEventType); + result["event_modifiers"] = LLSD::Integer(mRawKeyEvent->mEventModifiers); + result["event_keycode"] = LLSD::Integer(mRawKeyEvent->mEventKeyCode); + result["event_chars"] = (mRawKeyEvent->mEventChars) ? LLSD(LLSD::Integer(mRawKeyEvent->mEventChars)) : LLSD(); + result["event_umodchars"] = (mRawKeyEvent->mEventUnmodChars) ? LLSD(LLSD::Integer(mRawKeyEvent->mEventUnmodChars)) : LLSD(); + result["event_isrepeat"] = LLSD::Boolean(mRawKeyEvent->mEventRepeat); + } + + LL_DEBUGS() << "native key data is: " << result << LL_ENDL; + + return result; +} + +bool LLWindowMacOSX::dialogColorPicker( 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; + + error = NPickColor(&info); + + 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); +} + +void *LLWindowMacOSX::getPlatformWindow() +{ + // NOTE: this will be NULL in fullscreen mode. Plan accordingly. + return (void*)mWindow; +} + +// 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 +} + +void LLWindowMacOSX::allowLanguageTextInput(LLPreeditor *preeditor, bool b) +{ + if (preeditor != mPreeditor && !b) + { + // This condition may occur by a call to + // setEnabled(bool) against LLTextEditor or LLLineEditor + // when the control is not focused. + // We need to silently ignore the case so that + // the language input status of the focused control + // is not disturbed. + return; + } + + // Take care of old and new preeditors. + if (preeditor != mPreeditor || !b) + { + // We need to interrupt before updating mPreeditor, + // so that the fix string from input method goes to + // the old preeditor. + if (mLanguageTextInputAllowed) + { + interruptLanguageTextInput(); + } + mPreeditor = (b ? preeditor : NULL); + } + + if (b == mLanguageTextInputAllowed) + { + return; + } + mLanguageTextInputAllowed = b; + allowDirectMarkedTextInput(b, mGLView); // mLanguageTextInputAllowed and mMarkedTextAllowed should be updated at once (by Pell Smit +} + +class sharedContext +{ +public: + CGLContextObj mContext; +}; + +void* LLWindowMacOSX::createSharedContext() +{ + sharedContext* sc = new sharedContext(); + CGLCreateContext(mPixelFormat, mContext, &(sc->mContext)); + + if (sUseMultGL) + { + CGLEnable(mContext, kCGLCEMPEngine); + } + + return (void *)sc; +} + +void LLWindowMacOSX::makeContextCurrent(void* context) +{ + CGLSetCurrentContext(((sharedContext*)context)->mContext); + + //enable multi-threaded OpenGL + if (sUseMultGL) + { + CGLError cgl_err; + CGLContextObj ctx = CGLGetCurrentContext(); + + cgl_err = CGLEnable( ctx, kCGLCEMPEngine); + + if (cgl_err != kCGLNoError ) + { + LL_INFOS("GLInit") << "Multi-threaded OpenGL not available." << LL_ENDL; + } + else + { + LL_INFOS("GLInit") << "Multi-threaded OpenGL enabled." << LL_ENDL; + } + } + +} + +void LLWindowMacOSX::destroySharedContext(void* context) +{ + sharedContext* sc = (sharedContext*)context; + + CGLDestroyContext(sc->mContext); + + delete sc; +} + +void LLWindowMacOSX::toggleVSync(bool enable_vsync) +{ + GLint frames_per_swap = 0; + if (!enable_vsync) + { + frames_per_swap = 0; + } + else + { + frames_per_swap = 1; + } + + CGLSetParameter(mContext, kCGLCPSwapInterval, &frames_per_swap); +} + +void LLWindowMacOSX::interruptLanguageTextInput() +{ + commitCurrentPreedit(mGLView); +} + +std::vector<std::string> LLWindowMacOSX::getDisplaysResolutionList() +{ + std::vector<std::string> resolution_list; + + CGDirectDisplayID display_ids[10]; + uint32_t found_displays = 0; + CGError err = CGGetActiveDisplayList(10, display_ids, &found_displays); + + if (kCGErrorSuccess != err) + { + LL_WARNS() << "Couldn't get a list of active displays" << LL_ENDL; + return std::vector<std::string>(); + } + + for (uint32_t i = 0; i < found_displays; i++) + { + S32 monitor_width = CGDisplayPixelsWide(display_ids[i]); + S32 monitor_height = CGDisplayPixelsHigh(display_ids[i]); + + std::ostringstream sstream; + sstream << monitor_width << "x" << monitor_height;; + std::string res = sstream.str(); + + resolution_list.push_back(res); + } + + return resolution_list; +} + +//static +std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList() +{ + // Fonts previously in getFontListSans() have moved to fonts.xml. + return std::vector<std::string>(); +} + +// static +MASK LLWindowMacOSX::modifiersToMask(S16 modifiers) +{ + MASK mask = 0; + if(modifiers & MAC_SHIFT_KEY) { mask |= MASK_SHIFT; } + if(modifiers & (MAC_CMD_KEY | MAC_CTRL_KEY)) { mask |= MASK_CONTROL; } + if(modifiers & MAC_ALT_KEY) { mask |= MASK_ALT; } + return mask; +} + +F32 LLWindowMacOSX::getSystemUISize() +{ + return gHiDPISupport ? ::getDeviceUnitSize(mGLView) : LLWindow::getSystemUISize(); +} + +#if LL_OS_DRAGDROP_ENABLED +/* +S16 LLWindowMacOSX::dragTrackingHandler(DragTrackingMessage message, WindowRef theWindow, + void * handlerRefCon, DragRef drag) +{ + S16 result = 0; + LLWindowMacOSX *self = (LLWindowMacOSX*)handlerRefCon; + + LL_DEBUGS() << "drag tracking handler, message = " << message << LL_ENDL; + + switch(message) + { + case kDragTrackingInWindow: + result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_TRACK); + break; + + case kDragTrackingEnterHandler: + result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_START_TRACKING); + break; + + case kDragTrackingLeaveHandler: + result = self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_STOP_TRACKING); + break; + + default: + break; + } + + return result; +} +OSErr LLWindowMacOSX::dragReceiveHandler(WindowRef theWindow, void * handlerRefCon, + DragRef drag) +{ + LLWindowMacOSX *self = (LLWindowMacOSX*)handlerRefCon; + return self->handleDragNDrop(drag, LLWindowCallbacks::DNDA_DROPPED); + +} +*/ +void LLWindowMacOSX::handleDragNDrop(std::string url, LLWindowCallbacks::DragNDropAction action) +{ + MASK mask = LLWindowMacOSX::modifiersToMask(getModifiers()); + + float mouse_point[2]; + // This will return the mouse point in window coords + getCursorPos(mWindow, mouse_point); + LLCoordWindow window_coords(mouse_point[0], mouse_point[1]); + LLCoordGL gl_pos; + convertCoords(window_coords, &gl_pos); + + if(!url.empty()) + { + LLWindowCallbacks::DragNDropResult res = + mCallbacks->handleDragNDrop(this, gl_pos, mask, action, url); + + switch (res) { + case LLWindowCallbacks::DND_NONE: // No drop allowed + if (action == LLWindowCallbacks::DNDA_TRACK) + { + mDragOverrideCursor = 0; + } + else { + mDragOverrideCursor = -1; + } + break; + case LLWindowCallbacks::DND_MOVE: // Drop accepted would result in a "move" operation + mDragOverrideCursor = UI_CURSOR_NO; + break; + case LLWindowCallbacks::DND_COPY: // Drop accepted would result in a "copy" operation + mDragOverrideCursor = UI_CURSOR_ARROWCOPY; + break; + default: + mDragOverrideCursor = -1; + break; + } + // This overrides the cursor being set by setCursor. + // This is a bit of a hack workaround because lots of areas + // within the viewer just blindly set the cursor. + if (mDragOverrideCursor == -1) + { + // Restore the cursor + ECursorType temp_cursor = mCurrentCursor; + // get around the "setting the same cursor" code in setCursor() + mCurrentCursor = UI_CURSOR_COUNT; + setCursor(temp_cursor); + } + else { + // Override the cursor + switch (mDragOverrideCursor) { + case 0: + setArrowCursor(); + break; + case UI_CURSOR_NO: + setNotAllowedCursor(); + case UI_CURSOR_ARROWCOPY: + setCopyCursor(); + default: + break; + }; + } + } +} + +#endif // LL_OS_DRAGDROP_ENABLED diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 9ed51a9f15..3be89c255a 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -1,265 +1,265 @@ -/**
- * @file llwindowmacosx.h
- * @brief Mac implementation of LLWindow class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLWINDOWMACOSX_H
-#define LL_LLWINDOWMACOSX_H
-
-#include "llwindow.h"
-#include "llwindowcallbacks.h"
-#include "llwindowmacosx-objc.h"
-
-#include "lltimer.h"
-
-#include <ApplicationServices/ApplicationServices.h>
-#include <OpenGL/OpenGL.h>
-
-// AssertMacros.h does bad things.
-#include "fix_macros.h"
-#undef verify
-#undef require
-
-class LLWindowMacOSX : public LLWindow
-{
-public:
- void show() override;
- void hide() override;
- void close() override;
- bool getVisible() override;
- bool getMinimized() override;
- bool getMaximized() override;
- bool maximize() override;
- void minimize() override;
- void restore() override;
- bool getFullscreen();
- bool getPosition(LLCoordScreen *position) override;
- bool getSize(LLCoordScreen *size) override;
- bool getSize(LLCoordWindow *size) override;
- bool setPosition(LLCoordScreen position) override;
- bool setSizeImpl(LLCoordScreen size) override;
- bool setSizeImpl(LLCoordWindow size) override;
- bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override;
- bool setCursorPosition(LLCoordWindow position) override;
- bool getCursorPosition(LLCoordWindow *position) override;
- void showCursor() override;
- void hideCursor() override;
- void showCursorFromMouseMove() override;
- void hideCursorUntilMouseMove() override;
- bool isCursorHidden() override;
- void updateCursor() override;
- ECursorType getCursor() const override;
- void captureMouse() override;
- void releaseMouse() override;
- void setMouseClipping( bool b ) override;
- bool isClipboardTextAvailable() override;
- bool pasteTextFromClipboard(LLWString &dst) override;
- bool copyTextToClipboard(const LLWString & src) override;
- void flashIcon(F32 seconds) override;
- F32 getGamma() override;
- bool setGamma(const F32 gamma) override; // Set the gamma
- U32 getFSAASamples() override;
- void setFSAASamples(const U32 fsaa_samples) override;
- bool restoreGamma() override; // Restore original gamma table (before updating gamma)
- ESwapMethod getSwapMethod() override { return mSwapMethod; }
- void gatherInput() override;
- void delayInputProcessing() override {};
- void swapBuffers() override;
-
- // handy coordinate space conversion routines
- bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override;
- bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override;
- bool convertCoords(LLCoordWindow from, LLCoordGL *to) override;
- bool convertCoords(LLCoordGL from, LLCoordWindow *to) override;
- bool convertCoords(LLCoordScreen from, LLCoordGL *to) override;
- bool convertCoords(LLCoordGL from, LLCoordScreen *to) override;
-
- LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override;
- F32 getNativeAspectRatio() override;
- F32 getPixelAspectRatio() override;
- void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; }
-
- virtual void setMaxVRAMMegabytes(U32 max_vram) override {}
-
- void beforeDialog() override;
- void afterDialog() override;
-
- bool dialogColorPicker(F32 *r, F32 *g, F32 *b) override;
-
- void *getPlatformWindow() override;
- void bringToFront() override {};
-
- void allowLanguageTextInput(LLPreeditor *preeditor, bool b) override;
- void interruptLanguageTextInput() override;
- void spawnWebBrowser(const std::string& escaped_url, bool async) override;
- F32 getSystemUISize() override;
-
- bool getInputDevices(U32 device_type_filter,
- std::function<bool(std::string&, LLSD&, void*)> osx_callback,
- void* win_callback,
- void* userdata) override;
-
- static std::vector<std::string> getDisplaysResolutionList();
-
- static std::vector<std::string> getDynamicFallbackFontList();
-
- // Provide native key event data
- LLSD getNativeKeyData() override;
-
- void* getWindow() { return mWindow; }
- LLWindowCallbacks* getCallbacks() { return mCallbacks; }
- LLPreeditor* getPreeditor() { return mPreeditor; }
-
- void updateMouseDeltas(float* deltas);
- void getMouseDeltas(float* delta);
-
- void handleDragNDrop(std::string url, LLWindowCallbacks::DragNDropAction action);
-
- bool allowsLanguageInput() { return mLanguageTextInputAllowed; }
-
- //create a new GL context that shares a namespace with this Window's main GL context and make it current on the current thread
- // returns a pointer to be handed back to destroySharedConext/makeContextCurrent
- void* createSharedContext() override;
- //make the given context current on the current thread
- void makeContextCurrent(void* context) override;
- //destroy the given context that was retrieved by createSharedContext()
- //Must be called on the same thread that called createSharedContext()
- void destroySharedContext(void* context) override;
-
- void toggleVSync(bool enable_vsync) override;
-
-protected:
- LLWindowMacOSX(LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags,
- bool fullscreen, bool clearBg, bool enable_vsync, bool use_gl,
- bool ignore_pixel_depth,
- U32 fsaa_samples,
- U32 max_vram);
- ~LLWindowMacOSX();
-
- void initCursors();
- bool isValid() override;
- 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();
-
- bool shouldPostQuit() { return mPostQuit; }
-
- //Satisfy MAINT-3135 and MAINT-3288 with a flag.
- /*virtual */ void setOldResize(bool oldresize) override {setResizeMode(oldresize, mGLView); }
-
-private:
- void restoreGLContext();
-
-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 enable_vsync);
- void destroyContext();
- void setupFailure(const std::string& text, const std::string& caption, U32 type);
- void adjustCursorDecouple(bool warpingMouse = false);
- static MASK modifiersToMask(S16 modifiers);
-
-#if LL_OS_DRAGDROP_ENABLED
-
- //static OSErr dragTrackingHandler(DragTrackingMessage message, WindowRef theWindow, void * handlerRefCon, DragRef theDrag);
- //static OSErr dragReceiveHandler(WindowRef theWindow, void * handlerRefCon, DragRef theDrag);
-
-
-#endif // LL_OS_DRAGDROP_ENABLED
-
- //
- // Platform specific variables
- //
-
- // Use generic pointers here. This lets us do some funky Obj-C interop using Obj-C objects without having to worry about any compilation problems that may arise.
- NSWindowRef mWindow;
- GLViewRef mGLView;
- CGLContextObj mContext;
- CGLPixelFormatObj mPixelFormat;
- CGDirectDisplayID mDisplay;
-
- LLRect mOldMouseClip; // Screen rect to which the mouse cursor was globally constrained before we changed it in clipMouse()
- std::string mWindowTitle;
- double mOriginalAspectRatio;
- bool mSimulatedRightClick;
- U32 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 mMaximized;
- bool mMinimized;
- U32 mFSAASamples;
- bool mForceRebuild;
-
- S32 mDragOverrideCursor;
-
- // Input method management through Text Service Manager.
- bool mLanguageTextInputAllowed;
- LLPreeditor* mPreeditor;
-
-public:
- static bool sUseMultGL;
-
- friend class LLWindowManager;
-
-};
-
-
-class LLSplashScreenMacOSX : public LLSplashScreen
-{
-public:
- LLSplashScreenMacOSX();
- virtual ~LLSplashScreenMacOSX();
-
- void showImpl();
- void updateImpl(const std::string& mesg);
- void hideImpl();
-
-private:
- WindowRef mWindow;
-};
-
-S32 OSMessageBoxMacOSX(const std::string& text, const std::string& caption, U32 type);
-
-void load_url_external(const char* url);
-
-#endif //LL_LLWINDOWMACOSX_H
+/** + * @file llwindowmacosx.h + * @brief Mac implementation of LLWindow class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLWINDOWMACOSX_H +#define LL_LLWINDOWMACOSX_H + +#include "llwindow.h" +#include "llwindowcallbacks.h" +#include "llwindowmacosx-objc.h" + +#include "lltimer.h" + +#include <ApplicationServices/ApplicationServices.h> +#include <OpenGL/OpenGL.h> + +// AssertMacros.h does bad things. +#include "fix_macros.h" +#undef verify +#undef require + +class LLWindowMacOSX : public LLWindow +{ +public: + void show() override; + void hide() override; + void close() override; + bool getVisible() override; + bool getMinimized() override; + bool getMaximized() override; + bool maximize() override; + void minimize() override; + void restore() override; + bool getFullscreen(); + bool getPosition(LLCoordScreen *position) override; + bool getSize(LLCoordScreen *size) override; + bool getSize(LLCoordWindow *size) override; + bool setPosition(LLCoordScreen position) override; + bool setSizeImpl(LLCoordScreen size) override; + bool setSizeImpl(LLCoordWindow size) override; + bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL) override; + bool setCursorPosition(LLCoordWindow position) override; + bool getCursorPosition(LLCoordWindow *position) override; + void showCursor() override; + void hideCursor() override; + void showCursorFromMouseMove() override; + void hideCursorUntilMouseMove() override; + bool isCursorHidden() override; + void updateCursor() override; + ECursorType getCursor() const override; + void captureMouse() override; + void releaseMouse() override; + void setMouseClipping( bool b ) override; + bool isClipboardTextAvailable() override; + bool pasteTextFromClipboard(LLWString &dst) override; + bool copyTextToClipboard(const LLWString & src) override; + void flashIcon(F32 seconds) override; + F32 getGamma() override; + bool setGamma(const F32 gamma) override; // Set the gamma + U32 getFSAASamples() override; + void setFSAASamples(const U32 fsaa_samples) override; + bool restoreGamma() override; // Restore original gamma table (before updating gamma) + ESwapMethod getSwapMethod() override { return mSwapMethod; } + void gatherInput() override; + void delayInputProcessing() override {}; + void swapBuffers() override; + + // handy coordinate space conversion routines + bool convertCoords(LLCoordScreen from, LLCoordWindow *to) override; + bool convertCoords(LLCoordWindow from, LLCoordScreen *to) override; + bool convertCoords(LLCoordWindow from, LLCoordGL *to) override; + bool convertCoords(LLCoordGL from, LLCoordWindow *to) override; + bool convertCoords(LLCoordScreen from, LLCoordGL *to) override; + bool convertCoords(LLCoordGL from, LLCoordScreen *to) override; + + LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) override; + F32 getNativeAspectRatio() override; + F32 getPixelAspectRatio() override; + void setNativeAspectRatio(F32 ratio) override { mOverrideAspectRatio = ratio; } + + virtual void setMaxVRAMMegabytes(U32 max_vram) override {} + + void beforeDialog() override; + void afterDialog() override; + + bool dialogColorPicker(F32 *r, F32 *g, F32 *b) override; + + void *getPlatformWindow() override; + void bringToFront() override {}; + + void allowLanguageTextInput(LLPreeditor *preeditor, bool b) override; + void interruptLanguageTextInput() override; + void spawnWebBrowser(const std::string& escaped_url, bool async) override; + F32 getSystemUISize() override; + + bool getInputDevices(U32 device_type_filter, + std::function<bool(std::string&, LLSD&, void*)> osx_callback, + void* win_callback, + void* userdata) override; + + static std::vector<std::string> getDisplaysResolutionList(); + + static std::vector<std::string> getDynamicFallbackFontList(); + + // Provide native key event data + LLSD getNativeKeyData() override; + + void* getWindow() { return mWindow; } + LLWindowCallbacks* getCallbacks() { return mCallbacks; } + LLPreeditor* getPreeditor() { return mPreeditor; } + + void updateMouseDeltas(float* deltas); + void getMouseDeltas(float* delta); + + void handleDragNDrop(std::string url, LLWindowCallbacks::DragNDropAction action); + + bool allowsLanguageInput() { return mLanguageTextInputAllowed; } + + //create a new GL context that shares a namespace with this Window's main GL context and make it current on the current thread + // returns a pointer to be handed back to destroySharedConext/makeContextCurrent + void* createSharedContext() override; + //make the given context current on the current thread + void makeContextCurrent(void* context) override; + //destroy the given context that was retrieved by createSharedContext() + //Must be called on the same thread that called createSharedContext() + void destroySharedContext(void* context) override; + + void toggleVSync(bool enable_vsync) override; + +protected: + LLWindowMacOSX(LLWindowCallbacks* callbacks, + const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, + bool fullscreen, bool clearBg, bool enable_vsync, bool use_gl, + bool ignore_pixel_depth, + U32 fsaa_samples, + U32 max_vram); + ~LLWindowMacOSX(); + + void initCursors(); + bool isValid() override; + 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(); + + bool shouldPostQuit() { return mPostQuit; } + + //Satisfy MAINT-3135 and MAINT-3288 with a flag. + /*virtual */ void setOldResize(bool oldresize) override {setResizeMode(oldresize, mGLView); } + +private: + void restoreGLContext(); + +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 enable_vsync); + void destroyContext(); + void setupFailure(const std::string& text, const std::string& caption, U32 type); + void adjustCursorDecouple(bool warpingMouse = false); + static MASK modifiersToMask(S16 modifiers); + +#if LL_OS_DRAGDROP_ENABLED + + //static OSErr dragTrackingHandler(DragTrackingMessage message, WindowRef theWindow, void * handlerRefCon, DragRef theDrag); + //static OSErr dragReceiveHandler(WindowRef theWindow, void * handlerRefCon, DragRef theDrag); + + +#endif // LL_OS_DRAGDROP_ENABLED + + // + // Platform specific variables + // + + // Use generic pointers here. This lets us do some funky Obj-C interop using Obj-C objects without having to worry about any compilation problems that may arise. + NSWindowRef mWindow; + GLViewRef mGLView; + CGLContextObj mContext; + CGLPixelFormatObj mPixelFormat; + CGDirectDisplayID mDisplay; + + LLRect mOldMouseClip; // Screen rect to which the mouse cursor was globally constrained before we changed it in clipMouse() + std::string mWindowTitle; + double mOriginalAspectRatio; + bool mSimulatedRightClick; + U32 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 mMaximized; + bool mMinimized; + U32 mFSAASamples; + bool mForceRebuild; + + S32 mDragOverrideCursor; + + // Input method management through Text Service Manager. + bool mLanguageTextInputAllowed; + LLPreeditor* mPreeditor; + +public: + static bool sUseMultGL; + + friend class LLWindowManager; + +}; + + +class LLSplashScreenMacOSX : public LLSplashScreen +{ +public: + LLSplashScreenMacOSX(); + virtual ~LLSplashScreenMacOSX(); + + void showImpl(); + void updateImpl(const std::string& mesg); + void hideImpl(); + +private: + WindowRef mWindow; +}; + +S32 OSMessageBoxMacOSX(const std::string& text, const std::string& caption, U32 type); + +void load_url_external(const char* url); + +#endif //LL_LLWINDOWMACOSX_H diff --git a/indra/llwindow/llwindowmesaheadless.cpp b/indra/llwindow/llwindowmesaheadless.cpp index a0dd2bc086..6cbd737ed2 100644 --- a/indra/llwindow/llwindowmesaheadless.cpp +++ b/indra/llwindow/llwindowmesaheadless.cpp @@ -1,78 +1,78 @@ -/**
- * @file llwindowmesaheadless.cpp
- * @brief Platform-dependent implementation of llwindow
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "indra_constants.h"
-
-#include "llwindowmesaheadless.h"
-#include "llgl.h"
-
-#define MESA_CHANNEL_TYPE GL_UNSIGNED_SHORT
-#define MESA_CHANNEL_SIZE 2
-
-U16 *gMesaBuffer = NULL;
-
-//
-// LLWindowMesaHeadless
-//
-LLWindowMesaHeadless::LLWindowMesaHeadless(LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& 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(callbacks, fullscreen, flags)
-{
- if (use_gl)
- {
- LL_INFOS() << "MESA Init" << LL_ENDL;
- 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 ))
- {
- LL_ERRS() << "MESA: OSMesaMakeCurrent failed!" << LL_ENDL;
- }
-
- llverify(gGLManager.initGL());
- }
-}
-
-
-LLWindowMesaHeadless::~LLWindowMesaHeadless()
-{
- delete mMesaBuffer;
- OSMesaDestroyContext( mMesaContext );
-}
-
-void LLWindowMesaHeadless::swapBuffers()
-{
- glFinish();
-}
+/** + * @file llwindowmesaheadless.cpp + * @brief Platform-dependent implementation of llwindow + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include "indra_constants.h" + +#include "llwindowmesaheadless.h" +#include "llgl.h" + +#define MESA_CHANNEL_TYPE GL_UNSIGNED_SHORT +#define MESA_CHANNEL_SIZE 2 + +U16 *gMesaBuffer = NULL; + +// +// LLWindowMesaHeadless +// +LLWindowMesaHeadless::LLWindowMesaHeadless(LLWindowCallbacks* callbacks, + const std::string& title, const std::string& 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(callbacks, fullscreen, flags) +{ + if (use_gl) + { + LL_INFOS() << "MESA Init" << LL_ENDL; + 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 )) + { + LL_ERRS() << "MESA: OSMesaMakeCurrent failed!" << LL_ENDL; + } + + llverify(gGLManager.initGL()); + } +} + + +LLWindowMesaHeadless::~LLWindowMesaHeadless() +{ + delete mMesaBuffer; + OSMesaDestroyContext( mMesaContext ); +} + +void LLWindowMesaHeadless::swapBuffers() +{ + glFinish(); +} diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h index 2188e54fb2..0bf8c46a30 100644 --- a/indra/llwindow/llwindowmesaheadless.h +++ b/indra/llwindow/llwindowmesaheadless.h @@ -1,123 +1,123 @@ -/**
- * @file llwindowmesaheadless.h
- * @brief Windows implementation of LLWindow class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLWINDOWMESAHEADLESS_H
-#define LL_LLWINDOWMESAHEADLESS_H
-
-#if LL_MESA_HEADLESS
-
-#include "llwindow.h"
-#include "GL/glu.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*/ void minimize() {};
- /*virtual*/ void restore() {};
- /*virtual*/ bool getFullscreen() {return false;};
- /*virtual*/ bool getPosition(LLCoordScreen *position) {return false;};
- /*virtual*/ bool getSize(LLCoordScreen *size) {return false;};
- /*virtual*/ bool getSize(LLCoordWindow *size) {return false;};
- /*virtual*/ bool setPosition(LLCoordScreen position) {return false;};
- /*virtual*/ bool setSizeImpl(LLCoordScreen size) {return false;};
- /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL) {return false;};
- /*virtual*/ bool setCursorPosition(LLCoordWindow position) {return false;};
- /*virtual*/ bool getCursorPosition(LLCoordWindow *position) {return false;};
- /*virtual*/ void showCursor() {};
- /*virtual*/ void hideCursor() {};
- /*virtual*/ void showCursorFromMouseMove() {};
- /*virtual*/ void hideCursorUntilMouseMove() {};
- /*virtual*/ bool isCursorHidden() {return false;};
- /*virtual*/ void updateCursor() {};
- //virtual ECursorType getCursor() { return mCurrentCursor; };
- /*virtual*/ void captureMouse() {};
- /*virtual*/ void releaseMouse() {};
- /*virtual*/ void setMouseClipping( bool b ) {};
- /*virtual*/ bool isClipboardTextAvailable() {return false; };
- /*virtual*/ bool pasteTextFromClipboard(LLWString &dst) {return false; };
- /*virtual*/ bool copyTextToClipboard(const LLWString &src) {return false; };
- /*virtual*/ void flashIcon(F32 seconds) {};
- /*virtual*/ F32 getGamma() {return 1.0f; };
- /*virtual*/ bool setGamma(const F32 gamma) {return false; }; // Set the gamma
- /*virtual*/ bool restoreGamma() {return false; }; // Restore original gamma table (before updating gamma)
- /*virtual*/ void setFSAASamples(const U32 fsaa_samples) { /* FSAA not supported yet on Mesa headless.*/ }
- /*virtual*/ U32 getFSAASamples() { return 0; }
- //virtual ESwapMethod getSwapMethod() { return mSwapMethod; }
- /*virtual*/ void gatherInput() {};
- /*virtual*/ void delayInputProcessing() {};
- /*virtual*/ void swapBuffers();
- /*virtual*/ void restoreGLContext() {};
-
- // 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(LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& 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 std::string& mesg) {};
- /*virtual*/ void hideImpl() {};
-
-};
-
-#endif
-
-#endif //LL_LLWINDOWMESAHEADLESS_H
+/** + * @file llwindowmesaheadless.h + * @brief Windows implementation of LLWindow class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLWINDOWMESAHEADLESS_H +#define LL_LLWINDOWMESAHEADLESS_H + +#if LL_MESA_HEADLESS + +#include "llwindow.h" +#include "GL/glu.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*/ void minimize() {}; + /*virtual*/ void restore() {}; + /*virtual*/ bool getFullscreen() {return false;}; + /*virtual*/ bool getPosition(LLCoordScreen *position) {return false;}; + /*virtual*/ bool getSize(LLCoordScreen *size) {return false;}; + /*virtual*/ bool getSize(LLCoordWindow *size) {return false;}; + /*virtual*/ bool setPosition(LLCoordScreen position) {return false;}; + /*virtual*/ bool setSizeImpl(LLCoordScreen size) {return false;}; + /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL) {return false;}; + /*virtual*/ bool setCursorPosition(LLCoordWindow position) {return false;}; + /*virtual*/ bool getCursorPosition(LLCoordWindow *position) {return false;}; + /*virtual*/ void showCursor() {}; + /*virtual*/ void hideCursor() {}; + /*virtual*/ void showCursorFromMouseMove() {}; + /*virtual*/ void hideCursorUntilMouseMove() {}; + /*virtual*/ bool isCursorHidden() {return false;}; + /*virtual*/ void updateCursor() {}; + //virtual ECursorType getCursor() { return mCurrentCursor; }; + /*virtual*/ void captureMouse() {}; + /*virtual*/ void releaseMouse() {}; + /*virtual*/ void setMouseClipping( bool b ) {}; + /*virtual*/ bool isClipboardTextAvailable() {return false; }; + /*virtual*/ bool pasteTextFromClipboard(LLWString &dst) {return false; }; + /*virtual*/ bool copyTextToClipboard(const LLWString &src) {return false; }; + /*virtual*/ void flashIcon(F32 seconds) {}; + /*virtual*/ F32 getGamma() {return 1.0f; }; + /*virtual*/ bool setGamma(const F32 gamma) {return false; }; // Set the gamma + /*virtual*/ bool restoreGamma() {return false; }; // Restore original gamma table (before updating gamma) + /*virtual*/ void setFSAASamples(const U32 fsaa_samples) { /* FSAA not supported yet on Mesa headless.*/ } + /*virtual*/ U32 getFSAASamples() { return 0; } + //virtual ESwapMethod getSwapMethod() { return mSwapMethod; } + /*virtual*/ void gatherInput() {}; + /*virtual*/ void delayInputProcessing() {}; + /*virtual*/ void swapBuffers(); + /*virtual*/ void restoreGLContext() {}; + + // 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(LLWindowCallbacks* callbacks, + const std::string& title, const std::string& 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 std::string& mesg) {}; + /*virtual*/ void hideImpl() {}; + +}; + +#endif + +#endif //LL_LLWINDOWMESAHEADLESS_H diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 0eedca354d..7433ad6bd2 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -1,2697 +1,2697 @@ -/**
- * @file llwindowsdl.cpp
- * @brief SDL implementation of LLWindow class
- * @author This module has many fathers, and it shows.
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#if LL_SDL
-
-#include "linden_common.h"
-
-#include "llwindowsdl.h"
-
-#include "llwindowcallbacks.h"
-#include "llkeyboardsdl.h"
-
-#include "llerror.h"
-#include "llgl.h"
-#include "llstring.h"
-#include "lldir.h"
-#include "llfindlocale.h"
-
-#if LL_GTK
-extern "C" {
-# include "gtk/gtk.h"
-}
-#include <locale.h>
-#endif // LL_GTK
-
-extern "C" {
-# include "fontconfig/fontconfig.h"
-}
-
-#if LL_LINUX
-// not necessarily available on random SDL platforms, so #if LL_LINUX
-// for execv(), waitpid(), fork()
-# include <unistd.h>
-# include <sys/types.h>
-# include <sys/wait.h>
-#endif // LL_LINUX
-
-extern bool gDebugWindowProc;
-
-const S32 MAX_NUM_RESOLUTIONS = 200;
-
-// static variable for ATI mouse cursor crash work-around:
-static bool ATIbug = false;
-
-//
-// LLWindowSDL
-//
-
-#if LL_X11
-# include <X11/Xutil.h>
-#endif //LL_X11
-
-// TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for a similar
-// set of reasons): Stash a pointer to the LLWindowSDL object here and
-// maintain in the constructor and destructor. This assumes that there will
-// be only one object of this class at any time. Currently this is true.
-static LLWindowSDL *gWindowImplementation = NULL;
-
-
-void maybe_lock_display(void)
-{
- if (gWindowImplementation && gWindowImplementation->Lock_Display) {
- gWindowImplementation->Lock_Display();
- }
-}
-
-
-void maybe_unlock_display(void)
-{
- if (gWindowImplementation && gWindowImplementation->Unlock_Display) {
- gWindowImplementation->Unlock_Display();
- }
-}
-
-
-#if LL_GTK
-// Lazily initialize and check the runtime GTK version for goodness.
-// static
-bool LLWindowSDL::ll_try_gtk_init(void)
-{
- static bool done_gtk_diag = false;
- static bool gtk_is_good = false;
- static bool done_setlocale = false;
- static bool tried_gtk_init = false;
-
- if (!done_setlocale)
- {
- LL_INFOS() << "Starting GTK Initialization." << LL_ENDL;
- maybe_lock_display();
- gtk_disable_setlocale();
- maybe_unlock_display();
- done_setlocale = true;
- }
-
- if (!tried_gtk_init)
- {
- tried_gtk_init = true;
- if (!g_thread_supported ()) g_thread_init (NULL);
- maybe_lock_display();
- gtk_is_good = gtk_init_check(NULL, NULL);
- maybe_unlock_display();
- if (!gtk_is_good)
- LL_WARNS() << "GTK Initialization failed." << LL_ENDL;
- }
-
- if (gtk_is_good && !done_gtk_diag)
- {
- LL_INFOS() << "GTK Initialized." << LL_ENDL;
- LL_INFOS() << "- Compiled against GTK version "
- << GTK_MAJOR_VERSION << "."
- << GTK_MINOR_VERSION << "."
- << GTK_MICRO_VERSION << LL_ENDL;
- LL_INFOS() << "- Running against GTK version "
- << gtk_major_version << "."
- << gtk_minor_version << "."
- << gtk_micro_version << LL_ENDL;
- maybe_lock_display();
- const gchar* gtk_warning = gtk_check_version(
- GTK_MAJOR_VERSION,
- GTK_MINOR_VERSION,
- GTK_MICRO_VERSION);
- maybe_unlock_display();
- if (gtk_warning)
- {
- LL_WARNS() << "- GTK COMPATIBILITY WARNING: " <<
- gtk_warning << LL_ENDL;
- gtk_is_good = false;
- } else {
- LL_INFOS() << "- GTK version is good." << LL_ENDL;
- }
-
- done_gtk_diag = true;
- }
-
- return gtk_is_good;
-}
-#endif // LL_GTK
-
-
-#if LL_X11
-// static
-Window LLWindowSDL::get_SDL_XWindowID(void)
-{
- if (gWindowImplementation) {
- return gWindowImplementation->mSDL_XWindowID;
- }
- return None;
-}
-
-//static
-Display* LLWindowSDL::get_SDL_Display(void)
-{
- if (gWindowImplementation) {
- return gWindowImplementation->mSDL_Display;
- }
- return NULL;
-}
-#endif // LL_X11
-
-
-LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks,
- const std::string& 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, U32 fsaa_samples)
- : LLWindow(callbacks, fullscreen, flags),
- Lock_Display(NULL),
- Unlock_Display(NULL), mGamma(1.0f)
-{
- // Initialize the keyboard
- gKeyboard = new LLKeyboardSDL();
- gKeyboard->setCallbacks(callbacks);
- // 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;
- mNeedsResize = false;
- mOverrideAspectRatio = 0.f;
- mGrabbyKeyFlags = 0;
- mReallyCapturedCount = 0;
- mHaveInputFocus = -1;
- mIsMinimized = -1;
- mFSAASamples = fsaa_samples;
-
-#if LL_X11
- mSDL_XWindowID = None;
- mSDL_Display = NULL;
-#endif // LL_X11
-
-#if LL_GTK
- // We MUST be the first to initialize GTK so that GTK doesn't get badly
- // initialized with a non-C locale and cause lots of serious random
- // weirdness.
- ll_try_gtk_init();
-#endif // LL_GTK
-
- // Assume 4:3 aspect ratio until we know better
- mOriginalAspectRatio = 1024.0 / 768.0;
-
- if (title.empty())
- mWindowTitle = "SDL Window"; // *FIX: (?)
- else
- 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
-
- mKeyScanCode = 0;
- mKeyVirtualKey = 0;
- mKeyModifiers = KMOD_NONE;
-}
-
-static SDL_Surface *Load_BMP_Resource(const char *basename)
-{
- const int PATH_BUFFER_SIZE=1000;
- char path_buffer[PATH_BUFFER_SIZE]; /* Flawfinder: ignore */
-
- // 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);
-}
-
-#if LL_X11
-// This is an XFree86/XOrg-specific hack for detecting the amount of Video RAM
-// on this machine. It works by searching /var/log/var/log/Xorg.?.log or
-// /var/log/XFree86.?.log for a ': (VideoRAM ?|Memory): (%d+) kB' regex, where
-// '?' is the X11 display number derived from $DISPLAY
-static int x11_detect_VRAM_kb_fp(FILE *fp, const char *prefix_str)
-{
- const int line_buf_size = 1000;
- char line_buf[line_buf_size];
- while (fgets(line_buf, line_buf_size, fp))
- {
- //LL_DEBUGS() << "XLOG: " << line_buf << LL_ENDL;
-
- // Why the ad-hoc parser instead of using a regex? Our
- // favourite regex implementation - libboost_regex - is
- // quite a heavy and troublesome dependency for the client, so
- // it seems a shame to introduce it for such a simple task.
- // *FIXME: libboost_regex is a dependency now anyway, so we may
- // as well use it instead of this hand-rolled nonsense.
- const char *part1_template = prefix_str;
- const char part2_template[] = " kB";
- char *part1 = strstr(line_buf, part1_template);
- if (part1) // found start of matching line
- {
- part1 = &part1[strlen(part1_template)]; // -> after
- char *part2 = strstr(part1, part2_template);
- if (part2) // found end of matching line
- {
- // now everything between part1 and part2 is
- // supposed to be numeric, describing the
- // number of kB of Video RAM supported
- int rtn = 0;
- for (; part1 < part2; ++part1)
- {
- if (*part1 < '0' || *part1 > '9')
- {
- // unexpected char, abort parse
- rtn = 0;
- break;
- }
- rtn *= 10;
- rtn += (*part1) - '0';
- }
- if (rtn > 0)
- {
- // got the kB number. return it now.
- return rtn;
- }
- }
- }
- }
- return 0; // 'could not detect'
-}
-
-static int x11_detect_VRAM_kb()
-{
- std::string x_log_location("/var/log/");
- std::string fname;
- int rtn = 0; // 'could not detect'
- int display_num = 0;
- FILE *fp;
- char *display_env = getenv("DISPLAY"); // e.g. :0 or :0.0 or :1.0 etc
- // parse DISPLAY number so we can go grab the right log file
- if (display_env[0] == ':' &&
- display_env[1] >= '0' && display_env[1] <= '9')
- {
- display_num = display_env[1] - '0';
- }
-
- // *TODO: we could be smarter and see which of Xorg/XFree86 has the
- // freshest time-stamp.
-
- // Try Xorg log first
- fname = x_log_location;
- fname += "Xorg.";
- fname += ('0' + display_num);
- fname += ".log";
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- LL_INFOS() << "Looking in " << fname
- << " for VRAM info..." << LL_ENDL;
- rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: ");
- fclose(fp);
- if (0 == rtn)
- {
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- rtn = x11_detect_VRAM_kb_fp(fp, ": Video RAM: ");
- fclose(fp);
- if (0 == rtn)
- {
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: ");
- fclose(fp);
- }
- }
- }
- }
- }
- else
- {
- LL_INFOS() << "Could not open " << fname
- << " - skipped." << LL_ENDL;
- // Try old XFree86 log otherwise
- fname = x_log_location;
- fname += "XFree86.";
- fname += ('0' + display_num);
- fname += ".log";
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- LL_INFOS() << "Looking in " << fname
- << " for VRAM info..." << LL_ENDL;
- rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: ");
- fclose(fp);
- if (0 == rtn)
- {
- fp = fopen(fname.c_str(), "r");
- if (fp)
- {
- rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: ");
- fclose(fp);
- }
- }
- }
- else
- {
- LL_INFOS() << "Could not open " << fname
- << " - skipped." << LL_ENDL;
- }
- }
- return rtn;
-}
-#endif // LL_X11
-
-bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool disable_vsync)
-{
- //bool glneedsinit = false;
-
- LL_INFOS() << "createContext, fullscreen=" << fullscreen <<
- " size=" << width << "x" << height << LL_ENDL;
-
- // captures don't survive contexts
- mGrabbyKeyFlags = 0;
- mReallyCapturedCount = 0;
-
- if (SDL_Init(SDL_INIT_VIDEO) < 0)
- {
- LL_INFOS() << "sdl_init() failed! " << SDL_GetError() << LL_ENDL;
- setupFailure("sdl_init() failure, window creation error", "error", OSMB_OK);
- return false;
- }
-
- SDL_version c_sdl_version;
- SDL_VERSION(&c_sdl_version);
- LL_INFOS() << "Compiled against SDL "
- << int(c_sdl_version.major) << "."
- << int(c_sdl_version.minor) << "."
- << int(c_sdl_version.patch) << LL_ENDL;
- const SDL_version *r_sdl_version;
- r_sdl_version = SDL_Linked_Version();
- LL_INFOS() << " Running against SDL "
- << int(r_sdl_version->major) << "."
- << int(r_sdl_version->minor) << "."
- << int(r_sdl_version->patch) << LL_ENDL;
-
- const SDL_VideoInfo *video_info = SDL_GetVideoInfo( );
- if (!video_info)
- {
- LL_INFOS() << "SDL_GetVideoInfo() failed! " << SDL_GetError() << LL_ENDL;
- setupFailure("SDL_GetVideoInfo() failed, Window creation error", "Error", OSMB_OK);
- return false;
- }
-
- if (video_info->current_h > 0)
- {
- mOriginalAspectRatio = (float)video_info->current_w / (float)video_info->current_h;
- LL_INFOS() << "Original aspect ratio was " << video_info->current_w << ":" << video_info->current_h << "=" << mOriginalAspectRatio << LL_ENDL;
- }
-
- SDL_EnableUNICODE(1);
- SDL_WM_SetCaption(mWindowTitle.c_str(), mWindowTitle.c_str());
-
- // 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);
- // We need stencil support for a few (minor) things.
- if (!getenv("LL_GL_NO_STENCIL"))
- SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8);
-
- // *FIX: try to toggle vsync here?
-
- mFullscreen = fullscreen;
-
- int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT;
-
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-
- if (mFSAASamples > 0)
- {
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
- SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, mFSAASamples);
- }
-
- mSDLFlags = sdlflags;
-
- if (mFullscreen)
- {
- LL_INFOS() << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL;
-
- // 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;
-
- LL_INFOS() << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL;
-
- for(i=0; i < resolutionCount; i++)
- {
- F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
-
- LL_INFOS() << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL;
-
- if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
- (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
- {
- LL_INFOS() << " (new closest mode) " << LL_ENDL;
-
- // 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 && bits > 16)
- {
- SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
- mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
- }
-
- if (mWindow)
- {
- mFullscreen = true;
- mFullscreenWidth = mWindow->w;
- mFullscreenHeight = mWindow->h;
- mFullscreenBits = mWindow->format->BitsPerPixel;
- mFullscreenRefresh = -1;
-
- LL_INFOS() << "Running at " << mFullscreenWidth
- << "x" << mFullscreenHeight
- << "x" << mFullscreenBits
- << " @ " << mFullscreenRefresh
- << LL_ENDL;
- }
- else
- {
- LL_WARNS() << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << LL_ENDL;
- // No fullscreen support
- mFullscreen = false;
- mFullscreenWidth = -1;
- mFullscreenHeight = -1;
- mFullscreenBits = -1;
- mFullscreenRefresh = -1;
-
- std::string error = llformat("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;
-
- LL_INFOS() << "createContext: creating window " << width << "x" << height << "x" << bits << LL_ENDL;
- mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
- if (!mWindow && bits > 16)
- {
- SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
- mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
- }
-
- if (!mWindow)
- {
- LL_WARNS() << "createContext: window creation failure. SDL: " << SDL_GetError() << LL_ENDL;
- setupFailure("Window creation error", "Error", OSMB_OK);
- return false;
- }
- } else if (!mFullscreen && (mWindow != NULL))
- {
- LL_INFOS() << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << LL_ENDL;
- }
-
- // Detect video memory size.
-# if LL_X11
- gGLManager.mVRAM = x11_detect_VRAM_kb() / 1024;
- if (gGLManager.mVRAM != 0)
- {
- LL_INFOS() << "X11 log-parser detected " << gGLManager.mVRAM << "MB VRAM." << LL_ENDL;
- } else
-# endif // LL_X11
- {
- // fallback to letting SDL detect VRAM.
- // note: I've not seen SDL's detection ever actually find
- // VRAM != 0, but if SDL *does* detect it then that's a bonus.
- gGLManager.mVRAM = video_info->video_mem / 1024;
- if (gGLManager.mVRAM != 0)
- {
- LL_INFOS() << "SDL detected " << gGLManager.mVRAM << "MB VRAM." << LL_ENDL;
- }
- }
- // If VRAM is not detected, that is handled later
-
- // *TODO: Now would be an appropriate time to check for some
- // explicitly unsupported cards.
- //const char* RENDERER = (const char*) glGetString(GL_RENDERER);
-
- 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);
-
- LL_INFOS() << "GL buffer:" << LL_ENDL;
- LL_INFOS() << " Red Bits " << S32(redBits) << LL_ENDL;
- LL_INFOS() << " Green Bits " << S32(greenBits) << LL_ENDL;
- LL_INFOS() << " Blue Bits " << S32(blueBits) << LL_ENDL;
- LL_INFOS() << " Alpha Bits " << S32(alphaBits) << LL_ENDL;
- LL_INFOS() << " Depth Bits " << S32(depthBits) << LL_ENDL;
- LL_INFOS() << " Stencil Bits " << S32(stencilBits) << LL_ENDL;
-
- 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
- /* Grab the window manager specific information */
- SDL_SysWMinfo info;
- SDL_VERSION(&info.version);
- if ( SDL_GetWMInfo(&info) )
- {
- /* Save the information for later use */
- if ( info.subsystem == SDL_SYSWM_X11 )
- {
- mSDL_Display = info.info.x11.display;
- mSDL_XWindowID = info.info.x11.wmwindow;
- Lock_Display = info.info.x11.lock_func;
- Unlock_Display = info.info.x11.unlock_func;
- }
- else
- {
- LL_WARNS() << "We're not running under X11? Wild."
- << LL_ENDL;
- }
- }
- else
- {
- LL_WARNS() << "We're not running under any known WM. Wild."
- << LL_ENDL;
- }
-#endif // LL_X11
-
-
- //make sure multisampling is disabled by default
- glDisable(GL_MULTISAMPLE_ARB);
-
- // We need to do this here, once video is init'd
- if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
- SDL_DEFAULT_REPEAT_INTERVAL))
- LL_WARNS() << "Couldn't enable key-repeat: " << SDL_GetError() <<LL_ENDL;
-
- // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
- return true;
-}
-
-
-// changing fullscreen resolution, or switching between windowed and fullscreen mode.
-bool LLWindowSDL::switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp)
-{
- const bool needsRebuild = true; // Just nuke the context and start over.
- bool result = true;
-
- LL_INFOS() << "switchContext, fullscreen=" << fullscreen << LL_ENDL;
- stop_glerror();
- if(needsRebuild)
- {
- destroyContext();
- result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
- if (result)
- {
- gGLManager.initGL();
-
- //start with arrow cursor
- initCursors();
- setCursor( UI_CURSOR_ARROW );
- }
- }
-
- stop_glerror();
-
- return result;
-}
-
-void LLWindowSDL::destroyContext()
-{
- LL_INFOS() << "destroyContext begins" << LL_ENDL;
-
-#if LL_X11
- mSDL_Display = NULL;
- mSDL_XWindowID = None;
- Lock_Display = NULL;
- Unlock_Display = NULL;
-#endif // LL_X11
-
- // Clean up remaining GL state before blowing away window
- LL_INFOS() << "shutdownGL begins" << LL_ENDL;
- gGLManager.shutdownGL();
- LL_INFOS() << "SDL_QuitSS/VID begins" << LL_ENDL;
- SDL_QuitSubSystem(SDL_INIT_VIDEO); // *FIX: this might be risky...
-
- mWindow = NULL;
-}
-
-LLWindowSDL::~LLWindowSDL()
-{
- quitCursors();
- destroyContext();
-
- if(mSupportedResolutions != NULL)
- {
- delete []mSupportedResolutions;
- }
-
- gWindowImplementation = NULL;
-}
-
-
-void LLWindowSDL::show()
-{
- // *FIX: What to do with SDL?
-}
-
-void LLWindowSDL::hide()
-{
- // *FIX: What to do with SDL?
-}
-
-//virtual
-void LLWindowSDL::minimize()
-{
- // *FIX: What to do with SDL?
-}
-
-//virtual
-void LLWindowSDL::restore()
-{
- // *FIX: What to do with SDL?
-}
-
-
-// close() destroys all OS-specific code associated with a window.
-// Usually called from LLWindowManager::destroyWindow()
-void LLWindowSDL::close()
-{
- // Is window is already closed?
- // if (!mWindow)
- // {
- // return;
- // }
-
- // Make sure cursor is visible and we haven't mangled the clipping state.
- setMouseClipping(false);
- showCursor();
-
- destroyContext();
-}
-
-bool LLWindowSDL::isValid()
-{
- return (mWindow != NULL);
-}
-
-bool LLWindowSDL::getVisible()
-{
- bool result = false;
-
- // *FIX: This isn't really right...
- // Then what is?
- if (mWindow)
- {
- result = true;
- }
-
- return(result);
-}
-
-bool LLWindowSDL::getMinimized()
-{
- bool result = false;
-
- if (mWindow && (1 == mIsMinimized))
- {
- result = true;
- }
- return(result);
-}
-
-bool LLWindowSDL::getMaximized()
-{
- bool result = false;
-
- if (mWindow)
- {
- // TODO
- }
-
- return(result);
-}
-
-bool LLWindowSDL::maximize()
-{
- // TODO
- return false;
-}
-
-bool LLWindowSDL::getFullscreen()
-{
- return mFullscreen;
-}
-
-bool LLWindowSDL::getPosition(LLCoordScreen *position)
-{
- // *FIX: can anything be done with this?
- position->mX = 0;
- position->mY = 0;
- return true;
-}
-
-bool LLWindowSDL::getSize(LLCoordScreen *size)
-{
- if (mWindow)
- {
- size->mX = mWindow->w;
- size->mY = mWindow->h;
- return (true);
- }
-
- return (false);
-}
-
-bool LLWindowSDL::getSize(LLCoordWindow *size)
-{
- if (mWindow)
- {
- size->mX = mWindow->w;
- size->mY = mWindow->h;
- return (true);
- }
-
- return (false);
-}
-
-bool LLWindowSDL::setPosition(const LLCoordScreen position)
-{
- if(mWindow)
- {
- // *FIX: (?)
- //MacMoveWindow(mWindow, position.mX, position.mY, false);
- }
-
- return true;
-}
-
-bool LLWindowSDL::setSizeImpl(const LLCoordScreen size)
-{
- if(mWindow)
- {
- // Push a resize event onto SDL's queue - we'll handle it
- // when it comes out again.
- SDL_Event event;
- event.type = SDL_VIDEORESIZE;
- event.resize.w = size.mX;
- event.resize.h = size.mY;
- SDL_PushEvent(&event); // copied into queue
-
- return true;
- }
-
- return false;
-}
-
-bool LLWindowSDL::setSizeImpl(const LLCoordWindow size)
-{
- if(mWindow)
- {
- // Push a resize event onto SDL's queue - we'll handle it
- // when it comes out again.
- SDL_Event event;
- event.type = SDL_VIDEORESIZE;
- event.resize.w = size.mX;
- event.resize.h = size.mY;
- SDL_PushEvent(&event); // copied into queue
-
- return true;
- }
-
- return false;
-}
-
-
-void LLWindowSDL::swapBuffers()
-{
- if (mWindow)
- {
- SDL_GL_SwapBuffers();
- }
-}
-
-U32 LLWindowSDL::getFSAASamples()
-{
- return mFSAASamples;
-}
-
-void LLWindowSDL::setFSAASamples(const U32 samples)
-{
- mFSAASamples = samples;
-}
-
-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 )
-{
- //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
-}
-
-// virtual
-void LLWindowSDL::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately)
-{
- LLWindow::setMinSize(min_width, min_height, enforce_immediately);
-
-#if LL_X11
- // Set the minimum size limits for X11 window
- // so the window manager doesn't allow resizing below those limits.
- XSizeHints* hints = XAllocSizeHints();
- hints->flags |= PMinSize;
- hints->min_width = mMinWindowWidth;
- hints->min_height = mMinWindowHeight;
-
- XSetWMNormalHints(mSDL_Display, mSDL_XWindowID, hints);
-
- XFree(hints);
-#endif
-}
-
-bool LLWindowSDL::setCursorPosition(const LLCoordWindow position)
-{
- bool result = true;
- LLCoordScreen screen_pos;
-
- if (!convertCoords(position, &screen_pos))
- {
- return false;
- }
-
- //LL_INFOS() << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << LL_ENDL;
-
- // do the actual forced cursor move.
- SDL_WarpMouse(screen_pos.mX, screen_pos.mY);
-
- //LL_INFOS() << llformat("llcw %d,%d -> scr %d,%d", position.mX, position.mY, screen_pos.mX, screen_pos.mY) << LL_ENDL;
-
- 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);
-}
-
-
-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;
- if (getSize(&screen_size))
- {
- pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
- }
- }
-
- return pixel_aspect;
-}
-
-
-// This is to support 'temporarily windowed' mode so that
-// dialogs are still usable in fullscreen.
-void LLWindowSDL::beforeDialog()
-{
- bool running_x11 = false;
-#if LL_X11
- running_x11 = (mSDL_XWindowID != None);
-#endif //LL_X11
-
- LL_INFOS() << "LLWindowSDL::beforeDialog()" << LL_ENDL;
-
- if (SDLReallyCaptureInput(false)) // must ungrab input so popup works!
- {
- if (mFullscreen)
- {
- // need to temporarily go non-fullscreen; bless SDL
- // for providing a SDL_WM_ToggleFullScreen() - though
- // it only works in X11
- if (running_x11 && mWindow)
- {
- SDL_WM_ToggleFullScreen(mWindow);
- }
- }
- }
-
-#if LL_X11
- if (mSDL_Display)
- {
- // Everything that we/SDL asked for should happen before we
- // potentially hand control over to GTK.
- maybe_lock_display();
- XSync(mSDL_Display, False);
- maybe_unlock_display();
- }
-#endif // LL_X11
-
-#if LL_GTK
- // this is a good time to grab some GTK version information for
- // diagnostics, if not already done.
- ll_try_gtk_init();
-#endif // LL_GTK
-
- maybe_lock_display();
-}
-
-void LLWindowSDL::afterDialog()
-{
- bool running_x11 = false;
-#if LL_X11
- running_x11 = (mSDL_XWindowID != None);
-#endif //LL_X11
-
- LL_INFOS() << "LLWindowSDL::afterDialog()" << LL_ENDL;
-
- maybe_unlock_display();
-
- if (mFullscreen)
- {
- // need to restore fullscreen mode after dialog - only works
- // in X11
- if (running_x11 && mWindow)
- {
- SDL_WM_ToggleFullScreen(mWindow);
- }
- }
-}
-
-
-#if LL_X11
-// set/reset the XWMHints flag for 'urgency' that usually makes the icon flash
-void LLWindowSDL::x11_set_urgent(bool urgent)
-{
- if (mSDL_Display && !mFullscreen)
- {
- XWMHints *wm_hints;
-
- LL_INFOS() << "X11 hint for urgency, " << urgent << LL_ENDL;
-
- maybe_lock_display();
- wm_hints = XGetWMHints(mSDL_Display, mSDL_XWindowID);
- if (!wm_hints)
- wm_hints = XAllocWMHints();
-
- if (urgent)
- wm_hints->flags |= XUrgencyHint;
- else
- wm_hints->flags &= ~XUrgencyHint;
-
- XSetWMHints(mSDL_Display, mSDL_XWindowID, wm_hints);
- XFree(wm_hints);
- XSync(mSDL_Display, False);
- maybe_unlock_display();
- }
-}
-#endif // LL_X11
-
-void LLWindowSDL::flashIcon(F32 seconds)
-{
-#if !LL_X11
- LL_INFOS() << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL;
-#else
- LL_INFOS() << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL;
-
- 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_GTK
-bool LLWindowSDL::isClipboardTextAvailable()
-{
- if (ll_try_gtk_init())
- {
- GtkClipboard * const clipboard =
- gtk_clipboard_get(GDK_NONE);
- return gtk_clipboard_wait_is_text_available(clipboard) ?
- true : false;
- }
- return false; // failure
-}
-
-bool LLWindowSDL::pasteTextFromClipboard(LLWString &text)
-{
- if (ll_try_gtk_init())
- {
- GtkClipboard * const clipboard =
- gtk_clipboard_get(GDK_NONE);
- gchar * const data = gtk_clipboard_wait_for_text(clipboard);
- if (data)
- {
- text = LLWString(utf8str_to_wstring(data));
- g_free(data);
- return true;
- }
- }
- return false; // failure
-}
-
-bool LLWindowSDL::copyTextToClipboard(const LLWString &text)
-{
- if (ll_try_gtk_init())
- {
- const std::string utf8 = wstring_to_utf8str(text);
- GtkClipboard * const clipboard =
- gtk_clipboard_get(GDK_NONE);
- gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length());
- return true;
- }
- return false; // failure
-}
-
-
-bool LLWindowSDL::isPrimaryTextAvailable()
-{
- if (ll_try_gtk_init())
- {
- GtkClipboard * const clipboard =
- gtk_clipboard_get(GDK_SELECTION_PRIMARY);
- return gtk_clipboard_wait_is_text_available(clipboard) ?
- true : false;
- }
- return false; // failure
-}
-
-bool LLWindowSDL::pasteTextFromPrimary(LLWString &text)
-{
- if (ll_try_gtk_init())
- {
- GtkClipboard * const clipboard =
- gtk_clipboard_get(GDK_SELECTION_PRIMARY);
- gchar * const data = gtk_clipboard_wait_for_text(clipboard);
- if (data)
- {
- text = LLWString(utf8str_to_wstring(data));
- g_free(data);
- return true;
- }
- }
- return false; // failure
-}
-
-bool LLWindowSDL::copyTextToPrimary(const LLWString &text)
-{
- if (ll_try_gtk_init())
- {
- const std::string utf8 = wstring_to_utf8str(text);
- GtkClipboard * const clipboard =
- gtk_clipboard_get(GDK_SELECTION_PRIMARY);
- gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length());
- return true;
- }
- return false; // failure
-}
-
-#else
-
-bool LLWindowSDL::isClipboardTextAvailable()
-{
- return false; // unsupported
-}
-
-bool LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
-{
- return false; // unsupported
-}
-
-bool LLWindowSDL::copyTextToClipboard(const LLWString &s)
-{
- return false; // unsupported
-}
-
-bool LLWindowSDL::isPrimaryTextAvailable()
-{
- return false; // unsupported
-}
-
-bool LLWindowSDL::pasteTextFromPrimary(LLWString &dst)
-{
- return false; // unsupported
-}
-
-bool LLWindowSDL::copyTextToPrimary(const LLWString &s)
-{
- return false; // unsupported
-}
-
-#endif // LL_GTK
-
-LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
-{
- if (!mSupportedResolutions)
- {
- mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
- mNumSupportedResolutions = 0;
-
- SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
- if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) )
- {
- int count = 0;
- while (*modes && count<MAX_NUM_RESOLUTIONS) // they're sorted biggest to smallest, so find end...
- {
- modes++;
- count++;
- }
-
- while (count--)
- {
- modes--;
- SDL_Rect *r = *modes;
- int w = r->w;
- int h = r->h;
- if ((w >= 800) && (h >= 600))
- {
- // make sure we don't add the same resolution multiple times!
- if ( (mNumSupportedResolutions == 0) ||
- ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) &&
- (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) )
- {
- mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
- mSupportedResolutions[mNumSupportedResolutions].mHeight = h;
- mNumSupportedResolutions++;
- }
- }
- }
- }
- }
-
- num_resolutions = mNumSupportedResolutions;
- return mSupportedResolutions;
-}
-
-bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
-{
- if (!to)
- return false;
-
- to->mX = from.mX;
- to->mY = mWindow->h - from.mY - 1;
-
- return true;
-}
-
-bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
-{
- if (!to)
- return false;
-
- to->mX = from.mX;
- to->mY = mWindow->h - from.mY - 1;
-
- return true;
-}
-
-bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
-{
- if (!to)
- return false;
-
- // In the fullscreen case, window and screen coordinates are the same.
- to->mX = from.mX;
- to->mY = from.mY;
- return (true);
-}
-
-bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
-{
- if (!to)
- return false;
-
- // In the fullscreen case, window and screen coordinates are the same.
- to->mX = from.mX;
- to->mY = from.mY;
- return (true);
-}
-
-bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to)
-{
- LLCoordWindow window_coord;
-
- return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
-}
-
-bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to)
-{
- LLCoordWindow window_coord;
-
- return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
-}
-
-
-
-
-void LLWindowSDL::setupFailure(const std::string& text, const std::string& 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;
- LL_WARNS() << "ReallyCapture count was < 0" << LL_ENDL;
- }
-
- if (!mFullscreen) /* only bother if we're windowed anyway */
- {
-#if LL_X11
- if (mSDL_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)
- {
- //LL_INFOS() << "X11 POINTER GRABBY" << LL_ENDL;
- //newmode = SDL_WM_GrabInput(wantmode);
- maybe_lock_display();
- result = XGrabPointer(mSDL_Display, mSDL_XWindowID,
- True, 0, GrabModeAsync,
- GrabModeAsync,
- None, None, CurrentTime);
- maybe_unlock_display();
- if (GrabSuccess == result)
- newmode = SDL_GRAB_ON;
- else
- newmode = SDL_GRAB_OFF;
- } else if (wantmode == SDL_GRAB_OFF)
- {
- //LL_INFOS() << "X11 POINTER UNGRABBY" << LL_ENDL;
- newmode = SDL_GRAB_OFF;
- //newmode = SDL_WM_GrabInput(SDL_GRAB_OFF);
-
- maybe_lock_display();
- XUngrabPointer(mSDL_Display, CurrentTime);
- // Make sure the ungrab happens RIGHT NOW.
- XSync(mSDL_Display, False);
- maybe_unlock_display();
- } 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 ALT 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_RALT:
- mask = 1U << 1; break;
- case SDLK_LCTRL:
- mask = 1U << 2; break;
- case SDLK_RCTRL:
- mask = 1U << 3; break;
- default:
- break;
- }
-
- if (gain)
- mGrabbyKeyFlags |= mask;
- else
- mGrabbyKeyFlags &= ~mask;
-
- //LL_INFOS() << "mGrabbyKeyFlags=" << mGrabbyKeyFlags << LL_ENDL;
-
- /* 0 means we don't need to mousegrab, otherwise grab. */
- return mGrabbyKeyFlags;
-}
-
-
-void check_vm_bloat()
-{
-#if LL_LINUX
- // watch our own VM and RSS sizes, warn if we bloated rapidly
- static const std::string STATS_FILE = "/proc/self/stat";
- FILE *fp = fopen(STATS_FILE.c_str(), "r");
- if (fp)
- {
- static long long last_vm_size = 0;
- static long long last_rss_size = 0;
- const long long significant_vm_difference = 250 * 1024*1024;
- const long long significant_rss_difference = 50 * 1024*1024;
- long long this_vm_size = 0;
- long long this_rss_size = 0;
-
- ssize_t res;
- size_t dummy;
- char *ptr = NULL;
- for (int i=0; i<22; ++i) // parse past the values we don't want
- {
- res = getdelim(&ptr, &dummy, ' ', fp);
- if (-1 == res)
- {
- LL_WARNS() << "Unable to parse " << STATS_FILE << LL_ENDL;
- goto finally;
- }
- free(ptr);
- ptr = NULL;
- }
- // 23rd space-delimited entry is vsize
- res = getdelim(&ptr, &dummy, ' ', fp);
- llassert(ptr);
- if (-1 == res)
- {
- LL_WARNS() << "Unable to parse " << STATS_FILE << LL_ENDL;
- goto finally;
- }
- this_vm_size = atoll(ptr);
- free(ptr);
- ptr = NULL;
- // 24th space-delimited entry is RSS
- res = getdelim(&ptr, &dummy, ' ', fp);
- llassert(ptr);
- if (-1 == res)
- {
- LL_WARNS() << "Unable to parse " << STATS_FILE << LL_ENDL;
- goto finally;
- }
- this_rss_size = getpagesize() * atoll(ptr);
- free(ptr);
- ptr = NULL;
-
- LL_INFOS() << "VM SIZE IS NOW " << (this_vm_size/(1024*1024)) << " MB, RSS SIZE IS NOW " << (this_rss_size/(1024*1024)) << " MB" << LL_ENDL;
-
- if (llabs(last_vm_size - this_vm_size) >
- significant_vm_difference)
- {
- if (this_vm_size > last_vm_size)
- {
- LL_WARNS() << "VM size grew by " << (this_vm_size - last_vm_size)/(1024*1024) << " MB in last frame" << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "VM size shrank by " << (last_vm_size - this_vm_size)/(1024*1024) << " MB in last frame" << LL_ENDL;
- }
- }
-
- if (llabs(last_rss_size - this_rss_size) >
- significant_rss_difference)
- {
- if (this_rss_size > last_rss_size)
- {
- LL_WARNS() << "RSS size grew by " << (this_rss_size - last_rss_size)/(1024*1024) << " MB in last frame" << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "RSS size shrank by " << (last_rss_size - this_rss_size)/(1024*1024) << " MB in last frame" << LL_ENDL;
- }
- }
-
- last_rss_size = this_rss_size;
- last_vm_size = this_vm_size;
-
-finally:
- if (NULL != ptr)
- {
- free(ptr);
- ptr = NULL;
- }
- fclose(fp);
- }
-#endif // LL_LINUX
-}
-
-
-// virtual
-void LLWindowSDL::processMiscNativeEvents()
-{
-#if LL_GTK
- // Pump GTK events to avoid starvation for:
- // * DBUS servicing
- // * Anything else which quietly hooks into the default glib/GTK loop
- if (ll_try_gtk_init())
- {
- // Yuck, Mozilla's GTK callbacks play with the locale - push/pop
- // the locale to protect it, as exotic/non-C locales
- // causes our code lots of general critical weirdness
- // and crashness. (SL-35450)
- static std::string saved_locale;
- saved_locale = ll_safe_string(setlocale(LC_ALL, NULL));
-
- // Pump until we've nothing left to do or passed 1/15th of a
- // second pumping for this frame.
- static LLTimer pump_timer;
- pump_timer.reset();
- pump_timer.setTimerExpirySec(1.0f / 15.0f);
- do {
- // Always do at least one non-blocking pump
- gtk_main_iteration_do(false);
- } while (gtk_events_pending() &&
- !pump_timer.hasExpired());
-
- setlocale(LC_ALL, saved_locale.c_str() );
- }
-#endif // LL_GTK
-
- // hack - doesn't belong here - but this is just for debugging
- if (getenv("LL_DEBUG_BLOAT"))
- {
- check_vm_bloat();
- }
-}
-
-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;
-
- // Handle all outstanding SDL events
- 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:
- mKeyScanCode = event.key.keysym.scancode;
- mKeyVirtualKey = event.key.keysym.unicode;
- mKeyModifiers = event.key.keysym.mod;
-
- 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)
- {
- handleUnicodeUTF16(event.key.keysym.unicode,
- gKeyboard->currentMask(false));
- }
- break;
-
- case SDL_KEYUP:
- mKeyScanCode = event.key.keysym.scancode;
- mKeyVirtualKey = event.key.keysym.unicode;
- mKeyModifiers = event.key.keysym.mod;
-
- if (SDLCheckGrabbyKeys(event.key.keysym.sym, false) == 0)
- SDLReallyCaptureInput(false); // part of the fix for SL-13243
-
- 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
- {
- mCallbacks->handleRightMouseDown(this, openGlCoord, mask);
- }
-
- else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
- {
- mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask);
- }
- else if (event.button.button == 4) // mousewheel up...thanks to X11 for making SDL consider these "buttons".
- mCallbacks->handleScrollWheel(this, -1);
- else if (event.button.button == 5) // mousewheel down...thanks to X11 for making SDL consider these "buttons".
- mCallbacks->handleScrollWheel(this, 1);
-
- 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
- mCallbacks->handleRightMouseUp(this, openGlCoord, mask);
- else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
- 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?
- {
- LL_INFOS() << "Handling a resize event: " << event.resize.w <<
- "x" << event.resize.h << LL_ENDL;
-
- S32 width = llmax(event.resize.w, (S32)mMinWindowWidth);
- S32 height = llmax(event.resize.h, (S32)mMinWindowHeight);
-
- // *FIX: I'm not sure this is necessary!
- mWindow = SDL_SetVideoMode(width, height, 32, mSDLFlags);
- if (!mWindow)
- {
- // *FIX: More informative dialog?
- LL_INFOS() << "Could not recreate context after resize! Quitting..." << LL_ENDL;
- 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, width, height);
- 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)
- {
- mHaveInputFocus = !!event.active.gain;
-
- if (mHaveInputFocus)
- mCallbacks->handleFocus(this);
- else
- mCallbacks->handleFocusLost(this);
- }
- }
- if (event.active.state & SDL_APPACTIVE)
- {
- // Change in iconification/minimization state.
- if ((!event.active.gain) != mIsMinimized)
- {
- mIsMinimized = (!event.active.gain);
-
- mCallbacks->handleActivate(this, !mIsMinimized);
- LL_INFOS() << "SDL deiconification state switched to " << bool(event.active.gain) << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "Ignored bogus redundant SDL deiconification state switch to " << bool(event.active.gain) << LL_ENDL;
- }
- }
- 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:
- //LL_INFOS() << "Unhandled SDL event type " << event.type << LL_ENDL;
- break;
- }
- }
-
- updateCursor();
-
-#if LL_X11
- // This is a good time to stop flashing the icon if our mFlashTimer has
- // expired.
- if (mFlashing && mFlashTimer.hasExpired())
- {
- x11_set_urgent(false);
- 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;
- LL_DEBUGS() << "Loaded cursor file " << filename << " "
- << bmpsurface->w << "x" << bmpsurface->h << LL_ENDL;
- cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE,
- bmpsurface->w,
- bmpsurface->h,
- 32,
- SDL_SwapLE32(0xFFU),
- SDL_SwapLE32(0xFF00U),
- SDL_SwapLE32(0xFF0000U),
- SDL_SwapLE32(0xFF000000U));
- SDL_FillRect(cursurface, NULL, SDL_SwapLE32(0x00000000U));
-
- // Blit the cursor pixel data onto a 32-bit RGBA surface so we
- // only have to cope with processing one type of pixel format.
- if (0 == SDL_BlitSurface(bmpsurface, NULL,
- cursurface, NULL))
- {
- // n.b. we already checked that width is a multiple of 8.
- const int bitmap_bytes = (cursurface->w * cursurface->h) / 8;
- unsigned char *cursor_data = new unsigned char[bitmap_bytes];
- unsigned char *cursor_mask = new unsigned char[bitmap_bytes];
- memset(cursor_data, 0, bitmap_bytes);
- memset(cursor_mask, 0, bitmap_bytes);
- int i,j;
- // Walk the RGBA cursor pixel data, extracting both data and
- // mask to build SDL-friendly cursor bitmaps from. The mask
- // is inferred by color-keying against 200,200,200
- for (i=0; i<cursurface->h; ++i) {
- for (j=0; j<cursurface->w; ++j) {
- U8 *pixelp =
- ((U8*)cursurface->pixels)
- + cursurface->pitch * i
- + j*cursurface->format->BytesPerPixel;
- U8 srcred = pixelp[0];
- U8 srcgreen = pixelp[1];
- U8 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 {
- LL_WARNS() << "CURSOR BLIT FAILURE, cursurface: " << cursurface << LL_ENDL;
- }
- SDL_FreeSurface(cursurface);
- SDL_FreeSurface(bmpsurface);
- } else {
- LL_WARNS() << "CURSOR LOAD FAILURE " << filename << LL_ENDL;
- }
-
- return sdlcursor;
-}
-
-void LLWindowSDL::updateCursor()
-{
- if (ATIbug) {
- // cursor-updating is very flaky when this bug is
- // present; do nothing.
- return;
- }
-
- if (mCurrentCursor != mNextCursor)
- {
- if (mNextCursor < UI_CURSOR_COUNT)
- {
- SDL_Cursor *sdlcursor = mSDLCursors[mNextCursor];
- // 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 {
- LL_WARNS() << "Tried to set invalid cursor number " << mNextCursor << LL_ENDL;
- }
- mCurrentCursor = mNextCursor;
- }
-}
-
-void LLWindowSDL::initCursors()
-{
- int i;
- // Blank the cursor pointer array for those we may miss.
- for (i=0; i<UI_CURSOR_COUNT; ++i)
- {
- mSDLCursors[i] = NULL;
- }
- // Pre-make an SDL cursor for each of the known cursor types.
- // We hardcode the hotspots - to avoid that we'd have to write
- // a .cur file loader.
- // NOTE: SDL doesn't load RLE-compressed BMP files.
- mSDLCursors[UI_CURSOR_ARROW] = makeSDLCursorFromBMP("llarrow.BMP",0,0);
- mSDLCursors[UI_CURSOR_WAIT] = makeSDLCursorFromBMP("wait.BMP",12,15);
- mSDLCursors[UI_CURSOR_HAND] = makeSDLCursorFromBMP("hand.BMP",7,10);
- mSDLCursors[UI_CURSOR_IBEAM] = makeSDLCursorFromBMP("ibeam.BMP",15,16);
- mSDLCursors[UI_CURSOR_CROSS] = makeSDLCursorFromBMP("cross.BMP",16,14);
- mSDLCursors[UI_CURSOR_SIZENWSE] = makeSDLCursorFromBMP("sizenwse.BMP",14,17);
- mSDLCursors[UI_CURSOR_SIZENESW] = makeSDLCursorFromBMP("sizenesw.BMP",17,17);
- mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14);
- mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16);
- mSDLCursors[UI_CURSOR_SIZEALL] = makeSDLCursorFromBMP("sizeall.BMP", 17, 17);
- mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8);
- mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15);
- mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13);
- mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6);
- mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5);
- mSDLCursors[UI_CURSOR_TOOLCREATE] = makeSDLCursorFromBMP("lltoolcreate.BMP",7,7);
- mSDLCursors[UI_CURSOR_ARROWDRAG] = makeSDLCursorFromBMP("arrowdrag.BMP",0,0);
- mSDLCursors[UI_CURSOR_ARROWCOPY] = makeSDLCursorFromBMP("arrowcop.BMP",0,0);
- mSDLCursors[UI_CURSOR_ARROWDRAGMULTI] = makeSDLCursorFromBMP("llarrowdragmulti.BMP",0,0);
- mSDLCursors[UI_CURSOR_ARROWCOPYMULTI] = makeSDLCursorFromBMP("arrowcopmulti.BMP",0,0);
- mSDLCursors[UI_CURSOR_NOLOCKED] = makeSDLCursorFromBMP("llnolocked.BMP",8,8);
- mSDLCursors[UI_CURSOR_ARROWLOCKED] = makeSDLCursorFromBMP("llarrowlocked.BMP",0,0);
- mSDLCursors[UI_CURSOR_GRABLOCKED] = makeSDLCursorFromBMP("llgrablocked.BMP",2,13);
- mSDLCursors[UI_CURSOR_TOOLTRANSLATE] = makeSDLCursorFromBMP("lltooltranslate.BMP",0,0);
- mSDLCursors[UI_CURSOR_TOOLROTATE] = makeSDLCursorFromBMP("lltoolrotate.BMP",0,0);
- mSDLCursors[UI_CURSOR_TOOLSCALE] = makeSDLCursorFromBMP("lltoolscale.BMP",0,0);
- mSDLCursors[UI_CURSOR_TOOLCAMERA] = makeSDLCursorFromBMP("lltoolcamera.BMP",7,5);
- mSDLCursors[UI_CURSOR_TOOLPAN] = makeSDLCursorFromBMP("lltoolpan.BMP",7,5);
- mSDLCursors[UI_CURSOR_TOOLZOOMIN] = makeSDLCursorFromBMP("lltoolzoomin.BMP",7,5);
- mSDLCursors[UI_CURSOR_TOOLZOOMOUT] = makeSDLCursorFromBMP("lltoolzoomout.BMP", 7, 5);
- mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] = makeSDLCursorFromBMP("toolpickobject3.BMP",0,0);
- mSDLCursors[UI_CURSOR_TOOLPLAY] = makeSDLCursorFromBMP("toolplay.BMP",0,0);
- mSDLCursors[UI_CURSOR_TOOLPAUSE] = makeSDLCursorFromBMP("toolpause.BMP",0,0);
- mSDLCursors[UI_CURSOR_TOOLMEDIAOPEN] = makeSDLCursorFromBMP("toolmediaopen.BMP",0,0);
- mSDLCursors[UI_CURSOR_PIPETTE] = makeSDLCursorFromBMP("lltoolpipette.BMP",2,28);
- mSDLCursors[UI_CURSOR_TOOLSIT] = makeSDLCursorFromBMP("toolsit.BMP",20,15);
- mSDLCursors[UI_CURSOR_TOOLBUY] = makeSDLCursorFromBMP("toolbuy.BMP",20,15);
- mSDLCursors[UI_CURSOR_TOOLOPEN] = makeSDLCursorFromBMP("toolopen.BMP",20,15);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING] = makeSDLCursorFromBMP("lltoolpathfinding.BMP", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_START] = makeSDLCursorFromBMP("lltoolpathfindingpathstart.BMP", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD] = makeSDLCursorFromBMP("lltoolpathfindingpathstartadd.BMP", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_END] = makeSDLCursorFromBMP("lltoolpathfindingpathend.BMP", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD] = makeSDLCursorFromBMP("lltoolpathfindingpathendadd.BMP", 16, 16);
- mSDLCursors[UI_CURSOR_TOOLNO] = makeSDLCursorFromBMP("llno.BMP",8,8);
-
- if (getenv("LL_ATI_MOUSE_CURSOR_BUG") != NULL) {
- LL_INFOS() << "Disabling cursor updating due to LL_ATI_MOUSE_CURSOR_BUG" << LL_ENDL;
- ATIbug = true;
- }
-}
-
-void LLWindowSDL::quitCursors()
-{
- int i;
- if (mWindow)
- {
- for (i=0; i<UI_CURSOR_COUNT; ++i)
- {
- if (mSDLCursors[i])
- {
- SDL_FreeCursor(mSDLCursors[i]);
- mSDLCursors[i] = NULL;
- }
- }
- } else {
- // SDL doesn't refcount cursors, so if the window has
- // already been destroyed then the cursors have gone with it.
- LL_INFOS() << "Skipping quitCursors: mWindow already gone." << LL_ENDL;
- for (i=0; i<UI_CURSOR_COUNT; ++i)
- mSDLCursors[i] = NULL;
- }
-}
-
-void LLWindowSDL::captureMouse()
-{
- // SDL already enforces the semantics that captureMouse is
- // used for, i.e. that we continue to get mouse events as long
- // as a button is down regardless of whether we left the
- // window, and in a less obnoxious way than SDL_WM_GrabInput
- // which would confine the cursor to the window too.
-
- LL_DEBUGS() << "LLWindowSDL::captureMouse" << LL_ENDL;
-}
-
-void LLWindowSDL::releaseMouse()
-{
- // see LWindowSDL::captureMouse()
-
- LL_DEBUGS() << "LLWindowSDL::releaseMouse" << LL_ENDL;
-}
-
-void LLWindowSDL::hideCursor()
-{
- if(!mCursorHidden)
- {
- // LL_INFOS() << "hideCursor: hiding" << LL_ENDL;
- mCursorHidden = true;
- mHideCursorPermanent = true;
- SDL_ShowCursor(0);
- }
- else
- {
- // LL_INFOS() << "hideCursor: already hidden" << LL_ENDL;
- }
-}
-
-void LLWindowSDL::showCursor()
-{
- if(mCursorHidden)
- {
- // LL_INFOS() << "showCursor: showing" << LL_ENDL;
- mCursorHidden = false;
- mHideCursorPermanent = false;
- SDL_ShowCursor(1);
- }
- else
- {
- // LL_INFOS() << "showCursor: already visible" << LL_ENDL;
- }
-}
-
-void LLWindowSDL::showCursorFromMouseMove()
-{
- if (!mHideCursorPermanent)
- {
- showCursor();
- }
-}
-
-void LLWindowSDL::hideCursorUntilMouseMove()
-{
- if (!mHideCursorPermanent)
- {
- hideCursor();
- mHideCursorPermanent = false;
- }
-}
-
-
-
-//
-// LLSplashScreenSDL - I don't think we'll bother to implement this; it's
-// fairly obsolete at this point.
-//
-LLSplashScreenSDL::LLSplashScreenSDL()
-{
-}
-
-LLSplashScreenSDL::~LLSplashScreenSDL()
-{
-}
-
-void LLSplashScreenSDL::showImpl()
-{
-}
-
-void LLSplashScreenSDL::updateImpl(const std::string& mesg)
-{
-}
-
-void LLSplashScreenSDL::hideImpl()
-{
-}
-
-
-
-#if LL_GTK
-static void response_callback (GtkDialog *dialog,
- gint arg1,
- gpointer user_data)
-{
- gint *response = (gint*)user_data;
- *response = arg1;
- gtk_widget_destroy(GTK_WIDGET(dialog));
- gtk_main_quit();
-}
-
-S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type)
-{
- S32 rtn = OSBTN_CANCEL;
-
- if(gWindowImplementation != NULL)
- gWindowImplementation->beforeDialog();
-
- if (LLWindowSDL::ll_try_gtk_init())
- {
- GtkWidget *win = NULL;
-
- LL_INFOS() << "Creating a dialog because we're in windowed mode and GTK is happy." << LL_ENDL;
-
- 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, "%s",
- text.c_str());
-
-# 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 (gWindowImplementation &&
- gWindowImplementation->mSDL_XWindowID != None)
- {
- gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
- GdkWindow *gdkwin = gdk_window_foreign_new(gWindowImplementation->mSDL_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.empty())
- gtk_window_set_title(GTK_WINDOW(win), caption.c_str());
-
- gint response = GTK_RESPONSE_NONE;
- g_signal_connect (win,
- "response",
- G_CALLBACK (response_callback),
- &response);
-
- // we should be able to use 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();
-
- //LL_INFOS() << "response: " << response << LL_ENDL;
- 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
- {
- LL_INFOS() << "MSGBOX: " << caption << ": " << text << LL_ENDL;
- LL_INFOS() << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << LL_ENDL;
- 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);
-}
-
-
-/*
- Make the raw keyboard data available - used to poke through to LLQtWebKit so
- that Qt/Webkit has access to the virtual keycodes etc. that it needs
-*/
-LLSD LLWindowSDL::getNativeKeyData()
-{
- LLSD result = LLSD::emptyMap();
-
- U32 modifiers = 0; // pretend-native modifiers... oh what a tangled web we weave!
-
- // we go through so many levels of device abstraction that I can't really guess
- // what a plugin under GDK under Qt under SL under SDL under X11 considers
- // a 'native' modifier mask. this has been sort of reverse-engineered... they *appear*
- // to match GDK consts, but that may be co-incidence.
- modifiers |= (mKeyModifiers & KMOD_LSHIFT) ? 0x0001 : 0;
- modifiers |= (mKeyModifiers & KMOD_RSHIFT) ? 0x0001 : 0;// munge these into the same shift
- modifiers |= (mKeyModifiers & KMOD_CAPS) ? 0x0002 : 0;
- modifiers |= (mKeyModifiers & KMOD_LCTRL) ? 0x0004 : 0;
- modifiers |= (mKeyModifiers & KMOD_RCTRL) ? 0x0004 : 0;// munge these into the same ctrl
- modifiers |= (mKeyModifiers & KMOD_LALT) ? 0x0008 : 0;// untested
- modifiers |= (mKeyModifiers & KMOD_RALT) ? 0x0008 : 0;// untested
- // *todo: test ALTs - I don't have a case for testing these. Do you?
- // *todo: NUM? - I don't care enough right now (and it's not a GDK modifier).
-
- result["scan_code"] = (S32)mKeyScanCode;
- result["virtual_key"] = (S32)mKeyVirtualKey;
- result["modifiers"] = (S32)modifiers;
-
- return result;
-}
-
-
-bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b)
-{
- bool rtn = false;
-
- beforeDialog();
-
- if (ll_try_gtk_init())
- {
- 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 (mSDL_XWindowID != None)
- {
- gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
- GdkWindow *gdkwin = gdk_window_foreign_new(mSDL_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.pixel = 0;
- 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 std::string& text, const std::string& caption, U32 type)
-{
- LL_INFOS() << "MSGBOX: " << caption << ": " << text << LL_ENDL;
- return 0;
-}
-
-bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b)
-{
- return (false);
-}
-#endif // LL_GTK
-
-#if LL_LINUX
-// extracted from spawnWebBrowser for clarity and to eliminate
-// compiler confusion regarding close(int fd) vs. LLWindow::close()
-void exec_cmd(const std::string& cmd, const std::string& arg)
-{
- char* const argv[] = {(char*)cmd.c_str(), (char*)arg.c_str(), NULL};
- fflush(NULL);
- pid_t pid = fork();
- if (pid == 0)
- { // child
- // disconnect from stdin/stdout/stderr, or child will
- // keep our output pipe undesirably alive if it outlives us.
- close(0);
- close(1);
- close(2);
- // end ourself by running the command
- execv(cmd.c_str(), argv); /* Flawfinder: ignore */
- // if execv returns at all, there was a problem.
- LL_WARNS() << "execv failure when trying to start " << cmd << LL_ENDL;
- _exit(1); // _exit because we don't want atexit() clean-up!
- } else {
- if (pid > 0)
- {
- // parent - wait for child to die
- int childExitStatus;
- waitpid(pid, &childExitStatus, 0);
- } else {
- LL_WARNS() << "fork failure." << LL_ENDL;
- }
- }
-}
-#endif
-
-// Open a URL with the user's default web browser.
-// Must begin with protocol identifier.
-void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async)
-{
- bool found = false;
- S32 i;
- for (i = 0; i < gURLProtocolWhitelistCount; i++)
- {
- if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos)
- {
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- LL_WARNS() << "spawn_web_browser called for url with protocol not on whitelist: " << escaped_url << LL_ENDL;
- return;
- }
-
- LL_INFOS() << "spawn_web_browser: " << escaped_url << LL_ENDL;
-
-#if LL_LINUX
-# if LL_X11
- if (mSDL_Display)
- {
- maybe_lock_display();
- // Just in case - before forking.
- XSync(mSDL_Display, False);
- maybe_unlock_display();
- }
-# endif // LL_X11
-
- std::string cmd, arg;
- cmd = gDirUtilp->getAppRODataDir();
- cmd += gDirUtilp->getDirDelimiter();
- cmd += "etc";
- cmd += gDirUtilp->getDirDelimiter();
- cmd += "launch_url.sh";
- arg = escaped_url;
- exec_cmd(cmd, arg);
-#endif // LL_LINUX
-
- LL_INFOS() << "spawn_web_browser returning." << LL_ENDL;
-}
-
-
-void *LLWindowSDL::getPlatformWindow()
-{
-#if LL_GTK && LL_LLMOZLIB_ENABLED
- if (LLWindowSDL::ll_try_gtk_init())
- {
- maybe_lock_display();
-
- GtkWidget *owin = gtk_window_new(GTK_WINDOW_POPUP);
- // Why a layout widget? A MozContainer would be ideal, but
- // it involves exposing Mozilla headers to mozlib-using apps.
- // A layout widget with a GtkWindow parent has the desired
- // properties of being plain GTK, having a window, and being
- // derived from a GtkContainer.
- GtkWidget *rtnw = gtk_layout_new(NULL, NULL);
- gtk_container_add(GTK_CONTAINER(owin), rtnw);
- gtk_widget_realize(rtnw);
- GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(rtnw), GTK_NO_WINDOW);
-
- maybe_unlock_display();
-
- return rtnw;
- }
-#endif // LL_GTK && LL_LLMOZLIB_ENABLED
- // Unixoid mozilla really needs GTK.
- return NULL;
-}
-
-void LLWindowSDL::bringToFront()
-{
- // This is currently used when we are 'launched' to a specific
- // map position externally.
- LL_INFOS() << "bringToFront" << LL_ENDL;
-#if LL_X11
- if (mSDL_Display && !mFullscreen)
- {
- maybe_lock_display();
- XRaiseWindow(mSDL_Display, mSDL_XWindowID);
- XSync(mSDL_Display, False);
- maybe_unlock_display();
- }
-#endif // LL_X11
-}
-
-//static
-std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList()
-{
- // Use libfontconfig to find us a nice ordered list of fallback fonts
- // specific to this system.
- std::string final_fallback("/usr/share/fonts/truetype/kochi/kochi-gothic.ttf");
- const int max_font_count_cutoff = 40; // fonts are expensive in the current system, don't enumerate an arbitrary number of them
- // Our 'ideal' font properties which define the sorting results.
- // slant=0 means Roman, index=0 means the first face in a font file
- // (the one we actually use), weight=80 means medium weight,
- // spacing=0 means proportional spacing.
- std::string sort_order("slant=0:index=0:weight=80:spacing=0");
- // elide_unicode_coverage removes fonts from the list whose unicode
- // range is covered by fonts earlier in the list. This usually
- // removes ~90% of the fonts as redundant (which is great because
- // the font list can be huge), but might unnecessarily reduce the
- // renderable range if for some reason our FreeType actually fails
- // to use some of the fonts we want it to.
- const bool elide_unicode_coverage = true;
- std::vector<std::string> rtns;
- FcFontSet *fs = NULL;
- FcPattern *sortpat = NULL;
-
- LL_INFOS() << "Getting system font list from FontConfig..." << LL_ENDL;
-
- // If the user has a system-wide language preference, then favor
- // fonts from that language group. This doesn't affect the types
- // of languages that can be displayed, but ensures that their
- // preferred language is rendered from a single consistent font where
- // possible.
- FL_Locale *locale = NULL;
- FL_Success success = FL_FindLocale(&locale, FL_MESSAGES);
- if (success != 0)
- {
- if (success >= 2 && locale->lang) // confident!
- {
- LL_INFOS("AppInit") << "Language " << locale->lang << LL_ENDL;
- LL_INFOS("AppInit") << "Location " << locale->country << LL_ENDL;
- LL_INFOS("AppInit") << "Variant " << locale->variant << LL_ENDL;
-
- LL_INFOS() << "Preferring fonts of language: "
- << locale->lang
- << LL_ENDL;
- sort_order = "lang=" + std::string(locale->lang) + ":"
- + sort_order;
- }
- }
- FL_FreeLocale(&locale);
-
- if (!FcInit())
- {
- LL_WARNS() << "FontConfig failed to initialize." << LL_ENDL;
- rtns.push_back(final_fallback);
- return rtns;
- }
-
- sortpat = FcNameParse((FcChar8*) sort_order.c_str());
- if (sortpat)
- {
- // Sort the list of system fonts from most-to-least-desirable.
- FcResult result;
- fs = FcFontSort(NULL, sortpat, elide_unicode_coverage,
- NULL, &result);
- FcPatternDestroy(sortpat);
- }
-
- int found_font_count = 0;
- if (fs)
- {
- // Get the full pathnames to the fonts, where available,
- // which is what we really want.
- found_font_count = fs->nfont;
- for (int i=0; i<fs->nfont; ++i)
- {
- FcChar8 *filename;
- if (FcResultMatch == FcPatternGetString(fs->fonts[i],
- FC_FILE, 0,
- &filename)
- && filename)
- {
- rtns.push_back(std::string((const char*)filename));
- if (rtns.size() >= max_font_count_cutoff)
- break; // hit limit
- }
- }
- FcFontSetDestroy (fs);
- }
-
- LL_DEBUGS() << "Using font list: " << LL_ENDL;
- for (std::vector<std::string>::iterator it = rtns.begin();
- it != rtns.end();
- ++it)
- {
- LL_DEBUGS() << " file: " << *it << LL_ENDL;
- }
- LL_INFOS() << "Using " << rtns.size() << "/" << found_font_count << " system fonts." << LL_ENDL;
-
- rtns.push_back(final_fallback);
- return rtns;
-}
-
-#endif // LL_SDL
+/** + * @file llwindowsdl.cpp + * @brief SDL implementation of LLWindow class + * @author This module has many fathers, and it shows. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if LL_SDL + +#include "linden_common.h" + +#include "llwindowsdl.h" + +#include "llwindowcallbacks.h" +#include "llkeyboardsdl.h" + +#include "llerror.h" +#include "llgl.h" +#include "llstring.h" +#include "lldir.h" +#include "llfindlocale.h" + +#if LL_GTK +extern "C" { +# include "gtk/gtk.h" +} +#include <locale.h> +#endif // LL_GTK + +extern "C" { +# include "fontconfig/fontconfig.h" +} + +#if LL_LINUX +// not necessarily available on random SDL platforms, so #if LL_LINUX +// for execv(), waitpid(), fork() +# include <unistd.h> +# include <sys/types.h> +# include <sys/wait.h> +#endif // LL_LINUX + +extern bool gDebugWindowProc; + +const S32 MAX_NUM_RESOLUTIONS = 200; + +// static variable for ATI mouse cursor crash work-around: +static bool ATIbug = false; + +// +// LLWindowSDL +// + +#if LL_X11 +# include <X11/Xutil.h> +#endif //LL_X11 + +// TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for a similar +// set of reasons): Stash a pointer to the LLWindowSDL object here and +// maintain in the constructor and destructor. This assumes that there will +// be only one object of this class at any time. Currently this is true. +static LLWindowSDL *gWindowImplementation = NULL; + + +void maybe_lock_display(void) +{ + if (gWindowImplementation && gWindowImplementation->Lock_Display) { + gWindowImplementation->Lock_Display(); + } +} + + +void maybe_unlock_display(void) +{ + if (gWindowImplementation && gWindowImplementation->Unlock_Display) { + gWindowImplementation->Unlock_Display(); + } +} + + +#if LL_GTK +// Lazily initialize and check the runtime GTK version for goodness. +// static +bool LLWindowSDL::ll_try_gtk_init(void) +{ + static bool done_gtk_diag = false; + static bool gtk_is_good = false; + static bool done_setlocale = false; + static bool tried_gtk_init = false; + + if (!done_setlocale) + { + LL_INFOS() << "Starting GTK Initialization." << LL_ENDL; + maybe_lock_display(); + gtk_disable_setlocale(); + maybe_unlock_display(); + done_setlocale = true; + } + + if (!tried_gtk_init) + { + tried_gtk_init = true; + if (!g_thread_supported ()) g_thread_init (NULL); + maybe_lock_display(); + gtk_is_good = gtk_init_check(NULL, NULL); + maybe_unlock_display(); + if (!gtk_is_good) + LL_WARNS() << "GTK Initialization failed." << LL_ENDL; + } + + if (gtk_is_good && !done_gtk_diag) + { + LL_INFOS() << "GTK Initialized." << LL_ENDL; + LL_INFOS() << "- Compiled against GTK version " + << GTK_MAJOR_VERSION << "." + << GTK_MINOR_VERSION << "." + << GTK_MICRO_VERSION << LL_ENDL; + LL_INFOS() << "- Running against GTK version " + << gtk_major_version << "." + << gtk_minor_version << "." + << gtk_micro_version << LL_ENDL; + maybe_lock_display(); + const gchar* gtk_warning = gtk_check_version( + GTK_MAJOR_VERSION, + GTK_MINOR_VERSION, + GTK_MICRO_VERSION); + maybe_unlock_display(); + if (gtk_warning) + { + LL_WARNS() << "- GTK COMPATIBILITY WARNING: " << + gtk_warning << LL_ENDL; + gtk_is_good = false; + } else { + LL_INFOS() << "- GTK version is good." << LL_ENDL; + } + + done_gtk_diag = true; + } + + return gtk_is_good; +} +#endif // LL_GTK + + +#if LL_X11 +// static +Window LLWindowSDL::get_SDL_XWindowID(void) +{ + if (gWindowImplementation) { + return gWindowImplementation->mSDL_XWindowID; + } + return None; +} + +//static +Display* LLWindowSDL::get_SDL_Display(void) +{ + if (gWindowImplementation) { + return gWindowImplementation->mSDL_Display; + } + return NULL; +} +#endif // LL_X11 + + +LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, + const std::string& 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, U32 fsaa_samples) + : LLWindow(callbacks, fullscreen, flags), + Lock_Display(NULL), + Unlock_Display(NULL), mGamma(1.0f) +{ + // Initialize the keyboard + gKeyboard = new LLKeyboardSDL(); + gKeyboard->setCallbacks(callbacks); + // 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; + mNeedsResize = false; + mOverrideAspectRatio = 0.f; + mGrabbyKeyFlags = 0; + mReallyCapturedCount = 0; + mHaveInputFocus = -1; + mIsMinimized = -1; + mFSAASamples = fsaa_samples; + +#if LL_X11 + mSDL_XWindowID = None; + mSDL_Display = NULL; +#endif // LL_X11 + +#if LL_GTK + // We MUST be the first to initialize GTK so that GTK doesn't get badly + // initialized with a non-C locale and cause lots of serious random + // weirdness. + ll_try_gtk_init(); +#endif // LL_GTK + + // Assume 4:3 aspect ratio until we know better + mOriginalAspectRatio = 1024.0 / 768.0; + + if (title.empty()) + mWindowTitle = "SDL Window"; // *FIX: (?) + else + 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 + + mKeyScanCode = 0; + mKeyVirtualKey = 0; + mKeyModifiers = KMOD_NONE; +} + +static SDL_Surface *Load_BMP_Resource(const char *basename) +{ + const int PATH_BUFFER_SIZE=1000; + char path_buffer[PATH_BUFFER_SIZE]; /* Flawfinder: ignore */ + + // 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); +} + +#if LL_X11 +// This is an XFree86/XOrg-specific hack for detecting the amount of Video RAM +// on this machine. It works by searching /var/log/var/log/Xorg.?.log or +// /var/log/XFree86.?.log for a ': (VideoRAM ?|Memory): (%d+) kB' regex, where +// '?' is the X11 display number derived from $DISPLAY +static int x11_detect_VRAM_kb_fp(FILE *fp, const char *prefix_str) +{ + const int line_buf_size = 1000; + char line_buf[line_buf_size]; + while (fgets(line_buf, line_buf_size, fp)) + { + //LL_DEBUGS() << "XLOG: " << line_buf << LL_ENDL; + + // Why the ad-hoc parser instead of using a regex? Our + // favourite regex implementation - libboost_regex - is + // quite a heavy and troublesome dependency for the client, so + // it seems a shame to introduce it for such a simple task. + // *FIXME: libboost_regex is a dependency now anyway, so we may + // as well use it instead of this hand-rolled nonsense. + const char *part1_template = prefix_str; + const char part2_template[] = " kB"; + char *part1 = strstr(line_buf, part1_template); + if (part1) // found start of matching line + { + part1 = &part1[strlen(part1_template)]; // -> after + char *part2 = strstr(part1, part2_template); + if (part2) // found end of matching line + { + // now everything between part1 and part2 is + // supposed to be numeric, describing the + // number of kB of Video RAM supported + int rtn = 0; + for (; part1 < part2; ++part1) + { + if (*part1 < '0' || *part1 > '9') + { + // unexpected char, abort parse + rtn = 0; + break; + } + rtn *= 10; + rtn += (*part1) - '0'; + } + if (rtn > 0) + { + // got the kB number. return it now. + return rtn; + } + } + } + } + return 0; // 'could not detect' +} + +static int x11_detect_VRAM_kb() +{ + std::string x_log_location("/var/log/"); + std::string fname; + int rtn = 0; // 'could not detect' + int display_num = 0; + FILE *fp; + char *display_env = getenv("DISPLAY"); // e.g. :0 or :0.0 or :1.0 etc + // parse DISPLAY number so we can go grab the right log file + if (display_env[0] == ':' && + display_env[1] >= '0' && display_env[1] <= '9') + { + display_num = display_env[1] - '0'; + } + + // *TODO: we could be smarter and see which of Xorg/XFree86 has the + // freshest time-stamp. + + // Try Xorg log first + fname = x_log_location; + fname += "Xorg."; + fname += ('0' + display_num); + fname += ".log"; + fp = fopen(fname.c_str(), "r"); + if (fp) + { + LL_INFOS() << "Looking in " << fname + << " for VRAM info..." << LL_ENDL; + rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: "); + fclose(fp); + if (0 == rtn) + { + fp = fopen(fname.c_str(), "r"); + if (fp) + { + rtn = x11_detect_VRAM_kb_fp(fp, ": Video RAM: "); + fclose(fp); + if (0 == rtn) + { + fp = fopen(fname.c_str(), "r"); + if (fp) + { + rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: "); + fclose(fp); + } + } + } + } + } + else + { + LL_INFOS() << "Could not open " << fname + << " - skipped." << LL_ENDL; + // Try old XFree86 log otherwise + fname = x_log_location; + fname += "XFree86."; + fname += ('0' + display_num); + fname += ".log"; + fp = fopen(fname.c_str(), "r"); + if (fp) + { + LL_INFOS() << "Looking in " << fname + << " for VRAM info..." << LL_ENDL; + rtn = x11_detect_VRAM_kb_fp(fp, ": VideoRAM: "); + fclose(fp); + if (0 == rtn) + { + fp = fopen(fname.c_str(), "r"); + if (fp) + { + rtn = x11_detect_VRAM_kb_fp(fp, ": Memory: "); + fclose(fp); + } + } + } + else + { + LL_INFOS() << "Could not open " << fname + << " - skipped." << LL_ENDL; + } + } + return rtn; +} +#endif // LL_X11 + +bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, bool fullscreen, bool disable_vsync) +{ + //bool glneedsinit = false; + + LL_INFOS() << "createContext, fullscreen=" << fullscreen << + " size=" << width << "x" << height << LL_ENDL; + + // captures don't survive contexts + mGrabbyKeyFlags = 0; + mReallyCapturedCount = 0; + + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + LL_INFOS() << "sdl_init() failed! " << SDL_GetError() << LL_ENDL; + setupFailure("sdl_init() failure, window creation error", "error", OSMB_OK); + return false; + } + + SDL_version c_sdl_version; + SDL_VERSION(&c_sdl_version); + LL_INFOS() << "Compiled against SDL " + << int(c_sdl_version.major) << "." + << int(c_sdl_version.minor) << "." + << int(c_sdl_version.patch) << LL_ENDL; + const SDL_version *r_sdl_version; + r_sdl_version = SDL_Linked_Version(); + LL_INFOS() << " Running against SDL " + << int(r_sdl_version->major) << "." + << int(r_sdl_version->minor) << "." + << int(r_sdl_version->patch) << LL_ENDL; + + const SDL_VideoInfo *video_info = SDL_GetVideoInfo( ); + if (!video_info) + { + LL_INFOS() << "SDL_GetVideoInfo() failed! " << SDL_GetError() << LL_ENDL; + setupFailure("SDL_GetVideoInfo() failed, Window creation error", "Error", OSMB_OK); + return false; + } + + if (video_info->current_h > 0) + { + mOriginalAspectRatio = (float)video_info->current_w / (float)video_info->current_h; + LL_INFOS() << "Original aspect ratio was " << video_info->current_w << ":" << video_info->current_h << "=" << mOriginalAspectRatio << LL_ENDL; + } + + SDL_EnableUNICODE(1); + SDL_WM_SetCaption(mWindowTitle.c_str(), mWindowTitle.c_str()); + + // 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); + // We need stencil support for a few (minor) things. + if (!getenv("LL_GL_NO_STENCIL")) + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8); + + // *FIX: try to toggle vsync here? + + mFullscreen = fullscreen; + + int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT; + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + if (mFSAASamples > 0) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, mFSAASamples); + } + + mSDLFlags = sdlflags; + + if (mFullscreen) + { + LL_INFOS() << "createContext: setting up fullscreen " << width << "x" << height << LL_ENDL; + + // 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; + + LL_INFOS() << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << LL_ENDL; + + for(i=0; i < resolutionCount; i++) + { + F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight; + + LL_INFOS() << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << LL_ENDL; + + if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) && + (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio))) + { + LL_INFOS() << " (new closest mode) " << LL_ENDL; + + // 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 && bits > 16) + { + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN); + } + + if (mWindow) + { + mFullscreen = true; + mFullscreenWidth = mWindow->w; + mFullscreenHeight = mWindow->h; + mFullscreenBits = mWindow->format->BitsPerPixel; + mFullscreenRefresh = -1; + + LL_INFOS() << "Running at " << mFullscreenWidth + << "x" << mFullscreenHeight + << "x" << mFullscreenBits + << " @ " << mFullscreenRefresh + << LL_ENDL; + } + else + { + LL_WARNS() << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << LL_ENDL; + // No fullscreen support + mFullscreen = false; + mFullscreenWidth = -1; + mFullscreenHeight = -1; + mFullscreenBits = -1; + mFullscreenRefresh = -1; + + std::string error = llformat("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; + + LL_INFOS() << "createContext: creating window " << width << "x" << height << "x" << bits << LL_ENDL; + mWindow = SDL_SetVideoMode(width, height, bits, sdlflags); + if (!mWindow && bits > 16) + { + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + mWindow = SDL_SetVideoMode(width, height, bits, sdlflags); + } + + if (!mWindow) + { + LL_WARNS() << "createContext: window creation failure. SDL: " << SDL_GetError() << LL_ENDL; + setupFailure("Window creation error", "Error", OSMB_OK); + return false; + } + } else if (!mFullscreen && (mWindow != NULL)) + { + LL_INFOS() << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << LL_ENDL; + } + + // Detect video memory size. +# if LL_X11 + gGLManager.mVRAM = x11_detect_VRAM_kb() / 1024; + if (gGLManager.mVRAM != 0) + { + LL_INFOS() << "X11 log-parser detected " << gGLManager.mVRAM << "MB VRAM." << LL_ENDL; + } else +# endif // LL_X11 + { + // fallback to letting SDL detect VRAM. + // note: I've not seen SDL's detection ever actually find + // VRAM != 0, but if SDL *does* detect it then that's a bonus. + gGLManager.mVRAM = video_info->video_mem / 1024; + if (gGLManager.mVRAM != 0) + { + LL_INFOS() << "SDL detected " << gGLManager.mVRAM << "MB VRAM." << LL_ENDL; + } + } + // If VRAM is not detected, that is handled later + + // *TODO: Now would be an appropriate time to check for some + // explicitly unsupported cards. + //const char* RENDERER = (const char*) glGetString(GL_RENDERER); + + 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); + + LL_INFOS() << "GL buffer:" << LL_ENDL; + LL_INFOS() << " Red Bits " << S32(redBits) << LL_ENDL; + LL_INFOS() << " Green Bits " << S32(greenBits) << LL_ENDL; + LL_INFOS() << " Blue Bits " << S32(blueBits) << LL_ENDL; + LL_INFOS() << " Alpha Bits " << S32(alphaBits) << LL_ENDL; + LL_INFOS() << " Depth Bits " << S32(depthBits) << LL_ENDL; + LL_INFOS() << " Stencil Bits " << S32(stencilBits) << LL_ENDL; + + 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 + /* Grab the window manager specific information */ + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if ( SDL_GetWMInfo(&info) ) + { + /* Save the information for later use */ + if ( info.subsystem == SDL_SYSWM_X11 ) + { + mSDL_Display = info.info.x11.display; + mSDL_XWindowID = info.info.x11.wmwindow; + Lock_Display = info.info.x11.lock_func; + Unlock_Display = info.info.x11.unlock_func; + } + else + { + LL_WARNS() << "We're not running under X11? Wild." + << LL_ENDL; + } + } + else + { + LL_WARNS() << "We're not running under any known WM. Wild." + << LL_ENDL; + } +#endif // LL_X11 + + + //make sure multisampling is disabled by default + glDisable(GL_MULTISAMPLE_ARB); + + // We need to do this here, once video is init'd + if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, + SDL_DEFAULT_REPEAT_INTERVAL)) + LL_WARNS() << "Couldn't enable key-repeat: " << SDL_GetError() <<LL_ENDL; + + // Don't need to get the current gamma, since there's a call that restores it to the system defaults. + return true; +} + + +// changing fullscreen resolution, or switching between windowed and fullscreen mode. +bool LLWindowSDL::switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp) +{ + const bool needsRebuild = true; // Just nuke the context and start over. + bool result = true; + + LL_INFOS() << "switchContext, fullscreen=" << fullscreen << LL_ENDL; + stop_glerror(); + if(needsRebuild) + { + destroyContext(); + result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync); + if (result) + { + gGLManager.initGL(); + + //start with arrow cursor + initCursors(); + setCursor( UI_CURSOR_ARROW ); + } + } + + stop_glerror(); + + return result; +} + +void LLWindowSDL::destroyContext() +{ + LL_INFOS() << "destroyContext begins" << LL_ENDL; + +#if LL_X11 + mSDL_Display = NULL; + mSDL_XWindowID = None; + Lock_Display = NULL; + Unlock_Display = NULL; +#endif // LL_X11 + + // Clean up remaining GL state before blowing away window + LL_INFOS() << "shutdownGL begins" << LL_ENDL; + gGLManager.shutdownGL(); + LL_INFOS() << "SDL_QuitSS/VID begins" << LL_ENDL; + SDL_QuitSubSystem(SDL_INIT_VIDEO); // *FIX: this might be risky... + + mWindow = NULL; +} + +LLWindowSDL::~LLWindowSDL() +{ + quitCursors(); + destroyContext(); + + if(mSupportedResolutions != NULL) + { + delete []mSupportedResolutions; + } + + gWindowImplementation = NULL; +} + + +void LLWindowSDL::show() +{ + // *FIX: What to do with SDL? +} + +void LLWindowSDL::hide() +{ + // *FIX: What to do with SDL? +} + +//virtual +void LLWindowSDL::minimize() +{ + // *FIX: What to do with SDL? +} + +//virtual +void LLWindowSDL::restore() +{ + // *FIX: What to do with SDL? +} + + +// close() destroys all OS-specific code associated with a window. +// Usually called from LLWindowManager::destroyWindow() +void LLWindowSDL::close() +{ + // Is window is already closed? + // if (!mWindow) + // { + // return; + // } + + // Make sure cursor is visible and we haven't mangled the clipping state. + setMouseClipping(false); + showCursor(); + + destroyContext(); +} + +bool LLWindowSDL::isValid() +{ + return (mWindow != NULL); +} + +bool LLWindowSDL::getVisible() +{ + bool result = false; + + // *FIX: This isn't really right... + // Then what is? + if (mWindow) + { + result = true; + } + + return(result); +} + +bool LLWindowSDL::getMinimized() +{ + bool result = false; + + if (mWindow && (1 == mIsMinimized)) + { + result = true; + } + return(result); +} + +bool LLWindowSDL::getMaximized() +{ + bool result = false; + + if (mWindow) + { + // TODO + } + + return(result); +} + +bool LLWindowSDL::maximize() +{ + // TODO + return false; +} + +bool LLWindowSDL::getFullscreen() +{ + return mFullscreen; +} + +bool LLWindowSDL::getPosition(LLCoordScreen *position) +{ + // *FIX: can anything be done with this? + position->mX = 0; + position->mY = 0; + return true; +} + +bool LLWindowSDL::getSize(LLCoordScreen *size) +{ + if (mWindow) + { + size->mX = mWindow->w; + size->mY = mWindow->h; + return (true); + } + + return (false); +} + +bool LLWindowSDL::getSize(LLCoordWindow *size) +{ + if (mWindow) + { + size->mX = mWindow->w; + size->mY = mWindow->h; + return (true); + } + + return (false); +} + +bool LLWindowSDL::setPosition(const LLCoordScreen position) +{ + if(mWindow) + { + // *FIX: (?) + //MacMoveWindow(mWindow, position.mX, position.mY, false); + } + + return true; +} + +bool LLWindowSDL::setSizeImpl(const LLCoordScreen size) +{ + if(mWindow) + { + // Push a resize event onto SDL's queue - we'll handle it + // when it comes out again. + SDL_Event event; + event.type = SDL_VIDEORESIZE; + event.resize.w = size.mX; + event.resize.h = size.mY; + SDL_PushEvent(&event); // copied into queue + + return true; + } + + return false; +} + +bool LLWindowSDL::setSizeImpl(const LLCoordWindow size) +{ + if(mWindow) + { + // Push a resize event onto SDL's queue - we'll handle it + // when it comes out again. + SDL_Event event; + event.type = SDL_VIDEORESIZE; + event.resize.w = size.mX; + event.resize.h = size.mY; + SDL_PushEvent(&event); // copied into queue + + return true; + } + + return false; +} + + +void LLWindowSDL::swapBuffers() +{ + if (mWindow) + { + SDL_GL_SwapBuffers(); + } +} + +U32 LLWindowSDL::getFSAASamples() +{ + return mFSAASamples; +} + +void LLWindowSDL::setFSAASamples(const U32 samples) +{ + mFSAASamples = samples; +} + +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 ) +{ + //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF); +} + +// virtual +void LLWindowSDL::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately) +{ + LLWindow::setMinSize(min_width, min_height, enforce_immediately); + +#if LL_X11 + // Set the minimum size limits for X11 window + // so the window manager doesn't allow resizing below those limits. + XSizeHints* hints = XAllocSizeHints(); + hints->flags |= PMinSize; + hints->min_width = mMinWindowWidth; + hints->min_height = mMinWindowHeight; + + XSetWMNormalHints(mSDL_Display, mSDL_XWindowID, hints); + + XFree(hints); +#endif +} + +bool LLWindowSDL::setCursorPosition(const LLCoordWindow position) +{ + bool result = true; + LLCoordScreen screen_pos; + + if (!convertCoords(position, &screen_pos)) + { + return false; + } + + //LL_INFOS() << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << LL_ENDL; + + // do the actual forced cursor move. + SDL_WarpMouse(screen_pos.mX, screen_pos.mY); + + //LL_INFOS() << llformat("llcw %d,%d -> scr %d,%d", position.mX, position.mY, screen_pos.mX, screen_pos.mY) << LL_ENDL; + + 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); +} + + +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; + if (getSize(&screen_size)) + { + pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX; + } + } + + return pixel_aspect; +} + + +// This is to support 'temporarily windowed' mode so that +// dialogs are still usable in fullscreen. +void LLWindowSDL::beforeDialog() +{ + bool running_x11 = false; +#if LL_X11 + running_x11 = (mSDL_XWindowID != None); +#endif //LL_X11 + + LL_INFOS() << "LLWindowSDL::beforeDialog()" << LL_ENDL; + + if (SDLReallyCaptureInput(false)) // must ungrab input so popup works! + { + if (mFullscreen) + { + // need to temporarily go non-fullscreen; bless SDL + // for providing a SDL_WM_ToggleFullScreen() - though + // it only works in X11 + if (running_x11 && mWindow) + { + SDL_WM_ToggleFullScreen(mWindow); + } + } + } + +#if LL_X11 + if (mSDL_Display) + { + // Everything that we/SDL asked for should happen before we + // potentially hand control over to GTK. + maybe_lock_display(); + XSync(mSDL_Display, False); + maybe_unlock_display(); + } +#endif // LL_X11 + +#if LL_GTK + // this is a good time to grab some GTK version information for + // diagnostics, if not already done. + ll_try_gtk_init(); +#endif // LL_GTK + + maybe_lock_display(); +} + +void LLWindowSDL::afterDialog() +{ + bool running_x11 = false; +#if LL_X11 + running_x11 = (mSDL_XWindowID != None); +#endif //LL_X11 + + LL_INFOS() << "LLWindowSDL::afterDialog()" << LL_ENDL; + + maybe_unlock_display(); + + if (mFullscreen) + { + // need to restore fullscreen mode after dialog - only works + // in X11 + if (running_x11 && mWindow) + { + SDL_WM_ToggleFullScreen(mWindow); + } + } +} + + +#if LL_X11 +// set/reset the XWMHints flag for 'urgency' that usually makes the icon flash +void LLWindowSDL::x11_set_urgent(bool urgent) +{ + if (mSDL_Display && !mFullscreen) + { + XWMHints *wm_hints; + + LL_INFOS() << "X11 hint for urgency, " << urgent << LL_ENDL; + + maybe_lock_display(); + wm_hints = XGetWMHints(mSDL_Display, mSDL_XWindowID); + if (!wm_hints) + wm_hints = XAllocWMHints(); + + if (urgent) + wm_hints->flags |= XUrgencyHint; + else + wm_hints->flags &= ~XUrgencyHint; + + XSetWMHints(mSDL_Display, mSDL_XWindowID, wm_hints); + XFree(wm_hints); + XSync(mSDL_Display, False); + maybe_unlock_display(); + } +} +#endif // LL_X11 + +void LLWindowSDL::flashIcon(F32 seconds) +{ +#if !LL_X11 + LL_INFOS() << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; +#else + LL_INFOS() << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << LL_ENDL; + + 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_GTK +bool LLWindowSDL::isClipboardTextAvailable() +{ + if (ll_try_gtk_init()) + { + GtkClipboard * const clipboard = + gtk_clipboard_get(GDK_NONE); + return gtk_clipboard_wait_is_text_available(clipboard) ? + true : false; + } + return false; // failure +} + +bool LLWindowSDL::pasteTextFromClipboard(LLWString &text) +{ + if (ll_try_gtk_init()) + { + GtkClipboard * const clipboard = + gtk_clipboard_get(GDK_NONE); + gchar * const data = gtk_clipboard_wait_for_text(clipboard); + if (data) + { + text = LLWString(utf8str_to_wstring(data)); + g_free(data); + return true; + } + } + return false; // failure +} + +bool LLWindowSDL::copyTextToClipboard(const LLWString &text) +{ + if (ll_try_gtk_init()) + { + const std::string utf8 = wstring_to_utf8str(text); + GtkClipboard * const clipboard = + gtk_clipboard_get(GDK_NONE); + gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length()); + return true; + } + return false; // failure +} + + +bool LLWindowSDL::isPrimaryTextAvailable() +{ + if (ll_try_gtk_init()) + { + GtkClipboard * const clipboard = + gtk_clipboard_get(GDK_SELECTION_PRIMARY); + return gtk_clipboard_wait_is_text_available(clipboard) ? + true : false; + } + return false; // failure +} + +bool LLWindowSDL::pasteTextFromPrimary(LLWString &text) +{ + if (ll_try_gtk_init()) + { + GtkClipboard * const clipboard = + gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gchar * const data = gtk_clipboard_wait_for_text(clipboard); + if (data) + { + text = LLWString(utf8str_to_wstring(data)); + g_free(data); + return true; + } + } + return false; // failure +} + +bool LLWindowSDL::copyTextToPrimary(const LLWString &text) +{ + if (ll_try_gtk_init()) + { + const std::string utf8 = wstring_to_utf8str(text); + GtkClipboard * const clipboard = + gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gtk_clipboard_set_text(clipboard, utf8.c_str(), utf8.length()); + return true; + } + return false; // failure +} + +#else + +bool LLWindowSDL::isClipboardTextAvailable() +{ + return false; // unsupported +} + +bool LLWindowSDL::pasteTextFromClipboard(LLWString &dst) +{ + return false; // unsupported +} + +bool LLWindowSDL::copyTextToClipboard(const LLWString &s) +{ + return false; // unsupported +} + +bool LLWindowSDL::isPrimaryTextAvailable() +{ + return false; // unsupported +} + +bool LLWindowSDL::pasteTextFromPrimary(LLWString &dst) +{ + return false; // unsupported +} + +bool LLWindowSDL::copyTextToPrimary(const LLWString &s) +{ + return false; // unsupported +} + +#endif // LL_GTK + +LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions) +{ + if (!mSupportedResolutions) + { + mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS]; + mNumSupportedResolutions = 0; + + SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN); + if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) ) + { + int count = 0; + while (*modes && count<MAX_NUM_RESOLUTIONS) // they're sorted biggest to smallest, so find end... + { + modes++; + count++; + } + + while (count--) + { + modes--; + SDL_Rect *r = *modes; + int w = r->w; + int h = r->h; + if ((w >= 800) && (h >= 600)) + { + // make sure we don't add the same resolution multiple times! + if ( (mNumSupportedResolutions == 0) || + ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) && + (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) ) + { + mSupportedResolutions[mNumSupportedResolutions].mWidth = w; + mSupportedResolutions[mNumSupportedResolutions].mHeight = h; + mNumSupportedResolutions++; + } + } + } + } + } + + num_resolutions = mNumSupportedResolutions; + return mSupportedResolutions; +} + +bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to) +{ + if (!to) + return false; + + to->mX = from.mX; + to->mY = mWindow->h - from.mY - 1; + + return true; +} + +bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to) +{ + if (!to) + return false; + + to->mX = from.mX; + to->mY = mWindow->h - from.mY - 1; + + return true; +} + +bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to) +{ + if (!to) + return false; + + // In the fullscreen case, window and screen coordinates are the same. + to->mX = from.mX; + to->mY = from.mY; + return (true); +} + +bool LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to) +{ + if (!to) + return false; + + // In the fullscreen case, window and screen coordinates are the same. + to->mX = from.mX; + to->mY = from.mY; + return (true); +} + +bool LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to) +{ + LLCoordWindow window_coord; + + return(convertCoords(from, &window_coord) && convertCoords(window_coord, to)); +} + +bool LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to) +{ + LLCoordWindow window_coord; + + return(convertCoords(from, &window_coord) && convertCoords(window_coord, to)); +} + + + + +void LLWindowSDL::setupFailure(const std::string& text, const std::string& 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; + LL_WARNS() << "ReallyCapture count was < 0" << LL_ENDL; + } + + if (!mFullscreen) /* only bother if we're windowed anyway */ + { +#if LL_X11 + if (mSDL_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) + { + //LL_INFOS() << "X11 POINTER GRABBY" << LL_ENDL; + //newmode = SDL_WM_GrabInput(wantmode); + maybe_lock_display(); + result = XGrabPointer(mSDL_Display, mSDL_XWindowID, + True, 0, GrabModeAsync, + GrabModeAsync, + None, None, CurrentTime); + maybe_unlock_display(); + if (GrabSuccess == result) + newmode = SDL_GRAB_ON; + else + newmode = SDL_GRAB_OFF; + } else if (wantmode == SDL_GRAB_OFF) + { + //LL_INFOS() << "X11 POINTER UNGRABBY" << LL_ENDL; + newmode = SDL_GRAB_OFF; + //newmode = SDL_WM_GrabInput(SDL_GRAB_OFF); + + maybe_lock_display(); + XUngrabPointer(mSDL_Display, CurrentTime); + // Make sure the ungrab happens RIGHT NOW. + XSync(mSDL_Display, False); + maybe_unlock_display(); + } 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 ALT 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_RALT: + mask = 1U << 1; break; + case SDLK_LCTRL: + mask = 1U << 2; break; + case SDLK_RCTRL: + mask = 1U << 3; break; + default: + break; + } + + if (gain) + mGrabbyKeyFlags |= mask; + else + mGrabbyKeyFlags &= ~mask; + + //LL_INFOS() << "mGrabbyKeyFlags=" << mGrabbyKeyFlags << LL_ENDL; + + /* 0 means we don't need to mousegrab, otherwise grab. */ + return mGrabbyKeyFlags; +} + + +void check_vm_bloat() +{ +#if LL_LINUX + // watch our own VM and RSS sizes, warn if we bloated rapidly + static const std::string STATS_FILE = "/proc/self/stat"; + FILE *fp = fopen(STATS_FILE.c_str(), "r"); + if (fp) + { + static long long last_vm_size = 0; + static long long last_rss_size = 0; + const long long significant_vm_difference = 250 * 1024*1024; + const long long significant_rss_difference = 50 * 1024*1024; + long long this_vm_size = 0; + long long this_rss_size = 0; + + ssize_t res; + size_t dummy; + char *ptr = NULL; + for (int i=0; i<22; ++i) // parse past the values we don't want + { + res = getdelim(&ptr, &dummy, ' ', fp); + if (-1 == res) + { + LL_WARNS() << "Unable to parse " << STATS_FILE << LL_ENDL; + goto finally; + } + free(ptr); + ptr = NULL; + } + // 23rd space-delimited entry is vsize + res = getdelim(&ptr, &dummy, ' ', fp); + llassert(ptr); + if (-1 == res) + { + LL_WARNS() << "Unable to parse " << STATS_FILE << LL_ENDL; + goto finally; + } + this_vm_size = atoll(ptr); + free(ptr); + ptr = NULL; + // 24th space-delimited entry is RSS + res = getdelim(&ptr, &dummy, ' ', fp); + llassert(ptr); + if (-1 == res) + { + LL_WARNS() << "Unable to parse " << STATS_FILE << LL_ENDL; + goto finally; + } + this_rss_size = getpagesize() * atoll(ptr); + free(ptr); + ptr = NULL; + + LL_INFOS() << "VM SIZE IS NOW " << (this_vm_size/(1024*1024)) << " MB, RSS SIZE IS NOW " << (this_rss_size/(1024*1024)) << " MB" << LL_ENDL; + + if (llabs(last_vm_size - this_vm_size) > + significant_vm_difference) + { + if (this_vm_size > last_vm_size) + { + LL_WARNS() << "VM size grew by " << (this_vm_size - last_vm_size)/(1024*1024) << " MB in last frame" << LL_ENDL; + } + else + { + LL_INFOS() << "VM size shrank by " << (last_vm_size - this_vm_size)/(1024*1024) << " MB in last frame" << LL_ENDL; + } + } + + if (llabs(last_rss_size - this_rss_size) > + significant_rss_difference) + { + if (this_rss_size > last_rss_size) + { + LL_WARNS() << "RSS size grew by " << (this_rss_size - last_rss_size)/(1024*1024) << " MB in last frame" << LL_ENDL; + } + else + { + LL_INFOS() << "RSS size shrank by " << (last_rss_size - this_rss_size)/(1024*1024) << " MB in last frame" << LL_ENDL; + } + } + + last_rss_size = this_rss_size; + last_vm_size = this_vm_size; + +finally: + if (NULL != ptr) + { + free(ptr); + ptr = NULL; + } + fclose(fp); + } +#endif // LL_LINUX +} + + +// virtual +void LLWindowSDL::processMiscNativeEvents() +{ +#if LL_GTK + // Pump GTK events to avoid starvation for: + // * DBUS servicing + // * Anything else which quietly hooks into the default glib/GTK loop + if (ll_try_gtk_init()) + { + // Yuck, Mozilla's GTK callbacks play with the locale - push/pop + // the locale to protect it, as exotic/non-C locales + // causes our code lots of general critical weirdness + // and crashness. (SL-35450) + static std::string saved_locale; + saved_locale = ll_safe_string(setlocale(LC_ALL, NULL)); + + // Pump until we've nothing left to do or passed 1/15th of a + // second pumping for this frame. + static LLTimer pump_timer; + pump_timer.reset(); + pump_timer.setTimerExpirySec(1.0f / 15.0f); + do { + // Always do at least one non-blocking pump + gtk_main_iteration_do(false); + } while (gtk_events_pending() && + !pump_timer.hasExpired()); + + setlocale(LC_ALL, saved_locale.c_str() ); + } +#endif // LL_GTK + + // hack - doesn't belong here - but this is just for debugging + if (getenv("LL_DEBUG_BLOAT")) + { + check_vm_bloat(); + } +} + +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; + + // Handle all outstanding SDL events + 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: + mKeyScanCode = event.key.keysym.scancode; + mKeyVirtualKey = event.key.keysym.unicode; + mKeyModifiers = event.key.keysym.mod; + + 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) + { + handleUnicodeUTF16(event.key.keysym.unicode, + gKeyboard->currentMask(false)); + } + break; + + case SDL_KEYUP: + mKeyScanCode = event.key.keysym.scancode; + mKeyVirtualKey = event.key.keysym.unicode; + mKeyModifiers = event.key.keysym.mod; + + if (SDLCheckGrabbyKeys(event.key.keysym.sym, false) == 0) + SDLReallyCaptureInput(false); // part of the fix for SL-13243 + + 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 + { + mCallbacks->handleRightMouseDown(this, openGlCoord, mask); + } + + else if (event.button.button == SDL_BUTTON_MIDDLE) // middle + { + mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask); + } + else if (event.button.button == 4) // mousewheel up...thanks to X11 for making SDL consider these "buttons". + mCallbacks->handleScrollWheel(this, -1); + else if (event.button.button == 5) // mousewheel down...thanks to X11 for making SDL consider these "buttons". + mCallbacks->handleScrollWheel(this, 1); + + 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 + mCallbacks->handleRightMouseUp(this, openGlCoord, mask); + else if (event.button.button == SDL_BUTTON_MIDDLE) // middle + 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? + { + LL_INFOS() << "Handling a resize event: " << event.resize.w << + "x" << event.resize.h << LL_ENDL; + + S32 width = llmax(event.resize.w, (S32)mMinWindowWidth); + S32 height = llmax(event.resize.h, (S32)mMinWindowHeight); + + // *FIX: I'm not sure this is necessary! + mWindow = SDL_SetVideoMode(width, height, 32, mSDLFlags); + if (!mWindow) + { + // *FIX: More informative dialog? + LL_INFOS() << "Could not recreate context after resize! Quitting..." << LL_ENDL; + 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, width, height); + 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) + { + mHaveInputFocus = !!event.active.gain; + + if (mHaveInputFocus) + mCallbacks->handleFocus(this); + else + mCallbacks->handleFocusLost(this); + } + } + if (event.active.state & SDL_APPACTIVE) + { + // Change in iconification/minimization state. + if ((!event.active.gain) != mIsMinimized) + { + mIsMinimized = (!event.active.gain); + + mCallbacks->handleActivate(this, !mIsMinimized); + LL_INFOS() << "SDL deiconification state switched to " << bool(event.active.gain) << LL_ENDL; + } + else + { + LL_INFOS() << "Ignored bogus redundant SDL deiconification state switch to " << bool(event.active.gain) << LL_ENDL; + } + } + 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: + //LL_INFOS() << "Unhandled SDL event type " << event.type << LL_ENDL; + break; + } + } + + updateCursor(); + +#if LL_X11 + // This is a good time to stop flashing the icon if our mFlashTimer has + // expired. + if (mFlashing && mFlashTimer.hasExpired()) + { + x11_set_urgent(false); + 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; + LL_DEBUGS() << "Loaded cursor file " << filename << " " + << bmpsurface->w << "x" << bmpsurface->h << LL_ENDL; + cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE, + bmpsurface->w, + bmpsurface->h, + 32, + SDL_SwapLE32(0xFFU), + SDL_SwapLE32(0xFF00U), + SDL_SwapLE32(0xFF0000U), + SDL_SwapLE32(0xFF000000U)); + SDL_FillRect(cursurface, NULL, SDL_SwapLE32(0x00000000U)); + + // Blit the cursor pixel data onto a 32-bit RGBA surface so we + // only have to cope with processing one type of pixel format. + if (0 == SDL_BlitSurface(bmpsurface, NULL, + cursurface, NULL)) + { + // n.b. we already checked that width is a multiple of 8. + const int bitmap_bytes = (cursurface->w * cursurface->h) / 8; + unsigned char *cursor_data = new unsigned char[bitmap_bytes]; + unsigned char *cursor_mask = new unsigned char[bitmap_bytes]; + memset(cursor_data, 0, bitmap_bytes); + memset(cursor_mask, 0, bitmap_bytes); + int i,j; + // Walk the RGBA cursor pixel data, extracting both data and + // mask to build SDL-friendly cursor bitmaps from. The mask + // is inferred by color-keying against 200,200,200 + for (i=0; i<cursurface->h; ++i) { + for (j=0; j<cursurface->w; ++j) { + U8 *pixelp = + ((U8*)cursurface->pixels) + + cursurface->pitch * i + + j*cursurface->format->BytesPerPixel; + U8 srcred = pixelp[0]; + U8 srcgreen = pixelp[1]; + U8 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 { + LL_WARNS() << "CURSOR BLIT FAILURE, cursurface: " << cursurface << LL_ENDL; + } + SDL_FreeSurface(cursurface); + SDL_FreeSurface(bmpsurface); + } else { + LL_WARNS() << "CURSOR LOAD FAILURE " << filename << LL_ENDL; + } + + return sdlcursor; +} + +void LLWindowSDL::updateCursor() +{ + if (ATIbug) { + // cursor-updating is very flaky when this bug is + // present; do nothing. + return; + } + + if (mCurrentCursor != mNextCursor) + { + if (mNextCursor < UI_CURSOR_COUNT) + { + SDL_Cursor *sdlcursor = mSDLCursors[mNextCursor]; + // 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 { + LL_WARNS() << "Tried to set invalid cursor number " << mNextCursor << LL_ENDL; + } + mCurrentCursor = mNextCursor; + } +} + +void LLWindowSDL::initCursors() +{ + int i; + // Blank the cursor pointer array for those we may miss. + for (i=0; i<UI_CURSOR_COUNT; ++i) + { + mSDLCursors[i] = NULL; + } + // Pre-make an SDL cursor for each of the known cursor types. + // We hardcode the hotspots - to avoid that we'd have to write + // a .cur file loader. + // NOTE: SDL doesn't load RLE-compressed BMP files. + mSDLCursors[UI_CURSOR_ARROW] = makeSDLCursorFromBMP("llarrow.BMP",0,0); + mSDLCursors[UI_CURSOR_WAIT] = makeSDLCursorFromBMP("wait.BMP",12,15); + mSDLCursors[UI_CURSOR_HAND] = makeSDLCursorFromBMP("hand.BMP",7,10); + mSDLCursors[UI_CURSOR_IBEAM] = makeSDLCursorFromBMP("ibeam.BMP",15,16); + mSDLCursors[UI_CURSOR_CROSS] = makeSDLCursorFromBMP("cross.BMP",16,14); + mSDLCursors[UI_CURSOR_SIZENWSE] = makeSDLCursorFromBMP("sizenwse.BMP",14,17); + mSDLCursors[UI_CURSOR_SIZENESW] = makeSDLCursorFromBMP("sizenesw.BMP",17,17); + mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14); + mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16); + mSDLCursors[UI_CURSOR_SIZEALL] = makeSDLCursorFromBMP("sizeall.BMP", 17, 17); + mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8); + mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15); + mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13); + mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6); + mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5); + mSDLCursors[UI_CURSOR_TOOLCREATE] = makeSDLCursorFromBMP("lltoolcreate.BMP",7,7); + mSDLCursors[UI_CURSOR_ARROWDRAG] = makeSDLCursorFromBMP("arrowdrag.BMP",0,0); + mSDLCursors[UI_CURSOR_ARROWCOPY] = makeSDLCursorFromBMP("arrowcop.BMP",0,0); + mSDLCursors[UI_CURSOR_ARROWDRAGMULTI] = makeSDLCursorFromBMP("llarrowdragmulti.BMP",0,0); + mSDLCursors[UI_CURSOR_ARROWCOPYMULTI] = makeSDLCursorFromBMP("arrowcopmulti.BMP",0,0); + mSDLCursors[UI_CURSOR_NOLOCKED] = makeSDLCursorFromBMP("llnolocked.BMP",8,8); + mSDLCursors[UI_CURSOR_ARROWLOCKED] = makeSDLCursorFromBMP("llarrowlocked.BMP",0,0); + mSDLCursors[UI_CURSOR_GRABLOCKED] = makeSDLCursorFromBMP("llgrablocked.BMP",2,13); + mSDLCursors[UI_CURSOR_TOOLTRANSLATE] = makeSDLCursorFromBMP("lltooltranslate.BMP",0,0); + mSDLCursors[UI_CURSOR_TOOLROTATE] = makeSDLCursorFromBMP("lltoolrotate.BMP",0,0); + mSDLCursors[UI_CURSOR_TOOLSCALE] = makeSDLCursorFromBMP("lltoolscale.BMP",0,0); + mSDLCursors[UI_CURSOR_TOOLCAMERA] = makeSDLCursorFromBMP("lltoolcamera.BMP",7,5); + mSDLCursors[UI_CURSOR_TOOLPAN] = makeSDLCursorFromBMP("lltoolpan.BMP",7,5); + mSDLCursors[UI_CURSOR_TOOLZOOMIN] = makeSDLCursorFromBMP("lltoolzoomin.BMP",7,5); + mSDLCursors[UI_CURSOR_TOOLZOOMOUT] = makeSDLCursorFromBMP("lltoolzoomout.BMP", 7, 5); + mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] = makeSDLCursorFromBMP("toolpickobject3.BMP",0,0); + mSDLCursors[UI_CURSOR_TOOLPLAY] = makeSDLCursorFromBMP("toolplay.BMP",0,0); + mSDLCursors[UI_CURSOR_TOOLPAUSE] = makeSDLCursorFromBMP("toolpause.BMP",0,0); + mSDLCursors[UI_CURSOR_TOOLMEDIAOPEN] = makeSDLCursorFromBMP("toolmediaopen.BMP",0,0); + mSDLCursors[UI_CURSOR_PIPETTE] = makeSDLCursorFromBMP("lltoolpipette.BMP",2,28); + mSDLCursors[UI_CURSOR_TOOLSIT] = makeSDLCursorFromBMP("toolsit.BMP",20,15); + mSDLCursors[UI_CURSOR_TOOLBUY] = makeSDLCursorFromBMP("toolbuy.BMP",20,15); + mSDLCursors[UI_CURSOR_TOOLOPEN] = makeSDLCursorFromBMP("toolopen.BMP",20,15); + mSDLCursors[UI_CURSOR_TOOLPATHFINDING] = makeSDLCursorFromBMP("lltoolpathfinding.BMP", 16, 16); + mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_START] = makeSDLCursorFromBMP("lltoolpathfindingpathstart.BMP", 16, 16); + mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD] = makeSDLCursorFromBMP("lltoolpathfindingpathstartadd.BMP", 16, 16); + mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_END] = makeSDLCursorFromBMP("lltoolpathfindingpathend.BMP", 16, 16); + mSDLCursors[UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD] = makeSDLCursorFromBMP("lltoolpathfindingpathendadd.BMP", 16, 16); + mSDLCursors[UI_CURSOR_TOOLNO] = makeSDLCursorFromBMP("llno.BMP",8,8); + + if (getenv("LL_ATI_MOUSE_CURSOR_BUG") != NULL) { + LL_INFOS() << "Disabling cursor updating due to LL_ATI_MOUSE_CURSOR_BUG" << LL_ENDL; + ATIbug = true; + } +} + +void LLWindowSDL::quitCursors() +{ + int i; + if (mWindow) + { + for (i=0; i<UI_CURSOR_COUNT; ++i) + { + if (mSDLCursors[i]) + { + SDL_FreeCursor(mSDLCursors[i]); + mSDLCursors[i] = NULL; + } + } + } else { + // SDL doesn't refcount cursors, so if the window has + // already been destroyed then the cursors have gone with it. + LL_INFOS() << "Skipping quitCursors: mWindow already gone." << LL_ENDL; + for (i=0; i<UI_CURSOR_COUNT; ++i) + mSDLCursors[i] = NULL; + } +} + +void LLWindowSDL::captureMouse() +{ + // SDL already enforces the semantics that captureMouse is + // used for, i.e. that we continue to get mouse events as long + // as a button is down regardless of whether we left the + // window, and in a less obnoxious way than SDL_WM_GrabInput + // which would confine the cursor to the window too. + + LL_DEBUGS() << "LLWindowSDL::captureMouse" << LL_ENDL; +} + +void LLWindowSDL::releaseMouse() +{ + // see LWindowSDL::captureMouse() + + LL_DEBUGS() << "LLWindowSDL::releaseMouse" << LL_ENDL; +} + +void LLWindowSDL::hideCursor() +{ + if(!mCursorHidden) + { + // LL_INFOS() << "hideCursor: hiding" << LL_ENDL; + mCursorHidden = true; + mHideCursorPermanent = true; + SDL_ShowCursor(0); + } + else + { + // LL_INFOS() << "hideCursor: already hidden" << LL_ENDL; + } +} + +void LLWindowSDL::showCursor() +{ + if(mCursorHidden) + { + // LL_INFOS() << "showCursor: showing" << LL_ENDL; + mCursorHidden = false; + mHideCursorPermanent = false; + SDL_ShowCursor(1); + } + else + { + // LL_INFOS() << "showCursor: already visible" << LL_ENDL; + } +} + +void LLWindowSDL::showCursorFromMouseMove() +{ + if (!mHideCursorPermanent) + { + showCursor(); + } +} + +void LLWindowSDL::hideCursorUntilMouseMove() +{ + if (!mHideCursorPermanent) + { + hideCursor(); + mHideCursorPermanent = false; + } +} + + + +// +// LLSplashScreenSDL - I don't think we'll bother to implement this; it's +// fairly obsolete at this point. +// +LLSplashScreenSDL::LLSplashScreenSDL() +{ +} + +LLSplashScreenSDL::~LLSplashScreenSDL() +{ +} + +void LLSplashScreenSDL::showImpl() +{ +} + +void LLSplashScreenSDL::updateImpl(const std::string& mesg) +{ +} + +void LLSplashScreenSDL::hideImpl() +{ +} + + + +#if LL_GTK +static void response_callback (GtkDialog *dialog, + gint arg1, + gpointer user_data) +{ + gint *response = (gint*)user_data; + *response = arg1; + gtk_widget_destroy(GTK_WIDGET(dialog)); + gtk_main_quit(); +} + +S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type) +{ + S32 rtn = OSBTN_CANCEL; + + if(gWindowImplementation != NULL) + gWindowImplementation->beforeDialog(); + + if (LLWindowSDL::ll_try_gtk_init()) + { + GtkWidget *win = NULL; + + LL_INFOS() << "Creating a dialog because we're in windowed mode and GTK is happy." << LL_ENDL; + + 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, "%s", + text.c_str()); + +# 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 (gWindowImplementation && + gWindowImplementation->mSDL_XWindowID != None) + { + gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin + GdkWindow *gdkwin = gdk_window_foreign_new(gWindowImplementation->mSDL_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.empty()) + gtk_window_set_title(GTK_WINDOW(win), caption.c_str()); + + gint response = GTK_RESPONSE_NONE; + g_signal_connect (win, + "response", + G_CALLBACK (response_callback), + &response); + + // we should be able to use 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(); + + //LL_INFOS() << "response: " << response << LL_ENDL; + 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 + { + LL_INFOS() << "MSGBOX: " << caption << ": " << text << LL_ENDL; + LL_INFOS() << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << LL_ENDL; + 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); +} + + +/* + Make the raw keyboard data available - used to poke through to LLQtWebKit so + that Qt/Webkit has access to the virtual keycodes etc. that it needs +*/ +LLSD LLWindowSDL::getNativeKeyData() +{ + LLSD result = LLSD::emptyMap(); + + U32 modifiers = 0; // pretend-native modifiers... oh what a tangled web we weave! + + // we go through so many levels of device abstraction that I can't really guess + // what a plugin under GDK under Qt under SL under SDL under X11 considers + // a 'native' modifier mask. this has been sort of reverse-engineered... they *appear* + // to match GDK consts, but that may be co-incidence. + modifiers |= (mKeyModifiers & KMOD_LSHIFT) ? 0x0001 : 0; + modifiers |= (mKeyModifiers & KMOD_RSHIFT) ? 0x0001 : 0;// munge these into the same shift + modifiers |= (mKeyModifiers & KMOD_CAPS) ? 0x0002 : 0; + modifiers |= (mKeyModifiers & KMOD_LCTRL) ? 0x0004 : 0; + modifiers |= (mKeyModifiers & KMOD_RCTRL) ? 0x0004 : 0;// munge these into the same ctrl + modifiers |= (mKeyModifiers & KMOD_LALT) ? 0x0008 : 0;// untested + modifiers |= (mKeyModifiers & KMOD_RALT) ? 0x0008 : 0;// untested + // *todo: test ALTs - I don't have a case for testing these. Do you? + // *todo: NUM? - I don't care enough right now (and it's not a GDK modifier). + + result["scan_code"] = (S32)mKeyScanCode; + result["virtual_key"] = (S32)mKeyVirtualKey; + result["modifiers"] = (S32)modifiers; + + return result; +} + + +bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) +{ + bool rtn = false; + + beforeDialog(); + + if (ll_try_gtk_init()) + { + 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 (mSDL_XWindowID != None) + { + gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin + GdkWindow *gdkwin = gdk_window_foreign_new(mSDL_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.pixel = 0; + 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 std::string& text, const std::string& caption, U32 type) +{ + LL_INFOS() << "MSGBOX: " << caption << ": " << text << LL_ENDL; + return 0; +} + +bool LLWindowSDL::dialogColorPicker( F32 *r, F32 *g, F32 *b) +{ + return (false); +} +#endif // LL_GTK + +#if LL_LINUX +// extracted from spawnWebBrowser for clarity and to eliminate +// compiler confusion regarding close(int fd) vs. LLWindow::close() +void exec_cmd(const std::string& cmd, const std::string& arg) +{ + char* const argv[] = {(char*)cmd.c_str(), (char*)arg.c_str(), NULL}; + fflush(NULL); + pid_t pid = fork(); + if (pid == 0) + { // child + // disconnect from stdin/stdout/stderr, or child will + // keep our output pipe undesirably alive if it outlives us. + close(0); + close(1); + close(2); + // end ourself by running the command + execv(cmd.c_str(), argv); /* Flawfinder: ignore */ + // if execv returns at all, there was a problem. + LL_WARNS() << "execv failure when trying to start " << cmd << LL_ENDL; + _exit(1); // _exit because we don't want atexit() clean-up! + } else { + if (pid > 0) + { + // parent - wait for child to die + int childExitStatus; + waitpid(pid, &childExitStatus, 0); + } else { + LL_WARNS() << "fork failure." << LL_ENDL; + } + } +} +#endif + +// Open a URL with the user's default web browser. +// Must begin with protocol identifier. +void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) +{ + bool found = false; + S32 i; + for (i = 0; i < gURLProtocolWhitelistCount; i++) + { + if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos) + { + found = true; + break; + } + } + + if (!found) + { + LL_WARNS() << "spawn_web_browser called for url with protocol not on whitelist: " << escaped_url << LL_ENDL; + return; + } + + LL_INFOS() << "spawn_web_browser: " << escaped_url << LL_ENDL; + +#if LL_LINUX +# if LL_X11 + if (mSDL_Display) + { + maybe_lock_display(); + // Just in case - before forking. + XSync(mSDL_Display, False); + maybe_unlock_display(); + } +# endif // LL_X11 + + std::string cmd, arg; + cmd = gDirUtilp->getAppRODataDir(); + cmd += gDirUtilp->getDirDelimiter(); + cmd += "etc"; + cmd += gDirUtilp->getDirDelimiter(); + cmd += "launch_url.sh"; + arg = escaped_url; + exec_cmd(cmd, arg); +#endif // LL_LINUX + + LL_INFOS() << "spawn_web_browser returning." << LL_ENDL; +} + + +void *LLWindowSDL::getPlatformWindow() +{ +#if LL_GTK && LL_LLMOZLIB_ENABLED + if (LLWindowSDL::ll_try_gtk_init()) + { + maybe_lock_display(); + + GtkWidget *owin = gtk_window_new(GTK_WINDOW_POPUP); + // Why a layout widget? A MozContainer would be ideal, but + // it involves exposing Mozilla headers to mozlib-using apps. + // A layout widget with a GtkWindow parent has the desired + // properties of being plain GTK, having a window, and being + // derived from a GtkContainer. + GtkWidget *rtnw = gtk_layout_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(owin), rtnw); + gtk_widget_realize(rtnw); + GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(rtnw), GTK_NO_WINDOW); + + maybe_unlock_display(); + + return rtnw; + } +#endif // LL_GTK && LL_LLMOZLIB_ENABLED + // Unixoid mozilla really needs GTK. + return NULL; +} + +void LLWindowSDL::bringToFront() +{ + // This is currently used when we are 'launched' to a specific + // map position externally. + LL_INFOS() << "bringToFront" << LL_ENDL; +#if LL_X11 + if (mSDL_Display && !mFullscreen) + { + maybe_lock_display(); + XRaiseWindow(mSDL_Display, mSDL_XWindowID); + XSync(mSDL_Display, False); + maybe_unlock_display(); + } +#endif // LL_X11 +} + +//static +std::vector<std::string> LLWindowSDL::getDynamicFallbackFontList() +{ + // Use libfontconfig to find us a nice ordered list of fallback fonts + // specific to this system. + std::string final_fallback("/usr/share/fonts/truetype/kochi/kochi-gothic.ttf"); + const int max_font_count_cutoff = 40; // fonts are expensive in the current system, don't enumerate an arbitrary number of them + // Our 'ideal' font properties which define the sorting results. + // slant=0 means Roman, index=0 means the first face in a font file + // (the one we actually use), weight=80 means medium weight, + // spacing=0 means proportional spacing. + std::string sort_order("slant=0:index=0:weight=80:spacing=0"); + // elide_unicode_coverage removes fonts from the list whose unicode + // range is covered by fonts earlier in the list. This usually + // removes ~90% of the fonts as redundant (which is great because + // the font list can be huge), but might unnecessarily reduce the + // renderable range if for some reason our FreeType actually fails + // to use some of the fonts we want it to. + const bool elide_unicode_coverage = true; + std::vector<std::string> rtns; + FcFontSet *fs = NULL; + FcPattern *sortpat = NULL; + + LL_INFOS() << "Getting system font list from FontConfig..." << LL_ENDL; + + // If the user has a system-wide language preference, then favor + // fonts from that language group. This doesn't affect the types + // of languages that can be displayed, but ensures that their + // preferred language is rendered from a single consistent font where + // possible. + FL_Locale *locale = NULL; + FL_Success success = FL_FindLocale(&locale, FL_MESSAGES); + if (success != 0) + { + if (success >= 2 && locale->lang) // confident! + { + LL_INFOS("AppInit") << "Language " << locale->lang << LL_ENDL; + LL_INFOS("AppInit") << "Location " << locale->country << LL_ENDL; + LL_INFOS("AppInit") << "Variant " << locale->variant << LL_ENDL; + + LL_INFOS() << "Preferring fonts of language: " + << locale->lang + << LL_ENDL; + sort_order = "lang=" + std::string(locale->lang) + ":" + + sort_order; + } + } + FL_FreeLocale(&locale); + + if (!FcInit()) + { + LL_WARNS() << "FontConfig failed to initialize." << LL_ENDL; + rtns.push_back(final_fallback); + return rtns; + } + + sortpat = FcNameParse((FcChar8*) sort_order.c_str()); + if (sortpat) + { + // Sort the list of system fonts from most-to-least-desirable. + FcResult result; + fs = FcFontSort(NULL, sortpat, elide_unicode_coverage, + NULL, &result); + FcPatternDestroy(sortpat); + } + + int found_font_count = 0; + if (fs) + { + // Get the full pathnames to the fonts, where available, + // which is what we really want. + found_font_count = fs->nfont; + for (int i=0; i<fs->nfont; ++i) + { + FcChar8 *filename; + if (FcResultMatch == FcPatternGetString(fs->fonts[i], + FC_FILE, 0, + &filename) + && filename) + { + rtns.push_back(std::string((const char*)filename)); + if (rtns.size() >= max_font_count_cutoff) + break; // hit limit + } + } + FcFontSetDestroy (fs); + } + + LL_DEBUGS() << "Using font list: " << LL_ENDL; + for (std::vector<std::string>::iterator it = rtns.begin(); + it != rtns.end(); + ++it) + { + LL_DEBUGS() << " file: " << *it << LL_ENDL; + } + LL_INFOS() << "Using " << rtns.size() << "/" << found_font_count << " system fonts." << LL_ENDL; + + rtns.push_back(final_fallback); + return rtns; +} + +#endif // LL_SDL diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index 7f12464a37..196ad2986d 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -1,231 +1,231 @@ -/**
- * @file llwindowsdl.h
- * @brief SDL implementation of LLWindow class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLWINDOWSDL_H
-#define LL_LLWINDOWSDL_H
-
-// Simple Directmedia Layer (http://libsdl.org/) implementation of LLWindow class
-
-#include "llwindow.h"
-#include "lltimer.h"
-
-#include "SDL/SDL.h"
-#include "SDL/SDL_endian.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.
-#include "fix_macros.h"
-#undef verify
-#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*/ void minimize();
- /*virtual*/ void restore();
- /*virtual*/ bool getFullscreen();
- /*virtual*/ bool getPosition(LLCoordScreen *position);
- /*virtual*/ bool getSize(LLCoordScreen *size);
- /*virtual*/ bool getSize(LLCoordWindow *size);
- /*virtual*/ bool setPosition(LLCoordScreen position);
- /*virtual*/ bool setSizeImpl(LLCoordScreen size);
- /*virtual*/ bool setSizeImpl(LLCoordWindow size);
- /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL);
- /*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 updateCursor();
- /*virtual*/ void captureMouse();
- /*virtual*/ void releaseMouse();
- /*virtual*/ void setMouseClipping( bool b );
- /*virtual*/ void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true);
-
- /*virtual*/ bool isClipboardTextAvailable();
- /*virtual*/ bool pasteTextFromClipboard(LLWString &dst);
- /*virtual*/ bool copyTextToClipboard(const LLWString & src);
-
- /*virtual*/ bool isPrimaryTextAvailable();
- /*virtual*/ bool pasteTextFromPrimary(LLWString &dst);
- /*virtual*/ bool copyTextToPrimary(const LLWString & src);
-
- /*virtual*/ void flashIcon(F32 seconds);
- /*virtual*/ F32 getGamma();
- /*virtual*/ bool setGamma(const F32 gamma); // Set the gamma
- /*virtual*/ U32 getFSAASamples();
- /*virtual*/ void setFSAASamples(const U32 samples);
- /*virtual*/ bool restoreGamma(); // Restore original gamma table (before updating gamma)
- /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
- /*virtual*/ void processMiscNativeEvents();
- /*virtual*/ void gatherInput();
- /*virtual*/ void swapBuffers();
- /*virtual*/ void restoreGLContext() {};
-
- /*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 dialogColorPicker(F32 *r, F32 *g, F32 *b);
-
- /*virtual*/ void *getPlatformWindow();
- /*virtual*/ void bringToFront();
-
- /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async);
-
- static std::vector<std::string> getDynamicFallbackFontList();
-
- // Not great that these are public, but they have to be accessible
- // by non-class code and it's better than making them global.
-#if LL_X11
- Window mSDL_XWindowID;
- Display *mSDL_Display;
-#endif
- void (*Lock_Display)(void);
- void (*Unlock_Display)(void);
-
-#if LL_GTK
- // Lazily initialize and check the runtime GTK version for goodness.
- static bool ll_try_gtk_init(void);
-#endif // LL_GTK
-
-#if LL_X11
- static Window get_SDL_XWindowID(void);
- static Display* get_SDL_Display(void);
-#endif // LL_X11
-
-protected:
- LLWindowSDL(LLWindowCallbacks* callbacks,
- const std::string& 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, U32 fsaa_samples);
- ~LLWindowSDL();
-
- /*virtual*/ bool isValid();
- /*virtual*/ LLSD getNativeKeyData();
-
- void initCursors();
- void quitCursors();
- 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();
-
- 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 std::string& text, const std::string& caption, U32 type);
- void fixWindowSize(void);
- U32 SDLCheckGrabbyKeys(SDLKey keysym, bool gain);
- bool SDLReallyCaptureInput(bool capture);
-
- //
- // Platform specific variables
- //
- U32 mGrabbyKeyFlags;
- int mReallyCapturedCount;
- SDL_Surface * mWindow;
- std::string mWindowTitle;
- double mOriginalAspectRatio;
- bool mNeedsResize; // Constructor figured out the window is too big, it needs a resize.
- LLCoordScreen mNeedsResizeSize;
- F32 mOverrideAspectRatio;
- F32 mGamma;
- U32 mFSAASamples;
-
- 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;
-
-private:
-#if LL_X11
- void x11_set_urgent(bool urgent);
- bool mFlashing;
- LLTimer mFlashTimer;
-#endif //LL_X11
-
- U32 mKeyScanCode;
- U32 mKeyVirtualKey;
- SDLMod mKeyModifiers;
-};
-
-
-class LLSplashScreenSDL : public LLSplashScreen
-{
-public:
- LLSplashScreenSDL();
- virtual ~LLSplashScreenSDL();
-
- /*virtual*/ void showImpl();
- /*virtual*/ void updateImpl(const std::string& mesg);
- /*virtual*/ void hideImpl();
-};
-
-S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type);
-
-#endif //LL_LLWINDOWSDL_H
+/** + * @file llwindowsdl.h + * @brief SDL implementation of LLWindow class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLWINDOWSDL_H +#define LL_LLWINDOWSDL_H + +// Simple Directmedia Layer (http://libsdl.org/) implementation of LLWindow class + +#include "llwindow.h" +#include "lltimer.h" + +#include "SDL/SDL.h" +#include "SDL/SDL_endian.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. +#include "fix_macros.h" +#undef verify +#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*/ void minimize(); + /*virtual*/ void restore(); + /*virtual*/ bool getFullscreen(); + /*virtual*/ bool getPosition(LLCoordScreen *position); + /*virtual*/ bool getSize(LLCoordScreen *size); + /*virtual*/ bool getSize(LLCoordWindow *size); + /*virtual*/ bool setPosition(LLCoordScreen position); + /*virtual*/ bool setSizeImpl(LLCoordScreen size); + /*virtual*/ bool setSizeImpl(LLCoordWindow size); + /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool disable_vsync, const LLCoordScreen * const posp = NULL); + /*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 updateCursor(); + /*virtual*/ void captureMouse(); + /*virtual*/ void releaseMouse(); + /*virtual*/ void setMouseClipping( bool b ); + /*virtual*/ void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true); + + /*virtual*/ bool isClipboardTextAvailable(); + /*virtual*/ bool pasteTextFromClipboard(LLWString &dst); + /*virtual*/ bool copyTextToClipboard(const LLWString & src); + + /*virtual*/ bool isPrimaryTextAvailable(); + /*virtual*/ bool pasteTextFromPrimary(LLWString &dst); + /*virtual*/ bool copyTextToPrimary(const LLWString & src); + + /*virtual*/ void flashIcon(F32 seconds); + /*virtual*/ F32 getGamma(); + /*virtual*/ bool setGamma(const F32 gamma); // Set the gamma + /*virtual*/ U32 getFSAASamples(); + /*virtual*/ void setFSAASamples(const U32 samples); + /*virtual*/ bool restoreGamma(); // Restore original gamma table (before updating gamma) + /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; } + /*virtual*/ void processMiscNativeEvents(); + /*virtual*/ void gatherInput(); + /*virtual*/ void swapBuffers(); + /*virtual*/ void restoreGLContext() {}; + + /*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 dialogColorPicker(F32 *r, F32 *g, F32 *b); + + /*virtual*/ void *getPlatformWindow(); + /*virtual*/ void bringToFront(); + + /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + + static std::vector<std::string> getDynamicFallbackFontList(); + + // Not great that these are public, but they have to be accessible + // by non-class code and it's better than making them global. +#if LL_X11 + Window mSDL_XWindowID; + Display *mSDL_Display; +#endif + void (*Lock_Display)(void); + void (*Unlock_Display)(void); + +#if LL_GTK + // Lazily initialize and check the runtime GTK version for goodness. + static bool ll_try_gtk_init(void); +#endif // LL_GTK + +#if LL_X11 + static Window get_SDL_XWindowID(void); + static Display* get_SDL_Display(void); +#endif // LL_X11 + +protected: + LLWindowSDL(LLWindowCallbacks* callbacks, + const std::string& 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, U32 fsaa_samples); + ~LLWindowSDL(); + + /*virtual*/ bool isValid(); + /*virtual*/ LLSD getNativeKeyData(); + + void initCursors(); + void quitCursors(); + 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(); + + 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 std::string& text, const std::string& caption, U32 type); + void fixWindowSize(void); + U32 SDLCheckGrabbyKeys(SDLKey keysym, bool gain); + bool SDLReallyCaptureInput(bool capture); + + // + // Platform specific variables + // + U32 mGrabbyKeyFlags; + int mReallyCapturedCount; + SDL_Surface * mWindow; + std::string mWindowTitle; + double mOriginalAspectRatio; + bool mNeedsResize; // Constructor figured out the window is too big, it needs a resize. + LLCoordScreen mNeedsResizeSize; + F32 mOverrideAspectRatio; + F32 mGamma; + U32 mFSAASamples; + + 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; + +private: +#if LL_X11 + void x11_set_urgent(bool urgent); + bool mFlashing; + LLTimer mFlashTimer; +#endif //LL_X11 + + U32 mKeyScanCode; + U32 mKeyVirtualKey; + SDLMod mKeyModifiers; +}; + + +class LLSplashScreenSDL : public LLSplashScreen +{ +public: + LLSplashScreenSDL(); + virtual ~LLSplashScreenSDL(); + + /*virtual*/ void showImpl(); + /*virtual*/ void updateImpl(const std::string& mesg); + /*virtual*/ void hideImpl(); +}; + +S32 OSMessageBoxSDL(const std::string& text, const std::string& caption, U32 type); + +#endif //LL_LLWINDOWSDL_H diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index dad70aea51..7ea53816cf 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -1,5000 +1,5000 @@ -/**
- * @file llwindowwin32.cpp
- * @brief Platform-dependent implementation of llwindow
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#if LL_WINDOWS && !LL_MESA_HEADLESS
-
-#include "llwindowwin32.h"
-
-// LLWindow library includes
-#include "llkeyboardwin32.h"
-#include "lldragdropwin32.h"
-#include "llpreeditor.h"
-#include "llwindowcallbacks.h"
-
-// Linden library includes
-#include "llerror.h"
-#include "llexception.h"
-#include "llfasttimer.h"
-#include "llgl.h"
-#include "llstring.h"
-#include "lldir.h"
-#include "llsdutil.h"
-#include "llglslshader.h"
-#include "llthreadsafequeue.h"
-#include "stringize.h"
-#include "llframetimer.h"
-
-// System includes
-#include <commdlg.h>
-#include <WinUser.h>
-#include <mapi.h>
-#include <process.h> // for _spawn
-#include <shellapi.h>
-#include <fstream>
-#include <Imm.h>
-#include <iomanip>
-#include <future>
-#include <sstream>
-#include <utility> // std::pair
-
-#include <d3d9.h>
-#include <dxgi1_4.h>
-#include <timeapi.h>
-
-// Require DirectInput version 8
-#define DIRECTINPUT_VERSION 0x0800
-
-#include <dinput.h>
-#include <Dbt.h.>
-#include <InitGuid.h> // needed for llurlentry test to build on some systems
-#pragma comment(lib, "dxguid.lib") // needed for llurlentry test to build on some systems
-#pragma comment(lib, "dinput8")
-
-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;
-
-#ifndef WM_DPICHANGED
-#define WM_DPICHANGED 0x02E0
-#endif
-
-#ifndef USER_DEFAULT_SCREEN_DPI
-#define USER_DEFAULT_SCREEN_DPI 96 // Win7
-#endif
-
-// Claim a couple unused GetMessage() message IDs
-const UINT WM_DUMMY_(WM_USER + 0x0017);
-const UINT WM_POST_FUNCTION_(WM_USER + 0x0018);
-
-extern bool gDebugWindowProc;
-
-static std::thread::id sWindowThreadId;
-static std::thread::id sMainThreadId;
-
-#if 1 // flip to zero to enable assertions for functions being called from wrong thread
-#define ASSERT_MAIN_THREAD()
-#define ASSERT_WINDOW_THREAD()
-#else
-#define ASSERT_MAIN_THREAD() llassert(LLThread::currentID() == sMainThreadId)
-#define ASSERT_WINDOW_THREAD() llassert(LLThread::currentID() == sWindowThreadId)
-#endif
-
-
-LPWSTR gIconResource = IDI_APPLICATION;
-LPDIRECTINPUT8 gDirectInput8;
-
-LLW32MsgCallback gAsyncMsgCallback = NULL;
-
-#ifndef DPI_ENUMS_DECLARED
-
-typedef enum PROCESS_DPI_AWARENESS {
- PROCESS_DPI_UNAWARE = 0,
- PROCESS_SYSTEM_DPI_AWARE = 1,
- PROCESS_PER_MONITOR_DPI_AWARE = 2
-} PROCESS_DPI_AWARENESS;
-
-typedef enum MONITOR_DPI_TYPE {
- MDT_EFFECTIVE_DPI = 0,
- MDT_ANGULAR_DPI = 1,
- MDT_RAW_DPI = 2,
- MDT_DEFAULT = MDT_EFFECTIVE_DPI
-} MONITOR_DPI_TYPE;
-
-#endif
-
-typedef HRESULT(STDAPICALLTYPE *SetProcessDpiAwarenessType)(_In_ PROCESS_DPI_AWARENESS value);
-
-typedef HRESULT(STDAPICALLTYPE *GetProcessDpiAwarenessType)(
- _In_ HANDLE hprocess,
- _Out_ PROCESS_DPI_AWARENESS *value);
-
-typedef HRESULT(STDAPICALLTYPE *GetDpiForMonitorType)(
- _In_ HMONITOR hmonitor,
- _In_ MONITOR_DPI_TYPE dpiType,
- _Out_ UINT *dpiX,
- _Out_ UINT *dpiY);
-
-//
-// LLWindowWin32
-//
-
-void show_window_creation_error(const std::string& title)
-{
- LL_WARNS("Window") << title << LL_ENDL;
-}
-
-HGLRC SafeCreateContext(HDC &hdc)
-{
- __try
- {
- return wglCreateContext(hdc);
- }
- __except(EXCEPTION_EXECUTE_HANDLER)
- {
- return NULL;
- }
-}
-
-GLuint SafeChoosePixelFormat(HDC &hdc, const PIXELFORMATDESCRIPTOR *ppfd)
-{
- __try
- {
- return ChoosePixelFormat(hdc, ppfd);
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- // convert to C++ styled exception
- // C exception don't allow classes, so it's a regular char array
- char integer_string[32];
- sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
- throw std::exception(integer_string);
- }
-}
-
-//static
-bool LLWindowWin32::sIsClassRegistered = false;
-
-bool LLWindowWin32::sLanguageTextInputAllowed = true;
-bool LLWindowWin32::sWinIMEOpened = false;
-HKL LLWindowWin32::sWinInputLocale = 0;
-DWORD LLWindowWin32::sWinIMEConversionMode = IME_CMODE_NATIVE;
-DWORD LLWindowWin32::sWinIMESentenceMode = IME_SMODE_AUTOMATIC;
-LLCoordWindow LLWindowWin32::sWinIMEWindowPosition(-1,-1);
-
-// The following class LLWinImm delegates Windows IMM APIs.
-// It was originally introduced to support US Windows XP, on which we needed
-// to dynamically load IMM32.DLL and use GetProcAddress to resolve its entry
-// points. Now that that's moot, we retain this wrapper only for hooks for
-// metrics.
-
-class LLWinImm
-{
-public:
- static bool isAvailable() { return true; }
-
-public:
- // Wrappers for IMM API.
- static bool isIME(HKL hkl);
- static HIMC getContext(HWND hwnd);
- static bool releaseContext(HWND hwnd, HIMC himc);
- static bool getOpenStatus(HIMC himc);
- static bool setOpenStatus(HIMC himc, bool status);
- static bool getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence);
- static bool setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence);
- static bool getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form);
- static bool setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form);
- static LONG getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length);
- static bool setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength);
- static bool setCompositionFont(HIMC himc, LPLOGFONTW logfont);
- static bool setCandidateWindow(HIMC himc, LPCANDIDATEFORM candidate_form);
- static bool notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value);
-};
-
-// static
-bool LLWinImm::isIME(HKL hkl)
-{
- return ImmIsIME(hkl);
-}
-
-// static
-HIMC LLWinImm::getContext(HWND hwnd)
-{
- return ImmGetContext(hwnd);
-}
-
-//static
-bool LLWinImm::releaseContext(HWND hwnd, HIMC himc)
-{
- return ImmReleaseContext(hwnd, himc);
-}
-
-// static
-bool LLWinImm::getOpenStatus(HIMC himc)
-{
- return ImmGetOpenStatus(himc);
-}
-
-// static
-bool LLWinImm::setOpenStatus(HIMC himc, bool status)
-{
- return ImmSetOpenStatus(himc, status);
-}
-
-// static
-bool LLWinImm::getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence)
-{
- return ImmGetConversionStatus(himc, conversion, sentence);
-}
-
-// static
-bool LLWinImm::setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence)
-{
- return ImmSetConversionStatus(himc, conversion, sentence);
-}
-
-// static
-bool LLWinImm::getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)
-{
- return ImmGetCompositionWindow(himc, form);
-}
-
-// static
-bool LLWinImm::setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)
-{
- return ImmSetCompositionWindow(himc, form);
-}
-
-
-// static
-LONG LLWinImm::getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length)
-{
- return ImmGetCompositionString(himc, index, data, length);
-}
-
-
-// static
-bool LLWinImm::setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength)
-{
- return ImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength);
-}
-
-// static
-bool LLWinImm::setCompositionFont(HIMC himc, LPLOGFONTW pFont)
-{
- return ImmSetCompositionFont(himc, pFont);
-}
-
-// static
-bool LLWinImm::setCandidateWindow(HIMC himc, LPCANDIDATEFORM form)
-{
- return ImmSetCandidateWindow(himc, form);
-}
-
-// static
-bool LLWinImm::notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value)
-{
- return ImmNotifyIME(himc, action, index, value);
-}
-
-
-
-class LLMonitorInfo
-{
-public:
-
- std::vector<std::string> getResolutionsList() { return mResList; }
-
- LLMonitorInfo()
- {
- EnumDisplayMonitors(0, 0, MonitorEnum, (LPARAM)this);
- }
-
-private:
-
- static BOOL CALLBACK MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData)
- {
- int monitor_width = lprcMonitor->right - lprcMonitor->left;
- int monitor_height = lprcMonitor->bottom - lprcMonitor->top;
-
- std::ostringstream sstream;
- sstream << monitor_width << "x" << monitor_height;;
- std::string res = sstream.str();
-
- LLMonitorInfo* pThis = reinterpret_cast<LLMonitorInfo*>(pData);
- pThis->mResList.push_back(res);
-
- return TRUE;
- }
-
- std::vector<std::string> mResList;
-};
-
-static LLMonitorInfo sMonitorInfo;
-
-
-// Thread that owns the Window Handle
-// This whole struct is private to LLWindowWin32, which needs to mess with its
-// members, which is why it's a struct rather than a class. In effect, we make
-// the containing class a friend.
-struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool
-{
- static const int MAX_QUEUE_SIZE = 2048;
-
- LLThreadSafeQueue<MSG> mMessageQueue;
-
- LLWindowWin32Thread();
-
- void run() override;
- void close() override;
-
- // closes queue, wakes thread, waits until thread closes
- void wakeAndDestroy();
-
- void glReady()
- {
- mGLReady = true;
- }
-
- // initialzie DXGI adapter (for querying available VRAM)
- void initDX();
-
- // initialize D3D (if DXGI cannot be used)
- void initD3D();
-
- //clean up DXGI/D3D resources
- void cleanupDX();
-
- /// called by main thread to post work to this window thread
- template <typename CALLABLE>
- void post(CALLABLE&& func)
- {
- // Ignore bool return. Shutdown timing is tricky: the main thread can
- // end up trying to post a cursor position after having closed the
- // WorkQueue.
- getQueue().post(std::forward<CALLABLE>(func));
- }
-
- /**
- * Like post(), Post() is a way of conveying a single work item to this
- * thread. Its virtue is that it will definitely be executed "soon" rather
- * than potentially waiting for the next frame: it uses PostMessage() to
- * break us out of the window thread's blocked GetMessage() call. It's
- * more expensive, though, not only from the Windows API latency of
- * PostMessage() and GetMessage(), but also because it involves heap
- * allocation and release.
- *
- * Require HWND from caller, even though we store an HWND locally.
- * Otherwise, if our mWindowHandle was accessed from both threads, we'd
- * have to protect it with a mutex.
- */
- template <typename CALLABLE>
- void Post(HWND windowHandle, CALLABLE&& func)
- {
- // Move func to the heap. If we knew FuncType could fit into LPARAM,
- // we could simply instantiate FuncType and pass it by value. But
- // since we don't, we must put that on the heap as well as the
- // internal heap allocation it likely requires to store func.
- auto ptr = new FuncType(std::move(func));
- WPARAM wparam{ 0xF1C };
- LL_DEBUGS("Window") << "PostMessage(" << std::hex << windowHandle
- << ", " << WM_POST_FUNCTION_
- << ", " << wparam << std::dec << LL_ENDL;
- PostMessage(windowHandle, WM_POST_FUNCTION_, wparam, LPARAM(ptr));
- }
-
- using FuncType = std::function<void()>;
- // call GetMessage() and pull enqueue messages for later processing
- void gatherInput();
- HWND mWindowHandleThrd = NULL;
- HDC mhDCThrd = 0;
-
- // *HACK: Attempt to prevent startup crashes by deferring memory accounting
- // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18
- bool mGLReady = false;
-
- U32 mMaxVRAM = 0; // maximum amount of vram to allow in the "budget", or 0 for no maximum (see updateVRAMUsage)
-
- IDXGIAdapter3* mDXGIAdapter = nullptr;
- LPDIRECT3D9 mD3D = nullptr;
- LPDIRECT3DDEVICE9 mD3DDevice = nullptr;
-};
-
-
-LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& name, S32 x, S32 y, S32 width,
- S32 height, U32 flags,
- bool fullscreen, bool clearBg,
- bool enable_vsync, bool use_gl,
- bool ignore_pixel_depth,
- U32 fsaa_samples,
- U32 max_cores,
- U32 max_vram,
- F32 max_gl_version)
- :
- LLWindow(callbacks, fullscreen, flags),
- mMaxGLVersion(max_gl_version),
- mMaxCores(max_cores)
-{
- sMainThreadId = LLThread::currentID();
- mWindowThread = new LLWindowWin32Thread();
- mWindowThread->mMaxVRAM = max_vram;
-
- //MAINT-516 -- force a load of opengl32.dll just in case windows went sideways
- LoadLibrary(L"opengl32.dll");
-
-
- if (mMaxCores != 0)
- {
- HANDLE hProcess = GetCurrentProcess();
- mMaxCores = llmin(mMaxCores, (U32) 64);
- DWORD_PTR mask = 0;
-
- for (int i = 0; i < mMaxCores; ++i)
- {
- mask |= ((DWORD_PTR) 1) << i;
- }
-
- SetProcessAffinityMask(hProcess, mask);
- }
-
-#if 0 // this is probably a bad idea, but keep it in your back pocket if you see what looks like
- // process deprioritization during profiles
- // force high thread priority
- HANDLE hProcess = GetCurrentProcess();
-
- if (hProcess)
- {
- int priority = GetPriorityClass(hProcess);
- if (priority < REALTIME_PRIORITY_CLASS)
- {
- if (SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS))
- {
- LL_INFOS() << "Set process priority to REALTIME_PRIORITY_CLASS" << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "Failed to set process priority: " << std::hex << GetLastError() << LL_ENDL;
- }
- }
- }
-#endif
-
-#if 0 // this is also probably a bad idea, but keep it in your back pocket for getting main thread off of background thread cores (see also LLThread::threadRun)
- HANDLE hThread = GetCurrentThread();
-
- SYSTEM_INFO sysInfo;
-
- GetSystemInfo(&sysInfo);
- U32 core_count = sysInfo.dwNumberOfProcessors;
-
- if (max_cores != 0)
- {
- core_count = llmin(core_count, max_cores);
- }
-
- if (hThread)
- {
- int priority = GetThreadPriority(hThread);
-
- if (priority < THREAD_PRIORITY_TIME_CRITICAL)
- {
- if (SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL))
- {
- LL_INFOS() << "Set thread priority to THREAD_PRIORITY_TIME_CRITICAL" << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "Failed to set thread priority: " << std::hex << GetLastError() << LL_ENDL;
- }
-
- // tell main thread to prefer core 0
- SetThreadIdealProcessor(hThread, 0);
- }
- }
-#endif
-
-
- mFSAASamples = fsaa_samples;
- mIconResource = gIconResource;
- mOverrideAspectRatio = 0.f;
- mNativeAspectRatio = 0.f;
- mInputProcessingPaused = false;
- mPreeditor = NULL;
- mKeyCharCode = 0;
- mKeyScanCode = 0;
- mKeyVirtualKey = 0;
- mhDC = NULL;
- mhRC = NULL;
- memset(mCurrentGammaRamp, 0, sizeof(mCurrentGammaRamp));
- memset(mPrevGammaRamp, 0, sizeof(mPrevGammaRamp));
- mCustomGammaSet = false;
- mWindowHandle = NULL;
-
- mRect = {0, 0, 0, 0};
- mClientRect = {0, 0, 0, 0};
-
- if (!SystemParametersInfo(SPI_GETMOUSEVANISH, 0, &mMouseVanish, 0))
- {
- mMouseVanish = true;
- }
-
- // Initialize the keyboard
- gKeyboard = new LLKeyboardWin32();
- gKeyboard->setCallbacks(callbacks);
-
- // Initialize the Drag and Drop functionality
- mDragDrop = new LLDragDropWin32;
-
- // Initialize (boot strap) the Language text input management,
- // based on the system's (user's) default settings.
- allowLanguageTextInput(mPreeditor, false);
-
- WNDCLASS wc;
- RECT window_rect;
-
- // Set the window title
- if (title.empty())
- {
- mWindowTitle = new WCHAR[50];
- wsprintf(mWindowTitle, L"OpenGL Window");
- }
- else
- {
- mWindowTitle = new WCHAR[256]; // Assume title length < 255 chars.
- mbstowcs(mWindowTitle, title.c_str(), 255);
- mWindowTitle[255] = 0;
- }
-
- // Set the window class name
- if (name.empty())
- {
- mWindowClassName = new WCHAR[50];
- wsprintf(mWindowClassName, L"OpenGL Window");
- }
- else
- {
- mWindowClassName = new WCHAR[256]; // Assume title length < 255 chars.
- mbstowcs(mWindowClassName, name.c_str(), 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);
-
- // Init Direct Input - needed for joystick / Spacemouse
-
- LPDIRECTINPUT8 di8_interface;
- HRESULT status = DirectInput8Create(
- mhInstance, // HINSTANCE hinst,
- DIRECTINPUT_VERSION, // DWORD dwVersion,
- IID_IDirectInput8, // REFIID riidltf,
- (LPVOID*)&di8_interface, // LPVOID * ppvOut,
- NULL // LPUNKNOWN punkOuter
- );
- if (status == DI_OK)
- {
- gDirectInput8 = di8_interface;
- }
-
- 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(mCallbacks->translateString("MBRegClassFailed"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return;
- }
- sIsClassRegistered = true;
- }
-
- //-----------------------------------------------------------------------
- // Get the current refresh rate
- //-----------------------------------------------------------------------
-
- DEVMODE dev_mode;
- ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
- dev_mode.dmSize = sizeof(DEVMODE);
- 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;
- }
- mRefreshRate = current_refresh;
- //-----------------------------------------------------------------------
- // 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)
- {
- LL_WARNS("Window") << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << LL_ENDL;
- //success = false;
-
- if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
- {
- success = false;
- }
- else
- {
- if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
- {
- LL_WARNS("Window") << "Current BBP is OK falling back to that" << LL_ENDL;
- window_rect.right=width=dev_mode.dmPelsWidth;
- window_rect.bottom=height=dev_mode.dmPelsHeight;
- success = true;
- }
- else
- {
- LL_WARNS("Window") << "Current BBP is BAD" << LL_ENDL;
- 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;
-
- LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth
- << "x" << dev_mode.dmPelsHeight
- << "x" << dev_mode.dmBitsPerPel
- << " @ " << dev_mode.dmDisplayFrequency
- << LL_ENDL;
- }
- else
- {
- mFullscreen = false;
- mFullscreenWidth = -1;
- mFullscreenHeight = -1;
- mFullscreenBits = -1;
- mFullscreenRefresh = -1;
-
- std::map<std::string,std::string> args;
- args["[WIDTH]"] = llformat("%d", width);
- args["[HEIGHT]"] = llformat ("%d", height);
- OSMessageBox(mCallbacks->translateString("MBFullScreenErr", args),
- mCallbacks->translateString("MBError"), OSMB_OK);
- }
- }
-
- // 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 );
- // }
-
- // SL-12971 dual GPU display
- DISPLAY_DEVICEA display_device;
- int display_index = -1;
- DWORD display_flags = 0; // EDD_GET_DEVICE_INTERFACE_NAME ?
- const size_t display_bytes = sizeof(display_device);
-
- do
- {
- if (display_index >= 0)
- {
- // CHAR DeviceName [ 32] Adapter name
- // CHAR DeviceString[128]
- CHAR text[256];
-
- size_t name_len = strlen(display_device.DeviceName );
- size_t desc_len = strlen(display_device.DeviceString);
-
- CHAR *name = name_len ? display_device.DeviceName : "???";
- CHAR *desc = desc_len ? display_device.DeviceString : "???";
-
- sprintf(text, "Display Device %d: %s, %s", display_index, name, desc);
- LL_INFOS("Window") << text << LL_ENDL;
- }
-
- ::ZeroMemory(&display_device,display_bytes);
- display_device.cb = display_bytes;
-
- display_index++;
- } while( EnumDisplayDevicesA(NULL, display_index, &display_device, display_flags ));
-
- LL_INFOS("Window") << "Total Display Devices: " << display_index << LL_ENDL;
-
- //-----------------------------------------------------------------------
- // Create GL drawing context
- //-----------------------------------------------------------------------
- LLCoordScreen windowPos(x,y);
- LLCoordScreen windowSize(window_rect.right - window_rect.left,
- window_rect.bottom - window_rect.top);
- if (!switchContext(mFullscreen, windowSize, enable_vsync, &windowPos))
- {
- return;
- }
-
- //start with arrow cursor
- initCursors();
- setCursor( UI_CURSOR_ARROW );
-
- mRawMouse.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC
- mRawMouse.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE
- mRawMouse.dwFlags = 0; // adds mouse and also ignores legacy mouse messages
- mRawMouse.hwndTarget = 0;
-
- RegisterRawInputDevices(&mRawMouse, 1, sizeof(mRawMouse));
-
- // Initialize (boot strap) the Language text input management,
- // based on the system's (or user's) default settings.
- allowLanguageTextInput(NULL, false);
-}
-
-
-LLWindowWin32::~LLWindowWin32()
-{
- delete mDragDrop;
-
- delete [] mWindowTitle;
- mWindowTitle = NULL;
-
- delete [] mSupportedResolutions;
- mSupportedResolutions = NULL;
-
- delete [] mWindowClassName;
- mWindowClassName = NULL;
-
- delete mWindowThread;
-}
-
-void LLWindowWin32::show()
-{
- LL_DEBUGS("Window") << "Setting window to show" << LL_ENDL;
- ShowWindow(mWindowHandle, SW_SHOW);
- SetForegroundWindow(mWindowHandle);
- SetFocus(mWindowHandle);
-}
-
-void LLWindowWin32::hide()
-{
- setMouseClipping(false);
- ShowWindow(mWindowHandle, SW_HIDE);
-}
-
-//virtual
-void LLWindowWin32::minimize()
-{
- setMouseClipping(false);
- showCursor();
- ShowWindow(mWindowHandle, SW_MINIMIZE);
-}
-
-//virtual
-void LLWindowWin32::restore()
-{
- ShowWindow(mWindowHandle, SW_RESTORE);
- SetForegroundWindow(mWindowHandle);
- SetFocus(mWindowHandle);
-}
-
-// See SL-12170
-// According to callstack "c0000005 Access violation" happened inside __try block,
-// deep in DestroyWindow and crashed viewer, which shouldn't be possible.
-// I tried manually causing this exception and it was caught without issues, so
-// I'm turning off optimizations for this part to be sure code executes as intended
-// (it is a straw, but I have no idea why else __try can get overruled)
-#pragma optimize("", off)
-bool destroy_window_handler(HWND hWnd)
-{
- bool res;
- __try
- {
- res = DestroyWindow(hWnd);
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- res = false;
- }
- return res;
-}
-#pragma optimize("", on)
-
-// close() destroys all OS-specific code associated with a window.
-// Usually called from LLWindowManager::destroyWindow()
-void LLWindowWin32::close()
-{
- LL_DEBUGS("Window") << "Closing LLWindowWin32" << LL_ENDL;
- // Is window is already closed?
- if (!mWindowHandle)
- {
- return;
- }
-
- mDragDrop->reset();
-
-
- // Go back to screen mode written in the registry.
- if (mFullscreen)
- {
- resetDisplayResolution();
- }
-
- // Make sure cursor is visible and we haven't mangled the clipping state.
- showCursor();
- setMouseClipping(false);
- if (gKeyboard)
- {
- gKeyboard->resetKeys();
- }
-
- // Clean up remaining GL state
- if (gGLManager.mInited)
- {
- LL_INFOS("Window") << "Cleaning up GL" << LL_ENDL;
- gGLManager.shutdownGL();
- }
-
- LL_DEBUGS("Window") << "Releasing Context" << LL_ENDL;
- if (mhRC)
- {
- if (!wglMakeCurrent(NULL, NULL))
- {
- LL_WARNS("Window") << "Release of DC and RC failed" << LL_ENDL;
- }
-
- if (!wglDeleteContext(mhRC))
- {
- LL_WARNS("Window") << "Release of rendering context failed" << LL_ENDL;
- }
-
- mhRC = NULL;
- }
-
- // Restore gamma to the system values.
- restoreGamma();
-
- LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL;
-
- mhDC = NULL;
- mWindowHandle = NULL;
-
- mWindowThread->wakeAndDestroy();
-}
-
-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;
-
- mWindowThread->post([=]
- {
- WINDOWPLACEMENT placement;
- placement.length = sizeof(WINDOWPLACEMENT);
-
- if (GetWindowPlacement(mWindowHandle, &placement))
- {
- placement.showCmd = SW_MAXIMIZE;
- SetWindowPlacement(mWindowHandle, &placement);
- }
- });
-
- return true;
-}
-
-bool LLWindowWin32::getFullscreen()
-{
- return mFullscreen;
-}
-
-bool LLWindowWin32::getPosition(LLCoordScreen *position)
-{
- position->mX = mRect.left;
- position->mY = mRect.top;
- return true;
-}
-
-bool LLWindowWin32::getSize(LLCoordScreen *size)
-{
- size->mX = mRect.right - mRect.left;
- size->mY = mRect.bottom - mRect.top;
- return true;
-}
-
-bool LLWindowWin32::getSize(LLCoordWindow *size)
-{
- size->mX = mClientRect.right - mClientRect.left;
- size->mY = mClientRect.bottom - mClientRect.top;
- return true;
-}
-
-bool LLWindowWin32::setPosition(const LLCoordScreen position)
-{
- LLCoordScreen size;
-
- if (!mWindowHandle)
- {
- return false;
- }
- getSize(&size);
- moveWindow(position, size);
- return true;
-}
-
-bool LLWindowWin32::setSizeImpl(const LLCoordScreen size)
-{
- LLCoordScreen position;
-
- getPosition(&position);
- if (!mWindowHandle)
- {
- return false;
- }
-
- mWindowThread->post([=]()
- {
- WINDOWPLACEMENT placement;
- placement.length = sizeof(WINDOWPLACEMENT);
-
- if (GetWindowPlacement(mWindowHandle, &placement))
- {
- placement.showCmd = SW_RESTORE;
- SetWindowPlacement(mWindowHandle, &placement);
- }
- });
-
- moveWindow(position, size);
- return true;
-}
-
-bool LLWindowWin32::setSizeImpl(const LLCoordWindow size)
-{
- RECT window_rect = {0, 0, size.mX, size.mY };
- DWORD dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
- DWORD dw_style = WS_OVERLAPPEDWINDOW;
-
- AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style);
-
- return setSizeImpl(LLCoordScreen(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top));
-}
-
-// changing fullscreen resolution
-bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bool enable_vsync, const LLCoordScreen* const posp)
-{
- //called from main thread
- GLuint pixel_format;
- DEVMODE dev_mode;
- ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
- dev_mode.dmSize = sizeof(DEVMODE);
- DWORD current_refresh;
- DWORD dw_ex_style;
- DWORD dw_style;
- RECT window_rect = { 0, 0, 0, 0 };
- S32 width = size.mX;
- S32 height = size.mY;
- bool auto_show = false;
-
- if (mhRC)
- {
- auto_show = true;
- resetDisplayResolution();
- }
-
- if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
- {
- current_refresh = dev_mode.dmDisplayFrequency;
- }
- else
- {
- current_refresh = 60;
- }
- mRefreshRate = current_refresh;
-
- gGLManager.shutdownGL();
- //destroy gl context
- if (mhRC)
- {
- if (!wglMakeCurrent(NULL, NULL))
- {
- LL_WARNS("Window") << "Release of DC and RC failed" << LL_ENDL;
- }
-
- if (!wglDeleteContext(mhRC))
- {
- LL_WARNS("Window") << "Release of rendering context failed" << LL_ENDL;
- }
-
- 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)
- {
- LL_WARNS("Window") << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << LL_ENDL;
- 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;
-
- LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth
- << "x" << dev_mode.dmPelsHeight
- << "x" << dev_mode.dmBitsPerPel
- << " @ " << dev_mode.dmDisplayFrequency
- << LL_ENDL;
-
- 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.
- // This converts client rect to window rect, i.e. expands it by the window border size.
- 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;
-
- LL_INFOS("Window") << "Unable to run fullscreen at " << width << "x" << height << LL_ENDL;
- return false;
- }
- }
- else
- {
- mFullscreen = false;
- window_rect.left = (long)(posp ? posp->mX : 0);
- window_rect.right = (long)width + window_rect.left; // Windows GDI rects don't include rightmost pixel
- window_rect.top = (long)(posp ? posp->mY : 0);
- window_rect.bottom = (long)height + window_rect.top;
- // 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
- LL_DEBUGS("Window") << "Creating window with X: " << window_rect.left
- << " Y: " << window_rect.top
- << " Width: " << (window_rect.right - window_rect.left)
- << " Height: " << (window_rect.bottom - window_rect.top)
- << " Fullscreen: " << mFullscreen
- << LL_ENDL;
-
- recreateWindow(window_rect, dw_ex_style, dw_style);
-
- if (mWindowHandle)
- {
- LL_INFOS("Window") << "window is created." << LL_ENDL ;
- }
- else
- {
- LL_WARNS("Window") << "Window creation failed, code: " << GetLastError() << LL_ENDL;
- }
-
- //-----------------------------------------------------------------------
- // 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)
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBDevContextErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return false;
- }
-
- LL_INFOS("Window") << "Device context retrieved." << LL_ENDL ;
-
- try
- {
- // Looks like ChoosePixelFormat can crash in case of faulty driver
- if (!(pixel_format = SafeChoosePixelFormat(mhDC, &pfd)))
- {
- LL_WARNS("Window") << "ChoosePixelFormat failed, code: " << GetLastError() << LL_ENDL;
- OSMessageBox(mCallbacks->translateString("MBPixelFmtErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
- }
- catch (...)
- {
- LOG_UNHANDLED_EXCEPTION("ChoosePixelFormat");
- OSMessageBox(mCallbacks->translateString("MBPixelFmtErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
- LL_INFOS("Window") << "Pixel format chosen." << LL_ENDL ;
-
- // Verify what pixel format we actually received.
- if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
- &pfd))
- {
- OSMessageBox(mCallbacks->translateString("MBPixelFmtDescErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
- // (EXP-1765) dump pixel data to see if there is a pattern that leads to unreproducible crash
- LL_INFOS("Window") << "--- begin pixel format dump ---" << LL_ENDL ;
- LL_INFOS("Window") << "pixel_format is " << pixel_format << LL_ENDL ;
- LL_INFOS("Window") << "pfd.nSize: " << pfd.nSize << LL_ENDL ;
- LL_INFOS("Window") << "pfd.nVersion: " << pfd.nVersion << LL_ENDL ;
- LL_INFOS("Window") << "pfd.dwFlags: 0x" << std::hex << pfd.dwFlags << std::dec << LL_ENDL ;
- LL_INFOS("Window") << "pfd.iPixelType: " << (int)pfd.iPixelType << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cColorBits: " << (int)pfd.cColorBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cRedBits: " << (int)pfd.cRedBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cRedShift: " << (int)pfd.cRedShift << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cGreenBits: " << (int)pfd.cGreenBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cGreenShift: " << (int)pfd.cGreenShift << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cBlueBits: " << (int)pfd.cBlueBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cBlueShift: " << (int)pfd.cBlueShift << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cAlphaBits: " << (int)pfd.cAlphaBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cAlphaShift: " << (int)pfd.cAlphaShift << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cAccumBits: " << (int)pfd.cAccumBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cAccumRedBits: " << (int)pfd.cAccumRedBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cAccumGreenBits: " << (int)pfd.cAccumGreenBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cAccumBlueBits: " << (int)pfd.cAccumBlueBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cAccumAlphaBits: " << (int)pfd.cAccumAlphaBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cDepthBits: " << (int)pfd.cDepthBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cStencilBits: " << (int)pfd.cStencilBits << LL_ENDL ;
- LL_INFOS("Window") << "pfd.cAuxBuffers: " << (int)pfd.cAuxBuffers << LL_ENDL ;
- LL_INFOS("Window") << "pfd.iLayerType: " << (int)pfd.iLayerType << LL_ENDL ;
- LL_INFOS("Window") << "pfd.bReserved: " << (int)pfd.bReserved << LL_ENDL ;
- LL_INFOS("Window") << "pfd.dwLayerMask: " << pfd.dwLayerMask << LL_ENDL ;
- LL_INFOS("Window") << "pfd.dwVisibleMask: " << pfd.dwVisibleMask << LL_ENDL ;
- LL_INFOS("Window") << "pfd.dwDamageMask: " << pfd.dwDamageMask << LL_ENDL ;
- LL_INFOS("Window") << "--- end pixel format dump ---" << LL_ENDL ;
-
- if (!SetPixelFormat(mhDC, pixel_format, &pfd))
- {
- OSMessageBox(mCallbacks->translateString("MBPixelFmtSetErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
-
- if (!(mhRC = SafeCreateContext(mhDC)))
- {
- OSMessageBox(mCallbacks->translateString("MBGLContextErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
- if (!wglMakeCurrent(mhDC, mhRC))
- {
- OSMessageBox(mCallbacks->translateString("MBGLContextActErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
- LL_INFOS("Window") << "Drawing context is created." << LL_ENDL ;
-
- 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; //stencil buffer is deprecated (performance penalty)
- //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_ALPHA_BITS_ARB;
- attrib_list[cur_attrib++] = 0;
-
- U32 end_attrib = 0;
- if (mFSAASamples > 0)
- {
- end_attrib = cur_attrib;
- attrib_list[cur_attrib++] = WGL_SAMPLE_BUFFERS_ARB;
- attrib_list[cur_attrib++] = GL_TRUE;
-
- attrib_list[cur_attrib++] = WGL_SAMPLES_ARB;
- attrib_list[cur_attrib++] = mFSAASamples;
- }
-
- // 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);
-
- while(!result && mFSAASamples > 0)
- {
- LL_WARNS() << "FSAASamples: " << mFSAASamples << " not supported." << LL_ENDL ;
-
- mFSAASamples /= 2 ; //try to decrease sample pixel number until to disable anti-aliasing
- if(mFSAASamples < 2)
- {
- mFSAASamples = 0 ;
- }
-
- if (mFSAASamples > 0)
- {
- attrib_list[end_attrib + 3] = mFSAASamples;
- }
- else
- {
- cur_attrib = end_attrib ;
- end_attrib = 0 ;
- attrib_list[cur_attrib++] = 0 ; //end
- }
- result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
-
- if(result)
- {
- LL_WARNS() << "Only support FSAASamples: " << mFSAASamples << LL_ENDL ;
- }
- }
-
- if (!result)
- {
- LL_WARNS() << "mFSAASamples: " << mFSAASamples << LL_ENDL ;
-
- close();
- show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit");
- return false;
- }
-
- if (!num_formats)
- {
- if (end_attrib > 0)
- {
- LL_INFOS("Window") << "No valid pixel format for " << mFSAASamples << "x anti-aliasing." << LL_ENDL;
- attrib_list[end_attrib] = 0;
-
- BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
- if (!result)
- {
- close();
- show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit no AA");
- return false;
- }
- }
-
- if (!num_formats)
- {
- LL_INFOS("Window") << "No 32 bit z-buffer, trying 24 bits instead" << LL_ENDL;
- // 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)
- {
- LL_WARNS("Window") << "Couldn't get 24 bit z-buffer,trying 16 bits instead!" << LL_ENDL;
- 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;
- }
- }
- }
-
- LL_INFOS("Window") << "Choosing pixel formats: " << num_formats << " pixel formats returned" << LL_ENDL;
- }
-
- LL_INFOS("Window") << "pixel formats done." << LL_ENDL ;
-
- S32 swap_method = 0;
- S32 cur_format = 0;
-const S32 max_format = (S32)num_formats - 1;
- GLint swap_query = WGL_SWAP_METHOD_ARB;
-
- // SL-14705 Fix name tags showing in front of objects with AMD GPUs.
- // On AMD hardware we need to iterate from the first pixel format to the end.
- // Spec:
- // https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt
- while (wglGetPixelFormatAttribivARB(mhDC, pixel_formats[cur_format], 0, 1, &swap_query, &swap_method))
- {
- if (swap_method == WGL_SWAP_UNDEFINED_ARB)
- {
- break;
- }
- else if (cur_format >= max_format)
- {
- cur_format = 0;
- break;
- }
-
- ++cur_format;
- }
-
- pixel_format = pixel_formats[cur_format];
-
- if (mhDC != 0) // Does The Window Have A Device Context?
- {
- wglMakeCurrent(mhDC, 0); // Set The Current Active Rendering Context To Zero
- if (mhRC != 0) // Does The Window Have A Rendering Context?
- {
- wglDeleteContext (mhRC); // Release The Rendering Context
- mhRC = 0; // Zero The Rendering Context
- }
- }
-
- // will release and recreate mhDC, mWindowHandle
- recreateWindow(window_rect, dw_ex_style, dw_style);
-
- RECT rect;
- RECT client_rect;
- //initialize immediately on main thread
- if (GetWindowRect(mWindowHandle, &rect) &&
- GetClientRect(mWindowHandle, &client_rect))
- {
- mRect = rect;
- mClientRect = client_rect;
- };
-
- if (mWindowHandle)
- {
- LL_INFOS("Window") << "recreate window done." << LL_ENDL ;
- }
- else
- {
- // Note: if value is NULL GetDC retrieves the DC for the entire screen.
- LL_WARNS("Window") << "Window recreation failed, code: " << GetLastError() << LL_ENDL;
- }
-
- if (!mhDC)
- {
- OSMessageBox(mCallbacks->translateString("MBDevContextErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
- if (!SetPixelFormat(mhDC, pixel_format, &pfd))
- {
- OSMessageBox(mCallbacks->translateString("MBPixelFmtSetErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
- if (wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method))
- {
- switch (swap_method)
- {
- case WGL_SWAP_EXCHANGE_ARB:
- mSwapMethod = SWAP_METHOD_EXCHANGE;
- LL_DEBUGS("Window") << "Swap Method: Exchange" << LL_ENDL;
- break;
- case WGL_SWAP_COPY_ARB:
- mSwapMethod = SWAP_METHOD_COPY;
- LL_DEBUGS("Window") << "Swap Method: Copy" << LL_ENDL;
- break;
- case WGL_SWAP_UNDEFINED_ARB:
- mSwapMethod = SWAP_METHOD_UNDEFINED;
- LL_DEBUGS("Window") << "Swap Method: Undefined" << LL_ENDL;
- break;
- default:
- mSwapMethod = SWAP_METHOD_UNDEFINED;
- LL_DEBUGS("Window") << "Swap Method: Unknown" << LL_ENDL;
- break;
- }
- }
- }
- else
- {
- LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr"));
- // mWindowHandle is 0, going to crash either way
- LL_ERRS("Window") << "No wgl_ARB_pixel_format extension!" << LL_ENDL;
- }
-
- // Verify what pixel format we actually received.
- if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
- &pfd))
- {
- OSMessageBox(mCallbacks->translateString("MBPixelFmtDescErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
- LL_INFOS("Window") << "GL buffer: Color Bits " << S32(pfd.cColorBits)
- << " Alpha Bits " << S32(pfd.cAlphaBits)
- << " Depth Bits " << S32(pfd.cDepthBits)
- << LL_ENDL;
-
- mhRC = 0;
- if (wglCreateContextAttribsARB)
- { //attempt to create a specific versioned context
- mhRC = (HGLRC) createSharedContext();
- if (!mhRC)
- {
- return false;
- }
- }
-
- if (!wglMakeCurrent(mhDC, mhRC))
- {
- OSMessageBox(mCallbacks->translateString("MBGLContextActErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
- if (!gGLManager.initGL())
- {
- OSMessageBox(mCallbacks->translateString("MBVideoDrvErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- close();
- return false;
- }
-
- // Disable vertical sync for swap
- toggleVSync(enable_vsync);
-
- SetWindowLongPtr(mWindowHandle, GWLP_USERDATA, (LONG_PTR)this);
-
- // register this window as handling drag/drop events from the OS
- DragAcceptFiles( mWindowHandle, TRUE );
-
- mDragDrop->init( mWindowHandle );
-
- //register joystick timer callback
- SetTimer( mWindowHandle, 0, 1000 / 30, NULL ); // 30 fps timer
-
- // ok to post quit messages now
- mPostQuit = true;
-
- // *HACK: Attempt to prevent startup crashes by deferring memory accounting
- // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18
- mWindowThread->post([=]()
- {
- mWindowThread->glReady();
- });
-
- if (auto_show)
- {
- show();
- glClearColor(0.0f, 0.0f, 0.0f, 0.f);
- glClear(GL_COLOR_BUFFER_BIT);
- swapBuffers();
- }
-
- LL_PROFILER_GPU_CONTEXT;
-
- return true;
-}
-
-void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style)
-{
- auto oldWindowHandle = mWindowHandle;
- auto oldDCHandle = mhDC;
-
- // zero out mWindowHandle and mhDC before destroying window so window
- // thread falls back to peekmessage
- mWindowHandle = 0;
- mhDC = 0;
-
- std::promise<std::pair<HWND, HDC>> promise;
- // What follows must be done on the window thread.
- auto window_work =
- [this,
- self=mWindowThread,
- oldWindowHandle,
- oldDCHandle,
- // bind CreateWindowEx() parameters by value instead of
- // back-referencing LLWindowWin32 members
- windowClassName=mWindowClassName,
- windowTitle=mWindowTitle,
- hInstance=mhInstance,
- window_rect,
- dw_ex_style,
- dw_style,
- &promise]
- ()
- {
- LL_DEBUGS("Window") << "recreateWindow(): window_work entry" << LL_ENDL;
- self->mWindowHandleThrd = 0;
- self->mhDCThrd = 0;
-
- if (oldWindowHandle)
- {
- if (oldDCHandle && !ReleaseDC(oldWindowHandle, oldDCHandle))
- {
- LL_WARNS("Window") << "Failed to ReleaseDC" << LL_ENDL;
- }
-
- // important to call DestroyWindow() from the window thread
- if (!destroy_window_handler(oldWindowHandle))
- {
-
- LL_WARNS("Window") << "Failed to properly close window before recreating it!"
- << LL_ENDL;
- }
- }
-
- auto handle = CreateWindowEx(dw_ex_style,
- windowClassName,
- windowTitle,
- 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,
- hInstance,
- NULL);
-
- if (! handle)
- {
- // Failed to create window: clear the variables. This
- // assignment is valid because we're running on mWindowThread.
- self->mWindowHandleThrd = NULL;
- self->mhDCThrd = 0;
- }
- else
- {
- // Update mWindowThread's own mWindowHandle and mhDC.
- self->mWindowHandleThrd = handle;
- self->mhDCThrd = GetDC(handle);
- }
-
- updateWindowRect();
-
- // It's important to wake up the future either way.
- promise.set_value(std::make_pair(self->mWindowHandleThrd, self->mhDCThrd));
- LL_DEBUGS("Window") << "recreateWindow(): window_work done" << LL_ENDL;
- };
- // But how we pass window_work to the window thread depends on whether we
- // already have a window handle.
- if (!oldWindowHandle)
- {
- // Pass window_work using the WorkQueue: without an existing window
- // handle, the window thread can't call GetMessage().
- LL_DEBUGS("Window") << "posting window_work to WorkQueue" << LL_ENDL;
- mWindowThread->post(window_work);
- }
- else
- {
- // Pass window_work using PostMessage(). We can still
- // PostMessage(oldHandle) because oldHandle won't be destroyed until
- // the window thread has retrieved and executed window_work.
- LL_DEBUGS("Window") << "posting window_work to message queue" << LL_ENDL;
- mWindowThread->Post(oldWindowHandle, window_work);
- }
-
- auto future = promise.get_future();
- // This blocks until mWindowThread processes CreateWindowEx() and calls
- // promise.set_value().
- auto pair = future.get();
- mWindowHandle = pair.first;
- mhDC = pair.second;
-}
-
-void* LLWindowWin32::createSharedContext()
-{
- mMaxGLVersion = llclamp(mMaxGLVersion, 3.f, 4.6f);
-
- S32 version_major = llfloor(mMaxGLVersion);
- S32 version_minor = llround((mMaxGLVersion-version_major)*10);
-
- S32 attribs[] =
- {
- WGL_CONTEXT_MAJOR_VERSION_ARB, version_major,
- WGL_CONTEXT_MINOR_VERSION_ARB, version_minor,
- WGL_CONTEXT_PROFILE_MASK_ARB, LLRender::sGLCoreProfile ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
- WGL_CONTEXT_FLAGS_ARB, gDebugGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0,
- 0
- };
-
- HGLRC rc = 0;
-
- bool done = false;
- while (!done)
- {
- rc = wglCreateContextAttribsARB(mhDC, mhRC, attribs);
-
- if (!rc)
- {
- if (attribs[3] > 0)
- { //decrement minor version
- attribs[3]--;
- }
- else if (attribs[1] > 3)
- { //decrement major version and start minor version over at 3
- attribs[1]--;
- attribs[3] = 3;
- }
- else
- { //we reached 3.0 and still failed, bail out
- done = true;
- }
- }
- else
- {
- LL_INFOS() << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) <<
- (LLRender::sGLCoreProfile ? " core" : " compatibility") << " context." << LL_ENDL;
- done = true;
- }
- }
-
- if (!rc && !(rc = wglCreateContext(mhDC)))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBGLContextErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- }
-
- return rc;
-}
-
-void LLWindowWin32::makeContextCurrent(void* contextPtr)
-{
- wglMakeCurrent(mhDC, (HGLRC) contextPtr);
- LL_PROFILER_GPU_CONTEXT;
-}
-
-void LLWindowWin32::destroySharedContext(void* contextPtr)
-{
- wglDeleteContext((HGLRC)contextPtr);
-}
-
-void LLWindowWin32::toggleVSync(bool enable_vsync)
-{
- if (wglSwapIntervalEXT == nullptr)
- {
- LL_INFOS("Window") << "VSync: wglSwapIntervalEXT not initialized" << LL_ENDL;
- }
- else if (!enable_vsync)
- {
- LL_INFOS("Window") << "Disabling vertical sync" << LL_ENDL;
- wglSwapIntervalEXT(0);
- }
- else
- {
- LL_INFOS("Window") << "Enabling vertical sync" << LL_ENDL;
- wglSwapIntervalEXT(1);
- }
-}
-
-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 );
- }
- }
-
- // if the window was already maximized, MoveWindow seems to still set the maximized flag even if
- // the window is smaller than maximized.
- // So we're going to do a restore first (which is a ShowWindow call) (SL-44655).
-
- // THIS CAUSES DEV-15484 and DEV-15949
- //ShowWindow(mWindowHandle, SW_RESTORE);
- // NOW we can call MoveWindow
- mWindowThread->post([=]()
- {
- MoveWindow(mWindowHandle, position.mX, position.mY, size.mX, size.mY, TRUE);
- });
-}
-
-void LLWindowWin32::setTitle(const std::string title)
-{
- // TODO: Do we need to use the wide string version of this call
- // to support non-ascii usernames (and region names?)
- mWindowThread->post([=]()
- {
- SetWindowTextA(mWindowHandle, title.c_str());
- });
-}
-
-bool LLWindowWin32::setCursorPosition(const LLCoordWindow position)
-{
- ASSERT_MAIN_THREAD();
-
- if (!mWindowHandle)
- {
- return false;
- }
-
- LLCoordScreen screen_pos(position.convert());
-
- // instantly set the cursor position from the app's point of view
- mCursorPosition = position;
- mLastCursorPosition = position;
-
- // Inform the application of the new mouse position (needed for per-frame
- // hover/picking to function).
- mCallbacks->handleMouseMove(this, position.convert(), (MASK)0);
-
- // actually set the cursor position on the window thread
- mWindowThread->post([=]()
- {
- // actually set the OS cursor position
- SetCursorPos(screen_pos.mX, screen_pos.mY);
- });
-
- return true;
-}
-
-bool LLWindowWin32::getCursorPosition(LLCoordWindow *position)
-{
- ASSERT_MAIN_THREAD();
- if (!position)
- {
- return false;
- }
-
- *position = mCursorPosition;
- return true;
-}
-
-bool LLWindowWin32::getCursorDelta(LLCoordCommon* delta)
-{
- if (delta == nullptr)
- {
- return false;
- }
-
- *delta = mMouseFrameDelta;
-
- return true;
-}
-
-void LLWindowWin32::hideCursor()
-{
- ASSERT_MAIN_THREAD();
-
- mWindowThread->post([=]()
- {
- while (ShowCursor(FALSE) >= 0)
- {
- // nothing, wait for cursor to push down
- }
- });
-
- mCursorHidden = true;
- mHideCursorPermanent = true;
-}
-
-void LLWindowWin32::showCursor()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
-
- ASSERT_MAIN_THREAD();
-
- mWindowThread->post([=]()
- {
- // 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 && mMouseVanish)
- {
- 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_SIZEALL ] = LoadCursor(NULL, IDC_SIZEALL);
- 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_TOOLZOOMOUT ] = LoadCursor(module, TEXT("TOOLZOOMOUT"));
- mCursor[ UI_CURSOR_TOOLPICKOBJECT3 ] = LoadCursor(module, TEXT("TOOLPICKOBJECT3"));
- mCursor[ UI_CURSOR_PIPETTE ] = LoadCursor(module, TEXT("TOOLPIPETTE"));
- mCursor[ UI_CURSOR_TOOLSIT ] = LoadCursor(module, TEXT("TOOLSIT"));
- mCursor[ UI_CURSOR_TOOLBUY ] = LoadCursor(module, TEXT("TOOLBUY"));
- mCursor[ UI_CURSOR_TOOLOPEN ] = LoadCursor(module, TEXT("TOOLOPEN"));
- mCursor[ UI_CURSOR_TOOLPATHFINDING ] = LoadCursor(module, TEXT("TOOLPATHFINDING"));
- mCursor[ UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD ] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHSTARTADD"));
- mCursor[ UI_CURSOR_TOOLPATHFINDING_PATH_START ] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHSTART"));
- mCursor[ UI_CURSOR_TOOLPATHFINDING_PATH_END ] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHEND"));
- mCursor[ UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD ] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHENDADD"));
- mCursor[ UI_CURSOR_TOOLNO ] = LoadCursor(module, TEXT("TOOLNO"));
-
- // Color cursors
- mCursor[ UI_CURSOR_TOOLPLAY ] = loadColorCursor(TEXT("TOOLPLAY"));
- mCursor[ UI_CURSOR_TOOLPAUSE ] = loadColorCursor(TEXT("TOOLPAUSE"));
- mCursor[ UI_CURSOR_TOOLMEDIAOPEN ] = loadColorCursor(TEXT("TOOLMEDIAOPEN"));
-
- // 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::updateCursor()
-{
- ASSERT_MAIN_THREAD();
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32
- if (mNextCursor == UI_CURSOR_ARROW
- && mBusyCount > 0)
- {
- mNextCursor = UI_CURSOR_WORKING;
- }
-
- if( mCurrentCursor != mNextCursor )
- {
- mCurrentCursor = mNextCursor;
- auto nextCursor = mCursor[mNextCursor];
- mWindowThread->post([=]()
- {
- SetCursor(nextCursor);
- });
- }
-}
-
-ECursorType LLWindowWin32::getCursor() const
-{
- return mCurrentCursor;
-}
-
-void LLWindowWin32::captureMouse()
-{
- SetCapture(mWindowHandle);
-}
-
-void LLWindowWin32::releaseMouse()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
- ReleaseCapture();
-}
-
-
-void LLWindowWin32::delayInputProcessing()
-{
- mInputProcessingPaused = true;
-}
-
-
-void LLWindowWin32::gatherInput()
-{
- ASSERT_MAIN_THREAD();
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32
- MSG msg;
-
- {
- LLMutexLock lock(&mRawMouseMutex);
- mMouseFrameDelta = mRawMouseDelta;
-
- mRawMouseDelta.mX = 0;
- mRawMouseDelta.mY = 0;
- }
-
-
- if (mWindowThread->getQueue().size())
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - PostMessage");
- kickWindowThread();
- }
-
- while (mWindowThread->mMessageQueue.tryPopBack(msg))
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - message queue");
- if (mInputProcessingPaused)
- {
- continue;
- }
-
- // For async host by name support. Really hacky.
- if (gAsyncMsgCallback && (LL_WM_HOST_RESOLVED == msg.message))
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - callback");
- gAsyncMsgCallback(msg);
- }
- }
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - PeekMessage");
- S32 msg_count = 0;
- while ((msg_count < MAX_MESSAGE_PER_UPDATE) && PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_REMOVE))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- msg_count++;
- }
- }
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - function queue");
- //process any pending functions
- std::function<void()> curFunc;
- while (mFunctionQueue.tryPopBack(curFunc))
- {
- curFunc();
- }
- }
-
- // send one and only one mouse move event per frame BEFORE handling mouse button presses
- if (mLastCursorPosition != mCursorPosition)
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - mouse move");
- mCallbacks->handleMouseMove(this, mCursorPosition.convert(), mMouseMask);
- }
-
- mLastCursorPosition = mCursorPosition;
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - mouse queue");
- // handle mouse button presses AFTER updating mouse cursor position
- std::function<void()> curFunc;
- while (mMouseQueue.tryPopBack(curFunc))
- {
- curFunc();
- }
- }
-
- mInputProcessingPaused = false;
-
- updateCursor();
-}
-
-static LLTrace::BlockTimerStatHandle FTM_KEYHANDLER("Handle Keyboard");
-static LLTrace::BlockTimerStatHandle FTM_MOUSEHANDLER("Handle Mouse");
-
-#define WINDOW_IMP_POST(x) window_imp->post([=]() { x; })
-
-LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param)
-{
- ASSERT_WINDOW_THREAD();
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
-
- if (u_msg == WM_POST_FUNCTION_)
- {
- // from LLWindowWin32Thread::Post()
- // Cast l_param back to the pointer to the heap FuncType
- // allocated by Post(). Capture in unique_ptr so we'll delete
- // once we're done with it.
- std::unique_ptr<LLWindowWin32Thread::FuncType>
- ptr(reinterpret_cast<LLWindowWin32Thread::FuncType*>(l_param));
- (*ptr)();
- return 0;
- }
-
- // Ignore clicks not originated in the client area, i.e. mouse-up events not preceded with a WM_LBUTTONDOWN.
- // This helps prevent avatar walking after maximizing the window by double-clicking the title bar.
- static bool sHandleLeftMouseUp = true;
-
- // Ignore the double click received right after activating app.
- // This is to avoid triggering double click teleport after returning focus (see MAINT-3786).
- static bool sHandleDoubleClick = true;
-
- LLWindowWin32* window_imp = (LLWindowWin32*)GetWindowLongPtr(h_wnd, GWLP_USERDATA);
-
- if (NULL != window_imp)
- {
- // 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));
-
- // 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:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_TIMER");
- WINDOW_IMP_POST(window_imp->mCallbacks->handleTimerEvent(window_imp));
- break;
- }
-
- case WM_DEVICECHANGE:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DEVICECHANGE");
- if (w_param == DBT_DEVNODES_CHANGED || w_param == DBT_DEVICEARRIVAL)
- {
- WINDOW_IMP_POST(window_imp->mCallbacks->handleDeviceChange(window_imp));
-
- return 1;
- }
- break;
- }
-
- case WM_PAINT:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - 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_POST(window_imp->mCallbacks->handlePaint(window_imp, update_rect.left, update_rect.top,
- update_width, update_height));
- break;
- }
- case WM_PARENTNOTIFY:
- {
- break;
- }
-
- case WM_SETCURSOR:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - 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:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ENTERMENULOOP");
- WINDOW_IMP_POST(window_imp->mCallbacks->handleWindowBlock(window_imp));
- break;
- }
-
- case WM_EXITMENULOOP:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_EXITMENULOOP");
- WINDOW_IMP_POST(window_imp->mCallbacks->handleWindowUnblock(window_imp));
- break;
- }
-
- case WM_ACTIVATEAPP:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ACTIVATEAPP");
- window_imp->post([=]()
- {
- // This message should be sent whenever the app gains or loses focus.
- BOOL activating = (BOOL)w_param;
-
- 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();
- }
- }
-
- if (!activating)
- {
- sHandleDoubleClick = false;
- }
-
- window_imp->mCallbacks->handleActivateApp(window_imp, activating);
- });
- break;
- }
- case WM_ACTIVATE:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ACTIVATE");
- window_imp->post([=]()
- {
- // Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE
- BOOL activating = (LOWORD(w_param) != WA_INACTIVE);
-
- if (!activating && LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->interruptLanguageTextInput();
- }
- });
-
- break;
- }
-
- case WM_QUERYOPEN:
- // TODO: use this to return a nice icon
- break;
-
- case WM_SYSCOMMAND:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - 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:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_CLOSE");
- window_imp->post([=]()
- {
- // 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:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DESTROY");
- if (window_imp->shouldPostQuit())
- {
- PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0
- }
- return 0;
- }
- case WM_COMMAND:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_COMMAND");
- if (!HIWORD(w_param)) // this message is from a menu
- {
- WINDOW_IMP_POST(window_imp->mCallbacks->handleMenuSelect(window_imp, LOWORD(w_param)));
- }
- break;
- }
- case WM_SYSKEYDOWN:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SYSKEYDOWN");
- // allow system keys, such as ALT-F4 to be processed by Windows
- eat_keystroke = false;
- // intentional fall-through here
- [[fallthrough]];
- }
- case WM_KEYDOWN:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KEYDOWN");
- window_imp->post([=]()
- {
- window_imp->mKeyCharCode = 0; // don't know until wm_char comes in next
- window_imp->mKeyScanCode = (l_param >> 16) & 0xff;
- window_imp->mKeyVirtualKey = w_param;
- window_imp->mRawMsg = u_msg;
- window_imp->mRawWParam = w_param;
- window_imp->mRawLParam = l_param;
-
- gKeyboard->handleKeyDown(w_param, mask);
- });
- if (eat_keystroke) return 0; // skip DefWindowProc() handling if we're consuming the keypress
- break;
- }
- case WM_SYSKEYUP:
- eat_keystroke = false;
- // intentional fall-through here
- [[fallthrough]];
- case WM_KEYUP:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KEYUP");
- window_imp->post([=]()
- {
- window_imp->mKeyScanCode = (l_param >> 16) & 0xff;
- window_imp->mKeyVirtualKey = w_param;
- window_imp->mRawMsg = u_msg;
- window_imp->mRawWParam = w_param;
- window_imp->mRawLParam = l_param;
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KEYUP");
- gKeyboard->handleKeyUp(w_param, mask);
- }
- });
- if (eat_keystroke) return 0; // skip DefWindowProc() handling if we're consuming the keypress
- break;
- }
- case WM_IME_SETCONTEXT:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_SETCONTEXT");
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW;
- // Invoke DefWinProc with the modified LPARAM.
- }
- break;
- }
- case WM_IME_STARTCOMPOSITION:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_STARTCOMPOSITION");
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- WINDOW_IMP_POST(window_imp->handleStartCompositionMessage());
- return 0;
- }
- break;
- }
- case WM_IME_ENDCOMPOSITION:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_ENDCOMPOSITION");
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- return 0;
- }
- break;
- }
- case WM_IME_COMPOSITION:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_COMPOSITION");
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- WINDOW_IMP_POST(window_imp->handleCompositionMessage(l_param));
- return 0;
- }
- break;
- }
- case WM_IME_REQUEST:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_REQUEST");
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- LRESULT result;
- window_imp->handleImeRequests(w_param, l_param, &result);
- return result;
- }
- break;
- }
- case WM_CHAR:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_CHAR");
- window_imp->post([=]()
- {
- window_imp->mKeyCharCode = w_param;
- window_imp->mRawMsg = u_msg;
- window_imp->mRawWParam = w_param;
- window_imp->mRawLParam = l_param;
-
- // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need
- // to figure out how that works. - Doug
- //
- // ... Well, I don't think so.
- // How it works is explained in Win32 API document, but WM_UNICHAR didn't work
- // as specified at least on Windows XP SP1 Japanese version. I have never used
- // it since then, and I'm not sure whether it has been fixed now, but I don't think
- // it is worth trying. The good old WM_CHAR works just fine even for supplementary
- // characters. We just need to take care of surrogate pairs sent as two WM_CHAR's
- // by ourselves. It is not that tough. -- Alissa Sabre @ SL
-
- // Even if LLWindowCallbacks::handleUnicodeChar(llwchar, bool) returned false,
- // we *did* processed the event, so I believe we should not pass it to DefWindowProc...
- window_imp->handleUnicodeUTF16((U16)w_param, gKeyboard->currentMask(false));
- });
- return 0;
- }
- case WM_NCLBUTTONDOWN:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_NCLBUTTONDOWN");
- {
- // A click in a non-client area, e.g. title bar or window border.
- window_imp->post([=]()
- {
- sHandleLeftMouseUp = false;
- sHandleDoubleClick = true;
- });
- }
- break;
- }
- case WM_LBUTTONDOWN:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_LBUTTONDOWN");
- {
- LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
- window_imp->postMouseButtonEvent([=]()
- {
- sHandleLeftMouseUp = true;
-
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->interruptLanguageTextInput();
- }
-
- MASK mask = gKeyboard->currentMask(true);
- auto gl_coord = window_imp->mCursorPosition.convert();
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- window_imp->mCallbacks->handleMouseDown(window_imp, gl_coord, mask);
- });
-
- return 0;
- }
- break;
- }
-
- case WM_LBUTTONDBLCLK:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_LBUTTONDBLCLK");
- window_imp->postMouseButtonEvent([=]()
- {
- //RN: ignore right button double clicks for now
- //case WM_RBUTTONDBLCLK:
- if (!sHandleDoubleClick)
- {
- sHandleDoubleClick = true;
- return;
- }
- MASK mask = gKeyboard->currentMask(true);
-
- // generate move event to update mouse coordinates
- window_imp->mCursorPosition = window_coord;
- window_imp->mCallbacks->handleDoubleClick(window_imp, window_imp->mCursorPosition.convert(), mask);
- });
-
- return 0;
- }
- case WM_LBUTTONUP:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_LBUTTONUP");
- {
- window_imp->postMouseButtonEvent([=]()
- {
- LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
- if (!sHandleLeftMouseUp)
- {
- sHandleLeftMouseUp = true;
- return;
- }
- sHandleDoubleClick = true;
-
-
- MASK mask = gKeyboard->currentMask(true);
- // generate move event to update mouse coordinates
- window_imp->mCursorPosition = window_coord;
- window_imp->mCallbacks->handleMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask);
- });
- }
- return 0;
- }
- case WM_RBUTTONDBLCLK:
- case WM_RBUTTONDOWN:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_RBUTTONDOWN");
- {
- LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
- window_imp->post([=]()
- {
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- WINDOW_IMP_POST(window_imp->interruptLanguageTextInput());
- }
-
- MASK mask = gKeyboard->currentMask(true);
- // generate move event to update mouse coordinates
- auto gl_coord = window_imp->mCursorPosition.convert();
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- window_imp->mCallbacks->handleRightMouseDown(window_imp, gl_coord, mask);
- });
- }
- return 0;
- }
- break;
-
- case WM_RBUTTONUP:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_RBUTTONUP");
- {
- LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
- window_imp->postMouseButtonEvent([=]()
- {
- MASK mask = gKeyboard->currentMask(true);
- window_imp->mCallbacks->handleRightMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask);
- });
- }
- }
- break;
-
- case WM_MBUTTONDOWN:
- // case WM_MBUTTONDBLCLK:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MBUTTONDOWN");
- {
- LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
- window_imp->postMouseButtonEvent([=]()
- {
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->interruptLanguageTextInput();
- }
-
- MASK mask = gKeyboard->currentMask(true);
- window_imp->mCallbacks->handleMiddleMouseDown(window_imp, window_imp->mCursorPosition.convert(), mask);
- });
- }
- }
- break;
-
- case WM_MBUTTONUP:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MBUTTONUP");
- {
- LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
- window_imp->postMouseButtonEvent([=]()
- {
- MASK mask = gKeyboard->currentMask(true);
- window_imp->mCallbacks->handleMiddleMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask);
- });
- }
- }
- break;
- case WM_XBUTTONDOWN:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_XBUTTONDOWN");
- window_imp->postMouseButtonEvent([=]()
- {
- LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
- S32 button = GET_XBUTTON_WPARAM(w_param);
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->interruptLanguageTextInput();
- }
-
- MASK mask = gKeyboard->currentMask(true);
- // Windows uses numbers 1 and 2 for buttons, remap to 4, 5
- window_imp->mCallbacks->handleOtherMouseDown(window_imp, window_imp->mCursorPosition.convert(), mask, button + 3);
- });
-
- }
- break;
-
- case WM_XBUTTONUP:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_XBUTTONUP");
- window_imp->postMouseButtonEvent([=]()
- {
-
- LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER);
-
- S32 button = GET_XBUTTON_WPARAM(w_param);
- MASK mask = gKeyboard->currentMask(true);
- // Windows uses numbers 1 and 2 for buttons, remap to 4, 5
- window_imp->mCallbacks->handleOtherMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask, button + 3);
- });
- }
- break;
-
- case WM_MOUSEWHEEL:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MOUSEWHEEL");
- static short z_delta = 0;
-
- RECT client_rect;
-
- // eat scroll events that occur outside our window, since we use mouse position to direct scroll
- // instead of keyboard focus
- // NOTE: mouse_coord is in *window* coordinates for scroll events
- POINT mouse_coord = { (S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param) };
-
- if (ScreenToClient(window_imp->mWindowHandle, &mouse_coord)
- && GetClientRect(window_imp->mWindowHandle, &client_rect))
- {
- // we have a valid mouse point and client rect
- if (mouse_coord.x < client_rect.left || client_rect.right < mouse_coord.x
- || mouse_coord.y < client_rect.top || client_rect.bottom < mouse_coord.y)
- {
- // mouse is outside of client rect, so don't do anything
- return 0;
- }
- }
-
- S16 incoming_z_delta = HIWORD(w_param);
- z_delta += incoming_z_delta;
- // 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)
- {
- short clicks = -z_delta / WHEEL_DELTA;
- WINDOW_IMP_POST(window_imp->mCallbacks->handleScrollWheel(window_imp, clicks));
- 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;
- }
- */
- case WM_MOUSEHWHEEL:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MOUSEHWHEEL");
- static short h_delta = 0;
-
- RECT client_rect;
-
- // eat scroll events that occur outside our window, since we use mouse position to direct scroll
- // instead of keyboard focus
- // NOTE: mouse_coord is in *window* coordinates for scroll events
- POINT mouse_coord = { (S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param) };
-
- if (ScreenToClient(window_imp->mWindowHandle, &mouse_coord)
- && GetClientRect(window_imp->mWindowHandle, &client_rect))
- {
- // we have a valid mouse point and client rect
- if (mouse_coord.x < client_rect.left || client_rect.right < mouse_coord.x
- || mouse_coord.y < client_rect.top || client_rect.bottom < mouse_coord.y)
- {
- // mouse is outside of client rect, so don't do anything
- return 0;
- }
- }
-
- S16 incoming_h_delta = HIWORD(w_param);
- h_delta += incoming_h_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 (h_delta <= -WHEEL_DELTA || WHEEL_DELTA <= h_delta)
- {
- WINDOW_IMP_POST(window_imp->mCallbacks->handleScrollHWheel(window_imp, h_delta / WHEEL_DELTA));
- h_delta = 0;
- }
- return 0;
- }
- // Handle mouse movement within the window
- case WM_MOUSEMOVE:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MOUSEMOVE");
- // DO NOT use mouse event queue for move events to ensure cursor position is updated
- // when button events are handled
- WINDOW_IMP_POST(
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MOUSEMOVE lambda");
-
- MASK mask = gKeyboard->currentMask(true);
- window_imp->mMouseMask = mask;
- window_imp->mCursorPosition = window_coord;
- });
- return 0;
- }
-
- case WM_GETMINMAXINFO:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_GETMINMAXINFO");
- LPMINMAXINFO min_max = (LPMINMAXINFO)l_param;
- min_max->ptMinTrackSize.x = window_imp->mMinWindowWidth;
- min_max->ptMinTrackSize.y = window_imp->mMinWindowHeight;
- return 0;
- }
-
- case WM_MOVE:
- {
- window_imp->updateWindowRect();
- return 0;
- }
- case WM_SIZE:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SIZE");
- window_imp->updateWindowRect();
-
- // There's an odd behavior with WM_SIZE that I would call a bug. If
- // the window is maximized, and you call MoveWindow() with a size smaller
- // than a maximized window, it ends up sending WM_SIZE with w_param set
- // to SIZE_MAXIMIZED -- which isn't true. So the logic below doesn't work.
- // (SL-44655). Fixed it by calling ShowWindow(SW_RESTORE) first (see
- // LLWindowWin32::moveWindow in this file).
-
- // 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_POST(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_POST(window_imp->mCallbacks->handleActivate(window_imp, true));
- }
-
- // Also handle the minimization case
- if (w_param == SIZE_MINIMIZED && window_imp->mLastSizeWParam != SIZE_MINIMIZED)
- {
- WINDOW_IMP_POST(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_POST(window_imp->mCallbacks->handleResize(window_imp,
- LOWORD(l_param),
- HIWORD(l_param)));
- }
-
- window_imp->mLastSizeWParam = w_param;
-
- return 0;
- }
-
- case WM_DPICHANGED:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DPICHANGED");
- LPRECT lprc_new_scale;
- F32 new_scale = F32(LOWORD(w_param)) / F32(USER_DEFAULT_SCREEN_DPI);
- lprc_new_scale = (LPRECT)l_param;
- S32 new_width = lprc_new_scale->right - lprc_new_scale->left;
- S32 new_height = lprc_new_scale->bottom - lprc_new_scale->top;
- WINDOW_IMP_POST(window_imp->mCallbacks->handleDPIChanged(window_imp, new_scale, new_width, new_height));
-
- SetWindowPos(h_wnd,
- HWND_TOP,
- lprc_new_scale->left,
- lprc_new_scale->top,
- new_width,
- new_height,
- SWP_NOZORDER | SWP_NOACTIVATE);
-
- return 0;
- }
-
- case WM_SETFOCUS:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SETFOCUS");
- WINDOW_IMP_POST(window_imp->mCallbacks->handleFocus(window_imp));
- return 0;
- }
-
- case WM_KILLFOCUS:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KILLFOCUS");
- WINDOW_IMP_POST(window_imp->mCallbacks->handleFocusLost(window_imp));
- return 0;
- }
-
- case WM_COPYDATA:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_COPYDATA");
- {
- // received a URL
- PCOPYDATASTRUCT myCDS = (PCOPYDATASTRUCT)l_param;
- void* data = new U8[myCDS->cbData];
- memcpy(data, myCDS->lpData, myCDS->cbData);
- auto myType = myCDS->dwData;
-
- window_imp->post([=]()
- {
- window_imp->mCallbacks->handleDataCopy(window_imp, myType, data);
- delete[] data;
- });
- };
- return 0;
- }
- case WM_SETTINGCHANGE:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SETTINGCHANGE");
- if (w_param == SPI_SETMOUSEVANISH)
- {
- if (!SystemParametersInfo(SPI_GETMOUSEVANISH, 0, &window_imp->mMouseVanish, 0))
- {
- WINDOW_IMP_POST(window_imp->mMouseVanish = true);
- }
- }
- }
- break;
-
- case WM_INPUT:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("MWP - WM_INPUT");
-
- UINT dwSize = 0;
- GetRawInputData((HRAWINPUT)l_param, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
- llassert(dwSize < 1024);
-
- U8 lpb[1024];
-
- if (GetRawInputData((HRAWINPUT)l_param, RID_INPUT, (void*)lpb, &dwSize, sizeof(RAWINPUTHEADER)) == dwSize)
- {
- RAWINPUT* raw = (RAWINPUT*)lpb;
-
- if (raw->header.dwType == RIM_TYPEMOUSE)
- {
- LLMutexLock lock(&window_imp->mRawMouseMutex);
-
- bool absolute_coordinates = (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE);
-
- if (absolute_coordinates)
- {
- static S32 prev_absolute_x = 0;
- static S32 prev_absolute_y = 0;
- S32 absolute_x;
- S32 absolute_y;
-
- if ((raw->data.mouse.usFlags & 0x10) == 0x10) // touch screen? touch? Not defined in header
- {
- // touch screen spams (0,0) coordinates in a number of situations
- // (0,0) might need to be filtered
- absolute_x = raw->data.mouse.lLastX;
- absolute_y = raw->data.mouse.lLastY;
- }
- else
- {
- bool v_desktop = (raw->data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) == MOUSE_VIRTUAL_DESKTOP;
-
- S32 width = GetSystemMetrics(v_desktop ? SM_CXVIRTUALSCREEN : SM_CXSCREEN);
- S32 height = GetSystemMetrics(v_desktop ? SM_CYVIRTUALSCREEN : SM_CYSCREEN);
-
- absolute_x = (raw->data.mouse.lLastX / 65535.0f) * width;
- absolute_y = (raw->data.mouse.lLastY / 65535.0f) * height;
- }
-
- window_imp->mRawMouseDelta.mX += absolute_x - prev_absolute_x;
- window_imp->mRawMouseDelta.mY -= absolute_y - prev_absolute_y;
-
- prev_absolute_x = absolute_x;
- prev_absolute_y = absolute_y;
- }
- else
- {
- S32 speed;
- const S32 DEFAULT_SPEED(10);
- SystemParametersInfo(SPI_GETMOUSESPEED, 0, &speed, 0);
- if (speed == DEFAULT_SPEED)
- {
- window_imp->mRawMouseDelta.mX += raw->data.mouse.lLastX;
- window_imp->mRawMouseDelta.mY -= raw->data.mouse.lLastY;
- }
- else
- {
- window_imp->mRawMouseDelta.mX += round((F32)raw->data.mouse.lLastX * (F32)speed / DEFAULT_SPEED);
- window_imp->mRawMouseDelta.mY -= round((F32)raw->data.mouse.lLastY * (F32)speed / DEFAULT_SPEED);
- }
- }
- }
- }
- }
- break;
-
- //list of messages we get often that we don't care to log about
- case WM_NCHITTEST:
- case WM_NCMOUSEMOVE:
- case WM_NCMOUSELEAVE:
- case WM_MOVING:
- case WM_WINDOWPOSCHANGING:
- case WM_WINDOWPOSCHANGED:
- break;
-
- default:
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - default");
- LL_DEBUGS("Window") << "Unhandled windows message code: 0x" << std::hex << U32(u_msg) << LL_ENDL;
- }
- break;
- }
- }
- else
- {
- // (NULL == window_imp)
- LL_DEBUGS("Window") << "No window implementation to handle message with, message code: " << U32(u_msg) << LL_ENDL;
- }
-
- // pass unhandled messages down to Windows
- LRESULT ret;
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - DefWindowProc");
- ret = DefWindowProc(h_wnd, u_msg, w_param, l_param);
- }
- return ret;
-}
-
-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);
-}
-
-
-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);
- LLWStringUtil::removeWindowsCR(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);
- LLWStringUtil::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); /* Flawfinder: ignore */
- GlobalUnlock(hglobal_copy_utf16);
-
- if (SetClipboardData(CF_UNICODETEXT, hglobal_copy_utf16))
- {
- success = true;
- }
- }
- }
-
- CloseClipboard();
- }
-
- return success;
-}
-
-// Constrains the mouse to the window.
-void LLWindowWin32::setMouseClipping( bool b )
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
- ASSERT_MAIN_THREAD();
- 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;
-}
-
-void LLWindowWin32::flashIcon(F32 seconds)
-{
- mWindowThread->post([=]()
- {
- 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()
-{
- ASSERT_MAIN_THREAD();
- if (mCustomGammaSet)
- {
- LL_DEBUGS("Window") << "Restoring gamma" << LL_ENDL;
- mCustomGammaSet = false;
- return SetDeviceGammaRamp(mhDC, mPrevGammaRamp);
- }
- return true;
-}
-
-bool LLWindowWin32::setGamma(const F32 gamma)
-{
- ASSERT_MAIN_THREAD();
- mCurrentGamma = gamma;
-
- //Get the previous gamma ramp to restore later.
- if (!mCustomGammaSet)
- {
- if (!gGLManager.mIsIntel) // skip for Intel GPUs (see SL-11341)
- {
- LL_DEBUGS("Window") << "Getting the previous gamma ramp to restore later" << LL_ENDL;
- if (!GetDeviceGammaRamp(mhDC, mPrevGammaRamp))
- {
- LL_WARNS("Window") << "Failed to get the previous gamma ramp" << LL_ENDL;
- return false;
- }
- }
- mCustomGammaSet = true;
- }
-
- LL_DEBUGS("Window") << "Setting gamma to " << gamma << LL_ENDL;
-
- 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][i] =
- mCurrentGammaRamp[1][i] =
- mCurrentGammaRamp[2][i] = (WORD) value;
- };
-
- return SetDeviceGammaRamp ( mhDC, mCurrentGammaRamp );
-}
-
-void LLWindowWin32::setFSAASamples(const U32 fsaa_samples)
-{
- ASSERT_MAIN_THREAD();
- mFSAASamples = fsaa_samples;
-}
-
-U32 LLWindowWin32::getFSAASamples()
-{
- return mFSAASamples;
-}
-
-LLWindow::LLWindowResolution* LLWindowWin32::getSupportedResolutions(S32 &num_resolutions)
-{
- ASSERT_MAIN_THREAD();
- if (!mSupportedResolutions)
- {
- mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
- DEVMODE dev_mode;
- ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
- dev_mode.dmSize = sizeof(DEVMODE);
-
- 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;
- ::ZeroMemory(&dev_mode, sizeof(DEVMODE));
- dev_mode.dmSize = sizeof(DEVMODE);
- 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)
- {
- LL_WARNS("Window") << "setDisplayResolution failed, "
- << width << "x" << height << "x" << bits << " @ " << refresh << LL_ENDL;
- }
-
- return success;
-}
-
-// protected
-bool LLWindowWin32::setFullscreenResolution()
-{
- if (mFullscreen)
- {
- return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenBits, mFullscreenRefresh);
- }
- else
- {
- return false;
- }
-}
-
-// protected
-bool LLWindowWin32::resetDisplayResolution()
-{
- LL_DEBUGS("Window") << "resetDisplayResolution START" << LL_ENDL;
-
- LONG cds_result = ChangeDisplaySettings(NULL, 0);
-
- bool success = (DISP_CHANGE_SUCCESSFUL == cds_result);
-
- if (!success)
- {
- LL_WARNS("Window") << "resetDisplayResolution failed" << LL_ENDL;
- }
-
- LL_DEBUGS("Window") << "resetDisplayResolution END" << LL_ENDL;
-
- return success;
-}
-
-void LLWindowWin32::swapBuffers()
-{
- {
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
- SwapBuffers(mhDC);
- }
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("GPU Collect");
- LL_PROFILER_GPU_COLLECT;
- }
-}
-
-
-//
-// LLSplashScreenImp
-//
-LLSplashScreenWin32::LLSplashScreenWin32()
-: mWindow(NULL)
-{
-}
-
-LLSplashScreenWin32::~LLSplashScreenWin32()
-{
-}
-
-void LLSplashScreenWin32::showImpl()
-{
- // This appears to work. ???
- HINSTANCE hinst = GetModuleHandle(NULL);
-
- mWindow = CreateDialog(hinst,
- TEXT("SPLASHSCREEN"),
- NULL, // no parent
- (DLGPROC) LLSplashScreenWin32::windowProc);
- ShowWindow(mWindow, SW_SHOW);
-
- // Should set taskbar text without creating a header for the window (caption)
- SetWindowTextA(mWindow, "Second Life");
-}
-
-
-void LLSplashScreenWin32::updateImpl(const std::string& mesg)
-{
- if (!mWindow) return;
-
- int output_str_len = MultiByteToWideChar(CP_UTF8, 0, mesg.c_str(), mesg.length(), NULL, 0);
- if( output_str_len>1024 )
- return;
-
- WCHAR w_mesg[1025];//big enought to keep null terminatos
-
- MultiByteToWideChar (CP_UTF8, 0, mesg.c_str(), mesg.length(), w_mesg, output_str_len);
-
- //looks like MultiByteToWideChar didn't add null terminator to converted string, see EXT-4858
- w_mesg[output_str_len] = 0;
-
- SendDlgItemMessage(mWindow,
- 666, // HACK: text id
- WM_SETTEXT,
- FALSE,
- (LPARAM)w_mesg);
-}
-
-
-void LLSplashScreenWin32::hideImpl()
-{
- if (mWindow)
- {
- if (!destroy_window_handler(mWindow))
- {
- LL_WARNS("Window") << "Failed to properly close splash screen window!" << LL_ENDL;
- }
- mWindow = NULL;
- }
-}
-
-
-// static
-LRESULT CALLBACK LLSplashScreenWin32::windowProc(HWND h_wnd, UINT u_msg,
- WPARAM w_param, LPARAM l_param)
-{
- // Just give it to windows
- return DefWindowProc(h_wnd, u_msg, w_param, l_param);
-}
-
-//
-// Helper Funcs
-//
-
-S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 type)
-{
- UINT uType;
-
- switch(type)
- {
- case OSMB_OK:
- uType = MB_OK;
- break;
- case OSMB_OKCANCEL:
- uType = MB_OKCANCEL;
- break;
- case OSMB_YESNO:
- uType = MB_YESNO;
- break;
- default:
- uType = MB_OK;
- break;
- }
-
- int retval_win = MessageBoxW(NULL, // HWND
- ll_convert_string_to_wide(text).c_str(),
- ll_convert_string_to_wide(caption).c_str(),
- uType);
- S32 retval;
-
- switch(retval_win)
- {
- case IDYES:
- retval = OSBTN_YES;
- break;
- case IDNO:
- retval = OSBTN_NO;
- break;
- case IDOK:
- retval = OSBTN_OK;
- break;
- case IDCANCEL:
- retval = OSBTN_CANCEL;
- break;
- default:
- retval = OSBTN_CANCEL;
- break;
- }
-
- return retval;
-}
-
-
-void LLWindowWin32::spawnWebBrowser(const std::string& escaped_url, bool async)
-{
- bool found = false;
- S32 i;
- for (i = 0; i < gURLProtocolWhitelistCount; i++)
- {
- if (escaped_url.find(gURLProtocolWhitelist[i]) == 0)
- {
- found = true;
- break;
- }
- }
-
- if (!found)
- {
- LL_WARNS("Window") << "spawn_web_browser() called for url with protocol not on whitelist: " << escaped_url << LL_ENDL;
- return;
- }
-
- LL_INFOS("Window") << "Opening URL " << escaped_url << LL_ENDL;
-
- // replaced ShellExecute code with ShellExecuteEx since ShellExecute doesn't work
- // reliablly on Vista.
-
- // this is madness.. no, this is..
- LLWString url_wstring = utf8str_to_wstring( escaped_url );
- llutf16string url_utf16 = wstring_to_utf16str( url_wstring );
-
- // let the OS decide what to use to open the URL
- SHELLEXECUTEINFO sei = { sizeof( sei ) };
- // NOTE: this assumes that SL will stick around long enough to complete the DDE message exchange
- // necessary for ShellExecuteEx to complete
- if (async)
- {
- sei.fMask = SEE_MASK_ASYNCOK;
- }
- sei.nShow = SW_SHOWNORMAL;
- sei.lpVerb = L"open";
- sei.lpFile = url_utf16.c_str();
- ShellExecuteEx( &sei );
-}
-
-/*
- Make the raw keyboard data available - used to poke through to LLQtWebKit so
- that Qt/Webkit has access to the virtual keycodes etc. that it needs
-*/
-LLSD LLWindowWin32::getNativeKeyData()
-{
- LLSD result = LLSD::emptyMap();
-
- result["scan_code"] = (S32)mKeyScanCode;
- result["virtual_key"] = (S32)mKeyVirtualKey;
- result["msg"] = ll_sd_from_U32(mRawMsg);
- result["w_param"] = ll_sd_from_U32(mRawWParam);
- result["l_param"] = ll_sd_from_U32(mRawLParam);
-
- return result;
-}
-
-bool LLWindowWin32::dialogColorPicker( 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()
-{
- mWindowThread->post([=]()
- {
- BringWindowToTop(mWindowHandle);
- });
-}
-
-// set (OS) window focus back to the client
-void LLWindowWin32::focusClient()
-{
- mWindowThread->post([=]()
- {
- SetFocus(mWindowHandle);
- });
-}
-
-void LLWindowWin32::allowLanguageTextInput(LLPreeditor *preeditor, bool b)
-{
- if (b == sLanguageTextInputAllowed || !LLWinImm::isAvailable())
- {
- return;
- }
-
- if (preeditor != mPreeditor && !b)
- {
- // This condition may occur with a call to
- // setEnabled(bool) from LLTextEditor or LLLineEditor
- // when the control is not focused.
- // We need to silently ignore the case so that
- // the language input status of the focused control
- // is not disturbed.
- return;
- }
-
- // Take care of old and new preeditors.
- if (preeditor != mPreeditor || !b)
- {
- if (sLanguageTextInputAllowed)
- {
- interruptLanguageTextInput();
- }
- mPreeditor = (b ? preeditor : NULL);
- }
-
- sLanguageTextInputAllowed = b;
-
- if (sLanguageTextInputAllowed)
- {
- mWindowThread->post([=]()
- {
- // Allowing: Restore the previous IME status, so that the user has a feeling that the previous
- // text input continues naturally. Be careful, however, the IME status is meaningful only during the user keeps
- // using same Input Locale (aka Keyboard Layout).
- if (sWinIMEOpened && GetKeyboardLayout(0) == sWinInputLocale)
- {
- HIMC himc = LLWinImm::getContext(mWindowHandle);
- LLWinImm::setOpenStatus(himc, true);
- LLWinImm::setConversionStatus(himc, sWinIMEConversionMode, sWinIMESentenceMode);
- LLWinImm::releaseContext(mWindowHandle, himc);
- }
- });
- }
- else
- {
- mWindowThread->post([=]()
- {
- // Disallowing: Turn off the IME so that succeeding key events bypass IME and come to us directly.
- // However, do it after saving the current IME status. We need to restore the status when
- // allowing language text input again.
- sWinInputLocale = GetKeyboardLayout(0);
- sWinIMEOpened = LLWinImm::isIME(sWinInputLocale);
- if (sWinIMEOpened)
- {
- HIMC himc = LLWinImm::getContext(mWindowHandle);
- sWinIMEOpened = LLWinImm::getOpenStatus(himc);
- if (sWinIMEOpened)
- {
- LLWinImm::getConversionStatus(himc, &sWinIMEConversionMode, &sWinIMESentenceMode);
-
- // We need both ImmSetConversionStatus and ImmSetOpenStatus here to surely disable IME's
- // keyboard hooking, because Some IME reacts only on the former and some other on the latter...
- LLWinImm::setConversionStatus(himc, IME_CMODE_NOCONVERSION, sWinIMESentenceMode);
- LLWinImm::setOpenStatus(himc, false);
- }
- LLWinImm::releaseContext(mWindowHandle, himc);
- }
- });
- }
-}
-
-void LLWindowWin32::fillCandidateForm(const LLCoordGL& caret, const LLRect& bounds,
- CANDIDATEFORM *form)
-{
- LLCoordWindow caret_coord, top_left, bottom_right;
- convertCoords(caret, &caret_coord);
- convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left);
- convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right);
-
- memset(form, 0, sizeof(CANDIDATEFORM));
- form->dwStyle = CFS_EXCLUDE;
- form->ptCurrentPos.x = caret_coord.mX;
- form->ptCurrentPos.y = caret_coord.mY;
- form->rcArea.left = top_left.mX;
- form->rcArea.top = top_left.mY;
- form->rcArea.right = bottom_right.mX;
- form->rcArea.bottom = bottom_right.mY;
-}
-
-
-// Put the IME window at the right place (near current text input). Point coordinates should be the top of the current text line.
-void LLWindowWin32::setLanguageTextInput( const LLCoordGL & position )
-{
- if (sLanguageTextInputAllowed && LLWinImm::isAvailable())
- {
- HIMC himc = LLWinImm::getContext(mWindowHandle);
-
- LLCoordWindow win_pos;
- convertCoords( position, &win_pos );
-
- if ( win_pos.mX >= 0 && win_pos.mY >= 0 &&
- (win_pos.mX != sWinIMEWindowPosition.mX) || (win_pos.mY != sWinIMEWindowPosition.mY) )
- {
- COMPOSITIONFORM ime_form;
- memset( &ime_form, 0, sizeof(ime_form) );
- ime_form.dwStyle = CFS_POINT;
- ime_form.ptCurrentPos.x = win_pos.mX;
- ime_form.ptCurrentPos.y = win_pos.mY;
-
- LLWinImm::setCompositionWindow( himc, &ime_form );
-
- sWinIMEWindowPosition = win_pos;
- }
-
- LLWinImm::releaseContext(mWindowHandle, himc);
- }
-}
-
-
-void LLWindowWin32::fillCharPosition(const LLCoordGL& caret, const LLRect& bounds, const LLRect& control,
- IMECHARPOSITION *char_position)
-{
- LLCoordScreen caret_coord, top_left, bottom_right;
- convertCoords(caret, &caret_coord);
- convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left);
- convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right);
-
- char_position->pt.x = caret_coord.mX;
- char_position->pt.y = top_left.mY; // Windows wants the coordinate of upper left corner of a character...
- char_position->cLineHeight = bottom_right.mY - top_left.mY;
- char_position->rcDocument.left = top_left.mX;
- char_position->rcDocument.top = top_left.mY;
- char_position->rcDocument.right = bottom_right.mX;
- char_position->rcDocument.bottom = bottom_right.mY;
-}
-
-void LLWindowWin32::fillCompositionLogfont(LOGFONT *logfont)
-{
- // Our font is a list of FreeType recognized font files that may
- // not have a corresponding ones in Windows' fonts. Hence, we
- // can't simply tell Windows which font we are using. We will
- // notify a _standard_ font for a current input locale instead.
- // We use a hard-coded knowledge about the Windows' standard
- // configuration to do so...
-
- memset(logfont, 0, sizeof(LOGFONT));
-
- const WORD lang_id = LOWORD(GetKeyboardLayout(0));
- switch (PRIMARYLANGID(lang_id))
- {
- case LANG_CHINESE:
- // We need to identify one of two Chinese fonts.
- switch (SUBLANGID(lang_id))
- {
- case SUBLANG_CHINESE_SIMPLIFIED:
- case SUBLANG_CHINESE_SINGAPORE:
- logfont->lfCharSet = GB2312_CHARSET;
- lstrcpy(logfont->lfFaceName, TEXT("SimHei"));
- break;
- case SUBLANG_CHINESE_TRADITIONAL:
- case SUBLANG_CHINESE_HONGKONG:
- case SUBLANG_CHINESE_MACAU:
- default:
- logfont->lfCharSet = CHINESEBIG5_CHARSET;
- lstrcpy(logfont->lfFaceName, TEXT("MingLiU"));
- break;
- }
- break;
- case LANG_JAPANESE:
- logfont->lfCharSet = SHIFTJIS_CHARSET;
- lstrcpy(logfont->lfFaceName, TEXT("MS Gothic"));
- break;
- case LANG_KOREAN:
- logfont->lfCharSet = HANGUL_CHARSET;
- lstrcpy(logfont->lfFaceName, TEXT("Gulim"));
- break;
- default:
- logfont->lfCharSet = ANSI_CHARSET;
- lstrcpy(logfont->lfFaceName, TEXT("Tahoma"));
- break;
- }
-
- logfont->lfHeight = mPreeditor->getPreeditFontSize();
- logfont->lfWeight = FW_NORMAL;
-}
-
-U32 LLWindowWin32::fillReconvertString(const LLWString &text,
- S32 focus, S32 focus_length, RECONVERTSTRING *reconvert_string)
-{
- const llutf16string text_utf16 = wstring_to_utf16str(text);
- const DWORD required_size = sizeof(RECONVERTSTRING) + (text_utf16.length() + 1) * sizeof(WCHAR);
- if (reconvert_string && reconvert_string->dwSize >= required_size)
- {
- const DWORD focus_utf16_at = wstring_utf16_length(text, 0, focus);
- const DWORD focus_utf16_length = wstring_utf16_length(text, focus, focus_length);
-
- reconvert_string->dwVersion = 0;
- reconvert_string->dwStrLen = text_utf16.length();
- reconvert_string->dwStrOffset = sizeof(RECONVERTSTRING);
- reconvert_string->dwCompStrLen = focus_utf16_length;
- reconvert_string->dwCompStrOffset = focus_utf16_at * sizeof(WCHAR);
- reconvert_string->dwTargetStrLen = 0;
- reconvert_string->dwTargetStrOffset = focus_utf16_at * sizeof(WCHAR);
-
- const LPWSTR text = (LPWSTR)((BYTE *)reconvert_string + sizeof(RECONVERTSTRING));
- memcpy(text, text_utf16.c_str(), (text_utf16.length() + 1) * sizeof(WCHAR));
- }
- return required_size;
-}
-
-void LLWindowWin32::updateLanguageTextInputArea()
-{
- if (!mPreeditor || !LLWinImm::isAvailable())
- {
- return;
- }
-
- LLCoordGL caret_coord;
- LLRect preedit_bounds;
- if (mPreeditor->getPreeditLocation(-1, &caret_coord, &preedit_bounds, NULL))
- {
- mLanguageTextInputPointGL = caret_coord;
- mLanguageTextInputAreaGL = preedit_bounds;
-
- CANDIDATEFORM candidate_form;
- fillCandidateForm(caret_coord, preedit_bounds, &candidate_form);
-
- HIMC himc = LLWinImm::getContext(mWindowHandle);
- // Win32 document says there may be up to 4 candidate windows.
- // This magic number 4 appears only in the document, and
- // there are no constant/macro for the value...
- for (int i = 3; i >= 0; --i)
- {
- candidate_form.dwIndex = i;
- LLWinImm::setCandidateWindow(himc, &candidate_form);
- }
- LLWinImm::releaseContext(mWindowHandle, himc);
- }
-}
-
-void LLWindowWin32::interruptLanguageTextInput()
-{
- ASSERT_MAIN_THREAD();
- if (mPreeditor && LLWinImm::isAvailable())
- {
- HIMC himc = LLWinImm::getContext(mWindowHandle);
- LLWinImm::notifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
- LLWinImm::releaseContext(mWindowHandle, himc);
- }
-}
-
-void LLWindowWin32::handleStartCompositionMessage()
-{
- // Let IME know the font to use in feedback UI.
- LOGFONT logfont;
- fillCompositionLogfont(&logfont);
- HIMC himc = LLWinImm::getContext(mWindowHandle);
- LLWinImm::setCompositionFont(himc, &logfont);
- LLWinImm::releaseContext(mWindowHandle, himc);
-}
-
-// Handle WM_IME_COMPOSITION message.
-
-void LLWindowWin32::handleCompositionMessage(const U32 indexes)
-{
- if (!mPreeditor)
- {
- return;
- }
- bool needs_update = false;
- LLWString result_string;
- LLWString preedit_string;
- S32 preedit_string_utf16_length = 0;
- LLPreeditor::segment_lengths_t preedit_segment_lengths;
- LLPreeditor::standouts_t preedit_standouts;
-
- // Step I: Receive details of preedits from IME.
-
- HIMC himc = LLWinImm::getContext(mWindowHandle);
-
- if (indexes & GCS_RESULTSTR)
- {
- LONG size = LLWinImm::getCompositionString(himc, GCS_RESULTSTR, NULL, 0);
- if (size >= 0)
- {
- const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1];
- size = LLWinImm::getCompositionString(himc, GCS_RESULTSTR, data, size);
- if (size > 0)
- {
- result_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR)));
- }
- delete[] data;
- needs_update = true;
- }
- }
-
- if (indexes & GCS_COMPSTR)
- {
- LONG size = LLWinImm::getCompositionString(himc, GCS_COMPSTR, NULL, 0);
- if (size >= 0)
- {
- const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1];
- size = LLWinImm::getCompositionString(himc, GCS_COMPSTR, data, size);
- if (size > 0)
- {
- preedit_string_utf16_length = size / sizeof(WCHAR);
- preedit_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR)));
- }
- delete[] data;
- needs_update = true;
- }
- }
-
- if ((indexes & GCS_COMPCLAUSE) && preedit_string.length() > 0)
- {
- LONG size = LLWinImm::getCompositionString(himc, GCS_COMPCLAUSE, NULL, 0);
- if (size > 0)
- {
- const LPDWORD data = new DWORD[size / sizeof(DWORD)];
- size = LLWinImm::getCompositionString(himc, GCS_COMPCLAUSE, data, size);
- if (size >= sizeof(DWORD) * 2
- && data[0] == 0 && data[size / sizeof(DWORD) - 1] == preedit_string_utf16_length)
- {
- preedit_segment_lengths.resize(size / sizeof(DWORD) - 1);
- S32 offset = 0;
- for (U32 i = 0; i < preedit_segment_lengths.size(); i++)
- {
- const S32 length = wstring_wstring_length_from_utf16_length(preedit_string, offset, data[i + 1] - data[i]);
- preedit_segment_lengths[i] = length;
- offset += length;
- }
- }
- delete[] data;
- }
- }
-
- if ((indexes & GCS_COMPATTR) && preedit_segment_lengths.size() > 1)
- {
- LONG size = LLWinImm::getCompositionString(himc, GCS_COMPATTR, NULL, 0);
- if (size > 0)
- {
- const LPBYTE data = new BYTE[size / sizeof(BYTE)];
- size = LLWinImm::getCompositionString(himc, GCS_COMPATTR, data, size);
- if (size == preedit_string_utf16_length)
- {
- preedit_standouts.assign(preedit_segment_lengths.size(), false);
- S32 offset = 0;
- for (U32 i = 0; i < preedit_segment_lengths.size(); i++)
- {
- if (ATTR_TARGET_CONVERTED == data[offset] || ATTR_TARGET_NOTCONVERTED == data[offset])
- {
- preedit_standouts[i] = true;
- }
- offset += wstring_utf16_length(preedit_string, offset, preedit_segment_lengths[i]);
- }
- }
- delete[] data;
- }
- }
-
- S32 caret_position = preedit_string.length();
- if (indexes & GCS_CURSORPOS)
- {
- const S32 caret_position_utf16 = LLWinImm::getCompositionString(himc, GCS_CURSORPOS, NULL, 0);
- if (caret_position_utf16 >= 0 && caret_position <= preedit_string_utf16_length)
- {
- caret_position = wstring_wstring_length_from_utf16_length(preedit_string, 0, caret_position_utf16);
- }
- }
-
- if (indexes == 0)
- {
- // I'm not sure this condition really happens, but
- // Windows SDK document says it is an indication
- // of "reset everything."
- needs_update = true;
- }
-
- LLWinImm::releaseContext(mWindowHandle, himc);
-
- // Step II: Update the active preeditor.
-
- if (needs_update)
- {
- if (preedit_string.length() != 0 || result_string.length() != 0)
- {
- mPreeditor->resetPreedit();
- }
-
- if (result_string.length() > 0)
- {
- for (LLWString::const_iterator i = result_string.begin(); i != result_string.end(); i++)
- {
- mPreeditor->handleUnicodeCharHere(*i);
- }
- }
-
- if (preedit_string.length() == 0)
- {
- preedit_segment_lengths.clear();
- preedit_standouts.clear();
- }
- else
- {
- if (preedit_segment_lengths.size() == 0)
- {
- preedit_segment_lengths.assign(1, preedit_string.length());
- }
- if (preedit_standouts.size() == 0)
- {
- preedit_standouts.assign(preedit_segment_lengths.size(), false);
- }
- }
- mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths, preedit_standouts, caret_position);
-
- // Some IME doesn't query char position after WM_IME_COMPOSITION,
- // so we need to update them actively.
- updateLanguageTextInputArea();
- }
-}
-
-// Given a text and a focus range, find_context finds and returns a
-// surrounding context of the focused subtext. A variable pointed
-// to by offset receives the offset in llwchars of the beginning of
-// the returned context string in the given wtext.
-
-static LLWString find_context(const LLWString & wtext, S32 focus, S32 focus_length, S32 *offset)
-{
- static const S32 CONTEXT_EXCESS = 30; // This value is by experiences.
-
- const S32 e = llmin((S32) wtext.length(), focus + focus_length + CONTEXT_EXCESS);
- S32 end = focus + focus_length;
- while (end < e && '\n' != wtext[end])
- {
- end++;
- }
-
- const S32 s = llmax(0, focus - CONTEXT_EXCESS);
- S32 start = focus;
- while (start > s && '\n' != wtext[start - 1])
- {
- --start;
- }
-
- *offset = start;
- return wtext.substr(start, end - start);
-}
-
-// final stage of handling drop requests - both from WM_DROPFILES message
-// for files and via IDropTarget interface requests.
-LLWindowCallbacks::DragNDropResult LLWindowWin32::completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url )
-{
- ASSERT_MAIN_THREAD();
- return mCallbacks->handleDragNDrop( this, gl_coord, mask, action, url );
-}
-
-// Handle WM_IME_REQUEST message.
-// If it handled the message, returns true. Otherwise, false.
-// When it handled the message, the value to be returned from
-// the Window Procedure is set to *result.
-
-bool LLWindowWin32::handleImeRequests(WPARAM request, LPARAM param, LRESULT *result)
-{
- if ( mPreeditor )
- {
- switch (request)
- {
- case IMR_CANDIDATEWINDOW: // http://msdn2.microsoft.com/en-us/library/ms776080.aspx
- {
- LLCoordGL caret_coord;
- LLRect preedit_bounds;
- mPreeditor->getPreeditLocation(-1, &caret_coord, &preedit_bounds, NULL);
-
- CANDIDATEFORM *const form = (CANDIDATEFORM *)param;
- DWORD const dwIndex = form->dwIndex;
- fillCandidateForm(caret_coord, preedit_bounds, form);
- form->dwIndex = dwIndex;
-
- *result = 1;
- return true;
- }
- case IMR_QUERYCHARPOSITION:
- {
- IMECHARPOSITION *const char_position = (IMECHARPOSITION *)param;
-
- // char_position->dwCharPos counts in number of
- // WCHARs, i.e., UTF-16 encoding units, so we can't simply pass the
- // number to getPreeditLocation.
-
- const LLWString & wtext = mPreeditor->getPreeditString();
- S32 preedit, preedit_length;
- mPreeditor->getPreeditRange(&preedit, &preedit_length);
- LLCoordGL caret_coord;
- LLRect preedit_bounds, text_control;
- const S32 position = wstring_wstring_length_from_utf16_length(wtext, preedit, char_position->dwCharPos);
-
- if (!mPreeditor->getPreeditLocation(position, &caret_coord, &preedit_bounds, &text_control))
- {
- LL_WARNS("Window") << "*** IMR_QUERYCHARPOSITON called but getPreeditLocation failed." << LL_ENDL;
- return false;
- }
-
- fillCharPosition(caret_coord, preedit_bounds, text_control, char_position);
-
- *result = 1;
- return true;
- }
- case IMR_COMPOSITIONFONT:
- {
- fillCompositionLogfont((LOGFONT *)param);
-
- *result = 1;
- return true;
- }
- case IMR_RECONVERTSTRING:
- {
- mPreeditor->resetPreedit();
- const LLWString & wtext = mPreeditor->getPreeditString();
- S32 select, select_length;
- mPreeditor->getSelectionRange(&select, &select_length);
-
- S32 context_offset;
- const LLWString context = find_context(wtext, select, select_length, &context_offset);
-
- RECONVERTSTRING * const reconvert_string = (RECONVERTSTRING *)param;
- const U32 size = fillReconvertString(context, select - context_offset, select_length, reconvert_string);
- if (reconvert_string)
- {
- if (select_length == 0)
- {
- // Let the IME to decide the reconversion range, and
- // adjust the reconvert_string structure accordingly.
- HIMC himc = LLWinImm::getContext(mWindowHandle);
- const bool adjusted = LLWinImm::setCompositionString(himc,
- SCS_QUERYRECONVERTSTRING, reconvert_string, size, NULL, 0);
- LLWinImm::releaseContext(mWindowHandle, himc);
- if (adjusted)
- {
- const llutf16string & text_utf16 = wstring_to_utf16str(context);
- const S32 new_preedit_start = reconvert_string->dwCompStrOffset / sizeof(WCHAR);
- const S32 new_preedit_end = new_preedit_start + reconvert_string->dwCompStrLen;
- select = utf16str_wstring_length(text_utf16, new_preedit_start);
- select_length = utf16str_wstring_length(text_utf16, new_preedit_end) - select;
- select += context_offset;
- }
- }
- mPreeditor->markAsPreedit(select, select_length);
- }
-
- *result = size;
- return true;
- }
- case IMR_CONFIRMRECONVERTSTRING:
- {
- *result = 0;
- return true;
- }
- case IMR_DOCUMENTFEED:
- {
- const LLWString & wtext = mPreeditor->getPreeditString();
- S32 preedit, preedit_length;
- mPreeditor->getPreeditRange(&preedit, &preedit_length);
-
- S32 context_offset;
- LLWString context = find_context(wtext, preedit, preedit_length, &context_offset);
- preedit -= context_offset;
- preedit_length = llmin(preedit_length, (S32)context.length() - preedit);
- if (preedit_length > 0 && preedit >= 0)
- {
- // IMR_DOCUMENTFEED may be called when we have an active preedit.
- // We should pass the context string *excluding* the preedit string.
- // Otherwise, some IME are confused.
- context.erase(preedit, preedit_length);
- }
-
- RECONVERTSTRING *reconvert_string = (RECONVERTSTRING *)param;
- *result = fillReconvertString(context, preedit, 0, reconvert_string);
- return true;
- }
- default:
- return false;
- }
- }
-
- return false;
-}
-
-//static
-void LLWindowWin32::setDPIAwareness()
-{
- HMODULE hShcore = LoadLibrary(L"shcore.dll");
- if (hShcore != NULL)
- {
- SetProcessDpiAwarenessType pSPDA;
- pSPDA = (SetProcessDpiAwarenessType)GetProcAddress(hShcore, "SetProcessDpiAwareness");
- if (pSPDA)
- {
-
- HRESULT hr = pSPDA(PROCESS_PER_MONITOR_DPI_AWARE);
- if (hr != S_OK)
- {
- LL_WARNS() << "SetProcessDpiAwareness() function returned an error. Will use legacy DPI awareness API of Win XP/7" << LL_ENDL;
- }
- }
- FreeLibrary(hShcore);
- }
- else
- {
- LL_WARNS() << "Could not load shcore.dll library (included by <ShellScalingAPI.h> from Win 8.1 SDK. Will use legacy DPI awareness API of Win XP/7" << LL_ENDL;
- }
-}
-
-void* LLWindowWin32::getDirectInput8()
-{
- return &gDirectInput8;
-}
-
-bool LLWindowWin32::getInputDevices(U32 device_type_filter,
- std::function<bool(std::string&, LLSD&, void*)> osx_callback,
- void * di8_devices_callback,
- void* userdata)
-{
- if (gDirectInput8 != NULL)
- {
- // Enumerate devices
- HRESULT status = gDirectInput8->EnumDevices(
- (DWORD) device_type_filter, // DWORD dwDevType,
- (LPDIENUMDEVICESCALLBACK)di8_devices_callback, // LPDIENUMDEVICESCALLBACK lpCallback, // BOOL DIEnumDevicesCallback( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef ) // BOOL CALLBACK DinputDevice::DevicesCallback
- (LPVOID*)userdata, // LPVOID pvRef
- DIEDFL_ATTACHEDONLY // DWORD dwFlags
- );
-
- return status == DI_OK;
- }
- return false;
-}
-
-F32 LLWindowWin32::getSystemUISize()
-{
- F32 scale_value = 1.f;
- HWND hWnd = (HWND)getPlatformWindow();
- HDC hdc = GetDC(hWnd);
- HMONITOR hMonitor;
- HANDLE hProcess = GetCurrentProcess();
- PROCESS_DPI_AWARENESS dpi_awareness;
-
- HMODULE hShcore = LoadLibrary(L"shcore.dll");
-
- if (hShcore != NULL)
- {
- GetProcessDpiAwarenessType pGPDA;
- pGPDA = (GetProcessDpiAwarenessType)GetProcAddress(hShcore, "GetProcessDpiAwareness");
- GetDpiForMonitorType pGDFM;
- pGDFM = (GetDpiForMonitorType)GetProcAddress(hShcore, "GetDpiForMonitor");
- if (pGPDA != NULL && pGDFM != NULL)
- {
- pGPDA(hProcess, &dpi_awareness);
- if (dpi_awareness == PROCESS_PER_MONITOR_DPI_AWARE)
- {
- POINT pt;
- UINT dpix = 0, dpiy = 0;
- HRESULT hr = E_FAIL;
- RECT rect;
-
- GetWindowRect(hWnd, &rect);
- // Get the DPI for the monitor, on which the center of window is displayed and set the scaling factor
- pt.x = (rect.left + rect.right) / 2;
- pt.y = (rect.top + rect.bottom) / 2;
- hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
- hr = pGDFM(hMonitor, MDT_EFFECTIVE_DPI, &dpix, &dpiy);
- if (hr == S_OK)
- {
- scale_value = F32(dpix) / F32(USER_DEFAULT_SCREEN_DPI);
- }
- else
- {
- LL_WARNS() << "Could not determine DPI for monitor. Setting scale to default 100 %" << LL_ENDL;
- scale_value = 1.0f;
- }
- }
- else
- {
- LL_WARNS() << "Process is not per-monitor DPI-aware. Setting scale to default 100 %" << LL_ENDL;
- scale_value = 1.0f;
- }
- }
- FreeLibrary(hShcore);
- }
- else
- {
- LL_WARNS() << "Could not load shcore.dll library (included by <ShellScalingAPI.h> from Win 8.1 SDK). Using legacy DPI awareness API of Win XP/7" << LL_ENDL;
- scale_value = F32(GetDeviceCaps(hdc, LOGPIXELSX)) / F32(USER_DEFAULT_SCREEN_DPI);
- }
-
- ReleaseDC(hWnd, hdc);
- return scale_value;
-}
-
-//static
-std::vector<std::string> LLWindowWin32::getDisplaysResolutionList()
-{
- return sMonitorInfo.getResolutionsList();
-}
-
-//static
-std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList()
-{
- // Fonts previously in getFontListSans() have moved to fonts.xml.
- return std::vector<std::string>();
-}
-
-void LLWindowWin32::setMaxVRAMMegabytes(U32 max_vram)
-{
- if (mWindowThread)
- {
- mWindowThread->mMaxVRAM = max_vram;
- }
-}
-
-#endif // LL_WINDOWS
-
-inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread()
- : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, true /*should be false, temporary workaround for SL-18721*/)
-{
- LL::ThreadPool::start();
-}
-
-void LLWindowWin32::LLWindowWin32Thread::close()
-{
- if (!mQueue->isClosed())
- {
- LL_WARNS() << "Closing window thread without using destroy_window_handler" << LL_ENDL;
- LL::ThreadPool::close();
-
- // Workaround for SL-18721 in case window closes too early and abruptly
- LLSplashScreen::show();
- LLSplashScreen::update("..."); // will be updated later
- }
-}
-
-
-/**
- * LogChange is to log changes in status while trying to avoid spamming the
- * log with repeated messages, especially in a tight loop. It refuses to log
- * a continuous run of identical messages, but logs every time the message
- * changes. (It will happily spam when messages quickly bounce back and
- * forth.)
- */
-class LogChange
-{
-public:
- LogChange(const std::string& tag):
- mTag(tag)
- {}
-
- template <typename... Items>
- void always(Items&&... items)
- {
- // This odd construct ensures that the stringize() call is only
- // executed if DEBUG logging is enabled for the passed tag.
- LL_DEBUGS(mTag.c_str());
- log(LL_CONT, stringize(std::forward<Items>(items)...));
- LL_ENDL;
- }
-
- template <typename... Items>
- void onChange(Items&&... items)
- {
- LL_DEBUGS(mTag.c_str());
- auto str = stringize(std::forward<Items>(items)...);
- if (str != mPrev)
- {
- log(LL_CONT, str);
- }
- LL_ENDL;
- }
-
-private:
- void log(std::ostream& out, const std::string& message)
- {
- mPrev = message;
- out << message;
- }
- std::string mTag;
- std::string mPrev;
-};
-
-// Print hardware debug info about available graphics adapters in ordinal order
-void debugEnumerateGraphicsAdapters()
-{
- LL_INFOS("Window") << "Enumerating graphics adapters..." << LL_ENDL;
-
- IDXGIFactory1* factory;
- HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory);
- if (FAILED(res) || !factory)
- {
- LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL;
- }
- else
- {
- UINT graphics_adapter_index = 0;
- IDXGIAdapter3* dxgi_adapter;
- while (true)
- {
- res = factory->EnumAdapters(graphics_adapter_index, reinterpret_cast<IDXGIAdapter**>(&dxgi_adapter));
- if (FAILED(res))
- {
- if (graphics_adapter_index == 0)
- {
- LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL;
- }
- else
- {
- LL_INFOS("Window") << "Done enumerating graphics adapters" << LL_ENDL;
- }
- }
- else
- {
- DXGI_ADAPTER_DESC desc;
- dxgi_adapter->GetDesc(&desc);
- std::wstring description_w((wchar_t*)desc.Description);
- std::string description(description_w.begin(), description_w.end());
- LL_INFOS("Window") << "Graphics adapter index: " << graphics_adapter_index << ", "
- << "Description: " << description << ", "
- << "DeviceId: " << desc.DeviceId << ", "
- << "SubSysId: " << desc.SubSysId << ", "
- << "AdapterLuid: " << desc.AdapterLuid.HighPart << "_" << desc.AdapterLuid.LowPart << ", "
- << "DedicatedVideoMemory: " << desc.DedicatedVideoMemory / 1024 / 1024 << ", "
- << "DedicatedSystemMemory: " << desc.DedicatedSystemMemory / 1024 / 1024 << ", "
- << "SharedSystemMemory: " << desc.SharedSystemMemory / 1024 / 1024 << LL_ENDL;
- }
-
- if (dxgi_adapter)
- {
- dxgi_adapter->Release();
- dxgi_adapter = NULL;
- }
- else
- {
- break;
- }
-
- graphics_adapter_index++;
- }
- }
-
- if (factory)
- {
- factory->Release();
- }
-}
-
-void LLWindowWin32::LLWindowWin32Thread::initDX()
-{
- if (!mGLReady) { return; }
-
- if (mDXGIAdapter == NULL)
- {
- debugEnumerateGraphicsAdapters();
-
- IDXGIFactory4* pFactory = nullptr;
-
- HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&pFactory);
-
- if (FAILED(res))
- {
- LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL;
- }
- else
- {
- res = pFactory->EnumAdapters(0, reinterpret_cast<IDXGIAdapter**>(&mDXGIAdapter));
- if (FAILED(res))
- {
- LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "EnumAdapters success" << LL_ENDL;
- }
- }
-
- if (pFactory)
- {
- pFactory->Release();
- }
- }
-}
-
-void LLWindowWin32::LLWindowWin32Thread::initD3D()
-{
- if (!mGLReady) { return; }
-
- if (mDXGIAdapter == NULL && mD3DDevice == NULL && mWindowHandleThrd != 0)
- {
- mD3D = Direct3DCreate9(D3D_SDK_VERSION);
-
- D3DPRESENT_PARAMETERS d3dpp;
-
- ZeroMemory(&d3dpp, sizeof(d3dpp));
- d3dpp.Windowed = TRUE;
- d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
-
- HRESULT res = mD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mWindowHandleThrd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &mD3DDevice);
-
- if (FAILED(res))
- {
- LL_WARNS() << "(fallback) CreateDevice failed: 0x" << std::hex << res << LL_ENDL;
- }
- else
- {
- LL_INFOS() << "(fallback) CreateDevice success" << LL_ENDL;
- }
- }
-}
-
-void LLWindowWin32::LLWindowWin32Thread::cleanupDX()
-{
- //clean up DXGI/D3D resources
- if (mDXGIAdapter)
- {
- mDXGIAdapter->Release();
- mDXGIAdapter = nullptr;
- }
-
- if (mD3DDevice)
- {
- mD3DDevice->Release();
- mD3DDevice = nullptr;
- }
-
- if (mD3D)
- {
- mD3D->Release();
- mD3D = nullptr;
- }
-}
-
-void LLWindowWin32::LLWindowWin32Thread::run()
-{
- sWindowThreadId = std::this_thread::get_id();
- LogChange logger("Window");
-
- //as good a place as any to up the MM timer resolution (see ms_sleep)
- //attempt to set timer resolution to 1ms
- TIMECAPS tc;
- if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) == TIMERR_NOERROR)
- {
- timeBeginPeriod(llclamp((U32) 1, tc.wPeriodMin, tc.wPeriodMax));
- }
-
- while (! getQueue().done())
- {
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
-
- // lazily call initD3D inside this loop to catch when mGLReady has been set to true
- initDX();
-
- if (mWindowHandleThrd != 0)
- {
- // lazily call initD3D inside this loop to catch when mWindowHandle has been set, and mGLReady has been set to true
- // *TODO: Shutdown if this fails when mWindowHandle exists
- initD3D();
-
- MSG msg;
- BOOL status;
- if (mhDCThrd == 0)
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - PeekMessage");
- logger.onChange("PeekMessage(", std::hex, mWindowHandleThrd, ")");
- status = PeekMessage(&msg, mWindowHandleThrd, 0, 0, PM_REMOVE);
- }
- else
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - GetMessage");
- logger.always("GetMessage(", std::hex, mWindowHandleThrd, ")");
- status = GetMessage(&msg, NULL, 0, 0);
- }
- if (status > 0)
- {
- logger.always("got MSG (", std::hex, msg.hwnd, ", ", msg.message,
- ", ", msg.wParam, ")");
- TranslateMessage(&msg);
- DispatchMessage(&msg);
-
- mMessageQueue.pushFront(msg);
- }
- }
-
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - Function Queue");
- logger.onChange("runPending()");
- //process any pending functions
- getQueue().runPending();
- }
-
-#if 0
- {
- LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - Sleep");
- logger.always("sleep(1)");
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
- }
-#endif
- }
-
- cleanupDX();
-}
-
-void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy()
-{
- if (mQueue->isClosed())
- {
- LL_WARNS() << "Tried to close Queue. Win32 thread Queue already closed." << LL_ENDL;
- return;
- }
-
- // Make sure we don't leave a blank toolbar button.
- // Also hiding window now prevents user from suspending it
- // via some action (like dragging it around)
- ShowWindow(mWindowHandleThrd, SW_HIDE);
-
- // Schedule destruction
- HWND old_handle = mWindowHandleThrd;
- post([this]()
- {
- if (IsWindow(mWindowHandleThrd))
- {
- if (mhDCThrd)
- {
- if (!ReleaseDC(mWindowHandleThrd, mhDCThrd))
- {
- LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL;
- }
- mhDCThrd = NULL;
- }
-
- // This causes WM_DESTROY to be sent *immediately*
- if (!destroy_window_handler(mWindowHandleThrd))
- {
- LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL;
- }
- }
- else
- {
- // Something killed the window while we were busy destroying gl or handle somehow got broken
- LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL;
- }
- mWindowHandleThrd = NULL;
- mhDCThrd = NULL;
- mGLReady = false;
- });
-
- LL_DEBUGS("Window") << "Closing window's pool queue" << LL_ENDL;
- mQueue->close();
-
- // Post a nonsense user message to wake up the thread in
- // case it is waiting for a getMessage()
- if (old_handle)
- {
- WPARAM wparam{ 0xB0B0 };
- LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle
- << ", " << WM_DUMMY_
- << ", " << wparam << ")" << std::dec << LL_ENDL;
- PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337);
- }
-
- // There are cases where window will refuse to close,
- // can't wait forever on join, check state instead
- LLTimer timeout;
- timeout.setTimerExpirySec(2.0);
- while (!getQueue().done() && !timeout.hasExpired() && mWindowHandleThrd)
- {
- ms_sleep(100);
- }
-
- if (getQueue().done() || mWindowHandleThrd == NULL)
- {
- // Window is closed, started closing or is cleaning up
- // now wait for our single thread to die.
- if (mWindowHandleThrd)
- {
- LL_INFOS("Window") << "Window is closing, waiting on pool's thread to join, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL;
- }
- else
- {
- LL_DEBUGS("Window") << "Waiting on pool's thread, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL;
- }
- for (auto& pair : mThreads)
- {
- pair.second.join();
- }
- }
- else
- {
- // Something suspended window thread, can't afford to wait forever
- // so kill thread instead
- // Ex: This can happen if user starts dragging window arround (if it
- // was visible) or a modal notification pops up
- LL_WARNS("Window") << "Window is frozen, couldn't perform clean exit" << LL_ENDL;
-
- for (auto& pair : mThreads)
- {
- // very unsafe
- TerminateThread(pair.second.native_handle(), 0);
- pair.second.detach();
- cleanupDX();
- }
- }
- LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL;
-}
-
-void LLWindowWin32::post(const std::function<void()>& func)
-{
- mFunctionQueue.pushFront(func);
-}
-
-void LLWindowWin32::postMouseButtonEvent(const std::function<void()>& func)
-{
- mMouseQueue.pushFront(func);
-}
-
-void LLWindowWin32::kickWindowThread(HWND windowHandle)
-{
- if (! windowHandle)
- windowHandle = mWindowHandle;
- if (windowHandle)
- {
- // post a nonsense user message to wake up the Window Thread in
- // case any functions are pending and no windows events came
- // through this frame
- WPARAM wparam{ 0xB0B0 };
- LL_DEBUGS("Window") << "PostMessage(" << std::hex << windowHandle
- << ", " << WM_DUMMY_
- << ", " << wparam << ")" << std::dec << LL_ENDL;
- PostMessage(windowHandle, WM_DUMMY_, wparam, 0x1337);
- }
-}
-
-void LLWindowWin32::updateWindowRect()
-{
- LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32;
- //called from window thread
- RECT rect;
- RECT client_rect;
-
- if (GetWindowRect(mWindowHandle, &rect) &&
- GetClientRect(mWindowHandle, &client_rect))
- {
- post([=]
- {
- mRect = rect;
- mClientRect = client_rect;
- });
- }
-}
+/** + * @file llwindowwin32.cpp + * @brief Platform-dependent implementation of llwindow + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#if LL_WINDOWS && !LL_MESA_HEADLESS + +#include "llwindowwin32.h" + +// LLWindow library includes +#include "llkeyboardwin32.h" +#include "lldragdropwin32.h" +#include "llpreeditor.h" +#include "llwindowcallbacks.h" + +// Linden library includes +#include "llerror.h" +#include "llexception.h" +#include "llfasttimer.h" +#include "llgl.h" +#include "llstring.h" +#include "lldir.h" +#include "llsdutil.h" +#include "llglslshader.h" +#include "llthreadsafequeue.h" +#include "stringize.h" +#include "llframetimer.h" + +// System includes +#include <commdlg.h> +#include <WinUser.h> +#include <mapi.h> +#include <process.h> // for _spawn +#include <shellapi.h> +#include <fstream> +#include <Imm.h> +#include <iomanip> +#include <future> +#include <sstream> +#include <utility> // std::pair + +#include <d3d9.h> +#include <dxgi1_4.h> +#include <timeapi.h> + +// Require DirectInput version 8 +#define DIRECTINPUT_VERSION 0x0800 + +#include <dinput.h> +#include <Dbt.h.> +#include <InitGuid.h> // needed for llurlentry test to build on some systems +#pragma comment(lib, "dxguid.lib") // needed for llurlentry test to build on some systems +#pragma comment(lib, "dinput8") + +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; + +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#endif + +#ifndef USER_DEFAULT_SCREEN_DPI +#define USER_DEFAULT_SCREEN_DPI 96 // Win7 +#endif + +// Claim a couple unused GetMessage() message IDs +const UINT WM_DUMMY_(WM_USER + 0x0017); +const UINT WM_POST_FUNCTION_(WM_USER + 0x0018); + +extern bool gDebugWindowProc; + +static std::thread::id sWindowThreadId; +static std::thread::id sMainThreadId; + +#if 1 // flip to zero to enable assertions for functions being called from wrong thread +#define ASSERT_MAIN_THREAD() +#define ASSERT_WINDOW_THREAD() +#else +#define ASSERT_MAIN_THREAD() llassert(LLThread::currentID() == sMainThreadId) +#define ASSERT_WINDOW_THREAD() llassert(LLThread::currentID() == sWindowThreadId) +#endif + + +LPWSTR gIconResource = IDI_APPLICATION; +LPDIRECTINPUT8 gDirectInput8; + +LLW32MsgCallback gAsyncMsgCallback = NULL; + +#ifndef DPI_ENUMS_DECLARED + +typedef enum PROCESS_DPI_AWARENESS { + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} PROCESS_DPI_AWARENESS; + +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; + +#endif + +typedef HRESULT(STDAPICALLTYPE *SetProcessDpiAwarenessType)(_In_ PROCESS_DPI_AWARENESS value); + +typedef HRESULT(STDAPICALLTYPE *GetProcessDpiAwarenessType)( + _In_ HANDLE hprocess, + _Out_ PROCESS_DPI_AWARENESS *value); + +typedef HRESULT(STDAPICALLTYPE *GetDpiForMonitorType)( + _In_ HMONITOR hmonitor, + _In_ MONITOR_DPI_TYPE dpiType, + _Out_ UINT *dpiX, + _Out_ UINT *dpiY); + +// +// LLWindowWin32 +// + +void show_window_creation_error(const std::string& title) +{ + LL_WARNS("Window") << title << LL_ENDL; +} + +HGLRC SafeCreateContext(HDC &hdc) +{ + __try + { + return wglCreateContext(hdc); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + return NULL; + } +} + +GLuint SafeChoosePixelFormat(HDC &hdc, const PIXELFORMATDESCRIPTOR *ppfd) +{ + __try + { + return ChoosePixelFormat(hdc, ppfd); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + // convert to C++ styled exception + // C exception don't allow classes, so it's a regular char array + char integer_string[32]; + sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); + throw std::exception(integer_string); + } +} + +//static +bool LLWindowWin32::sIsClassRegistered = false; + +bool LLWindowWin32::sLanguageTextInputAllowed = true; +bool LLWindowWin32::sWinIMEOpened = false; +HKL LLWindowWin32::sWinInputLocale = 0; +DWORD LLWindowWin32::sWinIMEConversionMode = IME_CMODE_NATIVE; +DWORD LLWindowWin32::sWinIMESentenceMode = IME_SMODE_AUTOMATIC; +LLCoordWindow LLWindowWin32::sWinIMEWindowPosition(-1,-1); + +// The following class LLWinImm delegates Windows IMM APIs. +// It was originally introduced to support US Windows XP, on which we needed +// to dynamically load IMM32.DLL and use GetProcAddress to resolve its entry +// points. Now that that's moot, we retain this wrapper only for hooks for +// metrics. + +class LLWinImm +{ +public: + static bool isAvailable() { return true; } + +public: + // Wrappers for IMM API. + static bool isIME(HKL hkl); + static HIMC getContext(HWND hwnd); + static bool releaseContext(HWND hwnd, HIMC himc); + static bool getOpenStatus(HIMC himc); + static bool setOpenStatus(HIMC himc, bool status); + static bool getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence); + static bool setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence); + static bool getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form); + static bool setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form); + static LONG getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length); + static bool setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength); + static bool setCompositionFont(HIMC himc, LPLOGFONTW logfont); + static bool setCandidateWindow(HIMC himc, LPCANDIDATEFORM candidate_form); + static bool notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value); +}; + +// static +bool LLWinImm::isIME(HKL hkl) +{ + return ImmIsIME(hkl); +} + +// static +HIMC LLWinImm::getContext(HWND hwnd) +{ + return ImmGetContext(hwnd); +} + +//static +bool LLWinImm::releaseContext(HWND hwnd, HIMC himc) +{ + return ImmReleaseContext(hwnd, himc); +} + +// static +bool LLWinImm::getOpenStatus(HIMC himc) +{ + return ImmGetOpenStatus(himc); +} + +// static +bool LLWinImm::setOpenStatus(HIMC himc, bool status) +{ + return ImmSetOpenStatus(himc, status); +} + +// static +bool LLWinImm::getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence) +{ + return ImmGetConversionStatus(himc, conversion, sentence); +} + +// static +bool LLWinImm::setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence) +{ + return ImmSetConversionStatus(himc, conversion, sentence); +} + +// static +bool LLWinImm::getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form) +{ + return ImmGetCompositionWindow(himc, form); +} + +// static +bool LLWinImm::setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form) +{ + return ImmSetCompositionWindow(himc, form); +} + + +// static +LONG LLWinImm::getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length) +{ + return ImmGetCompositionString(himc, index, data, length); +} + + +// static +bool LLWinImm::setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength) +{ + return ImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength); +} + +// static +bool LLWinImm::setCompositionFont(HIMC himc, LPLOGFONTW pFont) +{ + return ImmSetCompositionFont(himc, pFont); +} + +// static +bool LLWinImm::setCandidateWindow(HIMC himc, LPCANDIDATEFORM form) +{ + return ImmSetCandidateWindow(himc, form); +} + +// static +bool LLWinImm::notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value) +{ + return ImmNotifyIME(himc, action, index, value); +} + + + +class LLMonitorInfo +{ +public: + + std::vector<std::string> getResolutionsList() { return mResList; } + + LLMonitorInfo() + { + EnumDisplayMonitors(0, 0, MonitorEnum, (LPARAM)this); + } + +private: + + static BOOL CALLBACK MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData) + { + int monitor_width = lprcMonitor->right - lprcMonitor->left; + int monitor_height = lprcMonitor->bottom - lprcMonitor->top; + + std::ostringstream sstream; + sstream << monitor_width << "x" << monitor_height;; + std::string res = sstream.str(); + + LLMonitorInfo* pThis = reinterpret_cast<LLMonitorInfo*>(pData); + pThis->mResList.push_back(res); + + return TRUE; + } + + std::vector<std::string> mResList; +}; + +static LLMonitorInfo sMonitorInfo; + + +// Thread that owns the Window Handle +// This whole struct is private to LLWindowWin32, which needs to mess with its +// members, which is why it's a struct rather than a class. In effect, we make +// the containing class a friend. +struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool +{ + static const int MAX_QUEUE_SIZE = 2048; + + LLThreadSafeQueue<MSG> mMessageQueue; + + LLWindowWin32Thread(); + + void run() override; + void close() override; + + // closes queue, wakes thread, waits until thread closes + void wakeAndDestroy(); + + void glReady() + { + mGLReady = true; + } + + // initialzie DXGI adapter (for querying available VRAM) + void initDX(); + + // initialize D3D (if DXGI cannot be used) + void initD3D(); + + //clean up DXGI/D3D resources + void cleanupDX(); + + /// called by main thread to post work to this window thread + template <typename CALLABLE> + void post(CALLABLE&& func) + { + // Ignore bool return. Shutdown timing is tricky: the main thread can + // end up trying to post a cursor position after having closed the + // WorkQueue. + getQueue().post(std::forward<CALLABLE>(func)); + } + + /** + * Like post(), Post() is a way of conveying a single work item to this + * thread. Its virtue is that it will definitely be executed "soon" rather + * than potentially waiting for the next frame: it uses PostMessage() to + * break us out of the window thread's blocked GetMessage() call. It's + * more expensive, though, not only from the Windows API latency of + * PostMessage() and GetMessage(), but also because it involves heap + * allocation and release. + * + * Require HWND from caller, even though we store an HWND locally. + * Otherwise, if our mWindowHandle was accessed from both threads, we'd + * have to protect it with a mutex. + */ + template <typename CALLABLE> + void Post(HWND windowHandle, CALLABLE&& func) + { + // Move func to the heap. If we knew FuncType could fit into LPARAM, + // we could simply instantiate FuncType and pass it by value. But + // since we don't, we must put that on the heap as well as the + // internal heap allocation it likely requires to store func. + auto ptr = new FuncType(std::move(func)); + WPARAM wparam{ 0xF1C }; + LL_DEBUGS("Window") << "PostMessage(" << std::hex << windowHandle + << ", " << WM_POST_FUNCTION_ + << ", " << wparam << std::dec << LL_ENDL; + PostMessage(windowHandle, WM_POST_FUNCTION_, wparam, LPARAM(ptr)); + } + + using FuncType = std::function<void()>; + // call GetMessage() and pull enqueue messages for later processing + void gatherInput(); + HWND mWindowHandleThrd = NULL; + HDC mhDCThrd = 0; + + // *HACK: Attempt to prevent startup crashes by deferring memory accounting + // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18 + bool mGLReady = false; + + U32 mMaxVRAM = 0; // maximum amount of vram to allow in the "budget", or 0 for no maximum (see updateVRAMUsage) + + IDXGIAdapter3* mDXGIAdapter = nullptr; + LPDIRECT3D9 mD3D = nullptr; + LPDIRECT3DDEVICE9 mD3DDevice = nullptr; +}; + + +LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, + const std::string& title, const std::string& name, S32 x, S32 y, S32 width, + S32 height, U32 flags, + bool fullscreen, bool clearBg, + bool enable_vsync, bool use_gl, + bool ignore_pixel_depth, + U32 fsaa_samples, + U32 max_cores, + U32 max_vram, + F32 max_gl_version) + : + LLWindow(callbacks, fullscreen, flags), + mMaxGLVersion(max_gl_version), + mMaxCores(max_cores) +{ + sMainThreadId = LLThread::currentID(); + mWindowThread = new LLWindowWin32Thread(); + mWindowThread->mMaxVRAM = max_vram; + + //MAINT-516 -- force a load of opengl32.dll just in case windows went sideways + LoadLibrary(L"opengl32.dll"); + + + if (mMaxCores != 0) + { + HANDLE hProcess = GetCurrentProcess(); + mMaxCores = llmin(mMaxCores, (U32) 64); + DWORD_PTR mask = 0; + + for (int i = 0; i < mMaxCores; ++i) + { + mask |= ((DWORD_PTR) 1) << i; + } + + SetProcessAffinityMask(hProcess, mask); + } + +#if 0 // this is probably a bad idea, but keep it in your back pocket if you see what looks like + // process deprioritization during profiles + // force high thread priority + HANDLE hProcess = GetCurrentProcess(); + + if (hProcess) + { + int priority = GetPriorityClass(hProcess); + if (priority < REALTIME_PRIORITY_CLASS) + { + if (SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS)) + { + LL_INFOS() << "Set process priority to REALTIME_PRIORITY_CLASS" << LL_ENDL; + } + else + { + LL_INFOS() << "Failed to set process priority: " << std::hex << GetLastError() << LL_ENDL; + } + } + } +#endif + +#if 0 // this is also probably a bad idea, but keep it in your back pocket for getting main thread off of background thread cores (see also LLThread::threadRun) + HANDLE hThread = GetCurrentThread(); + + SYSTEM_INFO sysInfo; + + GetSystemInfo(&sysInfo); + U32 core_count = sysInfo.dwNumberOfProcessors; + + if (max_cores != 0) + { + core_count = llmin(core_count, max_cores); + } + + if (hThread) + { + int priority = GetThreadPriority(hThread); + + if (priority < THREAD_PRIORITY_TIME_CRITICAL) + { + if (SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL)) + { + LL_INFOS() << "Set thread priority to THREAD_PRIORITY_TIME_CRITICAL" << LL_ENDL; + } + else + { + LL_INFOS() << "Failed to set thread priority: " << std::hex << GetLastError() << LL_ENDL; + } + + // tell main thread to prefer core 0 + SetThreadIdealProcessor(hThread, 0); + } + } +#endif + + + mFSAASamples = fsaa_samples; + mIconResource = gIconResource; + mOverrideAspectRatio = 0.f; + mNativeAspectRatio = 0.f; + mInputProcessingPaused = false; + mPreeditor = NULL; + mKeyCharCode = 0; + mKeyScanCode = 0; + mKeyVirtualKey = 0; + mhDC = NULL; + mhRC = NULL; + memset(mCurrentGammaRamp, 0, sizeof(mCurrentGammaRamp)); + memset(mPrevGammaRamp, 0, sizeof(mPrevGammaRamp)); + mCustomGammaSet = false; + mWindowHandle = NULL; + + mRect = {0, 0, 0, 0}; + mClientRect = {0, 0, 0, 0}; + + if (!SystemParametersInfo(SPI_GETMOUSEVANISH, 0, &mMouseVanish, 0)) + { + mMouseVanish = true; + } + + // Initialize the keyboard + gKeyboard = new LLKeyboardWin32(); + gKeyboard->setCallbacks(callbacks); + + // Initialize the Drag and Drop functionality + mDragDrop = new LLDragDropWin32; + + // Initialize (boot strap) the Language text input management, + // based on the system's (user's) default settings. + allowLanguageTextInput(mPreeditor, false); + + WNDCLASS wc; + RECT window_rect; + + // Set the window title + if (title.empty()) + { + mWindowTitle = new WCHAR[50]; + wsprintf(mWindowTitle, L"OpenGL Window"); + } + else + { + mWindowTitle = new WCHAR[256]; // Assume title length < 255 chars. + mbstowcs(mWindowTitle, title.c_str(), 255); + mWindowTitle[255] = 0; + } + + // Set the window class name + if (name.empty()) + { + mWindowClassName = new WCHAR[50]; + wsprintf(mWindowClassName, L"OpenGL Window"); + } + else + { + mWindowClassName = new WCHAR[256]; // Assume title length < 255 chars. + mbstowcs(mWindowClassName, name.c_str(), 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); + + // Init Direct Input - needed for joystick / Spacemouse + + LPDIRECTINPUT8 di8_interface; + HRESULT status = DirectInput8Create( + mhInstance, // HINSTANCE hinst, + DIRECTINPUT_VERSION, // DWORD dwVersion, + IID_IDirectInput8, // REFIID riidltf, + (LPVOID*)&di8_interface, // LPVOID * ppvOut, + NULL // LPUNKNOWN punkOuter + ); + if (status == DI_OK) + { + gDirectInput8 = di8_interface; + } + + 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(mCallbacks->translateString("MBRegClassFailed"), + mCallbacks->translateString("MBError"), OSMB_OK); + return; + } + sIsClassRegistered = true; + } + + //----------------------------------------------------------------------- + // Get the current refresh rate + //----------------------------------------------------------------------- + + DEVMODE dev_mode; + ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); + dev_mode.dmSize = sizeof(DEVMODE); + 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; + } + mRefreshRate = current_refresh; + //----------------------------------------------------------------------- + // 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) + { + LL_WARNS("Window") << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << LL_ENDL; + //success = false; + + if (!EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode)) + { + success = false; + } + else + { + if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL) + { + LL_WARNS("Window") << "Current BBP is OK falling back to that" << LL_ENDL; + window_rect.right=width=dev_mode.dmPelsWidth; + window_rect.bottom=height=dev_mode.dmPelsHeight; + success = true; + } + else + { + LL_WARNS("Window") << "Current BBP is BAD" << LL_ENDL; + 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; + + LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth + << "x" << dev_mode.dmPelsHeight + << "x" << dev_mode.dmBitsPerPel + << " @ " << dev_mode.dmDisplayFrequency + << LL_ENDL; + } + else + { + mFullscreen = false; + mFullscreenWidth = -1; + mFullscreenHeight = -1; + mFullscreenBits = -1; + mFullscreenRefresh = -1; + + std::map<std::string,std::string> args; + args["[WIDTH]"] = llformat("%d", width); + args["[HEIGHT]"] = llformat ("%d", height); + OSMessageBox(mCallbacks->translateString("MBFullScreenErr", args), + mCallbacks->translateString("MBError"), OSMB_OK); + } + } + + // 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 ); + // } + + // SL-12971 dual GPU display + DISPLAY_DEVICEA display_device; + int display_index = -1; + DWORD display_flags = 0; // EDD_GET_DEVICE_INTERFACE_NAME ? + const size_t display_bytes = sizeof(display_device); + + do + { + if (display_index >= 0) + { + // CHAR DeviceName [ 32] Adapter name + // CHAR DeviceString[128] + CHAR text[256]; + + size_t name_len = strlen(display_device.DeviceName ); + size_t desc_len = strlen(display_device.DeviceString); + + CHAR *name = name_len ? display_device.DeviceName : "???"; + CHAR *desc = desc_len ? display_device.DeviceString : "???"; + + sprintf(text, "Display Device %d: %s, %s", display_index, name, desc); + LL_INFOS("Window") << text << LL_ENDL; + } + + ::ZeroMemory(&display_device,display_bytes); + display_device.cb = display_bytes; + + display_index++; + } while( EnumDisplayDevicesA(NULL, display_index, &display_device, display_flags )); + + LL_INFOS("Window") << "Total Display Devices: " << display_index << LL_ENDL; + + //----------------------------------------------------------------------- + // Create GL drawing context + //----------------------------------------------------------------------- + LLCoordScreen windowPos(x,y); + LLCoordScreen windowSize(window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top); + if (!switchContext(mFullscreen, windowSize, enable_vsync, &windowPos)) + { + return; + } + + //start with arrow cursor + initCursors(); + setCursor( UI_CURSOR_ARROW ); + + mRawMouse.usUsagePage = 0x01; // HID_USAGE_PAGE_GENERIC + mRawMouse.usUsage = 0x02; // HID_USAGE_GENERIC_MOUSE + mRawMouse.dwFlags = 0; // adds mouse and also ignores legacy mouse messages + mRawMouse.hwndTarget = 0; + + RegisterRawInputDevices(&mRawMouse, 1, sizeof(mRawMouse)); + + // Initialize (boot strap) the Language text input management, + // based on the system's (or user's) default settings. + allowLanguageTextInput(NULL, false); +} + + +LLWindowWin32::~LLWindowWin32() +{ + delete mDragDrop; + + delete [] mWindowTitle; + mWindowTitle = NULL; + + delete [] mSupportedResolutions; + mSupportedResolutions = NULL; + + delete [] mWindowClassName; + mWindowClassName = NULL; + + delete mWindowThread; +} + +void LLWindowWin32::show() +{ + LL_DEBUGS("Window") << "Setting window to show" << LL_ENDL; + ShowWindow(mWindowHandle, SW_SHOW); + SetForegroundWindow(mWindowHandle); + SetFocus(mWindowHandle); +} + +void LLWindowWin32::hide() +{ + setMouseClipping(false); + ShowWindow(mWindowHandle, SW_HIDE); +} + +//virtual +void LLWindowWin32::minimize() +{ + setMouseClipping(false); + showCursor(); + ShowWindow(mWindowHandle, SW_MINIMIZE); +} + +//virtual +void LLWindowWin32::restore() +{ + ShowWindow(mWindowHandle, SW_RESTORE); + SetForegroundWindow(mWindowHandle); + SetFocus(mWindowHandle); +} + +// See SL-12170 +// According to callstack "c0000005 Access violation" happened inside __try block, +// deep in DestroyWindow and crashed viewer, which shouldn't be possible. +// I tried manually causing this exception and it was caught without issues, so +// I'm turning off optimizations for this part to be sure code executes as intended +// (it is a straw, but I have no idea why else __try can get overruled) +#pragma optimize("", off) +bool destroy_window_handler(HWND hWnd) +{ + bool res; + __try + { + res = DestroyWindow(hWnd); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + res = false; + } + return res; +} +#pragma optimize("", on) + +// close() destroys all OS-specific code associated with a window. +// Usually called from LLWindowManager::destroyWindow() +void LLWindowWin32::close() +{ + LL_DEBUGS("Window") << "Closing LLWindowWin32" << LL_ENDL; + // Is window is already closed? + if (!mWindowHandle) + { + return; + } + + mDragDrop->reset(); + + + // Go back to screen mode written in the registry. + if (mFullscreen) + { + resetDisplayResolution(); + } + + // Make sure cursor is visible and we haven't mangled the clipping state. + showCursor(); + setMouseClipping(false); + if (gKeyboard) + { + gKeyboard->resetKeys(); + } + + // Clean up remaining GL state + if (gGLManager.mInited) + { + LL_INFOS("Window") << "Cleaning up GL" << LL_ENDL; + gGLManager.shutdownGL(); + } + + LL_DEBUGS("Window") << "Releasing Context" << LL_ENDL; + if (mhRC) + { + if (!wglMakeCurrent(NULL, NULL)) + { + LL_WARNS("Window") << "Release of DC and RC failed" << LL_ENDL; + } + + if (!wglDeleteContext(mhRC)) + { + LL_WARNS("Window") << "Release of rendering context failed" << LL_ENDL; + } + + mhRC = NULL; + } + + // Restore gamma to the system values. + restoreGamma(); + + LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL; + + mhDC = NULL; + mWindowHandle = NULL; + + mWindowThread->wakeAndDestroy(); +} + +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; + + mWindowThread->post([=] + { + WINDOWPLACEMENT placement; + placement.length = sizeof(WINDOWPLACEMENT); + + if (GetWindowPlacement(mWindowHandle, &placement)) + { + placement.showCmd = SW_MAXIMIZE; + SetWindowPlacement(mWindowHandle, &placement); + } + }); + + return true; +} + +bool LLWindowWin32::getFullscreen() +{ + return mFullscreen; +} + +bool LLWindowWin32::getPosition(LLCoordScreen *position) +{ + position->mX = mRect.left; + position->mY = mRect.top; + return true; +} + +bool LLWindowWin32::getSize(LLCoordScreen *size) +{ + size->mX = mRect.right - mRect.left; + size->mY = mRect.bottom - mRect.top; + return true; +} + +bool LLWindowWin32::getSize(LLCoordWindow *size) +{ + size->mX = mClientRect.right - mClientRect.left; + size->mY = mClientRect.bottom - mClientRect.top; + return true; +} + +bool LLWindowWin32::setPosition(const LLCoordScreen position) +{ + LLCoordScreen size; + + if (!mWindowHandle) + { + return false; + } + getSize(&size); + moveWindow(position, size); + return true; +} + +bool LLWindowWin32::setSizeImpl(const LLCoordScreen size) +{ + LLCoordScreen position; + + getPosition(&position); + if (!mWindowHandle) + { + return false; + } + + mWindowThread->post([=]() + { + WINDOWPLACEMENT placement; + placement.length = sizeof(WINDOWPLACEMENT); + + if (GetWindowPlacement(mWindowHandle, &placement)) + { + placement.showCmd = SW_RESTORE; + SetWindowPlacement(mWindowHandle, &placement); + } + }); + + moveWindow(position, size); + return true; +} + +bool LLWindowWin32::setSizeImpl(const LLCoordWindow size) +{ + RECT window_rect = {0, 0, size.mX, size.mY }; + DWORD dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dw_style = WS_OVERLAPPEDWINDOW; + + AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style); + + return setSizeImpl(LLCoordScreen(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top)); +} + +// changing fullscreen resolution +bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bool enable_vsync, const LLCoordScreen* const posp) +{ + //called from main thread + GLuint pixel_format; + DEVMODE dev_mode; + ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); + dev_mode.dmSize = sizeof(DEVMODE); + DWORD current_refresh; + DWORD dw_ex_style; + DWORD dw_style; + RECT window_rect = { 0, 0, 0, 0 }; + S32 width = size.mX; + S32 height = size.mY; + bool auto_show = false; + + if (mhRC) + { + auto_show = true; + resetDisplayResolution(); + } + + if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode)) + { + current_refresh = dev_mode.dmDisplayFrequency; + } + else + { + current_refresh = 60; + } + mRefreshRate = current_refresh; + + gGLManager.shutdownGL(); + //destroy gl context + if (mhRC) + { + if (!wglMakeCurrent(NULL, NULL)) + { + LL_WARNS("Window") << "Release of DC and RC failed" << LL_ENDL; + } + + if (!wglDeleteContext(mhRC)) + { + LL_WARNS("Window") << "Release of rendering context failed" << LL_ENDL; + } + + 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) + { + LL_WARNS("Window") << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << LL_ENDL; + 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; + + LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth + << "x" << dev_mode.dmPelsHeight + << "x" << dev_mode.dmBitsPerPel + << " @ " << dev_mode.dmDisplayFrequency + << LL_ENDL; + + 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. + // This converts client rect to window rect, i.e. expands it by the window border size. + 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; + + LL_INFOS("Window") << "Unable to run fullscreen at " << width << "x" << height << LL_ENDL; + return false; + } + } + else + { + mFullscreen = false; + window_rect.left = (long)(posp ? posp->mX : 0); + window_rect.right = (long)width + window_rect.left; // Windows GDI rects don't include rightmost pixel + window_rect.top = (long)(posp ? posp->mY : 0); + window_rect.bottom = (long)height + window_rect.top; + // 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 + LL_DEBUGS("Window") << "Creating window with X: " << window_rect.left + << " Y: " << window_rect.top + << " Width: " << (window_rect.right - window_rect.left) + << " Height: " << (window_rect.bottom - window_rect.top) + << " Fullscreen: " << mFullscreen + << LL_ENDL; + + recreateWindow(window_rect, dw_ex_style, dw_style); + + if (mWindowHandle) + { + LL_INFOS("Window") << "window is created." << LL_ENDL ; + } + else + { + LL_WARNS("Window") << "Window creation failed, code: " << GetLastError() << LL_ENDL; + } + + //----------------------------------------------------------------------- + // 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) + { + close(); + OSMessageBox(mCallbacks->translateString("MBDevContextErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + return false; + } + + LL_INFOS("Window") << "Device context retrieved." << LL_ENDL ; + + try + { + // Looks like ChoosePixelFormat can crash in case of faulty driver + if (!(pixel_format = SafeChoosePixelFormat(mhDC, &pfd))) + { + LL_WARNS("Window") << "ChoosePixelFormat failed, code: " << GetLastError() << LL_ENDL; + OSMessageBox(mCallbacks->translateString("MBPixelFmtErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + } + catch (...) + { + LOG_UNHANDLED_EXCEPTION("ChoosePixelFormat"); + OSMessageBox(mCallbacks->translateString("MBPixelFmtErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + LL_INFOS("Window") << "Pixel format chosen." << LL_ENDL ; + + // Verify what pixel format we actually received. + if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), + &pfd)) + { + OSMessageBox(mCallbacks->translateString("MBPixelFmtDescErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + // (EXP-1765) dump pixel data to see if there is a pattern that leads to unreproducible crash + LL_INFOS("Window") << "--- begin pixel format dump ---" << LL_ENDL ; + LL_INFOS("Window") << "pixel_format is " << pixel_format << LL_ENDL ; + LL_INFOS("Window") << "pfd.nSize: " << pfd.nSize << LL_ENDL ; + LL_INFOS("Window") << "pfd.nVersion: " << pfd.nVersion << LL_ENDL ; + LL_INFOS("Window") << "pfd.dwFlags: 0x" << std::hex << pfd.dwFlags << std::dec << LL_ENDL ; + LL_INFOS("Window") << "pfd.iPixelType: " << (int)pfd.iPixelType << LL_ENDL ; + LL_INFOS("Window") << "pfd.cColorBits: " << (int)pfd.cColorBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cRedBits: " << (int)pfd.cRedBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cRedShift: " << (int)pfd.cRedShift << LL_ENDL ; + LL_INFOS("Window") << "pfd.cGreenBits: " << (int)pfd.cGreenBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cGreenShift: " << (int)pfd.cGreenShift << LL_ENDL ; + LL_INFOS("Window") << "pfd.cBlueBits: " << (int)pfd.cBlueBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cBlueShift: " << (int)pfd.cBlueShift << LL_ENDL ; + LL_INFOS("Window") << "pfd.cAlphaBits: " << (int)pfd.cAlphaBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cAlphaShift: " << (int)pfd.cAlphaShift << LL_ENDL ; + LL_INFOS("Window") << "pfd.cAccumBits: " << (int)pfd.cAccumBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cAccumRedBits: " << (int)pfd.cAccumRedBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cAccumGreenBits: " << (int)pfd.cAccumGreenBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cAccumBlueBits: " << (int)pfd.cAccumBlueBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cAccumAlphaBits: " << (int)pfd.cAccumAlphaBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cDepthBits: " << (int)pfd.cDepthBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cStencilBits: " << (int)pfd.cStencilBits << LL_ENDL ; + LL_INFOS("Window") << "pfd.cAuxBuffers: " << (int)pfd.cAuxBuffers << LL_ENDL ; + LL_INFOS("Window") << "pfd.iLayerType: " << (int)pfd.iLayerType << LL_ENDL ; + LL_INFOS("Window") << "pfd.bReserved: " << (int)pfd.bReserved << LL_ENDL ; + LL_INFOS("Window") << "pfd.dwLayerMask: " << pfd.dwLayerMask << LL_ENDL ; + LL_INFOS("Window") << "pfd.dwVisibleMask: " << pfd.dwVisibleMask << LL_ENDL ; + LL_INFOS("Window") << "pfd.dwDamageMask: " << pfd.dwDamageMask << LL_ENDL ; + LL_INFOS("Window") << "--- end pixel format dump ---" << LL_ENDL ; + + if (!SetPixelFormat(mhDC, pixel_format, &pfd)) + { + OSMessageBox(mCallbacks->translateString("MBPixelFmtSetErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + + if (!(mhRC = SafeCreateContext(mhDC))) + { + OSMessageBox(mCallbacks->translateString("MBGLContextErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + if (!wglMakeCurrent(mhDC, mhRC)) + { + OSMessageBox(mCallbacks->translateString("MBGLContextActErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + LL_INFOS("Window") << "Drawing context is created." << LL_ENDL ; + + 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; //stencil buffer is deprecated (performance penalty) + //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_ALPHA_BITS_ARB; + attrib_list[cur_attrib++] = 0; + + U32 end_attrib = 0; + if (mFSAASamples > 0) + { + end_attrib = cur_attrib; + attrib_list[cur_attrib++] = WGL_SAMPLE_BUFFERS_ARB; + attrib_list[cur_attrib++] = GL_TRUE; + + attrib_list[cur_attrib++] = WGL_SAMPLES_ARB; + attrib_list[cur_attrib++] = mFSAASamples; + } + + // 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); + + while(!result && mFSAASamples > 0) + { + LL_WARNS() << "FSAASamples: " << mFSAASamples << " not supported." << LL_ENDL ; + + mFSAASamples /= 2 ; //try to decrease sample pixel number until to disable anti-aliasing + if(mFSAASamples < 2) + { + mFSAASamples = 0 ; + } + + if (mFSAASamples > 0) + { + attrib_list[end_attrib + 3] = mFSAASamples; + } + else + { + cur_attrib = end_attrib ; + end_attrib = 0 ; + attrib_list[cur_attrib++] = 0 ; //end + } + result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats); + + if(result) + { + LL_WARNS() << "Only support FSAASamples: " << mFSAASamples << LL_ENDL ; + } + } + + if (!result) + { + LL_WARNS() << "mFSAASamples: " << mFSAASamples << LL_ENDL ; + + close(); + show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit"); + return false; + } + + if (!num_formats) + { + if (end_attrib > 0) + { + LL_INFOS("Window") << "No valid pixel format for " << mFSAASamples << "x anti-aliasing." << LL_ENDL; + attrib_list[end_attrib] = 0; + + BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats); + if (!result) + { + close(); + show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit no AA"); + return false; + } + } + + if (!num_formats) + { + LL_INFOS("Window") << "No 32 bit z-buffer, trying 24 bits instead" << LL_ENDL; + // 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) + { + LL_WARNS("Window") << "Couldn't get 24 bit z-buffer,trying 16 bits instead!" << LL_ENDL; + 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; + } + } + } + + LL_INFOS("Window") << "Choosing pixel formats: " << num_formats << " pixel formats returned" << LL_ENDL; + } + + LL_INFOS("Window") << "pixel formats done." << LL_ENDL ; + + S32 swap_method = 0; + S32 cur_format = 0; +const S32 max_format = (S32)num_formats - 1; + GLint swap_query = WGL_SWAP_METHOD_ARB; + + // SL-14705 Fix name tags showing in front of objects with AMD GPUs. + // On AMD hardware we need to iterate from the first pixel format to the end. + // Spec: + // https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt + while (wglGetPixelFormatAttribivARB(mhDC, pixel_formats[cur_format], 0, 1, &swap_query, &swap_method)) + { + if (swap_method == WGL_SWAP_UNDEFINED_ARB) + { + break; + } + else if (cur_format >= max_format) + { + cur_format = 0; + break; + } + + ++cur_format; + } + + pixel_format = pixel_formats[cur_format]; + + if (mhDC != 0) // Does The Window Have A Device Context? + { + wglMakeCurrent(mhDC, 0); // Set The Current Active Rendering Context To Zero + if (mhRC != 0) // Does The Window Have A Rendering Context? + { + wglDeleteContext (mhRC); // Release The Rendering Context + mhRC = 0; // Zero The Rendering Context + } + } + + // will release and recreate mhDC, mWindowHandle + recreateWindow(window_rect, dw_ex_style, dw_style); + + RECT rect; + RECT client_rect; + //initialize immediately on main thread + if (GetWindowRect(mWindowHandle, &rect) && + GetClientRect(mWindowHandle, &client_rect)) + { + mRect = rect; + mClientRect = client_rect; + }; + + if (mWindowHandle) + { + LL_INFOS("Window") << "recreate window done." << LL_ENDL ; + } + else + { + // Note: if value is NULL GetDC retrieves the DC for the entire screen. + LL_WARNS("Window") << "Window recreation failed, code: " << GetLastError() << LL_ENDL; + } + + if (!mhDC) + { + OSMessageBox(mCallbacks->translateString("MBDevContextErr"), mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + if (!SetPixelFormat(mhDC, pixel_format, &pfd)) + { + OSMessageBox(mCallbacks->translateString("MBPixelFmtSetErr"), + mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + if (wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method)) + { + switch (swap_method) + { + case WGL_SWAP_EXCHANGE_ARB: + mSwapMethod = SWAP_METHOD_EXCHANGE; + LL_DEBUGS("Window") << "Swap Method: Exchange" << LL_ENDL; + break; + case WGL_SWAP_COPY_ARB: + mSwapMethod = SWAP_METHOD_COPY; + LL_DEBUGS("Window") << "Swap Method: Copy" << LL_ENDL; + break; + case WGL_SWAP_UNDEFINED_ARB: + mSwapMethod = SWAP_METHOD_UNDEFINED; + LL_DEBUGS("Window") << "Swap Method: Undefined" << LL_ENDL; + break; + default: + mSwapMethod = SWAP_METHOD_UNDEFINED; + LL_DEBUGS("Window") << "Swap Method: Unknown" << LL_ENDL; + break; + } + } + } + else + { + LLError::LLUserWarningMsg::show(mCallbacks->translateString("MBVideoDrvErr")); + // mWindowHandle is 0, going to crash either way + LL_ERRS("Window") << "No wgl_ARB_pixel_format extension!" << LL_ENDL; + } + + // Verify what pixel format we actually received. + if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR), + &pfd)) + { + OSMessageBox(mCallbacks->translateString("MBPixelFmtDescErr"), mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + LL_INFOS("Window") << "GL buffer: Color Bits " << S32(pfd.cColorBits) + << " Alpha Bits " << S32(pfd.cAlphaBits) + << " Depth Bits " << S32(pfd.cDepthBits) + << LL_ENDL; + + mhRC = 0; + if (wglCreateContextAttribsARB) + { //attempt to create a specific versioned context + mhRC = (HGLRC) createSharedContext(); + if (!mhRC) + { + return false; + } + } + + if (!wglMakeCurrent(mhDC, mhRC)) + { + OSMessageBox(mCallbacks->translateString("MBGLContextActErr"), mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + if (!gGLManager.initGL()) + { + OSMessageBox(mCallbacks->translateString("MBVideoDrvErr"), mCallbacks->translateString("MBError"), OSMB_OK); + close(); + return false; + } + + // Disable vertical sync for swap + toggleVSync(enable_vsync); + + SetWindowLongPtr(mWindowHandle, GWLP_USERDATA, (LONG_PTR)this); + + // register this window as handling drag/drop events from the OS + DragAcceptFiles( mWindowHandle, TRUE ); + + mDragDrop->init( mWindowHandle ); + + //register joystick timer callback + SetTimer( mWindowHandle, 0, 1000 / 30, NULL ); // 30 fps timer + + // ok to post quit messages now + mPostQuit = true; + + // *HACK: Attempt to prevent startup crashes by deferring memory accounting + // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18 + mWindowThread->post([=]() + { + mWindowThread->glReady(); + }); + + if (auto_show) + { + show(); + glClearColor(0.0f, 0.0f, 0.0f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); + swapBuffers(); + } + + LL_PROFILER_GPU_CONTEXT; + + return true; +} + +void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style) +{ + auto oldWindowHandle = mWindowHandle; + auto oldDCHandle = mhDC; + + // zero out mWindowHandle and mhDC before destroying window so window + // thread falls back to peekmessage + mWindowHandle = 0; + mhDC = 0; + + std::promise<std::pair<HWND, HDC>> promise; + // What follows must be done on the window thread. + auto window_work = + [this, + self=mWindowThread, + oldWindowHandle, + oldDCHandle, + // bind CreateWindowEx() parameters by value instead of + // back-referencing LLWindowWin32 members + windowClassName=mWindowClassName, + windowTitle=mWindowTitle, + hInstance=mhInstance, + window_rect, + dw_ex_style, + dw_style, + &promise] + () + { + LL_DEBUGS("Window") << "recreateWindow(): window_work entry" << LL_ENDL; + self->mWindowHandleThrd = 0; + self->mhDCThrd = 0; + + if (oldWindowHandle) + { + if (oldDCHandle && !ReleaseDC(oldWindowHandle, oldDCHandle)) + { + LL_WARNS("Window") << "Failed to ReleaseDC" << LL_ENDL; + } + + // important to call DestroyWindow() from the window thread + if (!destroy_window_handler(oldWindowHandle)) + { + + LL_WARNS("Window") << "Failed to properly close window before recreating it!" + << LL_ENDL; + } + } + + auto handle = CreateWindowEx(dw_ex_style, + windowClassName, + windowTitle, + 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, + hInstance, + NULL); + + if (! handle) + { + // Failed to create window: clear the variables. This + // assignment is valid because we're running on mWindowThread. + self->mWindowHandleThrd = NULL; + self->mhDCThrd = 0; + } + else + { + // Update mWindowThread's own mWindowHandle and mhDC. + self->mWindowHandleThrd = handle; + self->mhDCThrd = GetDC(handle); + } + + updateWindowRect(); + + // It's important to wake up the future either way. + promise.set_value(std::make_pair(self->mWindowHandleThrd, self->mhDCThrd)); + LL_DEBUGS("Window") << "recreateWindow(): window_work done" << LL_ENDL; + }; + // But how we pass window_work to the window thread depends on whether we + // already have a window handle. + if (!oldWindowHandle) + { + // Pass window_work using the WorkQueue: without an existing window + // handle, the window thread can't call GetMessage(). + LL_DEBUGS("Window") << "posting window_work to WorkQueue" << LL_ENDL; + mWindowThread->post(window_work); + } + else + { + // Pass window_work using PostMessage(). We can still + // PostMessage(oldHandle) because oldHandle won't be destroyed until + // the window thread has retrieved and executed window_work. + LL_DEBUGS("Window") << "posting window_work to message queue" << LL_ENDL; + mWindowThread->Post(oldWindowHandle, window_work); + } + + auto future = promise.get_future(); + // This blocks until mWindowThread processes CreateWindowEx() and calls + // promise.set_value(). + auto pair = future.get(); + mWindowHandle = pair.first; + mhDC = pair.second; +} + +void* LLWindowWin32::createSharedContext() +{ + mMaxGLVersion = llclamp(mMaxGLVersion, 3.f, 4.6f); + + S32 version_major = llfloor(mMaxGLVersion); + S32 version_minor = llround((mMaxGLVersion-version_major)*10); + + S32 attribs[] = + { + WGL_CONTEXT_MAJOR_VERSION_ARB, version_major, + WGL_CONTEXT_MINOR_VERSION_ARB, version_minor, + WGL_CONTEXT_PROFILE_MASK_ARB, LLRender::sGLCoreProfile ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + WGL_CONTEXT_FLAGS_ARB, gDebugGL ? WGL_CONTEXT_DEBUG_BIT_ARB : 0, + 0 + }; + + HGLRC rc = 0; + + bool done = false; + while (!done) + { + rc = wglCreateContextAttribsARB(mhDC, mhRC, attribs); + + if (!rc) + { + if (attribs[3] > 0) + { //decrement minor version + attribs[3]--; + } + else if (attribs[1] > 3) + { //decrement major version and start minor version over at 3 + attribs[1]--; + attribs[3] = 3; + } + else + { //we reached 3.0 and still failed, bail out + done = true; + } + } + else + { + LL_INFOS() << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) << + (LLRender::sGLCoreProfile ? " core" : " compatibility") << " context." << LL_ENDL; + done = true; + } + } + + if (!rc && !(rc = wglCreateContext(mhDC))) + { + close(); + OSMessageBox(mCallbacks->translateString("MBGLContextErr"), mCallbacks->translateString("MBError"), OSMB_OK); + } + + return rc; +} + +void LLWindowWin32::makeContextCurrent(void* contextPtr) +{ + wglMakeCurrent(mhDC, (HGLRC) contextPtr); + LL_PROFILER_GPU_CONTEXT; +} + +void LLWindowWin32::destroySharedContext(void* contextPtr) +{ + wglDeleteContext((HGLRC)contextPtr); +} + +void LLWindowWin32::toggleVSync(bool enable_vsync) +{ + if (wglSwapIntervalEXT == nullptr) + { + LL_INFOS("Window") << "VSync: wglSwapIntervalEXT not initialized" << LL_ENDL; + } + else if (!enable_vsync) + { + LL_INFOS("Window") << "Disabling vertical sync" << LL_ENDL; + wglSwapIntervalEXT(0); + } + else + { + LL_INFOS("Window") << "Enabling vertical sync" << LL_ENDL; + wglSwapIntervalEXT(1); + } +} + +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 ); + } + } + + // if the window was already maximized, MoveWindow seems to still set the maximized flag even if + // the window is smaller than maximized. + // So we're going to do a restore first (which is a ShowWindow call) (SL-44655). + + // THIS CAUSES DEV-15484 and DEV-15949 + //ShowWindow(mWindowHandle, SW_RESTORE); + // NOW we can call MoveWindow + mWindowThread->post([=]() + { + MoveWindow(mWindowHandle, position.mX, position.mY, size.mX, size.mY, TRUE); + }); +} + +void LLWindowWin32::setTitle(const std::string title) +{ + // TODO: Do we need to use the wide string version of this call + // to support non-ascii usernames (and region names?) + mWindowThread->post([=]() + { + SetWindowTextA(mWindowHandle, title.c_str()); + }); +} + +bool LLWindowWin32::setCursorPosition(const LLCoordWindow position) +{ + ASSERT_MAIN_THREAD(); + + if (!mWindowHandle) + { + return false; + } + + LLCoordScreen screen_pos(position.convert()); + + // instantly set the cursor position from the app's point of view + mCursorPosition = position; + mLastCursorPosition = position; + + // Inform the application of the new mouse position (needed for per-frame + // hover/picking to function). + mCallbacks->handleMouseMove(this, position.convert(), (MASK)0); + + // actually set the cursor position on the window thread + mWindowThread->post([=]() + { + // actually set the OS cursor position + SetCursorPos(screen_pos.mX, screen_pos.mY); + }); + + return true; +} + +bool LLWindowWin32::getCursorPosition(LLCoordWindow *position) +{ + ASSERT_MAIN_THREAD(); + if (!position) + { + return false; + } + + *position = mCursorPosition; + return true; +} + +bool LLWindowWin32::getCursorDelta(LLCoordCommon* delta) +{ + if (delta == nullptr) + { + return false; + } + + *delta = mMouseFrameDelta; + + return true; +} + +void LLWindowWin32::hideCursor() +{ + ASSERT_MAIN_THREAD(); + + mWindowThread->post([=]() + { + while (ShowCursor(FALSE) >= 0) + { + // nothing, wait for cursor to push down + } + }); + + mCursorHidden = true; + mHideCursorPermanent = true; +} + +void LLWindowWin32::showCursor() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; + + ASSERT_MAIN_THREAD(); + + mWindowThread->post([=]() + { + // 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 && mMouseVanish) + { + 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_SIZEALL ] = LoadCursor(NULL, IDC_SIZEALL); + 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_TOOLZOOMOUT ] = LoadCursor(module, TEXT("TOOLZOOMOUT")); + mCursor[ UI_CURSOR_TOOLPICKOBJECT3 ] = LoadCursor(module, TEXT("TOOLPICKOBJECT3")); + mCursor[ UI_CURSOR_PIPETTE ] = LoadCursor(module, TEXT("TOOLPIPETTE")); + mCursor[ UI_CURSOR_TOOLSIT ] = LoadCursor(module, TEXT("TOOLSIT")); + mCursor[ UI_CURSOR_TOOLBUY ] = LoadCursor(module, TEXT("TOOLBUY")); + mCursor[ UI_CURSOR_TOOLOPEN ] = LoadCursor(module, TEXT("TOOLOPEN")); + mCursor[ UI_CURSOR_TOOLPATHFINDING ] = LoadCursor(module, TEXT("TOOLPATHFINDING")); + mCursor[ UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD ] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHSTARTADD")); + mCursor[ UI_CURSOR_TOOLPATHFINDING_PATH_START ] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHSTART")); + mCursor[ UI_CURSOR_TOOLPATHFINDING_PATH_END ] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHEND")); + mCursor[ UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD ] = LoadCursor(module, TEXT("TOOLPATHFINDINGPATHENDADD")); + mCursor[ UI_CURSOR_TOOLNO ] = LoadCursor(module, TEXT("TOOLNO")); + + // Color cursors + mCursor[ UI_CURSOR_TOOLPLAY ] = loadColorCursor(TEXT("TOOLPLAY")); + mCursor[ UI_CURSOR_TOOLPAUSE ] = loadColorCursor(TEXT("TOOLPAUSE")); + mCursor[ UI_CURSOR_TOOLMEDIAOPEN ] = loadColorCursor(TEXT("TOOLMEDIAOPEN")); + + // 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::updateCursor() +{ + ASSERT_MAIN_THREAD(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32 + if (mNextCursor == UI_CURSOR_ARROW + && mBusyCount > 0) + { + mNextCursor = UI_CURSOR_WORKING; + } + + if( mCurrentCursor != mNextCursor ) + { + mCurrentCursor = mNextCursor; + auto nextCursor = mCursor[mNextCursor]; + mWindowThread->post([=]() + { + SetCursor(nextCursor); + }); + } +} + +ECursorType LLWindowWin32::getCursor() const +{ + return mCurrentCursor; +} + +void LLWindowWin32::captureMouse() +{ + SetCapture(mWindowHandle); +} + +void LLWindowWin32::releaseMouse() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; + ReleaseCapture(); +} + + +void LLWindowWin32::delayInputProcessing() +{ + mInputProcessingPaused = true; +} + + +void LLWindowWin32::gatherInput() +{ + ASSERT_MAIN_THREAD(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32 + MSG msg; + + { + LLMutexLock lock(&mRawMouseMutex); + mMouseFrameDelta = mRawMouseDelta; + + mRawMouseDelta.mX = 0; + mRawMouseDelta.mY = 0; + } + + + if (mWindowThread->getQueue().size()) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - PostMessage"); + kickWindowThread(); + } + + while (mWindowThread->mMessageQueue.tryPopBack(msg)) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - message queue"); + if (mInputProcessingPaused) + { + continue; + } + + // For async host by name support. Really hacky. + if (gAsyncMsgCallback && (LL_WM_HOST_RESOLVED == msg.message)) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - callback"); + gAsyncMsgCallback(msg); + } + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - PeekMessage"); + S32 msg_count = 0; + while ((msg_count < MAX_MESSAGE_PER_UPDATE) && PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + msg_count++; + } + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - function queue"); + //process any pending functions + std::function<void()> curFunc; + while (mFunctionQueue.tryPopBack(curFunc)) + { + curFunc(); + } + } + + // send one and only one mouse move event per frame BEFORE handling mouse button presses + if (mLastCursorPosition != mCursorPosition) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - mouse move"); + mCallbacks->handleMouseMove(this, mCursorPosition.convert(), mMouseMask); + } + + mLastCursorPosition = mCursorPosition; + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("gi - mouse queue"); + // handle mouse button presses AFTER updating mouse cursor position + std::function<void()> curFunc; + while (mMouseQueue.tryPopBack(curFunc)) + { + curFunc(); + } + } + + mInputProcessingPaused = false; + + updateCursor(); +} + +static LLTrace::BlockTimerStatHandle FTM_KEYHANDLER("Handle Keyboard"); +static LLTrace::BlockTimerStatHandle FTM_MOUSEHANDLER("Handle Mouse"); + +#define WINDOW_IMP_POST(x) window_imp->post([=]() { x; }) + +LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param) +{ + ASSERT_WINDOW_THREAD(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; + + if (u_msg == WM_POST_FUNCTION_) + { + // from LLWindowWin32Thread::Post() + // Cast l_param back to the pointer to the heap FuncType + // allocated by Post(). Capture in unique_ptr so we'll delete + // once we're done with it. + std::unique_ptr<LLWindowWin32Thread::FuncType> + ptr(reinterpret_cast<LLWindowWin32Thread::FuncType*>(l_param)); + (*ptr)(); + return 0; + } + + // Ignore clicks not originated in the client area, i.e. mouse-up events not preceded with a WM_LBUTTONDOWN. + // This helps prevent avatar walking after maximizing the window by double-clicking the title bar. + static bool sHandleLeftMouseUp = true; + + // Ignore the double click received right after activating app. + // This is to avoid triggering double click teleport after returning focus (see MAINT-3786). + static bool sHandleDoubleClick = true; + + LLWindowWin32* window_imp = (LLWindowWin32*)GetWindowLongPtr(h_wnd, GWLP_USERDATA); + + if (NULL != window_imp) + { + // 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)); + + // 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: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_TIMER"); + WINDOW_IMP_POST(window_imp->mCallbacks->handleTimerEvent(window_imp)); + break; + } + + case WM_DEVICECHANGE: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DEVICECHANGE"); + if (w_param == DBT_DEVNODES_CHANGED || w_param == DBT_DEVICEARRIVAL) + { + WINDOW_IMP_POST(window_imp->mCallbacks->handleDeviceChange(window_imp)); + + return 1; + } + break; + } + + case WM_PAINT: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - 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_POST(window_imp->mCallbacks->handlePaint(window_imp, update_rect.left, update_rect.top, + update_width, update_height)); + break; + } + case WM_PARENTNOTIFY: + { + break; + } + + case WM_SETCURSOR: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - 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: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ENTERMENULOOP"); + WINDOW_IMP_POST(window_imp->mCallbacks->handleWindowBlock(window_imp)); + break; + } + + case WM_EXITMENULOOP: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_EXITMENULOOP"); + WINDOW_IMP_POST(window_imp->mCallbacks->handleWindowUnblock(window_imp)); + break; + } + + case WM_ACTIVATEAPP: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ACTIVATEAPP"); + window_imp->post([=]() + { + // This message should be sent whenever the app gains or loses focus. + BOOL activating = (BOOL)w_param; + + 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(); + } + } + + if (!activating) + { + sHandleDoubleClick = false; + } + + window_imp->mCallbacks->handleActivateApp(window_imp, activating); + }); + break; + } + case WM_ACTIVATE: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_ACTIVATE"); + window_imp->post([=]() + { + // Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE + BOOL activating = (LOWORD(w_param) != WA_INACTIVE); + + if (!activating && LLWinImm::isAvailable() && window_imp->mPreeditor) + { + window_imp->interruptLanguageTextInput(); + } + }); + + break; + } + + case WM_QUERYOPEN: + // TODO: use this to return a nice icon + break; + + case WM_SYSCOMMAND: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - 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: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_CLOSE"); + window_imp->post([=]() + { + // 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: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DESTROY"); + if (window_imp->shouldPostQuit()) + { + PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0 + } + return 0; + } + case WM_COMMAND: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_COMMAND"); + if (!HIWORD(w_param)) // this message is from a menu + { + WINDOW_IMP_POST(window_imp->mCallbacks->handleMenuSelect(window_imp, LOWORD(w_param))); + } + break; + } + case WM_SYSKEYDOWN: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SYSKEYDOWN"); + // allow system keys, such as ALT-F4 to be processed by Windows + eat_keystroke = false; + // intentional fall-through here + [[fallthrough]]; + } + case WM_KEYDOWN: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KEYDOWN"); + window_imp->post([=]() + { + window_imp->mKeyCharCode = 0; // don't know until wm_char comes in next + window_imp->mKeyScanCode = (l_param >> 16) & 0xff; + window_imp->mKeyVirtualKey = w_param; + window_imp->mRawMsg = u_msg; + window_imp->mRawWParam = w_param; + window_imp->mRawLParam = l_param; + + gKeyboard->handleKeyDown(w_param, mask); + }); + if (eat_keystroke) return 0; // skip DefWindowProc() handling if we're consuming the keypress + break; + } + case WM_SYSKEYUP: + eat_keystroke = false; + // intentional fall-through here + [[fallthrough]]; + case WM_KEYUP: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KEYUP"); + window_imp->post([=]() + { + window_imp->mKeyScanCode = (l_param >> 16) & 0xff; + window_imp->mKeyVirtualKey = w_param; + window_imp->mRawMsg = u_msg; + window_imp->mRawWParam = w_param; + window_imp->mRawLParam = l_param; + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KEYUP"); + gKeyboard->handleKeyUp(w_param, mask); + } + }); + if (eat_keystroke) return 0; // skip DefWindowProc() handling if we're consuming the keypress + break; + } + case WM_IME_SETCONTEXT: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_SETCONTEXT"); + if (LLWinImm::isAvailable() && window_imp->mPreeditor) + { + l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW; + // Invoke DefWinProc with the modified LPARAM. + } + break; + } + case WM_IME_STARTCOMPOSITION: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_STARTCOMPOSITION"); + if (LLWinImm::isAvailable() && window_imp->mPreeditor) + { + WINDOW_IMP_POST(window_imp->handleStartCompositionMessage()); + return 0; + } + break; + } + case WM_IME_ENDCOMPOSITION: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_ENDCOMPOSITION"); + if (LLWinImm::isAvailable() && window_imp->mPreeditor) + { + return 0; + } + break; + } + case WM_IME_COMPOSITION: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_COMPOSITION"); + if (LLWinImm::isAvailable() && window_imp->mPreeditor) + { + WINDOW_IMP_POST(window_imp->handleCompositionMessage(l_param)); + return 0; + } + break; + } + case WM_IME_REQUEST: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_IME_REQUEST"); + if (LLWinImm::isAvailable() && window_imp->mPreeditor) + { + LRESULT result; + window_imp->handleImeRequests(w_param, l_param, &result); + return result; + } + break; + } + case WM_CHAR: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_CHAR"); + window_imp->post([=]() + { + window_imp->mKeyCharCode = w_param; + window_imp->mRawMsg = u_msg; + window_imp->mRawWParam = w_param; + window_imp->mRawLParam = l_param; + + // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need + // to figure out how that works. - Doug + // + // ... Well, I don't think so. + // How it works is explained in Win32 API document, but WM_UNICHAR didn't work + // as specified at least on Windows XP SP1 Japanese version. I have never used + // it since then, and I'm not sure whether it has been fixed now, but I don't think + // it is worth trying. The good old WM_CHAR works just fine even for supplementary + // characters. We just need to take care of surrogate pairs sent as two WM_CHAR's + // by ourselves. It is not that tough. -- Alissa Sabre @ SL + + // Even if LLWindowCallbacks::handleUnicodeChar(llwchar, bool) returned false, + // we *did* processed the event, so I believe we should not pass it to DefWindowProc... + window_imp->handleUnicodeUTF16((U16)w_param, gKeyboard->currentMask(false)); + }); + return 0; + } + case WM_NCLBUTTONDOWN: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_NCLBUTTONDOWN"); + { + // A click in a non-client area, e.g. title bar or window border. + window_imp->post([=]() + { + sHandleLeftMouseUp = false; + sHandleDoubleClick = true; + }); + } + break; + } + case WM_LBUTTONDOWN: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_LBUTTONDOWN"); + { + LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER); + window_imp->postMouseButtonEvent([=]() + { + sHandleLeftMouseUp = true; + + if (LLWinImm::isAvailable() && window_imp->mPreeditor) + { + window_imp->interruptLanguageTextInput(); + } + + MASK mask = gKeyboard->currentMask(true); + auto gl_coord = window_imp->mCursorPosition.convert(); + window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask); + window_imp->mCallbacks->handleMouseDown(window_imp, gl_coord, mask); + }); + + return 0; + } + break; + } + + case WM_LBUTTONDBLCLK: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_LBUTTONDBLCLK"); + window_imp->postMouseButtonEvent([=]() + { + //RN: ignore right button double clicks for now + //case WM_RBUTTONDBLCLK: + if (!sHandleDoubleClick) + { + sHandleDoubleClick = true; + return; + } + MASK mask = gKeyboard->currentMask(true); + + // generate move event to update mouse coordinates + window_imp->mCursorPosition = window_coord; + window_imp->mCallbacks->handleDoubleClick(window_imp, window_imp->mCursorPosition.convert(), mask); + }); + + return 0; + } + case WM_LBUTTONUP: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_LBUTTONUP"); + { + window_imp->postMouseButtonEvent([=]() + { + LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER); + if (!sHandleLeftMouseUp) + { + sHandleLeftMouseUp = true; + return; + } + sHandleDoubleClick = true; + + + MASK mask = gKeyboard->currentMask(true); + // generate move event to update mouse coordinates + window_imp->mCursorPosition = window_coord; + window_imp->mCallbacks->handleMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask); + }); + } + return 0; + } + case WM_RBUTTONDBLCLK: + case WM_RBUTTONDOWN: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_RBUTTONDOWN"); + { + LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER); + window_imp->post([=]() + { + if (LLWinImm::isAvailable() && window_imp->mPreeditor) + { + WINDOW_IMP_POST(window_imp->interruptLanguageTextInput()); + } + + MASK mask = gKeyboard->currentMask(true); + // generate move event to update mouse coordinates + auto gl_coord = window_imp->mCursorPosition.convert(); + window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask); + window_imp->mCallbacks->handleRightMouseDown(window_imp, gl_coord, mask); + }); + } + return 0; + } + break; + + case WM_RBUTTONUP: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_RBUTTONUP"); + { + LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER); + window_imp->postMouseButtonEvent([=]() + { + MASK mask = gKeyboard->currentMask(true); + window_imp->mCallbacks->handleRightMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask); + }); + } + } + break; + + case WM_MBUTTONDOWN: + // case WM_MBUTTONDBLCLK: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MBUTTONDOWN"); + { + LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER); + window_imp->postMouseButtonEvent([=]() + { + if (LLWinImm::isAvailable() && window_imp->mPreeditor) + { + window_imp->interruptLanguageTextInput(); + } + + MASK mask = gKeyboard->currentMask(true); + window_imp->mCallbacks->handleMiddleMouseDown(window_imp, window_imp->mCursorPosition.convert(), mask); + }); + } + } + break; + + case WM_MBUTTONUP: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MBUTTONUP"); + { + LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER); + window_imp->postMouseButtonEvent([=]() + { + MASK mask = gKeyboard->currentMask(true); + window_imp->mCallbacks->handleMiddleMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask); + }); + } + } + break; + case WM_XBUTTONDOWN: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_XBUTTONDOWN"); + window_imp->postMouseButtonEvent([=]() + { + LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER); + S32 button = GET_XBUTTON_WPARAM(w_param); + if (LLWinImm::isAvailable() && window_imp->mPreeditor) + { + window_imp->interruptLanguageTextInput(); + } + + MASK mask = gKeyboard->currentMask(true); + // Windows uses numbers 1 and 2 for buttons, remap to 4, 5 + window_imp->mCallbacks->handleOtherMouseDown(window_imp, window_imp->mCursorPosition.convert(), mask, button + 3); + }); + + } + break; + + case WM_XBUTTONUP: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_XBUTTONUP"); + window_imp->postMouseButtonEvent([=]() + { + + LL_RECORD_BLOCK_TIME(FTM_MOUSEHANDLER); + + S32 button = GET_XBUTTON_WPARAM(w_param); + MASK mask = gKeyboard->currentMask(true); + // Windows uses numbers 1 and 2 for buttons, remap to 4, 5 + window_imp->mCallbacks->handleOtherMouseUp(window_imp, window_imp->mCursorPosition.convert(), mask, button + 3); + }); + } + break; + + case WM_MOUSEWHEEL: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MOUSEWHEEL"); + static short z_delta = 0; + + RECT client_rect; + + // eat scroll events that occur outside our window, since we use mouse position to direct scroll + // instead of keyboard focus + // NOTE: mouse_coord is in *window* coordinates for scroll events + POINT mouse_coord = { (S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param) }; + + if (ScreenToClient(window_imp->mWindowHandle, &mouse_coord) + && GetClientRect(window_imp->mWindowHandle, &client_rect)) + { + // we have a valid mouse point and client rect + if (mouse_coord.x < client_rect.left || client_rect.right < mouse_coord.x + || mouse_coord.y < client_rect.top || client_rect.bottom < mouse_coord.y) + { + // mouse is outside of client rect, so don't do anything + return 0; + } + } + + S16 incoming_z_delta = HIWORD(w_param); + z_delta += incoming_z_delta; + // 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) + { + short clicks = -z_delta / WHEEL_DELTA; + WINDOW_IMP_POST(window_imp->mCallbacks->handleScrollWheel(window_imp, clicks)); + 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; + } + */ + case WM_MOUSEHWHEEL: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MOUSEHWHEEL"); + static short h_delta = 0; + + RECT client_rect; + + // eat scroll events that occur outside our window, since we use mouse position to direct scroll + // instead of keyboard focus + // NOTE: mouse_coord is in *window* coordinates for scroll events + POINT mouse_coord = { (S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param) }; + + if (ScreenToClient(window_imp->mWindowHandle, &mouse_coord) + && GetClientRect(window_imp->mWindowHandle, &client_rect)) + { + // we have a valid mouse point and client rect + if (mouse_coord.x < client_rect.left || client_rect.right < mouse_coord.x + || mouse_coord.y < client_rect.top || client_rect.bottom < mouse_coord.y) + { + // mouse is outside of client rect, so don't do anything + return 0; + } + } + + S16 incoming_h_delta = HIWORD(w_param); + h_delta += incoming_h_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 (h_delta <= -WHEEL_DELTA || WHEEL_DELTA <= h_delta) + { + WINDOW_IMP_POST(window_imp->mCallbacks->handleScrollHWheel(window_imp, h_delta / WHEEL_DELTA)); + h_delta = 0; + } + return 0; + } + // Handle mouse movement within the window + case WM_MOUSEMOVE: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MOUSEMOVE"); + // DO NOT use mouse event queue for move events to ensure cursor position is updated + // when button events are handled + WINDOW_IMP_POST( + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_MOUSEMOVE lambda"); + + MASK mask = gKeyboard->currentMask(true); + window_imp->mMouseMask = mask; + window_imp->mCursorPosition = window_coord; + }); + return 0; + } + + case WM_GETMINMAXINFO: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_GETMINMAXINFO"); + LPMINMAXINFO min_max = (LPMINMAXINFO)l_param; + min_max->ptMinTrackSize.x = window_imp->mMinWindowWidth; + min_max->ptMinTrackSize.y = window_imp->mMinWindowHeight; + return 0; + } + + case WM_MOVE: + { + window_imp->updateWindowRect(); + return 0; + } + case WM_SIZE: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SIZE"); + window_imp->updateWindowRect(); + + // There's an odd behavior with WM_SIZE that I would call a bug. If + // the window is maximized, and you call MoveWindow() with a size smaller + // than a maximized window, it ends up sending WM_SIZE with w_param set + // to SIZE_MAXIMIZED -- which isn't true. So the logic below doesn't work. + // (SL-44655). Fixed it by calling ShowWindow(SW_RESTORE) first (see + // LLWindowWin32::moveWindow in this file). + + // 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_POST(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_POST(window_imp->mCallbacks->handleActivate(window_imp, true)); + } + + // Also handle the minimization case + if (w_param == SIZE_MINIMIZED && window_imp->mLastSizeWParam != SIZE_MINIMIZED) + { + WINDOW_IMP_POST(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_POST(window_imp->mCallbacks->handleResize(window_imp, + LOWORD(l_param), + HIWORD(l_param))); + } + + window_imp->mLastSizeWParam = w_param; + + return 0; + } + + case WM_DPICHANGED: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_DPICHANGED"); + LPRECT lprc_new_scale; + F32 new_scale = F32(LOWORD(w_param)) / F32(USER_DEFAULT_SCREEN_DPI); + lprc_new_scale = (LPRECT)l_param; + S32 new_width = lprc_new_scale->right - lprc_new_scale->left; + S32 new_height = lprc_new_scale->bottom - lprc_new_scale->top; + WINDOW_IMP_POST(window_imp->mCallbacks->handleDPIChanged(window_imp, new_scale, new_width, new_height)); + + SetWindowPos(h_wnd, + HWND_TOP, + lprc_new_scale->left, + lprc_new_scale->top, + new_width, + new_height, + SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + + case WM_SETFOCUS: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SETFOCUS"); + WINDOW_IMP_POST(window_imp->mCallbacks->handleFocus(window_imp)); + return 0; + } + + case WM_KILLFOCUS: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_KILLFOCUS"); + WINDOW_IMP_POST(window_imp->mCallbacks->handleFocusLost(window_imp)); + return 0; + } + + case WM_COPYDATA: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_COPYDATA"); + { + // received a URL + PCOPYDATASTRUCT myCDS = (PCOPYDATASTRUCT)l_param; + void* data = new U8[myCDS->cbData]; + memcpy(data, myCDS->lpData, myCDS->cbData); + auto myType = myCDS->dwData; + + window_imp->post([=]() + { + window_imp->mCallbacks->handleDataCopy(window_imp, myType, data); + delete[] data; + }); + }; + return 0; + } + case WM_SETTINGCHANGE: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - WM_SETTINGCHANGE"); + if (w_param == SPI_SETMOUSEVANISH) + { + if (!SystemParametersInfo(SPI_GETMOUSEVANISH, 0, &window_imp->mMouseVanish, 0)) + { + WINDOW_IMP_POST(window_imp->mMouseVanish = true); + } + } + } + break; + + case WM_INPUT: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("MWP - WM_INPUT"); + + UINT dwSize = 0; + GetRawInputData((HRAWINPUT)l_param, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); + llassert(dwSize < 1024); + + U8 lpb[1024]; + + if (GetRawInputData((HRAWINPUT)l_param, RID_INPUT, (void*)lpb, &dwSize, sizeof(RAWINPUTHEADER)) == dwSize) + { + RAWINPUT* raw = (RAWINPUT*)lpb; + + if (raw->header.dwType == RIM_TYPEMOUSE) + { + LLMutexLock lock(&window_imp->mRawMouseMutex); + + bool absolute_coordinates = (raw->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE); + + if (absolute_coordinates) + { + static S32 prev_absolute_x = 0; + static S32 prev_absolute_y = 0; + S32 absolute_x; + S32 absolute_y; + + if ((raw->data.mouse.usFlags & 0x10) == 0x10) // touch screen? touch? Not defined in header + { + // touch screen spams (0,0) coordinates in a number of situations + // (0,0) might need to be filtered + absolute_x = raw->data.mouse.lLastX; + absolute_y = raw->data.mouse.lLastY; + } + else + { + bool v_desktop = (raw->data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) == MOUSE_VIRTUAL_DESKTOP; + + S32 width = GetSystemMetrics(v_desktop ? SM_CXVIRTUALSCREEN : SM_CXSCREEN); + S32 height = GetSystemMetrics(v_desktop ? SM_CYVIRTUALSCREEN : SM_CYSCREEN); + + absolute_x = (raw->data.mouse.lLastX / 65535.0f) * width; + absolute_y = (raw->data.mouse.lLastY / 65535.0f) * height; + } + + window_imp->mRawMouseDelta.mX += absolute_x - prev_absolute_x; + window_imp->mRawMouseDelta.mY -= absolute_y - prev_absolute_y; + + prev_absolute_x = absolute_x; + prev_absolute_y = absolute_y; + } + else + { + S32 speed; + const S32 DEFAULT_SPEED(10); + SystemParametersInfo(SPI_GETMOUSESPEED, 0, &speed, 0); + if (speed == DEFAULT_SPEED) + { + window_imp->mRawMouseDelta.mX += raw->data.mouse.lLastX; + window_imp->mRawMouseDelta.mY -= raw->data.mouse.lLastY; + } + else + { + window_imp->mRawMouseDelta.mX += round((F32)raw->data.mouse.lLastX * (F32)speed / DEFAULT_SPEED); + window_imp->mRawMouseDelta.mY -= round((F32)raw->data.mouse.lLastY * (F32)speed / DEFAULT_SPEED); + } + } + } + } + } + break; + + //list of messages we get often that we don't care to log about + case WM_NCHITTEST: + case WM_NCMOUSEMOVE: + case WM_NCMOUSELEAVE: + case WM_MOVING: + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + break; + + default: + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - default"); + LL_DEBUGS("Window") << "Unhandled windows message code: 0x" << std::hex << U32(u_msg) << LL_ENDL; + } + break; + } + } + else + { + // (NULL == window_imp) + LL_DEBUGS("Window") << "No window implementation to handle message with, message code: " << U32(u_msg) << LL_ENDL; + } + + // pass unhandled messages down to Windows + LRESULT ret; + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("mwp - DefWindowProc"); + ret = DefWindowProc(h_wnd, u_msg, w_param, l_param); + } + return ret; +} + +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); +} + + +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); + LLWStringUtil::removeWindowsCR(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); + LLWStringUtil::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); /* Flawfinder: ignore */ + GlobalUnlock(hglobal_copy_utf16); + + if (SetClipboardData(CF_UNICODETEXT, hglobal_copy_utf16)) + { + success = true; + } + } + } + + CloseClipboard(); + } + + return success; +} + +// Constrains the mouse to the window. +void LLWindowWin32::setMouseClipping( bool b ) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; + ASSERT_MAIN_THREAD(); + 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; +} + +void LLWindowWin32::flashIcon(F32 seconds) +{ + mWindowThread->post([=]() + { + 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() +{ + ASSERT_MAIN_THREAD(); + if (mCustomGammaSet) + { + LL_DEBUGS("Window") << "Restoring gamma" << LL_ENDL; + mCustomGammaSet = false; + return SetDeviceGammaRamp(mhDC, mPrevGammaRamp); + } + return true; +} + +bool LLWindowWin32::setGamma(const F32 gamma) +{ + ASSERT_MAIN_THREAD(); + mCurrentGamma = gamma; + + //Get the previous gamma ramp to restore later. + if (!mCustomGammaSet) + { + if (!gGLManager.mIsIntel) // skip for Intel GPUs (see SL-11341) + { + LL_DEBUGS("Window") << "Getting the previous gamma ramp to restore later" << LL_ENDL; + if (!GetDeviceGammaRamp(mhDC, mPrevGammaRamp)) + { + LL_WARNS("Window") << "Failed to get the previous gamma ramp" << LL_ENDL; + return false; + } + } + mCustomGammaSet = true; + } + + LL_DEBUGS("Window") << "Setting gamma to " << gamma << LL_ENDL; + + 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][i] = + mCurrentGammaRamp[1][i] = + mCurrentGammaRamp[2][i] = (WORD) value; + }; + + return SetDeviceGammaRamp ( mhDC, mCurrentGammaRamp ); +} + +void LLWindowWin32::setFSAASamples(const U32 fsaa_samples) +{ + ASSERT_MAIN_THREAD(); + mFSAASamples = fsaa_samples; +} + +U32 LLWindowWin32::getFSAASamples() +{ + return mFSAASamples; +} + +LLWindow::LLWindowResolution* LLWindowWin32::getSupportedResolutions(S32 &num_resolutions) +{ + ASSERT_MAIN_THREAD(); + if (!mSupportedResolutions) + { + mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS]; + DEVMODE dev_mode; + ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); + dev_mode.dmSize = sizeof(DEVMODE); + + 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; + ::ZeroMemory(&dev_mode, sizeof(DEVMODE)); + dev_mode.dmSize = sizeof(DEVMODE); + 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) + { + LL_WARNS("Window") << "setDisplayResolution failed, " + << width << "x" << height << "x" << bits << " @ " << refresh << LL_ENDL; + } + + return success; +} + +// protected +bool LLWindowWin32::setFullscreenResolution() +{ + if (mFullscreen) + { + return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenBits, mFullscreenRefresh); + } + else + { + return false; + } +} + +// protected +bool LLWindowWin32::resetDisplayResolution() +{ + LL_DEBUGS("Window") << "resetDisplayResolution START" << LL_ENDL; + + LONG cds_result = ChangeDisplaySettings(NULL, 0); + + bool success = (DISP_CHANGE_SUCCESSFUL == cds_result); + + if (!success) + { + LL_WARNS("Window") << "resetDisplayResolution failed" << LL_ENDL; + } + + LL_DEBUGS("Window") << "resetDisplayResolution END" << LL_ENDL; + + return success; +} + +void LLWindowWin32::swapBuffers() +{ + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; + SwapBuffers(mhDC); + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("GPU Collect"); + LL_PROFILER_GPU_COLLECT; + } +} + + +// +// LLSplashScreenImp +// +LLSplashScreenWin32::LLSplashScreenWin32() +: mWindow(NULL) +{ +} + +LLSplashScreenWin32::~LLSplashScreenWin32() +{ +} + +void LLSplashScreenWin32::showImpl() +{ + // This appears to work. ??? + HINSTANCE hinst = GetModuleHandle(NULL); + + mWindow = CreateDialog(hinst, + TEXT("SPLASHSCREEN"), + NULL, // no parent + (DLGPROC) LLSplashScreenWin32::windowProc); + ShowWindow(mWindow, SW_SHOW); + + // Should set taskbar text without creating a header for the window (caption) + SetWindowTextA(mWindow, "Second Life"); +} + + +void LLSplashScreenWin32::updateImpl(const std::string& mesg) +{ + if (!mWindow) return; + + int output_str_len = MultiByteToWideChar(CP_UTF8, 0, mesg.c_str(), mesg.length(), NULL, 0); + if( output_str_len>1024 ) + return; + + WCHAR w_mesg[1025];//big enought to keep null terminatos + + MultiByteToWideChar (CP_UTF8, 0, mesg.c_str(), mesg.length(), w_mesg, output_str_len); + + //looks like MultiByteToWideChar didn't add null terminator to converted string, see EXT-4858 + w_mesg[output_str_len] = 0; + + SendDlgItemMessage(mWindow, + 666, // HACK: text id + WM_SETTEXT, + FALSE, + (LPARAM)w_mesg); +} + + +void LLSplashScreenWin32::hideImpl() +{ + if (mWindow) + { + if (!destroy_window_handler(mWindow)) + { + LL_WARNS("Window") << "Failed to properly close splash screen window!" << LL_ENDL; + } + mWindow = NULL; + } +} + + +// static +LRESULT CALLBACK LLSplashScreenWin32::windowProc(HWND h_wnd, UINT u_msg, + WPARAM w_param, LPARAM l_param) +{ + // Just give it to windows + return DefWindowProc(h_wnd, u_msg, w_param, l_param); +} + +// +// Helper Funcs +// + +S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 type) +{ + UINT uType; + + switch(type) + { + case OSMB_OK: + uType = MB_OK; + break; + case OSMB_OKCANCEL: + uType = MB_OKCANCEL; + break; + case OSMB_YESNO: + uType = MB_YESNO; + break; + default: + uType = MB_OK; + break; + } + + int retval_win = MessageBoxW(NULL, // HWND + ll_convert_string_to_wide(text).c_str(), + ll_convert_string_to_wide(caption).c_str(), + uType); + S32 retval; + + switch(retval_win) + { + case IDYES: + retval = OSBTN_YES; + break; + case IDNO: + retval = OSBTN_NO; + break; + case IDOK: + retval = OSBTN_OK; + break; + case IDCANCEL: + retval = OSBTN_CANCEL; + break; + default: + retval = OSBTN_CANCEL; + break; + } + + return retval; +} + + +void LLWindowWin32::spawnWebBrowser(const std::string& escaped_url, bool async) +{ + bool found = false; + S32 i; + for (i = 0; i < gURLProtocolWhitelistCount; i++) + { + if (escaped_url.find(gURLProtocolWhitelist[i]) == 0) + { + found = true; + break; + } + } + + if (!found) + { + LL_WARNS("Window") << "spawn_web_browser() called for url with protocol not on whitelist: " << escaped_url << LL_ENDL; + return; + } + + LL_INFOS("Window") << "Opening URL " << escaped_url << LL_ENDL; + + // replaced ShellExecute code with ShellExecuteEx since ShellExecute doesn't work + // reliablly on Vista. + + // this is madness.. no, this is.. + LLWString url_wstring = utf8str_to_wstring( escaped_url ); + llutf16string url_utf16 = wstring_to_utf16str( url_wstring ); + + // let the OS decide what to use to open the URL + SHELLEXECUTEINFO sei = { sizeof( sei ) }; + // NOTE: this assumes that SL will stick around long enough to complete the DDE message exchange + // necessary for ShellExecuteEx to complete + if (async) + { + sei.fMask = SEE_MASK_ASYNCOK; + } + sei.nShow = SW_SHOWNORMAL; + sei.lpVerb = L"open"; + sei.lpFile = url_utf16.c_str(); + ShellExecuteEx( &sei ); +} + +/* + Make the raw keyboard data available - used to poke through to LLQtWebKit so + that Qt/Webkit has access to the virtual keycodes etc. that it needs +*/ +LLSD LLWindowWin32::getNativeKeyData() +{ + LLSD result = LLSD::emptyMap(); + + result["scan_code"] = (S32)mKeyScanCode; + result["virtual_key"] = (S32)mKeyVirtualKey; + result["msg"] = ll_sd_from_U32(mRawMsg); + result["w_param"] = ll_sd_from_U32(mRawWParam); + result["l_param"] = ll_sd_from_U32(mRawLParam); + + return result; +} + +bool LLWindowWin32::dialogColorPicker( 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() +{ + mWindowThread->post([=]() + { + BringWindowToTop(mWindowHandle); + }); +} + +// set (OS) window focus back to the client +void LLWindowWin32::focusClient() +{ + mWindowThread->post([=]() + { + SetFocus(mWindowHandle); + }); +} + +void LLWindowWin32::allowLanguageTextInput(LLPreeditor *preeditor, bool b) +{ + if (b == sLanguageTextInputAllowed || !LLWinImm::isAvailable()) + { + return; + } + + if (preeditor != mPreeditor && !b) + { + // This condition may occur with a call to + // setEnabled(bool) from LLTextEditor or LLLineEditor + // when the control is not focused. + // We need to silently ignore the case so that + // the language input status of the focused control + // is not disturbed. + return; + } + + // Take care of old and new preeditors. + if (preeditor != mPreeditor || !b) + { + if (sLanguageTextInputAllowed) + { + interruptLanguageTextInput(); + } + mPreeditor = (b ? preeditor : NULL); + } + + sLanguageTextInputAllowed = b; + + if (sLanguageTextInputAllowed) + { + mWindowThread->post([=]() + { + // Allowing: Restore the previous IME status, so that the user has a feeling that the previous + // text input continues naturally. Be careful, however, the IME status is meaningful only during the user keeps + // using same Input Locale (aka Keyboard Layout). + if (sWinIMEOpened && GetKeyboardLayout(0) == sWinInputLocale) + { + HIMC himc = LLWinImm::getContext(mWindowHandle); + LLWinImm::setOpenStatus(himc, true); + LLWinImm::setConversionStatus(himc, sWinIMEConversionMode, sWinIMESentenceMode); + LLWinImm::releaseContext(mWindowHandle, himc); + } + }); + } + else + { + mWindowThread->post([=]() + { + // Disallowing: Turn off the IME so that succeeding key events bypass IME and come to us directly. + // However, do it after saving the current IME status. We need to restore the status when + // allowing language text input again. + sWinInputLocale = GetKeyboardLayout(0); + sWinIMEOpened = LLWinImm::isIME(sWinInputLocale); + if (sWinIMEOpened) + { + HIMC himc = LLWinImm::getContext(mWindowHandle); + sWinIMEOpened = LLWinImm::getOpenStatus(himc); + if (sWinIMEOpened) + { + LLWinImm::getConversionStatus(himc, &sWinIMEConversionMode, &sWinIMESentenceMode); + + // We need both ImmSetConversionStatus and ImmSetOpenStatus here to surely disable IME's + // keyboard hooking, because Some IME reacts only on the former and some other on the latter... + LLWinImm::setConversionStatus(himc, IME_CMODE_NOCONVERSION, sWinIMESentenceMode); + LLWinImm::setOpenStatus(himc, false); + } + LLWinImm::releaseContext(mWindowHandle, himc); + } + }); + } +} + +void LLWindowWin32::fillCandidateForm(const LLCoordGL& caret, const LLRect& bounds, + CANDIDATEFORM *form) +{ + LLCoordWindow caret_coord, top_left, bottom_right; + convertCoords(caret, &caret_coord); + convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left); + convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right); + + memset(form, 0, sizeof(CANDIDATEFORM)); + form->dwStyle = CFS_EXCLUDE; + form->ptCurrentPos.x = caret_coord.mX; + form->ptCurrentPos.y = caret_coord.mY; + form->rcArea.left = top_left.mX; + form->rcArea.top = top_left.mY; + form->rcArea.right = bottom_right.mX; + form->rcArea.bottom = bottom_right.mY; +} + + +// Put the IME window at the right place (near current text input). Point coordinates should be the top of the current text line. +void LLWindowWin32::setLanguageTextInput( const LLCoordGL & position ) +{ + if (sLanguageTextInputAllowed && LLWinImm::isAvailable()) + { + HIMC himc = LLWinImm::getContext(mWindowHandle); + + LLCoordWindow win_pos; + convertCoords( position, &win_pos ); + + if ( win_pos.mX >= 0 && win_pos.mY >= 0 && + (win_pos.mX != sWinIMEWindowPosition.mX) || (win_pos.mY != sWinIMEWindowPosition.mY) ) + { + COMPOSITIONFORM ime_form; + memset( &ime_form, 0, sizeof(ime_form) ); + ime_form.dwStyle = CFS_POINT; + ime_form.ptCurrentPos.x = win_pos.mX; + ime_form.ptCurrentPos.y = win_pos.mY; + + LLWinImm::setCompositionWindow( himc, &ime_form ); + + sWinIMEWindowPosition = win_pos; + } + + LLWinImm::releaseContext(mWindowHandle, himc); + } +} + + +void LLWindowWin32::fillCharPosition(const LLCoordGL& caret, const LLRect& bounds, const LLRect& control, + IMECHARPOSITION *char_position) +{ + LLCoordScreen caret_coord, top_left, bottom_right; + convertCoords(caret, &caret_coord); + convertCoords(LLCoordGL(bounds.mLeft, bounds.mTop), &top_left); + convertCoords(LLCoordGL(bounds.mRight, bounds.mBottom), &bottom_right); + + char_position->pt.x = caret_coord.mX; + char_position->pt.y = top_left.mY; // Windows wants the coordinate of upper left corner of a character... + char_position->cLineHeight = bottom_right.mY - top_left.mY; + char_position->rcDocument.left = top_left.mX; + char_position->rcDocument.top = top_left.mY; + char_position->rcDocument.right = bottom_right.mX; + char_position->rcDocument.bottom = bottom_right.mY; +} + +void LLWindowWin32::fillCompositionLogfont(LOGFONT *logfont) +{ + // Our font is a list of FreeType recognized font files that may + // not have a corresponding ones in Windows' fonts. Hence, we + // can't simply tell Windows which font we are using. We will + // notify a _standard_ font for a current input locale instead. + // We use a hard-coded knowledge about the Windows' standard + // configuration to do so... + + memset(logfont, 0, sizeof(LOGFONT)); + + const WORD lang_id = LOWORD(GetKeyboardLayout(0)); + switch (PRIMARYLANGID(lang_id)) + { + case LANG_CHINESE: + // We need to identify one of two Chinese fonts. + switch (SUBLANGID(lang_id)) + { + case SUBLANG_CHINESE_SIMPLIFIED: + case SUBLANG_CHINESE_SINGAPORE: + logfont->lfCharSet = GB2312_CHARSET; + lstrcpy(logfont->lfFaceName, TEXT("SimHei")); + break; + case SUBLANG_CHINESE_TRADITIONAL: + case SUBLANG_CHINESE_HONGKONG: + case SUBLANG_CHINESE_MACAU: + default: + logfont->lfCharSet = CHINESEBIG5_CHARSET; + lstrcpy(logfont->lfFaceName, TEXT("MingLiU")); + break; + } + break; + case LANG_JAPANESE: + logfont->lfCharSet = SHIFTJIS_CHARSET; + lstrcpy(logfont->lfFaceName, TEXT("MS Gothic")); + break; + case LANG_KOREAN: + logfont->lfCharSet = HANGUL_CHARSET; + lstrcpy(logfont->lfFaceName, TEXT("Gulim")); + break; + default: + logfont->lfCharSet = ANSI_CHARSET; + lstrcpy(logfont->lfFaceName, TEXT("Tahoma")); + break; + } + + logfont->lfHeight = mPreeditor->getPreeditFontSize(); + logfont->lfWeight = FW_NORMAL; +} + +U32 LLWindowWin32::fillReconvertString(const LLWString &text, + S32 focus, S32 focus_length, RECONVERTSTRING *reconvert_string) +{ + const llutf16string text_utf16 = wstring_to_utf16str(text); + const DWORD required_size = sizeof(RECONVERTSTRING) + (text_utf16.length() + 1) * sizeof(WCHAR); + if (reconvert_string && reconvert_string->dwSize >= required_size) + { + const DWORD focus_utf16_at = wstring_utf16_length(text, 0, focus); + const DWORD focus_utf16_length = wstring_utf16_length(text, focus, focus_length); + + reconvert_string->dwVersion = 0; + reconvert_string->dwStrLen = text_utf16.length(); + reconvert_string->dwStrOffset = sizeof(RECONVERTSTRING); + reconvert_string->dwCompStrLen = focus_utf16_length; + reconvert_string->dwCompStrOffset = focus_utf16_at * sizeof(WCHAR); + reconvert_string->dwTargetStrLen = 0; + reconvert_string->dwTargetStrOffset = focus_utf16_at * sizeof(WCHAR); + + const LPWSTR text = (LPWSTR)((BYTE *)reconvert_string + sizeof(RECONVERTSTRING)); + memcpy(text, text_utf16.c_str(), (text_utf16.length() + 1) * sizeof(WCHAR)); + } + return required_size; +} + +void LLWindowWin32::updateLanguageTextInputArea() +{ + if (!mPreeditor || !LLWinImm::isAvailable()) + { + return; + } + + LLCoordGL caret_coord; + LLRect preedit_bounds; + if (mPreeditor->getPreeditLocation(-1, &caret_coord, &preedit_bounds, NULL)) + { + mLanguageTextInputPointGL = caret_coord; + mLanguageTextInputAreaGL = preedit_bounds; + + CANDIDATEFORM candidate_form; + fillCandidateForm(caret_coord, preedit_bounds, &candidate_form); + + HIMC himc = LLWinImm::getContext(mWindowHandle); + // Win32 document says there may be up to 4 candidate windows. + // This magic number 4 appears only in the document, and + // there are no constant/macro for the value... + for (int i = 3; i >= 0; --i) + { + candidate_form.dwIndex = i; + LLWinImm::setCandidateWindow(himc, &candidate_form); + } + LLWinImm::releaseContext(mWindowHandle, himc); + } +} + +void LLWindowWin32::interruptLanguageTextInput() +{ + ASSERT_MAIN_THREAD(); + if (mPreeditor && LLWinImm::isAvailable()) + { + HIMC himc = LLWinImm::getContext(mWindowHandle); + LLWinImm::notifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0); + LLWinImm::releaseContext(mWindowHandle, himc); + } +} + +void LLWindowWin32::handleStartCompositionMessage() +{ + // Let IME know the font to use in feedback UI. + LOGFONT logfont; + fillCompositionLogfont(&logfont); + HIMC himc = LLWinImm::getContext(mWindowHandle); + LLWinImm::setCompositionFont(himc, &logfont); + LLWinImm::releaseContext(mWindowHandle, himc); +} + +// Handle WM_IME_COMPOSITION message. + +void LLWindowWin32::handleCompositionMessage(const U32 indexes) +{ + if (!mPreeditor) + { + return; + } + bool needs_update = false; + LLWString result_string; + LLWString preedit_string; + S32 preedit_string_utf16_length = 0; + LLPreeditor::segment_lengths_t preedit_segment_lengths; + LLPreeditor::standouts_t preedit_standouts; + + // Step I: Receive details of preedits from IME. + + HIMC himc = LLWinImm::getContext(mWindowHandle); + + if (indexes & GCS_RESULTSTR) + { + LONG size = LLWinImm::getCompositionString(himc, GCS_RESULTSTR, NULL, 0); + if (size >= 0) + { + const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1]; + size = LLWinImm::getCompositionString(himc, GCS_RESULTSTR, data, size); + if (size > 0) + { + result_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR))); + } + delete[] data; + needs_update = true; + } + } + + if (indexes & GCS_COMPSTR) + { + LONG size = LLWinImm::getCompositionString(himc, GCS_COMPSTR, NULL, 0); + if (size >= 0) + { + const LPWSTR data = new WCHAR[size / sizeof(WCHAR) + 1]; + size = LLWinImm::getCompositionString(himc, GCS_COMPSTR, data, size); + if (size > 0) + { + preedit_string_utf16_length = size / sizeof(WCHAR); + preedit_string = utf16str_to_wstring(llutf16string(data, size / sizeof(WCHAR))); + } + delete[] data; + needs_update = true; + } + } + + if ((indexes & GCS_COMPCLAUSE) && preedit_string.length() > 0) + { + LONG size = LLWinImm::getCompositionString(himc, GCS_COMPCLAUSE, NULL, 0); + if (size > 0) + { + const LPDWORD data = new DWORD[size / sizeof(DWORD)]; + size = LLWinImm::getCompositionString(himc, GCS_COMPCLAUSE, data, size); + if (size >= sizeof(DWORD) * 2 + && data[0] == 0 && data[size / sizeof(DWORD) - 1] == preedit_string_utf16_length) + { + preedit_segment_lengths.resize(size / sizeof(DWORD) - 1); + S32 offset = 0; + for (U32 i = 0; i < preedit_segment_lengths.size(); i++) + { + const S32 length = wstring_wstring_length_from_utf16_length(preedit_string, offset, data[i + 1] - data[i]); + preedit_segment_lengths[i] = length; + offset += length; + } + } + delete[] data; + } + } + + if ((indexes & GCS_COMPATTR) && preedit_segment_lengths.size() > 1) + { + LONG size = LLWinImm::getCompositionString(himc, GCS_COMPATTR, NULL, 0); + if (size > 0) + { + const LPBYTE data = new BYTE[size / sizeof(BYTE)]; + size = LLWinImm::getCompositionString(himc, GCS_COMPATTR, data, size); + if (size == preedit_string_utf16_length) + { + preedit_standouts.assign(preedit_segment_lengths.size(), false); + S32 offset = 0; + for (U32 i = 0; i < preedit_segment_lengths.size(); i++) + { + if (ATTR_TARGET_CONVERTED == data[offset] || ATTR_TARGET_NOTCONVERTED == data[offset]) + { + preedit_standouts[i] = true; + } + offset += wstring_utf16_length(preedit_string, offset, preedit_segment_lengths[i]); + } + } + delete[] data; + } + } + + S32 caret_position = preedit_string.length(); + if (indexes & GCS_CURSORPOS) + { + const S32 caret_position_utf16 = LLWinImm::getCompositionString(himc, GCS_CURSORPOS, NULL, 0); + if (caret_position_utf16 >= 0 && caret_position <= preedit_string_utf16_length) + { + caret_position = wstring_wstring_length_from_utf16_length(preedit_string, 0, caret_position_utf16); + } + } + + if (indexes == 0) + { + // I'm not sure this condition really happens, but + // Windows SDK document says it is an indication + // of "reset everything." + needs_update = true; + } + + LLWinImm::releaseContext(mWindowHandle, himc); + + // Step II: Update the active preeditor. + + if (needs_update) + { + if (preedit_string.length() != 0 || result_string.length() != 0) + { + mPreeditor->resetPreedit(); + } + + if (result_string.length() > 0) + { + for (LLWString::const_iterator i = result_string.begin(); i != result_string.end(); i++) + { + mPreeditor->handleUnicodeCharHere(*i); + } + } + + if (preedit_string.length() == 0) + { + preedit_segment_lengths.clear(); + preedit_standouts.clear(); + } + else + { + if (preedit_segment_lengths.size() == 0) + { + preedit_segment_lengths.assign(1, preedit_string.length()); + } + if (preedit_standouts.size() == 0) + { + preedit_standouts.assign(preedit_segment_lengths.size(), false); + } + } + mPreeditor->updatePreedit(preedit_string, preedit_segment_lengths, preedit_standouts, caret_position); + + // Some IME doesn't query char position after WM_IME_COMPOSITION, + // so we need to update them actively. + updateLanguageTextInputArea(); + } +} + +// Given a text and a focus range, find_context finds and returns a +// surrounding context of the focused subtext. A variable pointed +// to by offset receives the offset in llwchars of the beginning of +// the returned context string in the given wtext. + +static LLWString find_context(const LLWString & wtext, S32 focus, S32 focus_length, S32 *offset) +{ + static const S32 CONTEXT_EXCESS = 30; // This value is by experiences. + + const S32 e = llmin((S32) wtext.length(), focus + focus_length + CONTEXT_EXCESS); + S32 end = focus + focus_length; + while (end < e && '\n' != wtext[end]) + { + end++; + } + + const S32 s = llmax(0, focus - CONTEXT_EXCESS); + S32 start = focus; + while (start > s && '\n' != wtext[start - 1]) + { + --start; + } + + *offset = start; + return wtext.substr(start, end - start); +} + +// final stage of handling drop requests - both from WM_DROPFILES message +// for files and via IDropTarget interface requests. +LLWindowCallbacks::DragNDropResult LLWindowWin32::completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url ) +{ + ASSERT_MAIN_THREAD(); + return mCallbacks->handleDragNDrop( this, gl_coord, mask, action, url ); +} + +// Handle WM_IME_REQUEST message. +// If it handled the message, returns true. Otherwise, false. +// When it handled the message, the value to be returned from +// the Window Procedure is set to *result. + +bool LLWindowWin32::handleImeRequests(WPARAM request, LPARAM param, LRESULT *result) +{ + if ( mPreeditor ) + { + switch (request) + { + case IMR_CANDIDATEWINDOW: // http://msdn2.microsoft.com/en-us/library/ms776080.aspx + { + LLCoordGL caret_coord; + LLRect preedit_bounds; + mPreeditor->getPreeditLocation(-1, &caret_coord, &preedit_bounds, NULL); + + CANDIDATEFORM *const form = (CANDIDATEFORM *)param; + DWORD const dwIndex = form->dwIndex; + fillCandidateForm(caret_coord, preedit_bounds, form); + form->dwIndex = dwIndex; + + *result = 1; + return true; + } + case IMR_QUERYCHARPOSITION: + { + IMECHARPOSITION *const char_position = (IMECHARPOSITION *)param; + + // char_position->dwCharPos counts in number of + // WCHARs, i.e., UTF-16 encoding units, so we can't simply pass the + // number to getPreeditLocation. + + const LLWString & wtext = mPreeditor->getPreeditString(); + S32 preedit, preedit_length; + mPreeditor->getPreeditRange(&preedit, &preedit_length); + LLCoordGL caret_coord; + LLRect preedit_bounds, text_control; + const S32 position = wstring_wstring_length_from_utf16_length(wtext, preedit, char_position->dwCharPos); + + if (!mPreeditor->getPreeditLocation(position, &caret_coord, &preedit_bounds, &text_control)) + { + LL_WARNS("Window") << "*** IMR_QUERYCHARPOSITON called but getPreeditLocation failed." << LL_ENDL; + return false; + } + + fillCharPosition(caret_coord, preedit_bounds, text_control, char_position); + + *result = 1; + return true; + } + case IMR_COMPOSITIONFONT: + { + fillCompositionLogfont((LOGFONT *)param); + + *result = 1; + return true; + } + case IMR_RECONVERTSTRING: + { + mPreeditor->resetPreedit(); + const LLWString & wtext = mPreeditor->getPreeditString(); + S32 select, select_length; + mPreeditor->getSelectionRange(&select, &select_length); + + S32 context_offset; + const LLWString context = find_context(wtext, select, select_length, &context_offset); + + RECONVERTSTRING * const reconvert_string = (RECONVERTSTRING *)param; + const U32 size = fillReconvertString(context, select - context_offset, select_length, reconvert_string); + if (reconvert_string) + { + if (select_length == 0) + { + // Let the IME to decide the reconversion range, and + // adjust the reconvert_string structure accordingly. + HIMC himc = LLWinImm::getContext(mWindowHandle); + const bool adjusted = LLWinImm::setCompositionString(himc, + SCS_QUERYRECONVERTSTRING, reconvert_string, size, NULL, 0); + LLWinImm::releaseContext(mWindowHandle, himc); + if (adjusted) + { + const llutf16string & text_utf16 = wstring_to_utf16str(context); + const S32 new_preedit_start = reconvert_string->dwCompStrOffset / sizeof(WCHAR); + const S32 new_preedit_end = new_preedit_start + reconvert_string->dwCompStrLen; + select = utf16str_wstring_length(text_utf16, new_preedit_start); + select_length = utf16str_wstring_length(text_utf16, new_preedit_end) - select; + select += context_offset; + } + } + mPreeditor->markAsPreedit(select, select_length); + } + + *result = size; + return true; + } + case IMR_CONFIRMRECONVERTSTRING: + { + *result = 0; + return true; + } + case IMR_DOCUMENTFEED: + { + const LLWString & wtext = mPreeditor->getPreeditString(); + S32 preedit, preedit_length; + mPreeditor->getPreeditRange(&preedit, &preedit_length); + + S32 context_offset; + LLWString context = find_context(wtext, preedit, preedit_length, &context_offset); + preedit -= context_offset; + preedit_length = llmin(preedit_length, (S32)context.length() - preedit); + if (preedit_length > 0 && preedit >= 0) + { + // IMR_DOCUMENTFEED may be called when we have an active preedit. + // We should pass the context string *excluding* the preedit string. + // Otherwise, some IME are confused. + context.erase(preedit, preedit_length); + } + + RECONVERTSTRING *reconvert_string = (RECONVERTSTRING *)param; + *result = fillReconvertString(context, preedit, 0, reconvert_string); + return true; + } + default: + return false; + } + } + + return false; +} + +//static +void LLWindowWin32::setDPIAwareness() +{ + HMODULE hShcore = LoadLibrary(L"shcore.dll"); + if (hShcore != NULL) + { + SetProcessDpiAwarenessType pSPDA; + pSPDA = (SetProcessDpiAwarenessType)GetProcAddress(hShcore, "SetProcessDpiAwareness"); + if (pSPDA) + { + + HRESULT hr = pSPDA(PROCESS_PER_MONITOR_DPI_AWARE); + if (hr != S_OK) + { + LL_WARNS() << "SetProcessDpiAwareness() function returned an error. Will use legacy DPI awareness API of Win XP/7" << LL_ENDL; + } + } + FreeLibrary(hShcore); + } + else + { + LL_WARNS() << "Could not load shcore.dll library (included by <ShellScalingAPI.h> from Win 8.1 SDK. Will use legacy DPI awareness API of Win XP/7" << LL_ENDL; + } +} + +void* LLWindowWin32::getDirectInput8() +{ + return &gDirectInput8; +} + +bool LLWindowWin32::getInputDevices(U32 device_type_filter, + std::function<bool(std::string&, LLSD&, void*)> osx_callback, + void * di8_devices_callback, + void* userdata) +{ + if (gDirectInput8 != NULL) + { + // Enumerate devices + HRESULT status = gDirectInput8->EnumDevices( + (DWORD) device_type_filter, // DWORD dwDevType, + (LPDIENUMDEVICESCALLBACK)di8_devices_callback, // LPDIENUMDEVICESCALLBACK lpCallback, // BOOL DIEnumDevicesCallback( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef ) // BOOL CALLBACK DinputDevice::DevicesCallback + (LPVOID*)userdata, // LPVOID pvRef + DIEDFL_ATTACHEDONLY // DWORD dwFlags + ); + + return status == DI_OK; + } + return false; +} + +F32 LLWindowWin32::getSystemUISize() +{ + F32 scale_value = 1.f; + HWND hWnd = (HWND)getPlatformWindow(); + HDC hdc = GetDC(hWnd); + HMONITOR hMonitor; + HANDLE hProcess = GetCurrentProcess(); + PROCESS_DPI_AWARENESS dpi_awareness; + + HMODULE hShcore = LoadLibrary(L"shcore.dll"); + + if (hShcore != NULL) + { + GetProcessDpiAwarenessType pGPDA; + pGPDA = (GetProcessDpiAwarenessType)GetProcAddress(hShcore, "GetProcessDpiAwareness"); + GetDpiForMonitorType pGDFM; + pGDFM = (GetDpiForMonitorType)GetProcAddress(hShcore, "GetDpiForMonitor"); + if (pGPDA != NULL && pGDFM != NULL) + { + pGPDA(hProcess, &dpi_awareness); + if (dpi_awareness == PROCESS_PER_MONITOR_DPI_AWARE) + { + POINT pt; + UINT dpix = 0, dpiy = 0; + HRESULT hr = E_FAIL; + RECT rect; + + GetWindowRect(hWnd, &rect); + // Get the DPI for the monitor, on which the center of window is displayed and set the scaling factor + pt.x = (rect.left + rect.right) / 2; + pt.y = (rect.top + rect.bottom) / 2; + hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + hr = pGDFM(hMonitor, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + if (hr == S_OK) + { + scale_value = F32(dpix) / F32(USER_DEFAULT_SCREEN_DPI); + } + else + { + LL_WARNS() << "Could not determine DPI for monitor. Setting scale to default 100 %" << LL_ENDL; + scale_value = 1.0f; + } + } + else + { + LL_WARNS() << "Process is not per-monitor DPI-aware. Setting scale to default 100 %" << LL_ENDL; + scale_value = 1.0f; + } + } + FreeLibrary(hShcore); + } + else + { + LL_WARNS() << "Could not load shcore.dll library (included by <ShellScalingAPI.h> from Win 8.1 SDK). Using legacy DPI awareness API of Win XP/7" << LL_ENDL; + scale_value = F32(GetDeviceCaps(hdc, LOGPIXELSX)) / F32(USER_DEFAULT_SCREEN_DPI); + } + + ReleaseDC(hWnd, hdc); + return scale_value; +} + +//static +std::vector<std::string> LLWindowWin32::getDisplaysResolutionList() +{ + return sMonitorInfo.getResolutionsList(); +} + +//static +std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList() +{ + // Fonts previously in getFontListSans() have moved to fonts.xml. + return std::vector<std::string>(); +} + +void LLWindowWin32::setMaxVRAMMegabytes(U32 max_vram) +{ + if (mWindowThread) + { + mWindowThread->mMaxVRAM = max_vram; + } +} + +#endif // LL_WINDOWS + +inline LLWindowWin32::LLWindowWin32Thread::LLWindowWin32Thread() + : LL::ThreadPool("Window Thread", 1, MAX_QUEUE_SIZE, true /*should be false, temporary workaround for SL-18721*/) +{ + LL::ThreadPool::start(); +} + +void LLWindowWin32::LLWindowWin32Thread::close() +{ + if (!mQueue->isClosed()) + { + LL_WARNS() << "Closing window thread without using destroy_window_handler" << LL_ENDL; + LL::ThreadPool::close(); + + // Workaround for SL-18721 in case window closes too early and abruptly + LLSplashScreen::show(); + LLSplashScreen::update("..."); // will be updated later + } +} + + +/** + * LogChange is to log changes in status while trying to avoid spamming the + * log with repeated messages, especially in a tight loop. It refuses to log + * a continuous run of identical messages, but logs every time the message + * changes. (It will happily spam when messages quickly bounce back and + * forth.) + */ +class LogChange +{ +public: + LogChange(const std::string& tag): + mTag(tag) + {} + + template <typename... Items> + void always(Items&&... items) + { + // This odd construct ensures that the stringize() call is only + // executed if DEBUG logging is enabled for the passed tag. + LL_DEBUGS(mTag.c_str()); + log(LL_CONT, stringize(std::forward<Items>(items)...)); + LL_ENDL; + } + + template <typename... Items> + void onChange(Items&&... items) + { + LL_DEBUGS(mTag.c_str()); + auto str = stringize(std::forward<Items>(items)...); + if (str != mPrev) + { + log(LL_CONT, str); + } + LL_ENDL; + } + +private: + void log(std::ostream& out, const std::string& message) + { + mPrev = message; + out << message; + } + std::string mTag; + std::string mPrev; +}; + +// Print hardware debug info about available graphics adapters in ordinal order +void debugEnumerateGraphicsAdapters() +{ + LL_INFOS("Window") << "Enumerating graphics adapters..." << LL_ENDL; + + IDXGIFactory1* factory; + HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&factory); + if (FAILED(res) || !factory) + { + LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL; + } + else + { + UINT graphics_adapter_index = 0; + IDXGIAdapter3* dxgi_adapter; + while (true) + { + res = factory->EnumAdapters(graphics_adapter_index, reinterpret_cast<IDXGIAdapter**>(&dxgi_adapter)); + if (FAILED(res)) + { + if (graphics_adapter_index == 0) + { + LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL; + } + else + { + LL_INFOS("Window") << "Done enumerating graphics adapters" << LL_ENDL; + } + } + else + { + DXGI_ADAPTER_DESC desc; + dxgi_adapter->GetDesc(&desc); + std::wstring description_w((wchar_t*)desc.Description); + std::string description(description_w.begin(), description_w.end()); + LL_INFOS("Window") << "Graphics adapter index: " << graphics_adapter_index << ", " + << "Description: " << description << ", " + << "DeviceId: " << desc.DeviceId << ", " + << "SubSysId: " << desc.SubSysId << ", " + << "AdapterLuid: " << desc.AdapterLuid.HighPart << "_" << desc.AdapterLuid.LowPart << ", " + << "DedicatedVideoMemory: " << desc.DedicatedVideoMemory / 1024 / 1024 << ", " + << "DedicatedSystemMemory: " << desc.DedicatedSystemMemory / 1024 / 1024 << ", " + << "SharedSystemMemory: " << desc.SharedSystemMemory / 1024 / 1024 << LL_ENDL; + } + + if (dxgi_adapter) + { + dxgi_adapter->Release(); + dxgi_adapter = NULL; + } + else + { + break; + } + + graphics_adapter_index++; + } + } + + if (factory) + { + factory->Release(); + } +} + +void LLWindowWin32::LLWindowWin32Thread::initDX() +{ + if (!mGLReady) { return; } + + if (mDXGIAdapter == NULL) + { + debugEnumerateGraphicsAdapters(); + + IDXGIFactory4* pFactory = nullptr; + + HRESULT res = CreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&pFactory); + + if (FAILED(res)) + { + LL_WARNS() << "CreateDXGIFactory1 failed: 0x" << std::hex << res << LL_ENDL; + } + else + { + res = pFactory->EnumAdapters(0, reinterpret_cast<IDXGIAdapter**>(&mDXGIAdapter)); + if (FAILED(res)) + { + LL_WARNS() << "EnumAdapters failed: 0x" << std::hex << res << LL_ENDL; + } + else + { + LL_INFOS() << "EnumAdapters success" << LL_ENDL; + } + } + + if (pFactory) + { + pFactory->Release(); + } + } +} + +void LLWindowWin32::LLWindowWin32Thread::initD3D() +{ + if (!mGLReady) { return; } + + if (mDXGIAdapter == NULL && mD3DDevice == NULL && mWindowHandleThrd != 0) + { + mD3D = Direct3DCreate9(D3D_SDK_VERSION); + + D3DPRESENT_PARAMETERS d3dpp; + + ZeroMemory(&d3dpp, sizeof(d3dpp)); + d3dpp.Windowed = TRUE; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + + HRESULT res = mD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mWindowHandleThrd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &mD3DDevice); + + if (FAILED(res)) + { + LL_WARNS() << "(fallback) CreateDevice failed: 0x" << std::hex << res << LL_ENDL; + } + else + { + LL_INFOS() << "(fallback) CreateDevice success" << LL_ENDL; + } + } +} + +void LLWindowWin32::LLWindowWin32Thread::cleanupDX() +{ + //clean up DXGI/D3D resources + if (mDXGIAdapter) + { + mDXGIAdapter->Release(); + mDXGIAdapter = nullptr; + } + + if (mD3DDevice) + { + mD3DDevice->Release(); + mD3DDevice = nullptr; + } + + if (mD3D) + { + mD3D->Release(); + mD3D = nullptr; + } +} + +void LLWindowWin32::LLWindowWin32Thread::run() +{ + sWindowThreadId = std::this_thread::get_id(); + LogChange logger("Window"); + + //as good a place as any to up the MM timer resolution (see ms_sleep) + //attempt to set timer resolution to 1ms + TIMECAPS tc; + if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) == TIMERR_NOERROR) + { + timeBeginPeriod(llclamp((U32) 1, tc.wPeriodMin, tc.wPeriodMax)); + } + + while (! getQueue().done()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; + + // lazily call initD3D inside this loop to catch when mGLReady has been set to true + initDX(); + + if (mWindowHandleThrd != 0) + { + // lazily call initD3D inside this loop to catch when mWindowHandle has been set, and mGLReady has been set to true + // *TODO: Shutdown if this fails when mWindowHandle exists + initD3D(); + + MSG msg; + BOOL status; + if (mhDCThrd == 0) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - PeekMessage"); + logger.onChange("PeekMessage(", std::hex, mWindowHandleThrd, ")"); + status = PeekMessage(&msg, mWindowHandleThrd, 0, 0, PM_REMOVE); + } + else + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - GetMessage"); + logger.always("GetMessage(", std::hex, mWindowHandleThrd, ")"); + status = GetMessage(&msg, NULL, 0, 0); + } + if (status > 0) + { + logger.always("got MSG (", std::hex, msg.hwnd, ", ", msg.message, + ", ", msg.wParam, ")"); + TranslateMessage(&msg); + DispatchMessage(&msg); + + mMessageQueue.pushFront(msg); + } + } + + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - Function Queue"); + logger.onChange("runPending()"); + //process any pending functions + getQueue().runPending(); + } + +#if 0 + { + LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - Sleep"); + logger.always("sleep(1)"); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +#endif + } + + cleanupDX(); +} + +void LLWindowWin32::LLWindowWin32Thread::wakeAndDestroy() +{ + if (mQueue->isClosed()) + { + LL_WARNS() << "Tried to close Queue. Win32 thread Queue already closed." << LL_ENDL; + return; + } + + // Make sure we don't leave a blank toolbar button. + // Also hiding window now prevents user from suspending it + // via some action (like dragging it around) + ShowWindow(mWindowHandleThrd, SW_HIDE); + + // Schedule destruction + HWND old_handle = mWindowHandleThrd; + post([this]() + { + if (IsWindow(mWindowHandleThrd)) + { + if (mhDCThrd) + { + if (!ReleaseDC(mWindowHandleThrd, mhDCThrd)) + { + LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; + } + mhDCThrd = NULL; + } + + // This causes WM_DESTROY to be sent *immediately* + if (!destroy_window_handler(mWindowHandleThrd)) + { + LL_WARNS("Window") << "Failed to destroy Window! " << std::hex << GetLastError() << LL_ENDL; + } + } + else + { + // Something killed the window while we were busy destroying gl or handle somehow got broken + LL_WARNS("Window") << "Failed to destroy Window, invalid handle!" << LL_ENDL; + } + mWindowHandleThrd = NULL; + mhDCThrd = NULL; + mGLReady = false; + }); + + LL_DEBUGS("Window") << "Closing window's pool queue" << LL_ENDL; + mQueue->close(); + + // Post a nonsense user message to wake up the thread in + // case it is waiting for a getMessage() + if (old_handle) + { + WPARAM wparam{ 0xB0B0 }; + LL_DEBUGS("Window") << "PostMessage(" << std::hex << old_handle + << ", " << WM_DUMMY_ + << ", " << wparam << ")" << std::dec << LL_ENDL; + PostMessage(old_handle, WM_DUMMY_, wparam, 0x1337); + } + + // There are cases where window will refuse to close, + // can't wait forever on join, check state instead + LLTimer timeout; + timeout.setTimerExpirySec(2.0); + while (!getQueue().done() && !timeout.hasExpired() && mWindowHandleThrd) + { + ms_sleep(100); + } + + if (getQueue().done() || mWindowHandleThrd == NULL) + { + // Window is closed, started closing or is cleaning up + // now wait for our single thread to die. + if (mWindowHandleThrd) + { + LL_INFOS("Window") << "Window is closing, waiting on pool's thread to join, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL; + } + else + { + LL_DEBUGS("Window") << "Waiting on pool's thread, time since post: " << timeout.getElapsedSeconds() << "s" << LL_ENDL; + } + for (auto& pair : mThreads) + { + pair.second.join(); + } + } + else + { + // Something suspended window thread, can't afford to wait forever + // so kill thread instead + // Ex: This can happen if user starts dragging window arround (if it + // was visible) or a modal notification pops up + LL_WARNS("Window") << "Window is frozen, couldn't perform clean exit" << LL_ENDL; + + for (auto& pair : mThreads) + { + // very unsafe + TerminateThread(pair.second.native_handle(), 0); + pair.second.detach(); + cleanupDX(); + } + } + LL_DEBUGS("Window") << "thread pool shutdown complete" << LL_ENDL; +} + +void LLWindowWin32::post(const std::function<void()>& func) +{ + mFunctionQueue.pushFront(func); +} + +void LLWindowWin32::postMouseButtonEvent(const std::function<void()>& func) +{ + mMouseQueue.pushFront(func); +} + +void LLWindowWin32::kickWindowThread(HWND windowHandle) +{ + if (! windowHandle) + windowHandle = mWindowHandle; + if (windowHandle) + { + // post a nonsense user message to wake up the Window Thread in + // case any functions are pending and no windows events came + // through this frame + WPARAM wparam{ 0xB0B0 }; + LL_DEBUGS("Window") << "PostMessage(" << std::hex << windowHandle + << ", " << WM_DUMMY_ + << ", " << wparam << ")" << std::dec << LL_ENDL; + PostMessage(windowHandle, WM_DUMMY_, wparam, 0x1337); + } +} + +void LLWindowWin32::updateWindowRect() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; + //called from window thread + RECT rect; + RECT client_rect; + + if (GetWindowRect(mWindowHandle, &rect) && + GetClientRect(mWindowHandle, &client_rect)) + { + post([=] + { + mRect = rect; + mClientRect = client_rect; + }); + } +} diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 8516b45393..82541ace6e 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -1,295 +1,295 @@ -/**
- * @file llwindowwin32.h
- * @brief Windows implementation of LLWindow class
- *
- * $LicenseInfo:firstyear=2001&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLWINDOWWIN32_H
-#define LL_LLWINDOWWIN32_H
-
-// Limit Windows API to small and manageable set.
-#include "llwin32headerslean.h"
-
-#include "llwindow.h"
-#include "llwindowcallbacks.h"
-#include "lldragdropwin32.h"
-#include "llthread.h"
-#include "llthreadsafequeue.h"
-#include "llmutex.h"
-#include "workqueue.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*/ void minimize();
- /*virtual*/ void restore();
- /*virtual*/ bool getFullscreen();
- /*virtual*/ bool getPosition(LLCoordScreen *position);
- /*virtual*/ bool getSize(LLCoordScreen *size);
- /*virtual*/ bool getSize(LLCoordWindow *size);
- /*virtual*/ bool setPosition(LLCoordScreen position);
- /*virtual*/ bool setSizeImpl(LLCoordScreen size);
- /*virtual*/ bool setSizeImpl(LLCoordWindow size);
- /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL);
- /*virtual*/ void setTitle(const std::string title);
- void* createSharedContext() override;
- void makeContextCurrent(void* context) override;
- void destroySharedContext(void* context) override;
- /*virtual*/ void toggleVSync(bool enable_vsync);
- /*virtual*/ bool setCursorPosition(LLCoordWindow position);
- /*virtual*/ bool getCursorPosition(LLCoordWindow *position);
- /*virtual*/ bool getCursorDelta(LLCoordCommon* delta);
- /*virtual*/ void showCursor();
- /*virtual*/ void hideCursor();
- /*virtual*/ void showCursorFromMouseMove();
- /*virtual*/ void hideCursorUntilMouseMove();
- /*virtual*/ bool isCursorHidden();
- /*virtual*/ void updateCursor();
- /*virtual*/ ECursorType getCursor() const;
- /*virtual*/ void captureMouse();
- /*virtual*/ void releaseMouse();
- /*virtual*/ void setMouseClipping( bool b );
- /*virtual*/ bool isClipboardTextAvailable();
- /*virtual*/ bool pasteTextFromClipboard(LLWString &dst);
- /*virtual*/ bool copyTextToClipboard(const LLWString &src);
- /*virtual*/ void flashIcon(F32 seconds);
- /*virtual*/ F32 getGamma();
- /*virtual*/ bool setGamma(const F32 gamma); // Set the gamma
- /*virtual*/ void setFSAASamples(const U32 fsaa_samples);
- /*virtual*/ U32 getFSAASamples();
- /*virtual*/ bool restoreGamma(); // Restore original gamma table (before updating gamma)
- /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
- /*virtual*/ void gatherInput();
- /*virtual*/ void delayInputProcessing();
- /*virtual*/ void swapBuffers();
- /*virtual*/ void restoreGLContext() {};
-
- // 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 setMaxVRAMMegabytes(U32 max_vram) override;
-
- /*virtual*/ bool dialogColorPicker(F32 *r, F32 *g, F32 *b );
-
- /*virtual*/ void *getPlatformWindow();
- /*virtual*/ void bringToFront();
- /*virtual*/ void focusClient();
-
- /*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, bool b);
- /*virtual*/ void setLanguageTextInput( const LLCoordGL & pos );
- /*virtual*/ void updateLanguageTextInputArea();
- /*virtual*/ void interruptLanguageTextInput();
- /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async);
-
- /*virtual*/ F32 getSystemUISize();
-
- LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url );
-
- static std::vector<std::string> getDisplaysResolutionList();
- static std::vector<std::string> getDynamicFallbackFontList();
- static void setDPIAwareness();
-
- /*virtual*/ void* getDirectInput8();
- /*virtual*/ bool getInputDevices(U32 device_type_filter,
- std::function<bool(std::string&, LLSD&, void*)> osx_callback,
- void* win_callback,
- void* userdata);
-
- U32 getRawWParam() { return mRawWParam; }
-
-protected:
- LLWindowWin32(LLWindowCallbacks* callbacks,
- const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags,
- bool fullscreen, bool clearBg, bool enable_vsync, bool use_gl,
- bool ignore_pixel_depth, U32 fsaa_samples, U32 max_cores, U32 max_vram, F32 max_gl_version);
- ~LLWindowWin32();
-
- void initCursors();
- void initInputDevices();
- HCURSOR loadColorCursor(LPCTSTR name);
- bool isValid();
- void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
- virtual LLSD getNativeKeyData();
-
- // 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();
-
- bool shouldPostQuit() { return mPostQuit; }
-
- void fillCompositionForm(const LLRect& bounds, COMPOSITIONFORM *form);
- void fillCandidateForm(const LLCoordGL& caret, const LLRect& bounds, CANDIDATEFORM *form);
- void fillCharPosition(const LLCoordGL& caret, const LLRect& bounds, const LLRect& control, IMECHARPOSITION *char_position);
- void fillCompositionLogfont(LOGFONT *logfont);
- U32 fillReconvertString(const LLWString &text, S32 focus, S32 focus_length, RECONVERTSTRING *reconvert_string);
- void handleStartCompositionMessage();
- void handleCompositionMessage(U32 indexes);
- bool handleImeRequests(WPARAM request, LPARAM param, LRESULT *result);
-
-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 = 0; // window handle
- HGLRC mhRC = 0; // OpenGL rendering context
- HDC mhDC = 0; // Windows Device context handle
- HINSTANCE mhInstance; // handle to application instance
- 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
- LLCoordWindow mCursorPosition; // mouse cursor position, should only be mutated on main thread
- LLMutex mRawMouseMutex;
- RAWINPUTDEVICE mRawMouse;
- LLCoordWindow mLastCursorPosition; // mouse cursor position from previous frame
- LLCoordCommon mRawMouseDelta; // raw mouse delta according to window thread
- LLCoordCommon mMouseFrameDelta; // how much the mouse moved between the last two calls to gatherInput
-
- MASK mMouseMask;
-
- static bool sIsClassRegistered; // has the window class been registered?
-
- F32 mCurrentGamma;
- U32 mFSAASamples;
- U32 mMaxCores; // for debugging only -- maximum number of CPU cores to use, or 0 for no limit
- F32 mMaxGLVersion; // maximum OpenGL version to attempt to use (clamps to 3.2 - 4.6)
- WORD mPrevGammaRamp[3][256];
- WORD mCurrentGammaRamp[3][256];
- bool mCustomGammaSet;
-
- LPWSTR mIconResource;
- bool mInputProcessingPaused;
-
- // The following variables are for Language Text Input control.
- // They are all static, since one context is shared by all LLWindowWin32
- // instances.
- static bool sLanguageTextInputAllowed;
- static bool sWinIMEOpened;
- static HKL sWinInputLocale;
- static DWORD sWinIMEConversionMode;
- static DWORD sWinIMESentenceMode;
- static LLCoordWindow sWinIMEWindowPosition;
- LLCoordGL mLanguageTextInputPointGL;
- LLRect mLanguageTextInputAreaGL;
-
- LLPreeditor *mPreeditor;
-
- LLDragDropWin32* mDragDrop;
-
- U32 mKeyCharCode;
- U32 mKeyScanCode;
- U32 mKeyVirtualKey;
- U32 mRawMsg;
- U32 mRawWParam;
- U32 mRawLParam;
-
- bool mMouseVanish;
-
- // Cached values of GetWindowRect and GetClientRect to be used by app thread
- void updateWindowRect();
- RECT mRect;
- RECT mClientRect;
-
- struct LLWindowWin32Thread;
- LLWindowWin32Thread* mWindowThread = nullptr;
- LLThreadSafeQueue<std::function<void()>> mFunctionQueue;
- LLThreadSafeQueue<std::function<void()>> mMouseQueue;
- void post(const std::function<void()>& func);
- void postMouseButtonEvent(const std::function<void()>& func);
- void recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style);
- void kickWindowThread(HWND windowHandle=0);
-
- friend class LLWindowManager;
-};
-
-class LLSplashScreenWin32 : public LLSplashScreen
-{
-public:
- LLSplashScreenWin32();
- virtual ~LLSplashScreenWin32();
-
- /*virtual*/ void showImpl();
- /*virtual*/ void updateImpl(const std::string& 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;
-extern LPWSTR gIconResource;
-
-static void handleMessage( const MSG& msg );
-
-S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 type);
-
-#endif //LL_LLWINDOWWIN32_H
+/** + * @file llwindowwin32.h + * @brief Windows implementation of LLWindow class + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLWINDOWWIN32_H +#define LL_LLWINDOWWIN32_H + +// Limit Windows API to small and manageable set. +#include "llwin32headerslean.h" + +#include "llwindow.h" +#include "llwindowcallbacks.h" +#include "lldragdropwin32.h" +#include "llthread.h" +#include "llthreadsafequeue.h" +#include "llmutex.h" +#include "workqueue.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*/ void minimize(); + /*virtual*/ void restore(); + /*virtual*/ bool getFullscreen(); + /*virtual*/ bool getPosition(LLCoordScreen *position); + /*virtual*/ bool getSize(LLCoordScreen *size); + /*virtual*/ bool getSize(LLCoordWindow *size); + /*virtual*/ bool setPosition(LLCoordScreen position); + /*virtual*/ bool setSizeImpl(LLCoordScreen size); + /*virtual*/ bool setSizeImpl(LLCoordWindow size); + /*virtual*/ bool switchContext(bool fullscreen, const LLCoordScreen &size, bool enable_vsync, const LLCoordScreen * const posp = NULL); + /*virtual*/ void setTitle(const std::string title); + void* createSharedContext() override; + void makeContextCurrent(void* context) override; + void destroySharedContext(void* context) override; + /*virtual*/ void toggleVSync(bool enable_vsync); + /*virtual*/ bool setCursorPosition(LLCoordWindow position); + /*virtual*/ bool getCursorPosition(LLCoordWindow *position); + /*virtual*/ bool getCursorDelta(LLCoordCommon* delta); + /*virtual*/ void showCursor(); + /*virtual*/ void hideCursor(); + /*virtual*/ void showCursorFromMouseMove(); + /*virtual*/ void hideCursorUntilMouseMove(); + /*virtual*/ bool isCursorHidden(); + /*virtual*/ void updateCursor(); + /*virtual*/ ECursorType getCursor() const; + /*virtual*/ void captureMouse(); + /*virtual*/ void releaseMouse(); + /*virtual*/ void setMouseClipping( bool b ); + /*virtual*/ bool isClipboardTextAvailable(); + /*virtual*/ bool pasteTextFromClipboard(LLWString &dst); + /*virtual*/ bool copyTextToClipboard(const LLWString &src); + /*virtual*/ void flashIcon(F32 seconds); + /*virtual*/ F32 getGamma(); + /*virtual*/ bool setGamma(const F32 gamma); // Set the gamma + /*virtual*/ void setFSAASamples(const U32 fsaa_samples); + /*virtual*/ U32 getFSAASamples(); + /*virtual*/ bool restoreGamma(); // Restore original gamma table (before updating gamma) + /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; } + /*virtual*/ void gatherInput(); + /*virtual*/ void delayInputProcessing(); + /*virtual*/ void swapBuffers(); + /*virtual*/ void restoreGLContext() {}; + + // 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 setMaxVRAMMegabytes(U32 max_vram) override; + + /*virtual*/ bool dialogColorPicker(F32 *r, F32 *g, F32 *b ); + + /*virtual*/ void *getPlatformWindow(); + /*virtual*/ void bringToFront(); + /*virtual*/ void focusClient(); + + /*virtual*/ void allowLanguageTextInput(LLPreeditor *preeditor, bool b); + /*virtual*/ void setLanguageTextInput( const LLCoordGL & pos ); + /*virtual*/ void updateLanguageTextInputArea(); + /*virtual*/ void interruptLanguageTextInput(); + /*virtual*/ void spawnWebBrowser(const std::string& escaped_url, bool async); + + /*virtual*/ F32 getSystemUISize(); + + LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url ); + + static std::vector<std::string> getDisplaysResolutionList(); + static std::vector<std::string> getDynamicFallbackFontList(); + static void setDPIAwareness(); + + /*virtual*/ void* getDirectInput8(); + /*virtual*/ bool getInputDevices(U32 device_type_filter, + std::function<bool(std::string&, LLSD&, void*)> osx_callback, + void* win_callback, + void* userdata); + + U32 getRawWParam() { return mRawWParam; } + +protected: + LLWindowWin32(LLWindowCallbacks* callbacks, + const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, + bool fullscreen, bool clearBg, bool enable_vsync, bool use_gl, + bool ignore_pixel_depth, U32 fsaa_samples, U32 max_cores, U32 max_vram, F32 max_gl_version); + ~LLWindowWin32(); + + void initCursors(); + void initInputDevices(); + HCURSOR loadColorCursor(LPCTSTR name); + bool isValid(); + void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size); + virtual LLSD getNativeKeyData(); + + // 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(); + + bool shouldPostQuit() { return mPostQuit; } + + void fillCompositionForm(const LLRect& bounds, COMPOSITIONFORM *form); + void fillCandidateForm(const LLCoordGL& caret, const LLRect& bounds, CANDIDATEFORM *form); + void fillCharPosition(const LLCoordGL& caret, const LLRect& bounds, const LLRect& control, IMECHARPOSITION *char_position); + void fillCompositionLogfont(LOGFONT *logfont); + U32 fillReconvertString(const LLWString &text, S32 focus, S32 focus_length, RECONVERTSTRING *reconvert_string); + void handleStartCompositionMessage(); + void handleCompositionMessage(U32 indexes); + bool handleImeRequests(WPARAM request, LPARAM param, LRESULT *result); + +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 = 0; // window handle + HGLRC mhRC = 0; // OpenGL rendering context + HDC mhDC = 0; // Windows Device context handle + HINSTANCE mhInstance; // handle to application instance + 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 + LLCoordWindow mCursorPosition; // mouse cursor position, should only be mutated on main thread + LLMutex mRawMouseMutex; + RAWINPUTDEVICE mRawMouse; + LLCoordWindow mLastCursorPosition; // mouse cursor position from previous frame + LLCoordCommon mRawMouseDelta; // raw mouse delta according to window thread + LLCoordCommon mMouseFrameDelta; // how much the mouse moved between the last two calls to gatherInput + + MASK mMouseMask; + + static bool sIsClassRegistered; // has the window class been registered? + + F32 mCurrentGamma; + U32 mFSAASamples; + U32 mMaxCores; // for debugging only -- maximum number of CPU cores to use, or 0 for no limit + F32 mMaxGLVersion; // maximum OpenGL version to attempt to use (clamps to 3.2 - 4.6) + WORD mPrevGammaRamp[3][256]; + WORD mCurrentGammaRamp[3][256]; + bool mCustomGammaSet; + + LPWSTR mIconResource; + bool mInputProcessingPaused; + + // The following variables are for Language Text Input control. + // They are all static, since one context is shared by all LLWindowWin32 + // instances. + static bool sLanguageTextInputAllowed; + static bool sWinIMEOpened; + static HKL sWinInputLocale; + static DWORD sWinIMEConversionMode; + static DWORD sWinIMESentenceMode; + static LLCoordWindow sWinIMEWindowPosition; + LLCoordGL mLanguageTextInputPointGL; + LLRect mLanguageTextInputAreaGL; + + LLPreeditor *mPreeditor; + + LLDragDropWin32* mDragDrop; + + U32 mKeyCharCode; + U32 mKeyScanCode; + U32 mKeyVirtualKey; + U32 mRawMsg; + U32 mRawWParam; + U32 mRawLParam; + + bool mMouseVanish; + + // Cached values of GetWindowRect and GetClientRect to be used by app thread + void updateWindowRect(); + RECT mRect; + RECT mClientRect; + + struct LLWindowWin32Thread; + LLWindowWin32Thread* mWindowThread = nullptr; + LLThreadSafeQueue<std::function<void()>> mFunctionQueue; + LLThreadSafeQueue<std::function<void()>> mMouseQueue; + void post(const std::function<void()>& func); + void postMouseButtonEvent(const std::function<void()>& func); + void recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style); + void kickWindowThread(HWND windowHandle=0); + + friend class LLWindowManager; +}; + +class LLSplashScreenWin32 : public LLSplashScreen +{ +public: + LLSplashScreenWin32(); + virtual ~LLSplashScreenWin32(); + + /*virtual*/ void showImpl(); + /*virtual*/ void updateImpl(const std::string& 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; +extern LPWSTR gIconResource; + +static void handleMessage( const MSG& msg ); + +S32 OSMessageBoxWin32(const std::string& text, const std::string& caption, U32 type); + +#endif //LL_LLWINDOWWIN32_H |