diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/llwindow | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/llwindow')
37 files changed, 16837 insertions, 16323 deletions
diff --git a/indra/llwindow/llappdelegate-objc.h b/indra/llwindow/llappdelegate-objc.h index ceda7ff74c..ef36f7d4a8 100644 --- a/indra/llwindow/llappdelegate-objc.h +++ b/indra/llwindow/llappdelegate-objc.h @@ -28,11 +28,11 @@ #import "llopenglview-objc.h" @interface LLAppDelegate : NSObject <NSApplicationDelegate> { - LLNSWindow *window; - NSWindow *inputWindow; - LLNonInlineTextView *inputView; - NSTimer *frameTimer; - NSString *currentInputLanguage; + LLNSWindow *window; + NSWindow *inputWindow; + LLNonInlineTextView *inputView; + NSTimer *frameTimer; + NSString *currentInputLanguage; std::string secondLogPath; } diff --git a/indra/llwindow/llcursortypes.cpp b/indra/llwindow/llcursortypes.cpp index 3079cc2419..3e531873db 100644 --- a/indra/llwindow/llcursortypes.cpp +++ b/indra/llwindow/llcursortypes.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llcursortypes.cpp * @brief Cursor types and lookup of types from a string * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -30,63 +30,63 @@ ECursorType getCursorFromString(const std::string& cursor_string) { - static std::map<std::string,U32> cursor_string_table; - if (cursor_string_table.empty()) - { - cursor_string_table["UI_CURSOR_ARROW"] = UI_CURSOR_ARROW; - cursor_string_table["UI_CURSOR_WAIT"] = UI_CURSOR_WAIT; - cursor_string_table["UI_CURSOR_HAND"] = UI_CURSOR_HAND; - cursor_string_table["UI_CURSOR_IBEAM"] = UI_CURSOR_IBEAM; - cursor_string_table["UI_CURSOR_CROSS"] = UI_CURSOR_CROSS; - cursor_string_table["UI_CURSOR_SIZENWSE"] = UI_CURSOR_SIZENWSE; - cursor_string_table["UI_CURSOR_SIZENESW"] = UI_CURSOR_SIZENESW; - cursor_string_table["UI_CURSOR_SIZEWE"] = UI_CURSOR_SIZEWE; - cursor_string_table["UI_CURSOR_SIZENS"] = UI_CURSOR_SIZENS; - cursor_string_table["UI_CURSOR_SIZEALL"] = UI_CURSOR_SIZEALL; - cursor_string_table["UI_CURSOR_NO"] = UI_CURSOR_NO; - cursor_string_table["UI_CURSOR_WORKING"] = UI_CURSOR_WORKING; - cursor_string_table["UI_CURSOR_TOOLGRAB"] = UI_CURSOR_TOOLGRAB; - cursor_string_table["UI_CURSOR_TOOLLAND"] = UI_CURSOR_TOOLLAND; - cursor_string_table["UI_CURSOR_TOOLFOCUS"] = UI_CURSOR_TOOLFOCUS; - cursor_string_table["UI_CURSOR_TOOLCREATE"] = UI_CURSOR_TOOLCREATE; - cursor_string_table["UI_CURSOR_ARROWDRAG"] = UI_CURSOR_ARROWDRAG; - cursor_string_table["UI_CURSOR_ARROWCOPY"] = UI_CURSOR_ARROWCOPY; - cursor_string_table["UI_CURSOR_ARROWDRAGMULTI"] = UI_CURSOR_ARROWDRAGMULTI; - cursor_string_table["UI_CURSOR_ARROWCOPYMULTI"] = UI_CURSOR_ARROWCOPYMULTI; - cursor_string_table["UI_CURSOR_NOLOCKED"] = UI_CURSOR_NOLOCKED; - cursor_string_table["UI_CURSOR_ARROWLOCKED"] = UI_CURSOR_ARROWLOCKED; - cursor_string_table["UI_CURSOR_GRABLOCKED"] = UI_CURSOR_GRABLOCKED; - cursor_string_table["UI_CURSOR_TOOLTRANSLATE"] = UI_CURSOR_TOOLTRANSLATE; - cursor_string_table["UI_CURSOR_TOOLROTATE"] = UI_CURSOR_TOOLROTATE; - cursor_string_table["UI_CURSOR_TOOLSCALE"] = UI_CURSOR_TOOLSCALE; - cursor_string_table["UI_CURSOR_TOOLCAMERA"] = UI_CURSOR_TOOLCAMERA; - cursor_string_table["UI_CURSOR_TOOLPAN"] = UI_CURSOR_TOOLPAN; - cursor_string_table["UI_CURSOR_TOOLZOOMIN"] = UI_CURSOR_TOOLZOOMIN; - cursor_string_table["UI_CURSOR_TOOLZOOMOUT"] = UI_CURSOR_TOOLZOOMOUT; - cursor_string_table["UI_CURSOR_TOOLPICKOBJECT3"] = UI_CURSOR_TOOLPICKOBJECT3; - cursor_string_table["UI_CURSOR_TOOLPLAY"] = UI_CURSOR_TOOLPLAY; - cursor_string_table["UI_CURSOR_TOOLPAUSE"] = UI_CURSOR_TOOLPAUSE; - cursor_string_table["UI_CURSOR_TOOLMEDIAOPEN"] = UI_CURSOR_TOOLMEDIAOPEN; - cursor_string_table["UI_CURSOR_PIPETTE"] = UI_CURSOR_PIPETTE; - cursor_string_table["UI_CURSOR_TOOLSIT"] = UI_CURSOR_TOOLSIT; - cursor_string_table["UI_CURSOR_TOOLBUY"] = UI_CURSOR_TOOLBUY; - cursor_string_table["UI_CURSOR_TOOLOPEN"] = UI_CURSOR_TOOLOPEN; - cursor_string_table["UI_CURSOR_TOOLPATHFINDING"] = UI_CURSOR_TOOLPATHFINDING; - cursor_string_table["UI_CURSOR_TOOLPATHFINDINGPATHSTART"] = UI_CURSOR_TOOLPATHFINDING_PATH_START; - cursor_string_table["UI_CURSOR_TOOLPATHFINDINGPATHSTARTADD"] = UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD; - cursor_string_table["UI_CURSOR_TOOLPATHFINDINGPATHEND"] = UI_CURSOR_TOOLPATHFINDING_PATH_END; - cursor_string_table["UI_CURSOR_TOOLPATHFINDINGPATHENDADD"] = UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD; - cursor_string_table["UI_CURSOR_TOOLNO"] = UI_CURSOR_TOOLNO; - } + static std::map<std::string,U32> cursor_string_table; + if (cursor_string_table.empty()) + { + cursor_string_table["UI_CURSOR_ARROW"] = UI_CURSOR_ARROW; + cursor_string_table["UI_CURSOR_WAIT"] = UI_CURSOR_WAIT; + cursor_string_table["UI_CURSOR_HAND"] = UI_CURSOR_HAND; + cursor_string_table["UI_CURSOR_IBEAM"] = UI_CURSOR_IBEAM; + cursor_string_table["UI_CURSOR_CROSS"] = UI_CURSOR_CROSS; + cursor_string_table["UI_CURSOR_SIZENWSE"] = UI_CURSOR_SIZENWSE; + cursor_string_table["UI_CURSOR_SIZENESW"] = UI_CURSOR_SIZENESW; + cursor_string_table["UI_CURSOR_SIZEWE"] = UI_CURSOR_SIZEWE; + cursor_string_table["UI_CURSOR_SIZENS"] = UI_CURSOR_SIZENS; + cursor_string_table["UI_CURSOR_SIZEALL"] = UI_CURSOR_SIZEALL; + cursor_string_table["UI_CURSOR_NO"] = UI_CURSOR_NO; + cursor_string_table["UI_CURSOR_WORKING"] = UI_CURSOR_WORKING; + cursor_string_table["UI_CURSOR_TOOLGRAB"] = UI_CURSOR_TOOLGRAB; + cursor_string_table["UI_CURSOR_TOOLLAND"] = UI_CURSOR_TOOLLAND; + cursor_string_table["UI_CURSOR_TOOLFOCUS"] = UI_CURSOR_TOOLFOCUS; + cursor_string_table["UI_CURSOR_TOOLCREATE"] = UI_CURSOR_TOOLCREATE; + cursor_string_table["UI_CURSOR_ARROWDRAG"] = UI_CURSOR_ARROWDRAG; + cursor_string_table["UI_CURSOR_ARROWCOPY"] = UI_CURSOR_ARROWCOPY; + cursor_string_table["UI_CURSOR_ARROWDRAGMULTI"] = UI_CURSOR_ARROWDRAGMULTI; + cursor_string_table["UI_CURSOR_ARROWCOPYMULTI"] = UI_CURSOR_ARROWCOPYMULTI; + cursor_string_table["UI_CURSOR_NOLOCKED"] = UI_CURSOR_NOLOCKED; + cursor_string_table["UI_CURSOR_ARROWLOCKED"] = UI_CURSOR_ARROWLOCKED; + cursor_string_table["UI_CURSOR_GRABLOCKED"] = UI_CURSOR_GRABLOCKED; + cursor_string_table["UI_CURSOR_TOOLTRANSLATE"] = UI_CURSOR_TOOLTRANSLATE; + cursor_string_table["UI_CURSOR_TOOLROTATE"] = UI_CURSOR_TOOLROTATE; + cursor_string_table["UI_CURSOR_TOOLSCALE"] = UI_CURSOR_TOOLSCALE; + cursor_string_table["UI_CURSOR_TOOLCAMERA"] = UI_CURSOR_TOOLCAMERA; + cursor_string_table["UI_CURSOR_TOOLPAN"] = UI_CURSOR_TOOLPAN; + cursor_string_table["UI_CURSOR_TOOLZOOMIN"] = UI_CURSOR_TOOLZOOMIN; + cursor_string_table["UI_CURSOR_TOOLZOOMOUT"] = UI_CURSOR_TOOLZOOMOUT; + cursor_string_table["UI_CURSOR_TOOLPICKOBJECT3"] = UI_CURSOR_TOOLPICKOBJECT3; + cursor_string_table["UI_CURSOR_TOOLPLAY"] = UI_CURSOR_TOOLPLAY; + cursor_string_table["UI_CURSOR_TOOLPAUSE"] = UI_CURSOR_TOOLPAUSE; + cursor_string_table["UI_CURSOR_TOOLMEDIAOPEN"] = UI_CURSOR_TOOLMEDIAOPEN; + cursor_string_table["UI_CURSOR_PIPETTE"] = UI_CURSOR_PIPETTE; + cursor_string_table["UI_CURSOR_TOOLSIT"] = UI_CURSOR_TOOLSIT; + cursor_string_table["UI_CURSOR_TOOLBUY"] = UI_CURSOR_TOOLBUY; + cursor_string_table["UI_CURSOR_TOOLOPEN"] = UI_CURSOR_TOOLOPEN; + cursor_string_table["UI_CURSOR_TOOLPATHFINDING"] = UI_CURSOR_TOOLPATHFINDING; + cursor_string_table["UI_CURSOR_TOOLPATHFINDINGPATHSTART"] = UI_CURSOR_TOOLPATHFINDING_PATH_START; + cursor_string_table["UI_CURSOR_TOOLPATHFINDINGPATHSTARTADD"] = UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD; + cursor_string_table["UI_CURSOR_TOOLPATHFINDINGPATHEND"] = UI_CURSOR_TOOLPATHFINDING_PATH_END; + cursor_string_table["UI_CURSOR_TOOLPATHFINDINGPATHENDADD"] = UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD; + cursor_string_table["UI_CURSOR_TOOLNO"] = UI_CURSOR_TOOLNO; + } + + std::map<std::string,U32>::const_iterator iter = cursor_string_table.find(cursor_string); - std::map<std::string,U32>::const_iterator iter = cursor_string_table.find(cursor_string); - - if (iter != cursor_string_table.end()) - { - return (ECursorType)iter->second; - } + if (iter != cursor_string_table.end()) + { + return (ECursorType)iter->second; + } - return UI_CURSOR_ARROW; + return UI_CURSOR_ARROW; } diff --git a/indra/llwindow/llcursortypes.h b/indra/llwindow/llcursortypes.h index d03b18e275..9a0c2909bf 100644 --- a/indra/llwindow/llcursortypes.h +++ b/indra/llwindow/llcursortypes.h @@ -1,25 +1,25 @@ -/** +/** * @file llcursortypes.h * @brief Cursor types * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -29,51 +29,51 @@ // If you add types here, add them in LLCursor::getCursorFromString enum ECursorType { - UI_CURSOR_ARROW, - UI_CURSOR_WAIT, - UI_CURSOR_HAND, - UI_CURSOR_IBEAM, - UI_CURSOR_CROSS, - UI_CURSOR_SIZENWSE, - UI_CURSOR_SIZENESW, - UI_CURSOR_SIZEWE, - UI_CURSOR_SIZENS, - UI_CURSOR_SIZEALL, - UI_CURSOR_NO, - UI_CURSOR_WORKING, - UI_CURSOR_TOOLGRAB, - UI_CURSOR_TOOLLAND, - UI_CURSOR_TOOLFOCUS, - UI_CURSOR_TOOLCREATE, - UI_CURSOR_ARROWDRAG, - UI_CURSOR_ARROWCOPY, // drag with copy - UI_CURSOR_ARROWDRAGMULTI, - UI_CURSOR_ARROWCOPYMULTI, // drag with copy - UI_CURSOR_NOLOCKED, - UI_CURSOR_ARROWLOCKED, - UI_CURSOR_GRABLOCKED, - UI_CURSOR_TOOLTRANSLATE, - UI_CURSOR_TOOLROTATE, - UI_CURSOR_TOOLSCALE, - UI_CURSOR_TOOLCAMERA, - UI_CURSOR_TOOLPAN, - UI_CURSOR_TOOLZOOMIN, - UI_CURSOR_TOOLZOOMOUT, - UI_CURSOR_TOOLPICKOBJECT3, - UI_CURSOR_TOOLPLAY, - UI_CURSOR_TOOLPAUSE, - UI_CURSOR_TOOLMEDIAOPEN, - UI_CURSOR_PIPETTE, - UI_CURSOR_TOOLSIT, - UI_CURSOR_TOOLBUY, - UI_CURSOR_TOOLOPEN, - UI_CURSOR_TOOLPATHFINDING, - UI_CURSOR_TOOLPATHFINDING_PATH_START, - UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD, - UI_CURSOR_TOOLPATHFINDING_PATH_END, - UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD, - UI_CURSOR_TOOLNO, - UI_CURSOR_COUNT // Number of elements in this enum (NOT a cursor) + UI_CURSOR_ARROW, + UI_CURSOR_WAIT, + UI_CURSOR_HAND, + UI_CURSOR_IBEAM, + UI_CURSOR_CROSS, + UI_CURSOR_SIZENWSE, + UI_CURSOR_SIZENESW, + UI_CURSOR_SIZEWE, + UI_CURSOR_SIZENS, + UI_CURSOR_SIZEALL, + UI_CURSOR_NO, + UI_CURSOR_WORKING, + UI_CURSOR_TOOLGRAB, + UI_CURSOR_TOOLLAND, + UI_CURSOR_TOOLFOCUS, + UI_CURSOR_TOOLCREATE, + UI_CURSOR_ARROWDRAG, + UI_CURSOR_ARROWCOPY, // drag with copy + UI_CURSOR_ARROWDRAGMULTI, + UI_CURSOR_ARROWCOPYMULTI, // drag with copy + UI_CURSOR_NOLOCKED, + UI_CURSOR_ARROWLOCKED, + UI_CURSOR_GRABLOCKED, + UI_CURSOR_TOOLTRANSLATE, + UI_CURSOR_TOOLROTATE, + UI_CURSOR_TOOLSCALE, + UI_CURSOR_TOOLCAMERA, + UI_CURSOR_TOOLPAN, + UI_CURSOR_TOOLZOOMIN, + UI_CURSOR_TOOLZOOMOUT, + UI_CURSOR_TOOLPICKOBJECT3, + UI_CURSOR_TOOLPLAY, + UI_CURSOR_TOOLPAUSE, + UI_CURSOR_TOOLMEDIAOPEN, + UI_CURSOR_PIPETTE, + UI_CURSOR_TOOLSIT, + UI_CURSOR_TOOLBUY, + UI_CURSOR_TOOLOPEN, + UI_CURSOR_TOOLPATHFINDING, + UI_CURSOR_TOOLPATHFINDING_PATH_START, + UI_CURSOR_TOOLPATHFINDING_PATH_START_ADD, + UI_CURSOR_TOOLPATHFINDING_PATH_END, + UI_CURSOR_TOOLPATHFINDING_PATH_END_ADD, + UI_CURSOR_TOOLNO, + UI_CURSOR_COUNT // Number of elements in this enum (NOT a cursor) }; LL_COMMON_API ECursorType getCursorFromString(const std::string& cursor_string); diff --git a/indra/llwindow/lldragdropwin32.cpp b/indra/llwindow/lldragdropwin32.cpp index 51c3938ebc..e08a2c2fc6 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/lldragdropwin32.h b/indra/llwindow/lldragdropwin32.h index 4673242cba..1b30dced27 100644 --- a/indra/llwindow/lldragdropwin32.h +++ b/indra/llwindow/lldragdropwin32.h @@ -5,21 +5,21 @@ * $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$ */ @@ -36,16 +36,16 @@ class LLDragDropWin32 { - public: - LLDragDropWin32(); - ~LLDragDropWin32(); + public: + LLDragDropWin32(); + ~LLDragDropWin32(); - bool init( HWND hWnd ); - void reset(); + bool init( HWND hWnd ); + void reset(); - private: - IDropTarget* mDropTarget; - HWND mDropWindowHandle; + private: + IDropTarget* mDropTarget; + HWND mDropWindowHandle; }; #endif // LL_LLDRAGDROP32_H @@ -57,15 +57,15 @@ class LLDragDropWin32 #include "llwin32headerslean.h" #include <ole2.h> -// impostor class that does nothing +// impostor class that does nothing class LLDragDropWin32 { - public: - LLDragDropWin32() {}; - ~LLDragDropWin32() {}; + public: + LLDragDropWin32() {}; + ~LLDragDropWin32() {}; - bool init( HWND hWnd ) { return false; }; - void reset() { }; + bool init( HWND hWnd ) { return false; }; + void reset() { }; }; #endif // LL_LLDRAGDROP32_H diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp index 4c97829146..5d0e15d9f4 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 85b4bffd71..54447a87b3 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 e014a33231..361666d1c5 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 117ae797c5..4278ffb12f 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 32d094e046..098d9b62c7 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 da5972883a..df7b015e8d 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 1ebe868798..4a415a2f54 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 27346cbeac..5c5328a156 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 e850bd4d2b..eaf577108a 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 760e4a2e78..f1c069c242 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 497e000357..510d680941 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 d7752b7f6f..7788af8011 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 fe421fc492..6498d42642 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 1097894c32..fea750fa91 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/llopenglview-objc.h b/indra/llwindow/llopenglview-objc.h index 072d40f739..97f4125484 100644 --- a/indra/llwindow/llopenglview-objc.h +++ b/indra/llwindow/llopenglview-objc.h @@ -35,11 +35,11 @@ @interface LLOpenGLView : NSOpenGLView <NSTextInputClient> { - std::string mLastDraggedUrl; - unsigned int mModifiers; - float mMousePos[2]; - bool mHasMarkedText; - unsigned int mMarkedTextLength; + std::string mLastDraggedUrl; + unsigned int mModifiers; + float mMousePos[2]; + bool mHasMarkedText; + unsigned int mMarkedTextLength; bool mMarkedTextAllowed; bool mSimulatedRightClick; bool mOldResize; @@ -78,7 +78,7 @@ @interface LLNonInlineTextView : NSTextView { - LLOpenGLView *glview; + LLOpenGLView *glview; unichar mKeyPressed; } diff --git a/indra/llwindow/llpreeditor.h b/indra/llwindow/llpreeditor.h index 7f1daefdac..915957e63a 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 f1e6114f85..e8c1301230 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 396daec527..5eca3e8939 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -1,328 +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, void * devices_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 2b5899b774..0c328c9140 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 f729e95f9e..c38d34c206 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 ad278b38c8..fdcd6ca8a9 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 f422152ab2..1a2363c13a 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 e406f7dd19..609a441d66 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-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm index 690fe058db..2e75d309ea 100644 --- a/indra/llwindow/llwindowmacosx-objc.mm +++ b/indra/llwindow/llwindowmacosx-objc.mm @@ -27,6 +27,7 @@ #include <AppKit/AppKit.h> #include <Cocoa/Cocoa.h> +#include <errno.h> #include "llopenglview-objc.h" #include "llwindowmacosx-objc.h" #include "llappdelegate-objc.h" diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 73230805b4..fee74a9c25 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1,2172 +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> - -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((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; - } -} - -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 0d675671a9..9ed51a9f15 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -1,260 +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; - - 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 4b28587048..a0dd2bc086 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 1cb364a0ff..2188e54fb2 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 a0a8252e77..0eedca354d 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 c780975c04..7f12464a37 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 eb9ece6ea9..dad70aea51 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -1,4991 +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 - { - LL_WARNS("Window") << "No wgl_ARB_pixel_format extension, using default ChoosePixelFormat!" << 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 (!enable_vsync && wglSwapIntervalEXT) - { - LL_INFOS("Window") << "Disabling vertical sync" << LL_ENDL; - wglSwapIntervalEXT(0); - } - else if (wglSwapIntervalEXT) - { - 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, 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 7e320053a6..8516b45393 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -1,292 +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, void * di8_devices_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
|