summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/cmake/DragDrop.cmake23
-rw-r--r--indra/llwindow/CMakeLists.txt3
-rw-r--r--indra/llwindow/lldragdropwin32.cpp370
-rw-r--r--indra/llwindow/lldragdropwin32.h80
-rw-r--r--indra/llwindow/llwindowcallbacks.cpp5
-rw-r--r--indra/llwindow/llwindowcallbacks.h15
-rw-r--r--indra/llwindow/llwindowmacosx.cpp195
-rw-r--r--indra/llwindow/llwindowmacosx.h16
-rw-r--r--indra/llwindow/llwindowwin32.cpp7361
-rw-r--r--indra/llwindow/llwindowwin32.h6
-rw-r--r--indra/newview/CMakeLists.txt1
-rw-r--r--indra/newview/app_settings/settings.xml22
-rw-r--r--indra/newview/llpanellogin.cpp17
-rw-r--r--indra/newview/llpanellogin.h3
-rw-r--r--indra/newview/llviewerwindow.cpp134
-rw-r--r--indra/newview/llviewerwindow.h7
16 files changed, 4578 insertions, 3680 deletions
diff --git a/indra/cmake/DragDrop.cmake b/indra/cmake/DragDrop.cmake
new file mode 100644
index 0000000000..e56264f187
--- /dev/null
+++ b/indra/cmake/DragDrop.cmake
@@ -0,0 +1,23 @@
+# -*- cmake -*-
+
+if (VIEWER)
+
+ set(OS_DRAG_DROP ON CACHE BOOL "Build the viewer with OS level drag and drop turned on or off")
+
+ if (OS_DRAG_DROP)
+
+ if (WINDOWS)
+ add_definitions(-DLL_OS_DRAGDROP_ENABLED=1)
+ endif (WINDOWS)
+
+ if (DARWIN)
+ add_definitions(-DLL_OS_DRAGDROP_ENABLED=1)
+ endif (DARWIN)
+
+ if (LINUX)
+ add_definitions(-DLL_OS_DRAGDROP_ENABLED=0)
+ endif (LINUX)
+
+ endif (OS_DRAG_DROP)
+
+endif (VIEWER)
diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt
index 7b1cab696f..77c6fa57b6 100644
--- a/indra/llwindow/CMakeLists.txt
+++ b/indra/llwindow/CMakeLists.txt
@@ -12,6 +12,7 @@ project(llwindow)
include(00-Common)
include(DirectX)
+include(DragDrop)
include(LLCommon)
include(LLImage)
include(LLMath)
@@ -102,11 +103,13 @@ if (WINDOWS)
llwindowwin32.cpp
lldxhardware.cpp
llkeyboardwin32.cpp
+ lldragdropwin32.cpp
)
list(APPEND llwindow_HEADER_FILES
llwindowwin32.h
lldxhardware.h
llkeyboardwin32.h
+ lldragdropwin32.h
)
list(APPEND llwindow_LINK_LIBRARIES
comdlg32 # Common Dialogs for ChooseColor
diff --git a/indra/llwindow/lldragdropwin32.cpp b/indra/llwindow/lldragdropwin32.cpp
new file mode 100644
index 0000000000..1e10626e8a
--- /dev/null
+++ b/indra/llwindow/lldragdropwin32.cpp
@@ -0,0 +1,370 @@
+/**
+ * @file lldragdrop32.cpp
+ * @brief Handler for Windows specific drag and drop (OS to client) code
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/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)
+ {
+ };
+
+ 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 *)GetWindowLong(mAppWindowHandle, GWL_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 );
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+
+ LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, 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 *)GetWindowLong(mAppWindowHandle, GWL_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 );
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+
+ LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, 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 *)GetWindowLong(mAppWindowHandle, GWL_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 *)GetWindowLong( mAppWindowHandle, GWL_USERDATA );
+ if ( NULL != window_imp )
+ {
+ LLCoordGL gl_coord( 0, 0 );
+
+ 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 );
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ llinfos << "### (Drop) URL is: " << mDropUrl << llendl;
+ llinfos << "### raw coords are: " << pt.x << " x " << pt.y << llendl;
+ llinfos << "### client coords are: " << pt_client.x << " x " << pt_client.y << llendl;
+ llinfos << "### GL coords are: " << gl_coord.mX << " x " << gl_coord.mY << llendl;
+ llinfos << llendl;
+
+ // 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
new file mode 100644
index 0000000000..26c8e4aeff
--- /dev/null
+++ b/indra/llwindow/lldragdropwin32.h
@@ -0,0 +1,80 @@
+/**
+ * @file lldragdrop32.cpp
+ * @brief Handler for Windows specific drag and drop (OS to client) code
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#if LL_WINDOWS
+
+#if LL_OS_DRAGDROP_ENABLED
+
+#ifndef LL_LLDRAGDROP32_H
+#define LL_LLDRAGDROP32_H
+
+#include <windows.h>
+#include <ole2.h>
+
+class LLDragDropWin32
+{
+ public:
+ LLDragDropWin32();
+ ~LLDragDropWin32();
+
+ bool init( HWND hWnd );
+ void reset();
+
+ private:
+ IDropTarget* mDropTarget;
+ HWND mDropWindowHandle;
+};
+#endif // LL_LLDRAGDROP32_H
+
+#else // LL_OS_DRAGDROP_ENABLED
+
+#ifndef LL_LLDRAGDROP32_H
+#define LL_LLDRAGDROP32_H
+
+#include <windows.h>
+#include <ole2.h>
+
+// imposter class that does nothing
+class LLDragDropWin32
+{
+ public:
+ LLDragDropWin32() {};
+ ~LLDragDropWin32() {};
+
+ bool init( HWND hWnd ) { return false; };
+ void reset() { };
+};
+#endif // LL_LLDRAGDROP32_H
+
+#endif // LL_OS_DRAGDROP_ENABLED
+
+#endif // LL_WINDOWS
diff --git a/indra/llwindow/llwindowcallbacks.cpp b/indra/llwindow/llwindowcallbacks.cpp
index 72f9997149..6d9f012cc3 100644
--- a/indra/llwindow/llwindowcallbacks.cpp
+++ b/indra/llwindow/llwindowcallbacks.cpp
@@ -163,6 +163,11 @@ void LLWindowCallbacks::handleDataCopy(LLWindow *window, S32 data_type, void *da
{
}
+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;
diff --git a/indra/llwindow/llwindowcallbacks.h b/indra/llwindow/llwindowcallbacks.h
index abc66c42a2..42add8dde0 100644
--- a/indra/llwindow/llwindowcallbacks.h
+++ b/indra/llwindow/llwindowcallbacks.h
@@ -71,6 +71,21 @@ public:
virtual BOOL handleTimerEvent(LLWindow *window);
virtual BOOL handleDeviceChange(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);
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index ed62faece6..9ccd4c7f97 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -278,6 +278,8 @@ LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks,
mMoveEventCampartorUPP = NewEventComparatorUPP(staticMoveEventComparator);
mGlobalHandlerRef = NULL;
mWindowHandlerRef = NULL;
+
+ mDragOverrideCursor = -1;
// We're not clipping yet
SetRect( &mOldMouseClip, 0, 0, 0, 0 );
@@ -499,8 +501,11 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits
// Set up window event handlers (some window-related events ONLY go to window handlers.)
InstallStandardEventHandler(GetWindowEventTarget(mWindow));
- InstallWindowEventHandler (mWindow, mEventHandlerUPP, GetEventTypeCount (WindowHandlerEventList), WindowHandlerEventList, (void*)this, &mWindowHandlerRef); // add event handler
-
+ InstallWindowEventHandler(mWindow, mEventHandlerUPP, GetEventTypeCount (WindowHandlerEventList), WindowHandlerEventList, (void*)this, &mWindowHandlerRef); // add event handler
+#if LL_OS_DRAGDROP_ENABLED
+ InstallTrackingHandler( dragTrackingHandler, mWindow, (void*)this );
+ InstallReceiveHandler( dragReceiveHandler, mWindow, (void*)this );
+#endif // LL_OS_DRAGDROP_ENABLED
}
{
@@ -2174,11 +2179,8 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e
}
else
{
- MASK mask = 0;
- if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
- if(modifiers & (cmdKey | controlKey)) { mask |= MASK_CONTROL; }
- if(modifiers & optionKey) { mask |= MASK_ALT; }
-
+ MASK mask = LLWindowMacOSX::modifiersToMask(modifiers);
+
llassert( actualType == typeUnicodeText );
// The result is a UTF16 buffer. Pass the characters in turn to handleUnicodeChar.
@@ -2795,6 +2797,14 @@ void LLWindowMacOSX::setCursor(ECursorType cursor)
{
OSStatus result = noErr;
+ if (mDragOverrideCursor != -1)
+ {
+ // A drag is in progress...remember the requested cursor and we'll
+ // restore it when it is done
+ mCurrentCursor = cursor;
+ return;
+ }
+
if (cursor == UI_CURSOR_ARROW
&& mBusyCount > 0)
{
@@ -3379,3 +3389,174 @@ std::vector<std::string> LLWindowMacOSX::getDynamicFallbackFontList()
return std::vector<std::string>();
}
+// static
+MASK LLWindowMacOSX::modifiersToMask(SInt16 modifiers)
+{
+ MASK mask = 0;
+ if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
+ if(modifiers & (cmdKey | controlKey)) { mask |= MASK_CONTROL; }
+ if(modifiers & optionKey) { mask |= MASK_ALT; }
+ return mask;
+}
+
+#if LL_OS_DRAGDROP_ENABLED
+
+OSErr LLWindowMacOSX::dragTrackingHandler(DragTrackingMessage message, WindowRef theWindow,
+ void * handlerRefCon, DragRef drag)
+{
+ OSErr result = noErr;
+ LLWindowMacOSX *self = (LLWindowMacOSX*)handlerRefCon;
+
+ lldebugs << "drag tracking handler, message = " << message << llendl;
+
+ 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);
+
+}
+
+OSErr LLWindowMacOSX::handleDragNDrop(DragRef drag, LLWindowCallbacks::DragNDropAction action)
+{
+ OSErr result = dragNotAcceptedErr; // overall function result
+ OSErr err = noErr; // for local error handling
+
+ // Get the mouse position and modifiers of this drag.
+ SInt16 modifiers, mouseDownModifiers, mouseUpModifiers;
+ ::GetDragModifiers(drag, &modifiers, &mouseDownModifiers, &mouseUpModifiers);
+ MASK mask = LLWindowMacOSX::modifiersToMask(modifiers);
+
+ Point mouse_point;
+ // This will return the mouse point in global screen coords
+ ::GetDragMouse(drag, &mouse_point, NULL);
+ LLCoordScreen screen_coords(mouse_point.h, mouse_point.v);
+ LLCoordGL gl_pos;
+ convertCoords(screen_coords, &gl_pos);
+
+ // Look at the pasteboard and try to extract an URL from it
+ PasteboardRef pasteboard;
+ if(GetDragPasteboard(drag, &pasteboard) == noErr)
+ {
+ ItemCount num_items = 0;
+ // Treat an error here as an item count of 0
+ (void)PasteboardGetItemCount(pasteboard, &num_items);
+
+ // Only deal with single-item drags.
+ if(num_items == 1)
+ {
+ PasteboardItemID item_id = NULL;
+ CFArrayRef flavors = NULL;
+ CFDataRef data = NULL;
+
+ err = PasteboardGetItemIdentifier(pasteboard, 1, &item_id); // Yes, this really is 1-based.
+
+ // Try to extract an URL from the pasteboard
+ if(err == noErr)
+ {
+ err = PasteboardCopyItemFlavors( pasteboard, item_id, &flavors);
+ }
+
+ if(err == noErr)
+ {
+ if(CFArrayContainsValue(flavors, CFRangeMake(0, CFArrayGetCount(flavors)), kUTTypeURL))
+ {
+ // This is an URL.
+ err = PasteboardCopyItemFlavorData(pasteboard, item_id, kUTTypeURL, &data);
+ }
+ else if(CFArrayContainsValue(flavors, CFRangeMake(0, CFArrayGetCount(flavors)), kUTTypeUTF8PlainText))
+ {
+ // This is a string that might be an URL.
+ err = PasteboardCopyItemFlavorData(pasteboard, item_id, kUTTypeUTF8PlainText, &data);
+ }
+
+ }
+
+ if(flavors != NULL)
+ {
+ CFRelease(flavors);
+ }
+
+ if(data != NULL)
+ {
+ std::string url;
+ url.assign((char*)CFDataGetBytePtr(data), CFDataGetLength(data));
+ CFRelease(data);
+
+ 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 = kThemeNotAllowedCursor;
+ }
+ else {
+ mDragOverrideCursor = -1;
+ }
+ break;
+ case LLWindowCallbacks::DND_MOVE: // Drop accepted would result in a "move" operation
+ mDragOverrideCursor = kThemePointingHandCursor;
+ result = noErr;
+ break;
+ case LLWindowCallbacks::DND_COPY: // Drop accepted would result in a "copy" operation
+ mDragOverrideCursor = kThemeCopyArrowCursor;
+ result = noErr;
+ break;
+ case LLWindowCallbacks::DND_LINK: // Drop accepted would result in a "link" operation:
+ mDragOverrideCursor = kThemeAliasArrowCursor;
+ result = noErr;
+ 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
+ SetThemeCursor(mDragOverrideCursor);
+ }
+
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+#endif // LL_OS_DRAGDROP_ENABLED
diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h
index fbfa07fab4..377f10b6d4 100644
--- a/indra/llwindow/llwindowmacosx.h
+++ b/indra/llwindow/llwindowmacosx.h
@@ -34,6 +34,7 @@
#define LL_LLWINDOWMACOSX_H
#include "llwindow.h"
+#include "llwindowcallbacks.h"
#include "lltimer.h"
@@ -159,8 +160,15 @@ protected:
void adjustCursorDecouple(bool warpingMouse = false);
void fixWindowSize(void);
void stopDockTileBounce();
-
-
+ static MASK modifiersToMask(SInt16 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);
+ OSErr handleDragNDrop(DragRef theDrag, LLWindowCallbacks::DragNDropAction action);
+#endif // LL_OS_DRAGDROP_ENABLED
+
//
// Platform specific variables
//
@@ -193,11 +201,13 @@ protected:
U32 mFSAASamples;
BOOL mForceRebuild;
+ S32 mDragOverrideCursor;
+
F32 mBounceTime;
NMRec mBounceRec;
LLTimer mBounceTimer;
- // Imput method management through Text Service Manager.
+ // Input method management through Text Service Manager.
TSMDocumentID mTSMDocument;
BOOL mLanguageTextInputAllowed;
ScriptCode mTSMScriptCode;
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index b591111b75..ed39786b37 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -1,3668 +1,3693 @@
-/**
- * @file llwindowwin32.cpp
- * @brief Platform-dependent implementation of llwindow
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#if LL_WINDOWS && !LL_MESA_HEADLESS
-
-#include "llwindowwin32.h"
-
-// LLWindow library includes
-#include "llkeyboardwin32.h"
-#include "llpreeditor.h"
-#include "llwindowcallbacks.h"
-
-// Linden library includes
-#include "llerror.h"
-#include "llgl.h"
-#include "llstring.h"
-
-// System includes
-#include <commdlg.h>
-#include <WinUser.h>
-#include <mapi.h>
-#include <process.h> // for _spawn
-#include <shellapi.h>
-#include <Imm.h>
-
-// Require DirectInput version 8
-#define DIRECTINPUT_VERSION 0x0800
-
-#include <dinput.h>
-#include <Dbt.h.>
-
-#include "llmemtype.h"
-// culled from winuser.h
-#ifndef WM_MOUSEWHEEL /* Added to be compatible with later SDK's */
-const S32 WM_MOUSEWHEEL = 0x020A;
-#endif
-#ifndef WHEEL_DELTA /* Added to be compatible with later SDK's */
-const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
-#endif
-const S32 MAX_MESSAGE_PER_UPDATE = 20;
-const S32 BITS_PER_PIXEL = 32;
-const S32 MAX_NUM_RESOLUTIONS = 32;
-const F32 ICON_FLASH_TIME = 0.5f;
-
-extern BOOL gDebugWindowProc;
-
-LPWSTR gIconResource = IDI_APPLICATION;
-
-LLW32MsgCallback gAsyncMsgCallback = NULL;
-
-//
-// LLWindowWin32
-//
-
-void show_window_creation_error(const std::string& title)
-{
- LL_WARNS("Window") << title << LL_ENDL;
-}
-
-//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.
-// We need this because some language versions of Windows,
-// e.g., US version of Windows XP, doesn't install IMM32.DLL
-// as a default, and we can't link against imm32.lib statically.
-// I believe DLL loading of this type is best suited to do
-// in a static initialization of a class. What I'm not sure is
-// whether it follows the Linden Conding Standard...
-// See http://wiki.secondlife.com/wiki/Coding_standards#Static_Members
-
-class LLWinImm
-{
-public:
- static bool isAvailable() { return sTheInstance.mHImmDll != NULL; }
-
-public:
- // Wrappers for IMM API.
- static BOOL isIME(HKL hkl);
- static HWND getDefaultIMEWnd(HWND hwnd);
- 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);
-
-private:
- LLWinImm();
- ~LLWinImm();
-
-private:
- // Pointers to IMM API.
- BOOL (WINAPI *mImmIsIME)(HKL);
- HWND (WINAPI *mImmGetDefaultIMEWnd)(HWND);
- HIMC (WINAPI *mImmGetContext)(HWND);
- BOOL (WINAPI *mImmReleaseContext)(HWND, HIMC);
- BOOL (WINAPI *mImmGetOpenStatus)(HIMC);
- BOOL (WINAPI *mImmSetOpenStatus)(HIMC, BOOL);
- BOOL (WINAPI *mImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
- BOOL (WINAPI *mImmSetConversionStatus)(HIMC, DWORD, DWORD);
- BOOL (WINAPI *mImmGetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM);
- BOOL (WINAPI *mImmSetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM);
- LONG (WINAPI *mImmGetCompositionString)(HIMC, DWORD, LPVOID, DWORD);
- BOOL (WINAPI *mImmSetCompositionString)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD);
- BOOL (WINAPI *mImmSetCompositionFont)(HIMC, LPLOGFONTW);
- BOOL (WINAPI *mImmSetCandidateWindow)(HIMC, LPCANDIDATEFORM);
- BOOL (WINAPI *mImmNotifyIME)(HIMC, DWORD, DWORD, DWORD);
-
-private:
- HMODULE mHImmDll;
- static LLWinImm sTheInstance;
-};
-
-LLWinImm LLWinImm::sTheInstance;
-
-LLWinImm::LLWinImm() : mHImmDll(NULL)
-{
- // Check system metrics
- if ( !GetSystemMetrics( SM_DBCSENABLED ) )
- return;
-
-
- mHImmDll = LoadLibraryA("Imm32");
- if (mHImmDll != NULL)
- {
- mImmIsIME = (BOOL (WINAPI *)(HKL)) GetProcAddress(mHImmDll, "ImmIsIME");
- mImmGetDefaultIMEWnd = (HWND (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetDefaultIMEWnd");
- mImmGetContext = (HIMC (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetContext");
- mImmReleaseContext = (BOOL (WINAPI *)(HWND, HIMC)) GetProcAddress(mHImmDll, "ImmReleaseContext");
- mImmGetOpenStatus = (BOOL (WINAPI *)(HIMC)) GetProcAddress(mHImmDll, "ImmGetOpenStatus");
- mImmSetOpenStatus = (BOOL (WINAPI *)(HIMC, BOOL)) GetProcAddress(mHImmDll, "ImmSetOpenStatus");
- mImmGetConversionStatus = (BOOL (WINAPI *)(HIMC, LPDWORD, LPDWORD)) GetProcAddress(mHImmDll, "ImmGetConversionStatus");
- mImmSetConversionStatus = (BOOL (WINAPI *)(HIMC, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmSetConversionStatus");
- mImmGetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmGetCompositionWindow");
- mImmSetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmSetCompositionWindow");
- mImmGetCompositionString= (LONG (WINAPI *)(HIMC, DWORD, LPVOID, DWORD)) GetProcAddress(mHImmDll, "ImmGetCompositionStringW");
- mImmSetCompositionString= (BOOL (WINAPI *)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD)) GetProcAddress(mHImmDll, "ImmSetCompositionStringW");
- mImmSetCompositionFont = (BOOL (WINAPI *)(HIMC, LPLOGFONTW)) GetProcAddress(mHImmDll, "ImmSetCompositionFontW");
- mImmSetCandidateWindow = (BOOL (WINAPI *)(HIMC, LPCANDIDATEFORM)) GetProcAddress(mHImmDll, "ImmSetCandidateWindow");
- mImmNotifyIME = (BOOL (WINAPI *)(HIMC, DWORD, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmNotifyIME");
-
- if (mImmIsIME == NULL ||
- mImmGetDefaultIMEWnd == NULL ||
- mImmGetContext == NULL ||
- mImmReleaseContext == NULL ||
- mImmGetOpenStatus == NULL ||
- mImmSetOpenStatus == NULL ||
- mImmGetConversionStatus == NULL ||
- mImmSetConversionStatus == NULL ||
- mImmGetCompostitionWindow == NULL ||
- mImmSetCompostitionWindow == NULL ||
- mImmGetCompositionString == NULL ||
- mImmSetCompositionString == NULL ||
- mImmSetCompositionFont == NULL ||
- mImmSetCandidateWindow == NULL ||
- mImmNotifyIME == NULL)
- {
- // If any of the above API entires are not found, we can't use IMM API.
- // So, turn off the IMM support. We should log some warning message in
- // the case, since it is very unusual; these APIs are available from
- // the beginning, and all versions of IMM32.DLL should have them all.
- // Unfortunately, this code may be executed before initialization of
- // the logging channel (llwarns), and we can't do it here... Yes, this
- // is one of disadvantages to use static constraction to DLL loading.
- FreeLibrary(mHImmDll);
- mHImmDll = NULL;
-
- // If we unload the library, make sure all the function pointers are cleared
- mImmIsIME = NULL;
- mImmGetDefaultIMEWnd = NULL;
- mImmGetContext = NULL;
- mImmReleaseContext = NULL;
- mImmGetOpenStatus = NULL;
- mImmSetOpenStatus = NULL;
- mImmGetConversionStatus = NULL;
- mImmSetConversionStatus = NULL;
- mImmGetCompostitionWindow = NULL;
- mImmSetCompostitionWindow = NULL;
- mImmGetCompositionString = NULL;
- mImmSetCompositionString = NULL;
- mImmSetCompositionFont = NULL;
- mImmSetCandidateWindow = NULL;
- mImmNotifyIME = NULL;
- }
- }
-}
-
-
-// static
-BOOL LLWinImm::isIME(HKL hkl)
-{
- if ( sTheInstance.mImmIsIME )
- return sTheInstance.mImmIsIME(hkl);
- return FALSE;
-}
-
-// static
-HIMC LLWinImm::getContext(HWND hwnd)
-{
- if ( sTheInstance.mImmGetContext )
- return sTheInstance.mImmGetContext(hwnd);
- return 0;
-}
-
-//static
-BOOL LLWinImm::releaseContext(HWND hwnd, HIMC himc)
-{
- if ( sTheInstance.mImmIsIME )
- return sTheInstance.mImmReleaseContext(hwnd, himc);
- return FALSE;
-}
-
-// static
-BOOL LLWinImm::getOpenStatus(HIMC himc)
-{
- if ( sTheInstance.mImmGetOpenStatus )
- return sTheInstance.mImmGetOpenStatus(himc);
- return FALSE;
-}
-
-// static
-BOOL LLWinImm::setOpenStatus(HIMC himc, BOOL status)
-{
- if ( sTheInstance.mImmSetOpenStatus )
- return sTheInstance.mImmSetOpenStatus(himc, status);
- return FALSE;
-}
-
-// static
-BOOL LLWinImm::getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence)
-{
- if ( sTheInstance.mImmGetConversionStatus )
- return sTheInstance.mImmGetConversionStatus(himc, conversion, sentence);
- return FALSE;
-}
-
-// static
-BOOL LLWinImm::setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence)
-{
- if ( sTheInstance.mImmSetConversionStatus )
- return sTheInstance.mImmSetConversionStatus(himc, conversion, sentence);
- return FALSE;
-}
-
-// static
-BOOL LLWinImm::getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)
-{
- if ( sTheInstance.mImmGetCompostitionWindow )
- return sTheInstance.mImmGetCompostitionWindow(himc, form);
- return FALSE;
-}
-
-// static
-BOOL LLWinImm::setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)
-{
- if ( sTheInstance.mImmSetCompostitionWindow )
- return sTheInstance.mImmSetCompostitionWindow(himc, form);
- return FALSE;
-}
-
-
-// static
-LONG LLWinImm::getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length)
-{
- if ( sTheInstance.mImmGetCompositionString )
- return sTheInstance.mImmGetCompositionString(himc, index, data, length);
- return FALSE;
-}
-
-
-// static
-BOOL LLWinImm::setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength)
-{
- if ( sTheInstance.mImmSetCompositionString )
- return sTheInstance.mImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength);
- return FALSE;
-}
-
-// static
-BOOL LLWinImm::setCompositionFont(HIMC himc, LPLOGFONTW pFont)
-{
- if ( sTheInstance.mImmSetCompositionFont )
- return sTheInstance.mImmSetCompositionFont(himc, pFont);
- return FALSE;
-}
-
-// static
-BOOL LLWinImm::setCandidateWindow(HIMC himc, LPCANDIDATEFORM form)
-{
- if ( sTheInstance.mImmSetCandidateWindow )
- return sTheInstance.mImmSetCandidateWindow(himc, form);
- return FALSE;
-}
-
-// static
-BOOL LLWinImm::notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value)
-{
- if ( sTheInstance.mImmNotifyIME )
- return sTheInstance.mImmNotifyIME(himc, action, index, value);
- return FALSE;
-}
-
-
-
-
-// ----------------------------------------------------------------------------------------
-LLWinImm::~LLWinImm()
-{
- if (mHImmDll != NULL)
- {
- FreeLibrary(mHImmDll);
- mHImmDll = NULL;
- }
-}
-
-
-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 disable_vsync, BOOL use_gl,
- BOOL ignore_pixel_depth,
- U32 fsaa_samples)
- : LLWindow(callbacks, fullscreen, flags)
-{
- mFSAASamples = fsaa_samples;
- mIconResource = gIconResource;
- mOverrideAspectRatio = 0.f;
- mNativeAspectRatio = 0.f;
- mMousePositionModified = FALSE;
- mInputProcessingPaused = FALSE;
- mPreeditor = NULL;
- mhDC = NULL;
- mhRC = NULL;
-
- // Initialize the keyboard
- gKeyboard = new LLKeyboardWin32();
- gKeyboard->setCallbacks(callbacks);
-
- // 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);
- mWndProc = NULL;
-
- mSwapMethod = SWAP_METHOD_UNDEFINED;
-
- // No WPARAM yet.
- mLastSizeWParam = 0;
-
- // Windows GDI rects don't include rightmost pixel
- window_rect.left = (long) 0;
- window_rect.right = (long) width;
- window_rect.top = (long) 0;
- window_rect.bottom = (long) height;
-
- // Grab screen size to sanitize the window
- S32 window_border_y = GetSystemMetrics(SM_CYBORDER);
- S32 virtual_screen_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
- S32 virtual_screen_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
- S32 virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
- S32 virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
-
- if (x < virtual_screen_x) x = virtual_screen_x;
- if (y < virtual_screen_y - window_border_y) y = virtual_screen_y - window_border_y;
-
- if (x + width > virtual_screen_x + virtual_screen_width) x = virtual_screen_x + virtual_screen_width - width;
- if (y + height > virtual_screen_y + virtual_screen_height) y = virtual_screen_y + virtual_screen_height - height;
-
- if (!sIsClassRegistered)
- {
- // Force redraw when resized and create a private device context
-
- // Makes double click messages.
- wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
-
- // Set message handler function
- wc.lpfnWndProc = (WNDPROC) mainWindowProc;
-
- // unused
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
-
- wc.hInstance = mhInstance;
- wc.hIcon = LoadIcon(mhInstance, mIconResource);
-
- // We will set the cursor ourselves
- wc.hCursor = NULL;
-
- // background color is not used
- if (clearBg)
- {
- wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
- }
- else
- {
- wc.hbrBackground = (HBRUSH) NULL;
- }
-
- // we don't use windows menus
- wc.lpszMenuName = NULL;
-
- wc.lpszClassName = mWindowClassName;
-
- if (!RegisterClass(&wc))
- {
- OSMessageBox(mCallbacks->translateString("MBRegClassFailed"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return;
- }
- sIsClassRegistered = TRUE;
- }
-
- //-----------------------------------------------------------------------
- // Get the current refresh rate
- //-----------------------------------------------------------------------
-
- DEVMODE dev_mode;
- DWORD current_refresh;
- if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
- {
- current_refresh = dev_mode.dmDisplayFrequency;
- mNativeAspectRatio = ((F32)dev_mode.dmPelsWidth) / ((F32)dev_mode.dmPelsHeight);
- }
- else
- {
- current_refresh = 60;
- }
-
- //-----------------------------------------------------------------------
- // Drop resolution and go fullscreen
- // use a display mode with our desired size and depth, with a refresh
- // rate as close at possible to the users' default
- //-----------------------------------------------------------------------
- if (mFullscreen)
- {
- BOOL success = FALSE;
- DWORD closest_refresh = 0;
-
- for (S32 mode_num = 0;; mode_num++)
- {
- if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
- {
- break;
- }
-
- if (dev_mode.dmPelsWidth == width &&
- dev_mode.dmPelsHeight == height &&
- dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
- {
- success = TRUE;
- if ((dev_mode.dmDisplayFrequency - current_refresh)
- < (closest_refresh - current_refresh))
- {
- closest_refresh = dev_mode.dmDisplayFrequency;
- }
- }
- }
-
- if (closest_refresh == 0)
- {
- LL_WARNS("Window") << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << 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 );
- // }
-
-
- //-----------------------------------------------------------------------
- // 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, TRUE, &windowPos))
- {
- return;
- }
-
- //start with arrow cursor
- initCursors();
- setCursor( UI_CURSOR_ARROW );
-
- // Initialize (boot strap) the Language text input management,
- // based on the system's (or user's) default settings.
- allowLanguageTextInput(NULL, FALSE);
-}
-
-
-LLWindowWin32::~LLWindowWin32()
-{
- delete [] mWindowTitle;
- mWindowTitle = NULL;
-
- delete [] mSupportedResolutions;
- mSupportedResolutions = NULL;
-
- delete mWindowClassName;
- mWindowClassName = NULL;
-}
-
-void LLWindowWin32::show()
-{
- ShowWindow(mWindowHandle, SW_SHOW);
- SetForegroundWindow(mWindowHandle);
- SetFocus(mWindowHandle);
-}
-
-void LLWindowWin32::hide()
-{
- setMouseClipping(FALSE);
- ShowWindow(mWindowHandle, SW_HIDE);
-}
-
-//virtual
-void LLWindowWin32::minimize()
-{
- setMouseClipping(FALSE);
- showCursor();
- ShowWindow(mWindowHandle, SW_MINIMIZE);
-}
-
-//virtual
-void LLWindowWin32::restore()
-{
- ShowWindow(mWindowHandle, SW_RESTORE);
- SetForegroundWindow(mWindowHandle);
- SetFocus(mWindowHandle);
-}
-
-
-// close() destroys all OS-specific code associated with a window.
-// Usually called from LLWindowManager::destroyWindow()
-void LLWindowWin32::close()
-{
- LL_DEBUGS("Window") << "Closing LLWindowWin32" << LL_ENDL;
- // Is window is already closed?
- if (!mWindowHandle)
- {
- return;
- }
-
- // Make sure cursor is visible and we haven't mangled the clipping state.
- setMouseClipping(FALSE);
- showCursor();
-
- // Go back to screen mode written in the registry.
- if (mFullscreen)
- {
- resetDisplayResolution();
- }
-
- // Clean up remaining GL state
- LL_DEBUGS("Window") << "Shutting down 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();
-
- if (mhDC && !ReleaseDC(mWindowHandle, mhDC))
- {
- LL_WARNS("Window") << "Release of ghDC failed" << LL_ENDL;
- mhDC = NULL;
- }
-
- LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL;
-
- // Don't process events in our mainWindowProc any longer.
- SetWindowLong(mWindowHandle, GWL_USERDATA, NULL);
-
- // Make sure we don't leave a blank toolbar button.
- ShowWindow(mWindowHandle, SW_HIDE);
-
- // This causes WM_DESTROY to be sent *immediately*
- if (!DestroyWindow(mWindowHandle))
- {
- OSMessageBox(mCallbacks->translateString("MBDestroyWinFailed"),
- mCallbacks->translateString("MBShutdownErr"),
- OSMB_OK);
- }
-
- mWindowHandle = NULL;
-}
-
-BOOL LLWindowWin32::isValid()
-{
- return (mWindowHandle != NULL);
-}
-
-BOOL LLWindowWin32::getVisible()
-{
- return (mWindowHandle && IsWindowVisible(mWindowHandle));
-}
-
-BOOL LLWindowWin32::getMinimized()
-{
- return (mWindowHandle && IsIconic(mWindowHandle));
-}
-
-BOOL LLWindowWin32::getMaximized()
-{
- return (mWindowHandle && IsZoomed(mWindowHandle));
-}
-
-BOOL LLWindowWin32::maximize()
-{
- BOOL success = FALSE;
- if (!mWindowHandle) return success;
-
- WINDOWPLACEMENT placement;
- placement.length = sizeof(WINDOWPLACEMENT);
-
- success = GetWindowPlacement(mWindowHandle, &placement);
- if (!success) return success;
-
- placement.showCmd = SW_MAXIMIZE;
-
- success = SetWindowPlacement(mWindowHandle, &placement);
- return success;
-}
-
-BOOL LLWindowWin32::getFullscreen()
-{
- return mFullscreen;
-}
-
-BOOL LLWindowWin32::getPosition(LLCoordScreen *position)
-{
- RECT window_rect;
-
- if (!mWindowHandle ||
- !GetWindowRect(mWindowHandle, &window_rect) ||
- NULL == position)
- {
- return FALSE;
- }
-
- position->mX = window_rect.left;
- position->mY = window_rect.top;
- return TRUE;
-}
-
-BOOL LLWindowWin32::getSize(LLCoordScreen *size)
-{
- RECT window_rect;
-
- if (!mWindowHandle ||
- !GetWindowRect(mWindowHandle, &window_rect) ||
- NULL == size)
- {
- return FALSE;
- }
-
- size->mX = window_rect.right - window_rect.left;
- size->mY = window_rect.bottom - window_rect.top;
- return TRUE;
-}
-
-BOOL LLWindowWin32::getSize(LLCoordWindow *size)
-{
- RECT client_rect;
-
- if (!mWindowHandle ||
- !GetClientRect(mWindowHandle, &client_rect) ||
- NULL == size)
- {
- return FALSE;
- }
-
- size->mX = client_rect.right - client_rect.left;
- size->mY = client_rect.bottom - client_rect.top;
- return TRUE;
-}
-
-BOOL LLWindowWin32::setPosition(const LLCoordScreen position)
-{
- LLCoordScreen size;
-
- if (!mWindowHandle)
- {
- return FALSE;
- }
- getSize(&size);
- moveWindow(position, size);
- return TRUE;
-}
-
-BOOL LLWindowWin32::setSize(const LLCoordScreen size)
-{
- LLCoordScreen position;
-
- getPosition(&position);
- if (!mWindowHandle)
- {
- return FALSE;
- }
-
- moveWindow(position, size);
- return TRUE;
-}
-
-// changing fullscreen resolution
-BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp)
-{
- GLuint pixel_format;
- DEVMODE dev_mode;
- DWORD current_refresh;
- DWORD dw_ex_style;
- DWORD dw_style;
- RECT window_rect;
- S32 width = size.mX;
- S32 height = size.mY;
- 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;
- }
-
- 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
- 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
- DestroyWindow(mWindowHandle);
- mWindowHandle = CreateWindowEx(dw_ex_style,
- mWindowClassName,
- mWindowTitle,
- WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
- window_rect.left, // x pos
- window_rect.top, // y pos
- window_rect.right - window_rect.left, // width
- window_rect.bottom - window_rect.top, // height
- NULL,
- NULL,
- mhInstance,
- NULL);
-
- //-----------------------------------------------------------------------
- // Create GL drawing context
- //-----------------------------------------------------------------------
- static PIXELFORMATDESCRIPTOR pfd =
- {
- sizeof(PIXELFORMATDESCRIPTOR),
- 1,
- PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
- PFD_TYPE_RGBA,
- BITS_PER_PIXEL,
- 0, 0, 0, 0, 0, 0, // RGB bits and shift, unused
- 8, // alpha bits
- 0, // alpha shift
- 0, // accum bits
- 0, 0, 0, 0, // accum RGBA
- 24, // depth bits
- 8, // stencil bits, avi added for stencil test
- 0,
- PFD_MAIN_PLANE,
- 0,
- 0, 0, 0
- };
-
- if (!(mhDC = GetDC(mWindowHandle)))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBDevContextErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (!(pixel_format = ChoosePixelFormat(mhDC, &pfd)))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBPixelFmtErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- // Verify what pixel format we actually received.
- if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
- &pfd))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBPixelFmtDescErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (pfd.cColorBits < 32)
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBTrueColorWindow"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (pfd.cAlphaBits < 8)
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBAlpha"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (!SetPixelFormat(mhDC, pixel_format, &pfd))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBPixelFmtSetErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (!(mhRC = wglCreateContext(mhDC)))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBGLContextErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (!wglMakeCurrent(mhDC, mhRC))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBGLContextActErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- gGLManager.initWGL();
-
- if (wglChoosePixelFormatARB)
- {
- // OK, at this point, use the ARB wglChoosePixelFormatsARB function to see if we
- // can get exactly what we want.
- GLint attrib_list[256];
- S32 cur_attrib = 0;
-
- attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB;
- attrib_list[cur_attrib++] = 24;
-
- attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB;
- attrib_list[cur_attrib++] = 8;
-
- attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB;
- attrib_list[cur_attrib++] = GL_TRUE;
-
- attrib_list[cur_attrib++] = WGL_ACCELERATION_ARB;
- attrib_list[cur_attrib++] = WGL_FULL_ACCELERATION_ARB;
-
- attrib_list[cur_attrib++] = WGL_SUPPORT_OPENGL_ARB;
- attrib_list[cur_attrib++] = GL_TRUE;
-
- attrib_list[cur_attrib++] = WGL_DOUBLE_BUFFER_ARB;
- attrib_list[cur_attrib++] = GL_TRUE;
-
- attrib_list[cur_attrib++] = WGL_COLOR_BITS_ARB;
- attrib_list[cur_attrib++] = 24;
-
- attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB;
- attrib_list[cur_attrib++] = 8;
-
- 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);
- if (!result)
- {
- 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;
- }
-
-
-
- S32 swap_method = 0;
- S32 cur_format = num_formats-1;
- GLint swap_query = WGL_SWAP_METHOD_ARB;
-
- BOOL found_format = FALSE;
-
- while (!found_format && wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method))
- {
- if (swap_method == WGL_SWAP_UNDEFINED_ARB || cur_format <= 0)
- {
- found_format = TRUE;
- }
- else
- {
- --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
-
- }
- ReleaseDC (mWindowHandle, mhDC); // Release The Device Context
- mhDC = 0; // Zero The Device Context
- }
- DestroyWindow (mWindowHandle); // Destroy The Window
-
-
- mWindowHandle = CreateWindowEx(dw_ex_style,
- mWindowClassName,
- mWindowTitle,
- WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
- window_rect.left, // x pos
- window_rect.top, // y pos
- window_rect.right - window_rect.left, // width
- window_rect.bottom - window_rect.top, // height
- NULL,
- NULL,
- mhInstance,
- NULL);
-
- if (!(mhDC = GetDC(mWindowHandle)))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBDevContextErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (!SetPixelFormat(mhDC, pixel_format, &pfd))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBPixelFmtSetErr"),
- mCallbacks->translateString("MBError"), OSMB_OK);
- 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))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBPixelFmtDescErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- LL_INFOS("Window") << "GL buffer: Color Bits " << S32(pfd.cColorBits)
- << " Alpha Bits " << S32(pfd.cAlphaBits)
- << " Depth Bits " << S32(pfd.cDepthBits)
- << LL_ENDL;
-
- // make sure we have 32 bits per pixel
- if (pfd.cColorBits < 32 || GetDeviceCaps(mhDC, BITSPIXEL) < 32)
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBTrueColorWindow"), mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (pfd.cAlphaBits < 8)
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBAlpha"), mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (!(mhRC = wglCreateContext(mhDC)))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBGLContextErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (!wglMakeCurrent(mhDC, mhRC))
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBGLContextActErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- if (!gGLManager.initGL())
- {
- close();
- OSMessageBox(mCallbacks->translateString("MBVideoDrvErr"), mCallbacks->translateString("MBError"), OSMB_OK);
- return FALSE;
- }
-
- // Disable vertical sync for swap
- if (disable_vsync && wglSwapIntervalEXT)
- {
- LL_DEBUGS("Window") << "Disabling vertical sync" << LL_ENDL;
- wglSwapIntervalEXT(0);
- }
- else
- {
- LL_DEBUGS("Window") << "Keeping vertical sync" << LL_ENDL;
- }
-
- SetWindowLong(mWindowHandle, GWL_USERDATA, (U32)this);
-
- //register joystick timer callback
- SetTimer( mWindowHandle, 0, 1000 / 30, NULL ); // 30 fps timer
-
- // ok to post quit messages now
- mPostQuit = TRUE;
-
- if (auto_show)
- {
- show();
- glClearColor(0.0f, 0.0f, 0.0f, 0.f);
- glClear(GL_COLOR_BUFFER_BIT);
- swapBuffers();
- }
-
- return TRUE;
-}
-
-void LLWindowWin32::moveWindow( const LLCoordScreen& position, const LLCoordScreen& size )
-{
- if( mIsMouseClipping )
- {
- RECT client_rect_in_screen_space;
- if( getClientRectInScreenSpace( &client_rect_in_screen_space ) )
- {
- ClipCursor( &client_rect_in_screen_space );
- }
- }
-
- // 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
- MoveWindow(mWindowHandle, position.mX, position.mY, size.mX, size.mY, TRUE);
-}
-
-BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position)
-{
- LLCoordScreen screen_pos;
-
- mMousePositionModified = TRUE;
- if (!mWindowHandle)
- {
- return FALSE;
- }
-
- if (!convertCoords(position, &screen_pos))
- {
- return FALSE;
- }
-
- // Inform the application of the new mouse position (needed for per-frame
- // hover/picking to function).
- LLCoordGL gl_pos;
- convertCoords(position, &gl_pos);
- mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
-
- // DEV-18951 VWR-8524 Camera moves wildly when alt-clicking.
- // Because we have preemptively notified the application of the new
- // mouse position via handleMouseMove() above, we need to clear out
- // any stale mouse move events. RN/JC
- MSG msg;
- while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
- { }
-
- return SetCursorPos(screen_pos.mX, screen_pos.mY);
-}
-
-BOOL LLWindowWin32::getCursorPosition(LLCoordWindow *position)
-{
- POINT cursor_point;
- LLCoordScreen screen_pos;
-
- if (!mWindowHandle ||
- !GetCursorPos(&cursor_point))
- {
- return FALSE;
- }
-
- screen_pos.mX = cursor_point.x;
- screen_pos.mY = cursor_point.y;
-
- return convertCoords(screen_pos, position);
-}
-
-void LLWindowWin32::hideCursor()
-{
- while (ShowCursor(FALSE) >= 0)
- {
- // nothing, wait for cursor to push down
- }
- mCursorHidden = TRUE;
- mHideCursorPermanent = TRUE;
-}
-
-void LLWindowWin32::showCursor()
-{
- // makes sure the cursor shows up
- while (ShowCursor(TRUE) < 0)
- {
- // do nothing, wait for cursor to pop out
- }
- mCursorHidden = FALSE;
- mHideCursorPermanent = FALSE;
-}
-
-void LLWindowWin32::showCursorFromMouseMove()
-{
- if (!mHideCursorPermanent)
- {
- showCursor();
- }
-}
-
-void LLWindowWin32::hideCursorUntilMouseMove()
-{
- if (!mHideCursorPermanent)
- {
- hideCursor();
- mHideCursorPermanent = FALSE;
- }
-}
-
-BOOL LLWindowWin32::isCursorHidden()
-{
- return mCursorHidden;
-}
-
-
-HCURSOR LLWindowWin32::loadColorCursor(LPCTSTR name)
-{
- return (HCURSOR)LoadImage(mhInstance,
- name,
- IMAGE_CURSOR,
- 0, // default width
- 0, // default height
- LR_DEFAULTCOLOR);
-}
-
-
-void LLWindowWin32::initCursors()
-{
- mCursor[ UI_CURSOR_ARROW ] = LoadCursor(NULL, IDC_ARROW);
- mCursor[ UI_CURSOR_WAIT ] = LoadCursor(NULL, IDC_WAIT);
- mCursor[ UI_CURSOR_HAND ] = LoadCursor(NULL, IDC_HAND);
- mCursor[ UI_CURSOR_IBEAM ] = LoadCursor(NULL, IDC_IBEAM);
- mCursor[ UI_CURSOR_CROSS ] = LoadCursor(NULL, IDC_CROSS);
- mCursor[ UI_CURSOR_SIZENWSE ] = LoadCursor(NULL, IDC_SIZENWSE);
- mCursor[ UI_CURSOR_SIZENESW ] = LoadCursor(NULL, IDC_SIZENESW);
- mCursor[ UI_CURSOR_SIZEWE ] = LoadCursor(NULL, IDC_SIZEWE);
- mCursor[ UI_CURSOR_SIZENS ] = LoadCursor(NULL, IDC_SIZENS);
- mCursor[ UI_CURSOR_NO ] = LoadCursor(NULL, IDC_NO);
- mCursor[ UI_CURSOR_WORKING ] = LoadCursor(NULL, IDC_APPSTARTING);
-
- HMODULE module = GetModuleHandle(NULL);
- mCursor[ UI_CURSOR_TOOLGRAB ] = LoadCursor(module, TEXT("TOOLGRAB"));
- mCursor[ UI_CURSOR_TOOLLAND ] = LoadCursor(module, TEXT("TOOLLAND"));
- mCursor[ UI_CURSOR_TOOLFOCUS ] = LoadCursor(module, TEXT("TOOLFOCUS"));
- mCursor[ UI_CURSOR_TOOLCREATE ] = LoadCursor(module, TEXT("TOOLCREATE"));
- mCursor[ UI_CURSOR_ARROWDRAG ] = LoadCursor(module, TEXT("ARROWDRAG"));
- mCursor[ UI_CURSOR_ARROWCOPY ] = LoadCursor(module, TEXT("ARROWCOPY"));
- mCursor[ UI_CURSOR_ARROWDRAGMULTI ] = LoadCursor(module, TEXT("ARROWDRAGMULTI"));
- mCursor[ UI_CURSOR_ARROWCOPYMULTI ] = LoadCursor(module, TEXT("ARROWCOPYMULTI"));
- mCursor[ UI_CURSOR_NOLOCKED ] = LoadCursor(module, TEXT("NOLOCKED"));
- mCursor[ UI_CURSOR_ARROWLOCKED ]= LoadCursor(module, TEXT("ARROWLOCKED"));
- mCursor[ UI_CURSOR_GRABLOCKED ] = LoadCursor(module, TEXT("GRABLOCKED"));
- mCursor[ UI_CURSOR_TOOLTRANSLATE ] = LoadCursor(module, TEXT("TOOLTRANSLATE"));
- mCursor[ UI_CURSOR_TOOLROTATE ] = LoadCursor(module, TEXT("TOOLROTATE"));
- mCursor[ UI_CURSOR_TOOLSCALE ] = LoadCursor(module, TEXT("TOOLSCALE"));
- mCursor[ UI_CURSOR_TOOLCAMERA ] = LoadCursor(module, TEXT("TOOLCAMERA"));
- mCursor[ UI_CURSOR_TOOLPAN ] = LoadCursor(module, TEXT("TOOLPAN"));
- mCursor[ UI_CURSOR_TOOLZOOMIN ] = LoadCursor(module, TEXT("TOOLZOOMIN"));
- mCursor[ UI_CURSOR_TOOLPICKOBJECT3 ] = LoadCursor(module, TEXT("TOOLPICKOBJECT3"));
- mCursor[ UI_CURSOR_PIPETTE ] = LoadCursor(module, TEXT("TOOLPIPETTE"));
-
- // Color cursors
- mCursor[UI_CURSOR_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::setCursor(ECursorType cursor)
-{
- if (cursor == UI_CURSOR_ARROW
- && mBusyCount > 0)
- {
- cursor = UI_CURSOR_WORKING;
- }
-
- if( mCurrentCursor != cursor )
- {
- mCurrentCursor = cursor;
- SetCursor( mCursor[cursor] );
- }
-}
-
-ECursorType LLWindowWin32::getCursor() const
-{
- return mCurrentCursor;
-}
-
-void LLWindowWin32::captureMouse()
-{
- SetCapture(mWindowHandle);
-}
-
-void LLWindowWin32::releaseMouse()
-{
- // *NOTE:Mani ReleaseCapture will spawn new windows messages...
- // which will in turn call our MainWindowProc. It therefore requires
- // pausing *and more importantly resumption* of the mainlooptimeout...
- // just like DispatchMessage below.
- mCallbacks->handlePauseWatchdog(this);
- ReleaseCapture();
- mCallbacks->handleResumeWatchdog(this);
-}
-
-
-void LLWindowWin32::delayInputProcessing()
-{
- mInputProcessingPaused = TRUE;
-}
-
-void LLWindowWin32::gatherInput()
-{
- MSG msg;
- int msg_count = 0;
-
- LLMemType m1(LLMemType::MTYPE_GATHER_INPUT);
-
- while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg_count < MAX_MESSAGE_PER_UPDATE)
- {
- mCallbacks->handlePingWatchdog(this, "Main:TranslateGatherInput");
- TranslateMessage(&msg);
-
- // turn watchdog off in here to not fail if windows is doing something wacky
- mCallbacks->handlePauseWatchdog(this);
- DispatchMessage(&msg);
- mCallbacks->handleResumeWatchdog(this);
- msg_count++;
-
- if ( mInputProcessingPaused )
- {
- break;
- }
- /* Attempted workaround for problem where typing fast and hitting
- return would result in only part of the text being sent. JC
-
- BOOL key_posted = TranslateMessage(&msg);
- DispatchMessage(&msg);
- msg_count++;
-
- // If a key was translated, a WM_CHAR might have been posted to the end
- // of the event queue. We need it immediately.
- if (key_posted && msg.message == WM_KEYDOWN)
- {
- if (PeekMessage(&msg, NULL, WM_CHAR, WM_CHAR, PM_REMOVE))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- msg_count++;
- }
- }
- */
- mCallbacks->handlePingWatchdog(this, "Main:AsyncCallbackGatherInput");
- // For async host by name support. Really hacky.
- if (gAsyncMsgCallback && (LL_WM_HOST_RESOLVED == msg.message))
- {
- gAsyncMsgCallback(msg);
- }
- }
-
- mInputProcessingPaused = FALSE;
-
- // clear this once we've processed all mouse messages that might have occurred after
- // we slammed the mouse position
- mMousePositionModified = FALSE;
-}
-
-static LLFastTimer::DeclareTimer FTM_KEYHANDLER("Handle Keyboard");
-static LLFastTimer::DeclareTimer FTM_MOUSEHANDLER("Handle Mouse");
-
-LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param)
-{
- LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(h_wnd, GWL_USERDATA);
-
-
- if (NULL != window_imp)
- {
- window_imp->mCallbacks->handleResumeWatchdog(window_imp);
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:StartWndProc");
- // Has user provided their own window callback?
- if (NULL != window_imp->mWndProc)
- {
- if (!window_imp->mWndProc(h_wnd, u_msg, w_param, l_param))
- {
- // user has handled window message
- return 0;
- }
- }
-
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:PreSwitchWndProc");
-
- // Juggle to make sure we can get negative positions for when
- // mouse is outside window.
- LLCoordWindow window_coord((S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param));
-
- // This doesn't work, as LOWORD returns unsigned short.
- //LLCoordWindow window_coord(LOWORD(l_param), HIWORD(l_param));
- LLCoordGL gl_coord;
-
- // pass along extended flag in mask
- MASK mask = (l_param>>16 & KF_EXTENDED) ? MASK_EXTENDED : 0x0;
- BOOL eat_keystroke = TRUE;
-
- switch(u_msg)
- {
- RECT update_rect;
- S32 update_width;
- S32 update_height;
-
- case WM_TIMER:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_TIMER");
- window_imp->mCallbacks->handleTimerEvent(window_imp);
- break;
-
- case WM_DEVICECHANGE:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_DEVICECHANGE");
- if (gDebugWindowProc)
- {
- llinfos << " WM_DEVICECHANGE: wParam=" << w_param
- << "; lParam=" << l_param << llendl;
- }
- if (w_param == DBT_DEVNODES_CHANGED || w_param == DBT_DEVICEARRIVAL)
- {
- if (window_imp->mCallbacks->handleDeviceChange(window_imp))
- {
- return 0;
- }
- }
- break;
-
- case WM_PAINT:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_PAINT");
- GetUpdateRect(window_imp->mWindowHandle, &update_rect, FALSE);
- update_width = update_rect.right - update_rect.left + 1;
- update_height = update_rect.bottom - update_rect.top + 1;
- window_imp->mCallbacks->handlePaint(window_imp, update_rect.left, update_rect.top,
- update_width, update_height);
- break;
- case WM_PARENTNOTIFY:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_PARENTNOTIFY");
- u_msg = u_msg;
- break;
-
- case WM_SETCURSOR:
- // This message is sent whenever the cursor is moved in a window.
- // You need to set the appropriate cursor appearance.
-
- // Only take control of cursor over client region of window
- // This allows Windows(tm) to handle resize cursors, etc.
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SETCURSOR");
- if (LOWORD(l_param) == HTCLIENT)
- {
- SetCursor(window_imp->mCursor[ window_imp->mCurrentCursor] );
- return 0;
- }
- break;
-
- case WM_ENTERMENULOOP:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_ENTERMENULOOP");
- window_imp->mCallbacks->handleWindowBlock(window_imp);
- break;
-
- case WM_EXITMENULOOP:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_EXITMENULOOP");
- window_imp->mCallbacks->handleWindowUnblock(window_imp);
- break;
-
- case WM_ACTIVATEAPP:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_ACTIVATEAPP");
- {
- // This message should be sent whenever the app gains or loses focus.
- BOOL activating = (BOOL) w_param;
- BOOL minimized = window_imp->getMinimized();
-
- if (gDebugWindowProc)
- {
- LL_INFOS("Window") << "WINDOWPROC ActivateApp "
- << " activating " << S32(activating)
- << " minimized " << S32(minimized)
- << " fullscreen " << S32(window_imp->mFullscreen)
- << LL_ENDL;
- }
-
- 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();
- }
- }
-
- window_imp->mCallbacks->handleActivateApp(window_imp, activating);
-
- break;
- }
-
- case WM_ACTIVATE:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_ACTIVATE");
- {
- // Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE
- BOOL activating = (LOWORD(w_param) != WA_INACTIVE);
-
- BOOL minimized = BOOL(HIWORD(w_param));
-
- if (!activating && LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->interruptLanguageTextInput();
- }
-
- // JC - I'm not sure why, but if we don't report that we handled the
- // WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work
- // properly when we run fullscreen.
- if (gDebugWindowProc)
- {
- LL_INFOS("Window") << "WINDOWPROC Activate "
- << " activating " << S32(activating)
- << " minimized " << S32(minimized)
- << LL_ENDL;
- }
-
- // Don't handle this.
- break;
- }
-
- case WM_QUERYOPEN:
- // TODO: use this to return a nice icon
- break;
-
- case WM_SYSCOMMAND:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main: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:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_CLOSE");
- // Will the app allow the window to close?
- if (window_imp->mCallbacks->handleCloseRequest(window_imp))
- {
- // Get the app to initiate cleanup.
- window_imp->mCallbacks->handleQuit(window_imp);
- // The app is responsible for calling destroyWindow when done with GL
- }
- return 0;
-
- case WM_DESTROY:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_DESTROY");
- if (window_imp->shouldPostQuit())
- {
- PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0
- }
- return 0;
-
- case WM_COMMAND:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_COMMAND");
- if (!HIWORD(w_param)) // this message is from a menu
- {
- window_imp->mCallbacks->handleMenuSelect(window_imp, LOWORD(w_param));
- }
- break;
-
- case WM_SYSKEYDOWN:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SYSKEYDOWN");
- // allow system keys, such as ALT-F4 to be processed by Windows
- eat_keystroke = FALSE;
- case WM_KEYDOWN:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_KEYDOWN");
- {
- if (gDebugWindowProc)
- {
- LL_INFOS("Window") << "Debug WindowProc WM_KEYDOWN "
- << " key " << S32(w_param)
- << LL_ENDL;
- }
- if(gKeyboard->handleKeyDown(w_param, mask) && eat_keystroke)
- {
- return 0;
- }
- // pass on to windows if we didn't handle it
- break;
- }
- case WM_SYSKEYUP:
- eat_keystroke = FALSE;
- case WM_KEYUP:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_KEYUP");
- LLFastTimer t2(FTM_KEYHANDLER);
-
- if (gDebugWindowProc)
- {
- LL_INFOS("Window") << "Debug WindowProc WM_KEYUP "
- << " key " << S32(w_param)
- << LL_ENDL;
- }
- if (gKeyboard->handleKeyUp(w_param, mask) && eat_keystroke)
- {
- return 0;
- }
-
- // pass on to windows
- break;
- }
- case WM_IME_SETCONTEXT:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_SETCONTEXT");
- if (gDebugWindowProc)
- {
- llinfos << "WM_IME_SETCONTEXT" << llendl;
- }
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW;
- // Invoke DefWinProc with the modified LPARAM.
- }
- break;
-
- case WM_IME_STARTCOMPOSITION:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_STARTCOMPOSITION");
- if (gDebugWindowProc)
- {
- llinfos << "WM_IME_STARTCOMPOSITION" << llendl;
- }
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->handleStartCompositionMessage();
- return 0;
- }
- break;
-
- case WM_IME_ENDCOMPOSITION:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_ENDCOMPOSITION");
- if (gDebugWindowProc)
- {
- llinfos << "WM_IME_ENDCOMPOSITION" << llendl;
- }
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- return 0;
- }
- break;
-
- case WM_IME_COMPOSITION:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_COMPOSITION");
- if (gDebugWindowProc)
- {
- llinfos << "WM_IME_COMPOSITION" << llendl;
- }
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->handleCompositionMessage(l_param);
- return 0;
- }
- break;
-
- case WM_IME_REQUEST:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_REQUEST");
- if (gDebugWindowProc)
- {
- llinfos << "WM_IME_REQUEST" << llendl;
- }
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- LRESULT result = 0;
- if (window_imp->handleImeRequests(w_param, l_param, &result))
- {
- return result;
- }
- }
- break;
-
- case WM_CHAR:
- // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need
- // to figure out how that works. - Doug
- //
- // ... 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
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_CHAR");
- if (gDebugWindowProc)
- {
- LL_INFOS("Window") << "Debug WindowProc WM_CHAR "
- << " key " << S32(w_param)
- << LL_ENDL;
- }
- // 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_LBUTTONDOWN:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONDOWN");
- LLFastTimer t2(FTM_MOUSEHANDLER);
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->interruptLanguageTextInput();
- }
-
- // Because we move the cursor position in the app, we need to query
- // to find out where the cursor at the time the event is handled.
- // If we don't do this, many clicks could get buffered up, and if the
- // first click changes the cursor position, all subsequent clicks
- // will occur at the wrong location. JC
- LLCoordWindow cursor_coord_window;
- if (window_imp->mMousePositionModified)
- {
- window_imp->getCursorPosition(&cursor_coord_window);
- window_imp->convertCoords(cursor_coord_window, &gl_coord);
- }
- else
- {
- window_imp->convertCoords(window_coord, &gl_coord);
- }
- MASK mask = gKeyboard->currentMask(TRUE);
- // generate move event to update mouse coordinates
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- if (window_imp->mCallbacks->handleMouseDown(window_imp, gl_coord, mask))
- {
- return 0;
- }
- }
- break;
-
- case WM_LBUTTONDBLCLK:
- //RN: ignore right button double clicks for now
- //case WM_RBUTTONDBLCLK:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONDBLCLK");
- // Because we move the cursor position in the app, we need to query
- // to find out where the cursor at the time the event is handled.
- // If we don't do this, many clicks could get buffered up, and if the
- // first click changes the cursor position, all subsequent clicks
- // will occur at the wrong location. JC
- LLCoordWindow cursor_coord_window;
- if (window_imp->mMousePositionModified)
- {
- window_imp->getCursorPosition(&cursor_coord_window);
- window_imp->convertCoords(cursor_coord_window, &gl_coord);
- }
- else
- {
- window_imp->convertCoords(window_coord, &gl_coord);
- }
- MASK mask = gKeyboard->currentMask(TRUE);
- // generate move event to update mouse coordinates
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- if (window_imp->mCallbacks->handleDoubleClick(window_imp, gl_coord, mask) )
- {
- return 0;
- }
- }
- break;
-
- case WM_LBUTTONUP:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONUP");
- LLFastTimer t2(FTM_MOUSEHANDLER);
- //if (gDebugClicks)
- //{
- // LL_INFOS("Window") << "WndProc left button up" << LL_ENDL;
- //}
- // Because we move the cursor position in the app, we need to query
- // to find out where the cursor at the time the event is handled.
- // If we don't do this, many clicks could get buffered up, and if the
- // first click changes the cursor position, all subsequent clicks
- // will occur at the wrong location. JC
- LLCoordWindow cursor_coord_window;
- if (window_imp->mMousePositionModified)
- {
- window_imp->getCursorPosition(&cursor_coord_window);
- window_imp->convertCoords(cursor_coord_window, &gl_coord);
- }
- else
- {
- window_imp->convertCoords(window_coord, &gl_coord);
- }
- MASK mask = gKeyboard->currentMask(TRUE);
- // generate move event to update mouse coordinates
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- if (window_imp->mCallbacks->handleMouseUp(window_imp, gl_coord, mask))
- {
- return 0;
- }
- }
- break;
-
- case WM_RBUTTONDBLCLK:
- case WM_RBUTTONDOWN:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_RBUTTONDOWN");
- LLFastTimer t2(FTM_MOUSEHANDLER);
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->interruptLanguageTextInput();
- }
-
- // Because we move the cursor position in the llviewerapp, we need to query
- // to find out where the cursor at the time the event is handled.
- // If we don't do this, many clicks could get buffered up, and if the
- // first click changes the cursor position, all subsequent clicks
- // will occur at the wrong location. JC
- LLCoordWindow cursor_coord_window;
- if (window_imp->mMousePositionModified)
- {
- window_imp->getCursorPosition(&cursor_coord_window);
- window_imp->convertCoords(cursor_coord_window, &gl_coord);
- }
- else
- {
- window_imp->convertCoords(window_coord, &gl_coord);
- }
- MASK mask = gKeyboard->currentMask(TRUE);
- // generate move event to update mouse coordinates
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- if (window_imp->mCallbacks->handleRightMouseDown(window_imp, gl_coord, mask))
- {
- return 0;
- }
- }
- break;
-
- case WM_RBUTTONUP:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_RBUTTONUP");
- LLFastTimer t2(FTM_MOUSEHANDLER);
- // Because we move the cursor position in the app, we need to query
- // to find out where the cursor at the time the event is handled.
- // If we don't do this, many clicks could get buffered up, and if the
- // first click changes the cursor position, all subsequent clicks
- // will occur at the wrong location. JC
- LLCoordWindow cursor_coord_window;
- if (window_imp->mMousePositionModified)
- {
- window_imp->getCursorPosition(&cursor_coord_window);
- window_imp->convertCoords(cursor_coord_window, &gl_coord);
- }
- else
- {
- window_imp->convertCoords(window_coord, &gl_coord);
- }
- MASK mask = gKeyboard->currentMask(TRUE);
- // generate move event to update mouse coordinates
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- if (window_imp->mCallbacks->handleRightMouseUp(window_imp, gl_coord, mask))
- {
- return 0;
- }
- }
- break;
-
- case WM_MBUTTONDOWN:
-// case WM_MBUTTONDBLCLK:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MBUTTONDOWN");
- LLFastTimer t2(FTM_MOUSEHANDLER);
- if (LLWinImm::isAvailable() && window_imp->mPreeditor)
- {
- window_imp->interruptLanguageTextInput();
- }
-
- // Because we move the cursor position in tllviewerhe app, we need to query
- // to find out where the cursor at the time the event is handled.
- // If we don't do this, many clicks could get buffered up, and if the
- // first click changes the cursor position, all subsequent clicks
- // will occur at the wrong location. JC
- LLCoordWindow cursor_coord_window;
- if (window_imp->mMousePositionModified)
- {
- window_imp->getCursorPosition(&cursor_coord_window);
- window_imp->convertCoords(cursor_coord_window, &gl_coord);
- }
- else
- {
- window_imp->convertCoords(window_coord, &gl_coord);
- }
- MASK mask = gKeyboard->currentMask(TRUE);
- // generate move event to update mouse coordinates
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- if (window_imp->mCallbacks->handleMiddleMouseDown(window_imp, gl_coord, mask))
- {
- return 0;
- }
- }
- break;
-
- case WM_MBUTTONUP:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MBUTTONUP");
- LLFastTimer t2(FTM_MOUSEHANDLER);
- // Because we move the cursor position in the llviewer app, we need to query
- // to find out where the cursor at the time the event is handled.
- // If we don't do this, many clicks could get buffered up, and if the
- // first click changes the cursor position, all subsequent clicks
- // will occur at the wrong location. JC
- LLCoordWindow cursor_coord_window;
- if (window_imp->mMousePositionModified)
- {
- window_imp->getCursorPosition(&cursor_coord_window);
- window_imp->convertCoords(cursor_coord_window, &gl_coord);
- }
- else
- {
- window_imp->convertCoords(window_coord, &gl_coord);
- }
- MASK mask = gKeyboard->currentMask(TRUE);
- // generate move event to update mouse coordinates
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- if (window_imp->mCallbacks->handleMiddleMouseUp(window_imp, gl_coord, mask))
- {
- return 0;
- }
- }
- break;
-
- case WM_MOUSEWHEEL:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main: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)
- {
- window_imp->mCallbacks->handleScrollWheel(window_imp, -z_delta / WHEEL_DELTA);
- z_delta = 0;
- }
- return 0;
- }
- /*
- // TODO: add this after resolving _WIN32_WINNT issue
- case WM_MOUSELEAVE:
- {
- window_imp->mCallbacks->handleMouseLeave(window_imp);
-
- // TRACKMOUSEEVENT track_mouse_event;
- // track_mouse_event.cbSize = sizeof( TRACKMOUSEEVENT );
- // track_mouse_event.dwFlags = TME_LEAVE;
- // track_mouse_event.hwndTrack = h_wnd;
- // track_mouse_event.dwHoverTime = HOVER_DEFAULT;
- // TrackMouseEvent( &track_mouse_event );
- return 0;
- }
- */
- // Handle mouse movement within the window
- case WM_MOUSEMOVE:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MOUSEMOVE");
- window_imp->convertCoords(window_coord, &gl_coord);
- MASK mask = gKeyboard->currentMask(TRUE);
- window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
- return 0;
- }
-
- case WM_SIZE:
- {
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SIZE");
- S32 width = S32( LOWORD(l_param) );
- S32 height = S32( HIWORD(l_param) );
-
- if (gDebugWindowProc)
- {
- BOOL maximized = ( w_param == SIZE_MAXIMIZED );
- BOOL restored = ( w_param == SIZE_RESTORED );
- BOOL minimized = ( w_param == SIZE_MINIMIZED );
-
- LL_INFOS("Window") << "WINDOWPROC Size "
- << width << "x" << height
- << " max " << S32(maximized)
- << " min " << S32(minimized)
- << " rest " << S32(restored)
- << LL_ENDL;
- }
-
- // 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->mCallbacks->handleActivate(window_imp, TRUE);
- }
-
- // handle case of window being maximized from fully minimized state
- if (w_param == SIZE_MAXIMIZED && window_imp->mLastSizeWParam != SIZE_MAXIMIZED)
- {
- window_imp->mCallbacks->handleActivate(window_imp, TRUE);
- }
-
- // Also handle the minimization case
- if (w_param == SIZE_MINIMIZED && window_imp->mLastSizeWParam != SIZE_MINIMIZED)
- {
- window_imp->mCallbacks->handleActivate(window_imp, FALSE);
- }
-
- // Actually resize all of our views
- if (w_param != SIZE_MINIMIZED)
- {
- // Ignore updates for minimizing and minimized "windows"
- window_imp->mCallbacks->handleResize( window_imp,
- LOWORD(l_param),
- HIWORD(l_param) );
- }
-
- window_imp->mLastSizeWParam = w_param;
-
- return 0;
- }
-
- case WM_SETFOCUS:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SETFOCUS");
- if (gDebugWindowProc)
- {
- LL_INFOS("Window") << "WINDOWPROC SetFocus" << LL_ENDL;
- }
- window_imp->mCallbacks->handleFocus(window_imp);
- return 0;
-
- case WM_KILLFOCUS:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_KILLFOCUS");
- if (gDebugWindowProc)
- {
- LL_INFOS("Window") << "WINDOWPROC KillFocus" << LL_ENDL;
- }
- window_imp->mCallbacks->handleFocusLost(window_imp);
- return 0;
-
- case WM_COPYDATA:
- window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_COPYDATA");
- // received a URL
- PCOPYDATASTRUCT myCDS = (PCOPYDATASTRUCT) l_param;
- window_imp->mCallbacks->handleDataCopy(window_imp, myCDS->dwData, myCDS->lpData);
- return 0;
- }
-
- window_imp->mCallbacks->handlePauseWatchdog(window_imp);
- }
-
-
- // pass unhandled messages down to Windows
- return DefWindowProc(h_wnd, u_msg, w_param, l_param);
-}
-
-BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to)
-{
- S32 client_height;
- RECT client_rect;
- LLCoordWindow window_position;
-
- if (!mWindowHandle ||
- !GetClientRect(mWindowHandle, &client_rect) ||
- NULL == to)
- {
- return FALSE;
- }
-
- to->mX = from.mX;
- client_height = client_rect.bottom - client_rect.top;
- to->mY = client_height - from.mY - 1;
-
- return TRUE;
-}
-
-BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to)
-{
- S32 client_height;
- RECT client_rect;
-
- if (!mWindowHandle ||
- !GetClientRect(mWindowHandle, &client_rect) ||
- NULL == to)
- {
- return FALSE;
- }
-
- to->mX = from.mX;
- client_height = client_rect.bottom - client_rect.top;
- to->mY = client_height - from.mY - 1;
-
- return TRUE;
-}
-
-BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to)
-{
- POINT mouse_point;
-
- mouse_point.x = from.mX;
- mouse_point.y = from.mY;
- BOOL result = ScreenToClient(mWindowHandle, &mouse_point);
-
- if (result)
- {
- to->mX = mouse_point.x;
- to->mY = mouse_point.y;
- }
-
- return result;
-}
-
-BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to)
-{
- POINT mouse_point;
-
- mouse_point.x = from.mX;
- mouse_point.y = from.mY;
- BOOL result = ClientToScreen(mWindowHandle, &mouse_point);
-
- if (result)
- {
- to->mX = mouse_point.x;
- to->mY = mouse_point.y;
- }
-
- return result;
-}
-
-BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to)
-{
- LLCoordWindow window_coord;
-
- if (!mWindowHandle || (NULL == to))
- {
- return FALSE;
- }
-
- convertCoords(from, &window_coord);
- convertCoords(window_coord, to);
- return TRUE;
-}
-
-BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen *to)
-{
- LLCoordWindow window_coord;
-
- if (!mWindowHandle || (NULL == to))
- {
- return FALSE;
- }
-
- convertCoords(from, &window_coord);
- convertCoords(window_coord, to);
- return TRUE;
-}
-
-
-BOOL LLWindowWin32::isClipboardTextAvailable()
-{
- return IsClipboardFormatAvailable(CF_UNICODETEXT);
-}
-
-
-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::removeCRLF(dst);
- GlobalUnlock(h_data);
- success = TRUE;
- }
- }
- CloseClipboard();
- }
- }
-
- return success;
-}
-
-
-BOOL LLWindowWin32::copyTextToClipboard(const LLWString& wstr)
-{
- BOOL success = FALSE;
-
- if (OpenClipboard(mWindowHandle))
- {
- EmptyClipboard();
-
- // Provide a copy of the data in Unicode format.
- LLWString sanitized_string(wstr);
- 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 )
-{
- 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)
-{
- FLASHWINFO flash_info;
-
- flash_info.cbSize = sizeof(FLASHWINFO);
- flash_info.hwnd = mWindowHandle;
- flash_info.dwFlags = FLASHW_TRAY;
- flash_info.uCount = UINT(seconds / ICON_FLASH_TIME);
- flash_info.dwTimeout = DWORD(1000.f * ICON_FLASH_TIME); // milliseconds
- FlashWindowEx(&flash_info);
-}
-
-F32 LLWindowWin32::getGamma()
-{
- return mCurrentGamma;
-}
-
-BOOL LLWindowWin32::restoreGamma()
-{
- return SetDeviceGammaRamp(mhDC, mPrevGammaRamp);
-}
-
-BOOL LLWindowWin32::setGamma(const F32 gamma)
-{
- mCurrentGamma = gamma;
-
- 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 * 256 + i ] =
- mCurrentGammaRamp [ 1 * 256 + i ] =
- mCurrentGammaRamp [ 2 * 256 + i ] = ( WORD )value;
- };
-
- return SetDeviceGammaRamp ( mhDC, mCurrentGammaRamp );
-}
-
-void LLWindowWin32::setFSAASamples(const U32 fsaa_samples)
-{
- mFSAASamples = fsaa_samples;
-}
-
-U32 LLWindowWin32::getFSAASamples()
-{
- return mFSAASamples;
-}
-
-LLWindow::LLWindowResolution* LLWindowWin32::getSupportedResolutions(S32 &num_resolutions)
-{
- if (!mSupportedResolutions)
- {
- mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
- DEVMODE dev_mode;
-
- mNumSupportedResolutions = 0;
- for (S32 mode_num = 0; mNumSupportedResolutions < MAX_NUM_RESOLUTIONS; mode_num++)
- {
- if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
- {
- break;
- }
-
- if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL &&
- dev_mode.dmPelsWidth >= 800 &&
- dev_mode.dmPelsHeight >= 600)
- {
- BOOL resolution_exists = FALSE;
- for(S32 i = 0; i < mNumSupportedResolutions; i++)
- {
- if (mSupportedResolutions[i].mWidth == dev_mode.dmPelsWidth &&
- mSupportedResolutions[i].mHeight == dev_mode.dmPelsHeight)
- {
- resolution_exists = TRUE;
- }
- }
- if (!resolution_exists)
- {
- mSupportedResolutions[mNumSupportedResolutions].mWidth = dev_mode.dmPelsWidth;
- mSupportedResolutions[mNumSupportedResolutions].mHeight = dev_mode.dmPelsHeight;
- mNumSupportedResolutions++;
- }
- }
- }
- }
-
- num_resolutions = mNumSupportedResolutions;
- return mSupportedResolutions;
-}
-
-
-F32 LLWindowWin32::getNativeAspectRatio()
-{
- if (mOverrideAspectRatio > 0.f)
- {
- return mOverrideAspectRatio;
- }
- else if (mNativeAspectRatio > 0.f)
- {
- // we grabbed this value at startup, based on the user's desktop settings
- return mNativeAspectRatio;
- }
- // RN: this hack presumes that the largest supported resolution is monitor-limited
- // and that pixels in that mode are square, therefore defining the native aspect ratio
- // of the monitor...this seems to work to a close approximation for most CRTs/LCDs
- S32 num_resolutions;
- LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
-
- return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
-}
-
-F32 LLWindowWin32::getPixelAspectRatio()
-{
- F32 pixel_aspect = 1.f;
- if (getFullscreen())
- {
- LLCoordScreen screen_size;
- getSize(&screen_size);
- pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
- }
-
- return pixel_aspect;
-}
-
-// Change display resolution. Returns true if successful.
-// protected
-BOOL LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh)
-{
- DEVMODE dev_mode;
- dev_mode.dmSize = sizeof(dev_mode);
- BOOL success = FALSE;
-
- // Don't change anything if we don't have to
- if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
- {
- if (dev_mode.dmPelsWidth == width &&
- dev_mode.dmPelsHeight == height &&
- dev_mode.dmBitsPerPel == bits &&
- dev_mode.dmDisplayFrequency == refresh )
- {
- // ...display mode identical, do nothing
- return TRUE;
- }
- }
-
- memset(&dev_mode, 0, sizeof(dev_mode));
- dev_mode.dmSize = sizeof(dev_mode);
- dev_mode.dmPelsWidth = width;
- dev_mode.dmPelsHeight = height;
- dev_mode.dmBitsPerPel = bits;
- dev_mode.dmDisplayFrequency = refresh;
- dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
-
- // CDS_FULLSCREEN indicates that this is a temporary change to the device mode.
- LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN);
-
- success = (DISP_CHANGE_SUCCESSFUL == cds_result);
-
- if (!success)
- {
- 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()
-{
- SwapBuffers(mhDC);
-}
-
-
-//
-// LLSplashScreenImp
-//
-LLSplashScreenWin32::LLSplashScreenWin32()
-: mWindow(NULL)
-{
-}
-
-LLSplashScreenWin32::~LLSplashScreenWin32()
-{
-}
-
-void LLSplashScreenWin32::showImpl()
-{
- // This appears to work. ???
- HINSTANCE hinst = GetModuleHandle(NULL);
-
- mWindow = CreateDialog(hinst,
- TEXT("SPLASHSCREEN"),
- NULL, // no parent
- (DLGPROC) LLSplashScreenWin32::windowProc);
- ShowWindow(mWindow, SW_SHOW);
-}
-
-
-void LLSplashScreenWin32::updateImpl(const std::string& mesg)
-{
- if (!mWindow) return;
-
- WCHAR w_mesg[1024];
- mbstowcs(w_mesg, mesg.c_str(), 1024);
-
- SendDlgItemMessage(mWindow,
- 666, // HACK: text id
- WM_SETTEXT,
- FALSE,
- (LPARAM)w_mesg);
-}
-
-
-void LLSplashScreenWin32::hideImpl()
-{
- if (mWindow)
- {
- DestroyWindow(mWindow);
- mWindow = NULL;
- }
-}
-
-
-// static
-LRESULT CALLBACK LLSplashScreenWin32::windowProc(HWND h_wnd, UINT u_msg,
- WPARAM w_param, LPARAM l_param)
-{
- // Just give it to windows
- return DefWindowProc(h_wnd, u_msg, w_param, l_param);
-}
-
-//
-// Helper Funcs
-//
-
-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;
- }
-
- // HACK! Doesn't properly handle wide strings!
- int retval_win = MessageBoxA(NULL, text.c_str(), 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 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 ) };
- sei.fMask = SEE_MASK_FLAG_DDEWAIT;
- sei.nShow = SW_SHOWNORMAL;
- sei.lpVerb = L"open";
- sei.lpFile = url_utf16.c_str();
- ShellExecuteEx( &sei );
-
- //// TODO: LEAVING OLD CODE HERE SO I DON'T BONE OTHER MERGES
- //// DELETE THIS ONCE THE MERGES ARE DONE
-
- // Figure out the user's default web browser
- // HKEY_CLASSES_ROOT\http\shell\open\command
- /*
- std::string reg_path_str = gURLProtocolWhitelistHandler[i] + "\\shell\\open\\command";
- WCHAR reg_path_wstr[256];
- mbstowcs( reg_path_wstr, reg_path_str.c_str(), LL_ARRAY_SIZE(reg_path_wstr) );
-
- HKEY key;
- WCHAR browser_open_wstr[1024];
- DWORD buffer_length = 1024;
- RegOpenKeyEx(HKEY_CLASSES_ROOT, reg_path_wstr, 0, KEY_QUERY_VALUE, &key);
- RegQueryValueEx(key, NULL, NULL, NULL, (LPBYTE)browser_open_wstr, &buffer_length);
- RegCloseKey(key);
-
- // Convert to STL string
- LLWString browser_open_wstring = utf16str_to_wstring(browser_open_wstr);
-
- if (browser_open_wstring.length() < 2)
- {
- LL_WARNS("Window") << "Invalid browser executable in registry " << browser_open_wstring << LL_ENDL;
- return;
- }
-
- // Extract the process that's supposed to be launched
- LLWString browser_executable;
- if (browser_open_wstring[0] == '"')
- {
- // executable is quoted, find the matching quote
- size_t quote_pos = browser_open_wstring.find('"', 1);
- // copy out the string including both quotes
- browser_executable = browser_open_wstring.substr(0, quote_pos+1);
- }
- else
- {
- // executable not quoted, find a space
- size_t space_pos = browser_open_wstring.find(' ', 1);
- browser_executable = browser_open_wstring.substr(0, space_pos);
- }
-
- LL_DEBUGS("Window") << "Browser reg key: " << wstring_to_utf8str(browser_open_wstring) << LL_ENDL;
- LL_INFOS("Window") << "Browser executable: " << wstring_to_utf8str(browser_executable) << LL_ENDL;
-
- // Convert URL to wide string for Windows API
- // Assume URL is UTF8, as can come from scripts
- LLWString url_wstring = utf8str_to_wstring(escaped_url);
- llutf16string url_utf16 = wstring_to_utf16str(url_wstring);
-
- // Convert executable and path to wide string for Windows API
- llutf16string browser_exec_utf16 = wstring_to_utf16str(browser_executable);
-
- // ShellExecute returns HINSTANCE for backwards compatiblity.
- // MS docs say to cast to int and compare to 32.
- HWND our_window = NULL;
- LPCWSTR directory_wstr = NULL;
- int retval = (int) ShellExecute(our_window, // Flawfinder: ignore
- L"open",
- browser_exec_utf16.c_str(),
- url_utf16.c_str(),
- directory_wstr,
- SW_SHOWNORMAL);
- if (retval > 32)
- {
- LL_DEBUGS("Window") << "load_url success with " << retval << LL_ENDL;
- }
- else
- {
- LL_INFOS("Window") << "load_url failure with " << retval << LL_ENDL;
- }
- */
-}
-
-
-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()
-{
- BringWindowToTop(mWindowHandle);
-}
-
-// set (OS) window focus back to the client
-void LLWindowWin32::focusClient()
-{
- 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 )
- {
- // 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
- {
- // 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.set( win_pos.mX, win_pos.mY );
- }
-
- 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()
-{
- if (mPreeditor)
- {
- if (LLWinImm::isAvailable())
- {
- HIMC himc = LLWinImm::getContext(mWindowHandle);
- LLWinImm::notifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
- LLWinImm::releaseContext(mWindowHandle, himc);
- }
-
- // Win32 document says there will be no composition string
- // after NI_COMPOSITIONSTR returns. The following call to
- // resetPreedit should be a NOP unless IME goes mad...
- mPreeditor->resetPreedit();
- }
-}
-
-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)
-{
- 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)
- {
- 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);
-}
-
-// 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(U32 request, U32 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 = FALSE;
- 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;
- if (preedit_length)
- {
- // 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
-std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList()
-{
- // Fonts previously in getFontListSans() have moved to fonts.xml.
- return std::vector<std::string>();
-}
-
-
-#endif // LL_WINDOWS
+/**
+ * @file llwindowwin32.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/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 "llgl.h"
+#include "llstring.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>
+
+// Require DirectInput version 8
+#define DIRECTINPUT_VERSION 0x0800
+
+#include <dinput.h>
+#include <Dbt.h.>
+
+#include "llmemtype.h"
+// culled from winuser.h
+#ifndef WM_MOUSEWHEEL /* Added to be compatible with later SDK's */
+const S32 WM_MOUSEWHEEL = 0x020A;
+#endif
+#ifndef WHEEL_DELTA /* Added to be compatible with later SDK's */
+const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
+#endif
+const S32 MAX_MESSAGE_PER_UPDATE = 20;
+const S32 BITS_PER_PIXEL = 32;
+const S32 MAX_NUM_RESOLUTIONS = 32;
+const F32 ICON_FLASH_TIME = 0.5f;
+
+extern BOOL gDebugWindowProc;
+
+LPWSTR gIconResource = IDI_APPLICATION;
+
+LLW32MsgCallback gAsyncMsgCallback = NULL;
+
+//
+// LLWindowWin32
+//
+
+void show_window_creation_error(const std::string& title)
+{
+ LL_WARNS("Window") << title << LL_ENDL;
+}
+
+//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.
+// We need this because some language versions of Windows,
+// e.g., US version of Windows XP, doesn't install IMM32.DLL
+// as a default, and we can't link against imm32.lib statically.
+// I believe DLL loading of this type is best suited to do
+// in a static initialization of a class. What I'm not sure is
+// whether it follows the Linden Conding Standard...
+// See http://wiki.secondlife.com/wiki/Coding_standards#Static_Members
+
+class LLWinImm
+{
+public:
+ static bool isAvailable() { return sTheInstance.mHImmDll != NULL; }
+
+public:
+ // Wrappers for IMM API.
+ static BOOL isIME(HKL hkl);
+ static HWND getDefaultIMEWnd(HWND hwnd);
+ 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);
+
+private:
+ LLWinImm();
+ ~LLWinImm();
+
+private:
+ // Pointers to IMM API.
+ BOOL (WINAPI *mImmIsIME)(HKL);
+ HWND (WINAPI *mImmGetDefaultIMEWnd)(HWND);
+ HIMC (WINAPI *mImmGetContext)(HWND);
+ BOOL (WINAPI *mImmReleaseContext)(HWND, HIMC);
+ BOOL (WINAPI *mImmGetOpenStatus)(HIMC);
+ BOOL (WINAPI *mImmSetOpenStatus)(HIMC, BOOL);
+ BOOL (WINAPI *mImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD);
+ BOOL (WINAPI *mImmSetConversionStatus)(HIMC, DWORD, DWORD);
+ BOOL (WINAPI *mImmGetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM);
+ BOOL (WINAPI *mImmSetCompostitionWindow)(HIMC, LPCOMPOSITIONFORM);
+ LONG (WINAPI *mImmGetCompositionString)(HIMC, DWORD, LPVOID, DWORD);
+ BOOL (WINAPI *mImmSetCompositionString)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD);
+ BOOL (WINAPI *mImmSetCompositionFont)(HIMC, LPLOGFONTW);
+ BOOL (WINAPI *mImmSetCandidateWindow)(HIMC, LPCANDIDATEFORM);
+ BOOL (WINAPI *mImmNotifyIME)(HIMC, DWORD, DWORD, DWORD);
+
+private:
+ HMODULE mHImmDll;
+ static LLWinImm sTheInstance;
+};
+
+LLWinImm LLWinImm::sTheInstance;
+
+LLWinImm::LLWinImm() : mHImmDll(NULL)
+{
+ // Check system metrics
+ if ( !GetSystemMetrics( SM_DBCSENABLED ) )
+ return;
+
+
+ mHImmDll = LoadLibraryA("Imm32");
+ if (mHImmDll != NULL)
+ {
+ mImmIsIME = (BOOL (WINAPI *)(HKL)) GetProcAddress(mHImmDll, "ImmIsIME");
+ mImmGetDefaultIMEWnd = (HWND (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetDefaultIMEWnd");
+ mImmGetContext = (HIMC (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetContext");
+ mImmReleaseContext = (BOOL (WINAPI *)(HWND, HIMC)) GetProcAddress(mHImmDll, "ImmReleaseContext");
+ mImmGetOpenStatus = (BOOL (WINAPI *)(HIMC)) GetProcAddress(mHImmDll, "ImmGetOpenStatus");
+ mImmSetOpenStatus = (BOOL (WINAPI *)(HIMC, BOOL)) GetProcAddress(mHImmDll, "ImmSetOpenStatus");
+ mImmGetConversionStatus = (BOOL (WINAPI *)(HIMC, LPDWORD, LPDWORD)) GetProcAddress(mHImmDll, "ImmGetConversionStatus");
+ mImmSetConversionStatus = (BOOL (WINAPI *)(HIMC, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmSetConversionStatus");
+ mImmGetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmGetCompositionWindow");
+ mImmSetCompostitionWindow = (BOOL (WINAPI *)(HIMC, LPCOMPOSITIONFORM)) GetProcAddress(mHImmDll, "ImmSetCompositionWindow");
+ mImmGetCompositionString= (LONG (WINAPI *)(HIMC, DWORD, LPVOID, DWORD)) GetProcAddress(mHImmDll, "ImmGetCompositionStringW");
+ mImmSetCompositionString= (BOOL (WINAPI *)(HIMC, DWORD, LPVOID, DWORD, LPVOID, DWORD)) GetProcAddress(mHImmDll, "ImmSetCompositionStringW");
+ mImmSetCompositionFont = (BOOL (WINAPI *)(HIMC, LPLOGFONTW)) GetProcAddress(mHImmDll, "ImmSetCompositionFontW");
+ mImmSetCandidateWindow = (BOOL (WINAPI *)(HIMC, LPCANDIDATEFORM)) GetProcAddress(mHImmDll, "ImmSetCandidateWindow");
+ mImmNotifyIME = (BOOL (WINAPI *)(HIMC, DWORD, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmNotifyIME");
+
+ if (mImmIsIME == NULL ||
+ mImmGetDefaultIMEWnd == NULL ||
+ mImmGetContext == NULL ||
+ mImmReleaseContext == NULL ||
+ mImmGetOpenStatus == NULL ||
+ mImmSetOpenStatus == NULL ||
+ mImmGetConversionStatus == NULL ||
+ mImmSetConversionStatus == NULL ||
+ mImmGetCompostitionWindow == NULL ||
+ mImmSetCompostitionWindow == NULL ||
+ mImmGetCompositionString == NULL ||
+ mImmSetCompositionString == NULL ||
+ mImmSetCompositionFont == NULL ||
+ mImmSetCandidateWindow == NULL ||
+ mImmNotifyIME == NULL)
+ {
+ // If any of the above API entires are not found, we can't use IMM API.
+ // So, turn off the IMM support. We should log some warning message in
+ // the case, since it is very unusual; these APIs are available from
+ // the beginning, and all versions of IMM32.DLL should have them all.
+ // Unfortunately, this code may be executed before initialization of
+ // the logging channel (llwarns), and we can't do it here... Yes, this
+ // is one of disadvantages to use static constraction to DLL loading.
+ FreeLibrary(mHImmDll);
+ mHImmDll = NULL;
+
+ // If we unload the library, make sure all the function pointers are cleared
+ mImmIsIME = NULL;
+ mImmGetDefaultIMEWnd = NULL;
+ mImmGetContext = NULL;
+ mImmReleaseContext = NULL;
+ mImmGetOpenStatus = NULL;
+ mImmSetOpenStatus = NULL;
+ mImmGetConversionStatus = NULL;
+ mImmSetConversionStatus = NULL;
+ mImmGetCompostitionWindow = NULL;
+ mImmSetCompostitionWindow = NULL;
+ mImmGetCompositionString = NULL;
+ mImmSetCompositionString = NULL;
+ mImmSetCompositionFont = NULL;
+ mImmSetCandidateWindow = NULL;
+ mImmNotifyIME = NULL;
+ }
+ }
+}
+
+
+// static
+BOOL LLWinImm::isIME(HKL hkl)
+{
+ if ( sTheInstance.mImmIsIME )
+ return sTheInstance.mImmIsIME(hkl);
+ return FALSE;
+}
+
+// static
+HIMC LLWinImm::getContext(HWND hwnd)
+{
+ if ( sTheInstance.mImmGetContext )
+ return sTheInstance.mImmGetContext(hwnd);
+ return 0;
+}
+
+//static
+BOOL LLWinImm::releaseContext(HWND hwnd, HIMC himc)
+{
+ if ( sTheInstance.mImmIsIME )
+ return sTheInstance.mImmReleaseContext(hwnd, himc);
+ return FALSE;
+}
+
+// static
+BOOL LLWinImm::getOpenStatus(HIMC himc)
+{
+ if ( sTheInstance.mImmGetOpenStatus )
+ return sTheInstance.mImmGetOpenStatus(himc);
+ return FALSE;
+}
+
+// static
+BOOL LLWinImm::setOpenStatus(HIMC himc, BOOL status)
+{
+ if ( sTheInstance.mImmSetOpenStatus )
+ return sTheInstance.mImmSetOpenStatus(himc, status);
+ return FALSE;
+}
+
+// static
+BOOL LLWinImm::getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence)
+{
+ if ( sTheInstance.mImmGetConversionStatus )
+ return sTheInstance.mImmGetConversionStatus(himc, conversion, sentence);
+ return FALSE;
+}
+
+// static
+BOOL LLWinImm::setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence)
+{
+ if ( sTheInstance.mImmSetConversionStatus )
+ return sTheInstance.mImmSetConversionStatus(himc, conversion, sentence);
+ return FALSE;
+}
+
+// static
+BOOL LLWinImm::getCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)
+{
+ if ( sTheInstance.mImmGetCompostitionWindow )
+ return sTheInstance.mImmGetCompostitionWindow(himc, form);
+ return FALSE;
+}
+
+// static
+BOOL LLWinImm::setCompositionWindow(HIMC himc, LPCOMPOSITIONFORM form)
+{
+ if ( sTheInstance.mImmSetCompostitionWindow )
+ return sTheInstance.mImmSetCompostitionWindow(himc, form);
+ return FALSE;
+}
+
+
+// static
+LONG LLWinImm::getCompositionString(HIMC himc, DWORD index, LPVOID data, DWORD length)
+{
+ if ( sTheInstance.mImmGetCompositionString )
+ return sTheInstance.mImmGetCompositionString(himc, index, data, length);
+ return FALSE;
+}
+
+
+// static
+BOOL LLWinImm::setCompositionString(HIMC himc, DWORD index, LPVOID pComp, DWORD compLength, LPVOID pRead, DWORD readLength)
+{
+ if ( sTheInstance.mImmSetCompositionString )
+ return sTheInstance.mImmSetCompositionString(himc, index, pComp, compLength, pRead, readLength);
+ return FALSE;
+}
+
+// static
+BOOL LLWinImm::setCompositionFont(HIMC himc, LPLOGFONTW pFont)
+{
+ if ( sTheInstance.mImmSetCompositionFont )
+ return sTheInstance.mImmSetCompositionFont(himc, pFont);
+ return FALSE;
+}
+
+// static
+BOOL LLWinImm::setCandidateWindow(HIMC himc, LPCANDIDATEFORM form)
+{
+ if ( sTheInstance.mImmSetCandidateWindow )
+ return sTheInstance.mImmSetCandidateWindow(himc, form);
+ return FALSE;
+}
+
+// static
+BOOL LLWinImm::notifyIME(HIMC himc, DWORD action, DWORD index, DWORD value)
+{
+ if ( sTheInstance.mImmNotifyIME )
+ return sTheInstance.mImmNotifyIME(himc, action, index, value);
+ return FALSE;
+}
+
+
+
+
+// ----------------------------------------------------------------------------------------
+LLWinImm::~LLWinImm()
+{
+ if (mHImmDll != NULL)
+ {
+ FreeLibrary(mHImmDll);
+ mHImmDll = NULL;
+ }
+}
+
+
+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 disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth,
+ U32 fsaa_samples)
+ : LLWindow(callbacks, fullscreen, flags)
+{
+ mFSAASamples = fsaa_samples;
+ mIconResource = gIconResource;
+ mOverrideAspectRatio = 0.f;
+ mNativeAspectRatio = 0.f;
+ mMousePositionModified = FALSE;
+ mInputProcessingPaused = FALSE;
+ mPreeditor = NULL;
+ mhDC = NULL;
+ mhRC = NULL;
+
+ // 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);
+ mWndProc = NULL;
+
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+
+ // No WPARAM yet.
+ mLastSizeWParam = 0;
+
+ // Windows GDI rects don't include rightmost pixel
+ window_rect.left = (long) 0;
+ window_rect.right = (long) width;
+ window_rect.top = (long) 0;
+ window_rect.bottom = (long) height;
+
+ // Grab screen size to sanitize the window
+ S32 window_border_y = GetSystemMetrics(SM_CYBORDER);
+ S32 virtual_screen_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ S32 virtual_screen_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ S32 virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ S32 virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+
+ if (x < virtual_screen_x) x = virtual_screen_x;
+ if (y < virtual_screen_y - window_border_y) y = virtual_screen_y - window_border_y;
+
+ if (x + width > virtual_screen_x + virtual_screen_width) x = virtual_screen_x + virtual_screen_width - width;
+ if (y + height > virtual_screen_y + virtual_screen_height) y = virtual_screen_y + virtual_screen_height - height;
+
+ if (!sIsClassRegistered)
+ {
+ // Force redraw when resized and create a private device context
+
+ // Makes double click messages.
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
+
+ // Set message handler function
+ wc.lpfnWndProc = (WNDPROC) mainWindowProc;
+
+ // unused
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+
+ wc.hInstance = mhInstance;
+ wc.hIcon = LoadIcon(mhInstance, mIconResource);
+
+ // We will set the cursor ourselves
+ wc.hCursor = NULL;
+
+ // background color is not used
+ if (clearBg)
+ {
+ wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
+ }
+ else
+ {
+ wc.hbrBackground = (HBRUSH) NULL;
+ }
+
+ // we don't use windows menus
+ wc.lpszMenuName = NULL;
+
+ wc.lpszClassName = mWindowClassName;
+
+ if (!RegisterClass(&wc))
+ {
+ OSMessageBox(mCallbacks->translateString("MBRegClassFailed"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ return;
+ }
+ sIsClassRegistered = TRUE;
+ }
+
+ //-----------------------------------------------------------------------
+ // Get the current refresh rate
+ //-----------------------------------------------------------------------
+
+ DEVMODE dev_mode;
+ DWORD current_refresh;
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
+ {
+ current_refresh = dev_mode.dmDisplayFrequency;
+ mNativeAspectRatio = ((F32)dev_mode.dmPelsWidth) / ((F32)dev_mode.dmPelsHeight);
+ }
+ else
+ {
+ current_refresh = 60;
+ }
+
+ //-----------------------------------------------------------------------
+ // Drop resolution and go fullscreen
+ // use a display mode with our desired size and depth, with a refresh
+ // rate as close at possible to the users' default
+ //-----------------------------------------------------------------------
+ if (mFullscreen)
+ {
+ BOOL success = FALSE;
+ DWORD closest_refresh = 0;
+
+ for (S32 mode_num = 0;; mode_num++)
+ {
+ if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
+ {
+ break;
+ }
+
+ if (dev_mode.dmPelsWidth == width &&
+ dev_mode.dmPelsHeight == height &&
+ dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
+ {
+ success = TRUE;
+ if ((dev_mode.dmDisplayFrequency - current_refresh)
+ < (closest_refresh - current_refresh))
+ {
+ closest_refresh = dev_mode.dmDisplayFrequency;
+ }
+ }
+ }
+
+ if (closest_refresh == 0)
+ {
+ LL_WARNS("Window") << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << 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 );
+ // }
+
+
+ //-----------------------------------------------------------------------
+ // 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, TRUE, &windowPos))
+ {
+ return;
+ }
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+
+ // 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;
+}
+
+void LLWindowWin32::show()
+{
+ 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);
+}
+
+
+// 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();
+
+ // Make sure cursor is visible and we haven't mangled the clipping state.
+ setMouseClipping(FALSE);
+ showCursor();
+
+ // Go back to screen mode written in the registry.
+ if (mFullscreen)
+ {
+ resetDisplayResolution();
+ }
+
+ // Clean up remaining GL state
+ LL_DEBUGS("Window") << "Shutting down 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();
+
+ if (mhDC && !ReleaseDC(mWindowHandle, mhDC))
+ {
+ LL_WARNS("Window") << "Release of ghDC failed" << LL_ENDL;
+ mhDC = NULL;
+ }
+
+ LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL;
+
+ // Don't process events in our mainWindowProc any longer.
+ SetWindowLong(mWindowHandle, GWL_USERDATA, NULL);
+
+ // Make sure we don't leave a blank toolbar button.
+ ShowWindow(mWindowHandle, SW_HIDE);
+
+ // This causes WM_DESTROY to be sent *immediately*
+ if (!DestroyWindow(mWindowHandle))
+ {
+ OSMessageBox(mCallbacks->translateString("MBDestroyWinFailed"),
+ mCallbacks->translateString("MBShutdownErr"),
+ OSMB_OK);
+ }
+
+ mWindowHandle = NULL;
+}
+
+BOOL LLWindowWin32::isValid()
+{
+ return (mWindowHandle != NULL);
+}
+
+BOOL LLWindowWin32::getVisible()
+{
+ return (mWindowHandle && IsWindowVisible(mWindowHandle));
+}
+
+BOOL LLWindowWin32::getMinimized()
+{
+ return (mWindowHandle && IsIconic(mWindowHandle));
+}
+
+BOOL LLWindowWin32::getMaximized()
+{
+ return (mWindowHandle && IsZoomed(mWindowHandle));
+}
+
+BOOL LLWindowWin32::maximize()
+{
+ BOOL success = FALSE;
+ if (!mWindowHandle) return success;
+
+ WINDOWPLACEMENT placement;
+ placement.length = sizeof(WINDOWPLACEMENT);
+
+ success = GetWindowPlacement(mWindowHandle, &placement);
+ if (!success) return success;
+
+ placement.showCmd = SW_MAXIMIZE;
+
+ success = SetWindowPlacement(mWindowHandle, &placement);
+ return success;
+}
+
+BOOL LLWindowWin32::getFullscreen()
+{
+ return mFullscreen;
+}
+
+BOOL LLWindowWin32::getPosition(LLCoordScreen *position)
+{
+ RECT window_rect;
+
+ if (!mWindowHandle ||
+ !GetWindowRect(mWindowHandle, &window_rect) ||
+ NULL == position)
+ {
+ return FALSE;
+ }
+
+ position->mX = window_rect.left;
+ position->mY = window_rect.top;
+ return TRUE;
+}
+
+BOOL LLWindowWin32::getSize(LLCoordScreen *size)
+{
+ RECT window_rect;
+
+ if (!mWindowHandle ||
+ !GetWindowRect(mWindowHandle, &window_rect) ||
+ NULL == size)
+ {
+ return FALSE;
+ }
+
+ size->mX = window_rect.right - window_rect.left;
+ size->mY = window_rect.bottom - window_rect.top;
+ return TRUE;
+}
+
+BOOL LLWindowWin32::getSize(LLCoordWindow *size)
+{
+ RECT client_rect;
+
+ if (!mWindowHandle ||
+ !GetClientRect(mWindowHandle, &client_rect) ||
+ NULL == size)
+ {
+ return FALSE;
+ }
+
+ size->mX = client_rect.right - client_rect.left;
+ size->mY = client_rect.bottom - client_rect.top;
+ return TRUE;
+}
+
+BOOL LLWindowWin32::setPosition(const LLCoordScreen position)
+{
+ LLCoordScreen size;
+
+ if (!mWindowHandle)
+ {
+ return FALSE;
+ }
+ getSize(&size);
+ moveWindow(position, size);
+ return TRUE;
+}
+
+BOOL LLWindowWin32::setSize(const LLCoordScreen size)
+{
+ LLCoordScreen position;
+
+ getPosition(&position);
+ if (!mWindowHandle)
+ {
+ return FALSE;
+ }
+
+ moveWindow(position, size);
+ return TRUE;
+}
+
+// changing fullscreen resolution
+BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp)
+{
+ GLuint pixel_format;
+ DEVMODE dev_mode;
+ DWORD current_refresh;
+ DWORD dw_ex_style;
+ DWORD dw_style;
+ RECT window_rect;
+ S32 width = size.mX;
+ S32 height = size.mY;
+ 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;
+ }
+
+ 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
+ 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
+ DestroyWindow(mWindowHandle);
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ window_rect.left, // x pos
+ window_rect.top, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ //-----------------------------------------------------------------------
+ // Create GL drawing context
+ //-----------------------------------------------------------------------
+ static PIXELFORMATDESCRIPTOR pfd =
+ {
+ sizeof(PIXELFORMATDESCRIPTOR),
+ 1,
+ PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+ PFD_TYPE_RGBA,
+ BITS_PER_PIXEL,
+ 0, 0, 0, 0, 0, 0, // RGB bits and shift, unused
+ 8, // alpha bits
+ 0, // alpha shift
+ 0, // accum bits
+ 0, 0, 0, 0, // accum RGBA
+ 24, // depth bits
+ 8, // stencil bits, avi added for stencil test
+ 0,
+ PFD_MAIN_PLANE,
+ 0,
+ 0, 0, 0
+ };
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBDevContextErr"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (!(pixel_format = ChoosePixelFormat(mhDC, &pfd)))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBPixelFmtErr"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ // Verify what pixel format we actually received.
+ if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBPixelFmtDescErr"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (pfd.cColorBits < 32)
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBTrueColorWindow"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (pfd.cAlphaBits < 8)
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBAlpha"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBPixelFmtSetErr"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBGLContextErr"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBGLContextActErr"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ gGLManager.initWGL();
+
+ if (wglChoosePixelFormatARB)
+ {
+ // OK, at this point, use the ARB wglChoosePixelFormatsARB function to see if we
+ // can get exactly what we want.
+ GLint attrib_list[256];
+ S32 cur_attrib = 0;
+
+ attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_ACCELERATION_ARB;
+ attrib_list[cur_attrib++] = WGL_FULL_ACCELERATION_ARB;
+
+ attrib_list[cur_attrib++] = WGL_SUPPORT_OPENGL_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_DOUBLE_BUFFER_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_COLOR_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ 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);
+ if (!result)
+ {
+ 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;
+ }
+
+
+
+ S32 swap_method = 0;
+ S32 cur_format = num_formats-1;
+ GLint swap_query = WGL_SWAP_METHOD_ARB;
+
+ BOOL found_format = FALSE;
+
+ while (!found_format && wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method))
+ {
+ if (swap_method == WGL_SWAP_UNDEFINED_ARB || cur_format <= 0)
+ {
+ found_format = TRUE;
+ }
+ else
+ {
+ --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
+
+ }
+ ReleaseDC (mWindowHandle, mhDC); // Release The Device Context
+ mhDC = 0; // Zero The Device Context
+ }
+ DestroyWindow (mWindowHandle); // Destroy The Window
+
+
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ window_rect.left, // x pos
+ window_rect.top, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBDevContextErr"), mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBPixelFmtSetErr"),
+ mCallbacks->translateString("MBError"), OSMB_OK);
+ 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))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBPixelFmtDescErr"), mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ LL_INFOS("Window") << "GL buffer: Color Bits " << S32(pfd.cColorBits)
+ << " Alpha Bits " << S32(pfd.cAlphaBits)
+ << " Depth Bits " << S32(pfd.cDepthBits)
+ << LL_ENDL;
+
+ // make sure we have 32 bits per pixel
+ if (pfd.cColorBits < 32 || GetDeviceCaps(mhDC, BITSPIXEL) < 32)
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBTrueColorWindow"), mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (pfd.cAlphaBits < 8)
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBAlpha"), mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBGLContextErr"), mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBGLContextActErr"), mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ if (!gGLManager.initGL())
+ {
+ close();
+ OSMessageBox(mCallbacks->translateString("MBVideoDrvErr"), mCallbacks->translateString("MBError"), OSMB_OK);
+ return FALSE;
+ }
+
+ // Disable vertical sync for swap
+ if (disable_vsync && wglSwapIntervalEXT)
+ {
+ LL_DEBUGS("Window") << "Disabling vertical sync" << LL_ENDL;
+ wglSwapIntervalEXT(0);
+ }
+ else
+ {
+ LL_DEBUGS("Window") << "Keeping vertical sync" << LL_ENDL;
+ }
+
+ SetWindowLong(mWindowHandle, GWL_USERDATA, (U32)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;
+
+ if (auto_show)
+ {
+ show();
+ glClearColor(0.0f, 0.0f, 0.0f, 0.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ swapBuffers();
+ }
+
+ return TRUE;
+}
+
+void LLWindowWin32::moveWindow( const LLCoordScreen& position, const LLCoordScreen& size )
+{
+ if( mIsMouseClipping )
+ {
+ RECT client_rect_in_screen_space;
+ if( getClientRectInScreenSpace( &client_rect_in_screen_space ) )
+ {
+ ClipCursor( &client_rect_in_screen_space );
+ }
+ }
+
+ // 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
+ MoveWindow(mWindowHandle, position.mX, position.mY, size.mX, size.mY, TRUE);
+}
+
+BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position)
+{
+ LLCoordScreen screen_pos;
+
+ mMousePositionModified = TRUE;
+ if (!mWindowHandle)
+ {
+ return FALSE;
+ }
+
+ if (!convertCoords(position, &screen_pos))
+ {
+ return FALSE;
+ }
+
+ // Inform the application of the new mouse position (needed for per-frame
+ // hover/picking to function).
+ LLCoordGL gl_pos;
+ convertCoords(position, &gl_pos);
+ mCallbacks->handleMouseMove(this, gl_pos, (MASK)0);
+
+ // DEV-18951 VWR-8524 Camera moves wildly when alt-clicking.
+ // Because we have preemptively notified the application of the new
+ // mouse position via handleMouseMove() above, we need to clear out
+ // any stale mouse move events. RN/JC
+ MSG msg;
+ while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
+ { }
+
+ return SetCursorPos(screen_pos.mX, screen_pos.mY);
+}
+
+BOOL LLWindowWin32::getCursorPosition(LLCoordWindow *position)
+{
+ POINT cursor_point;
+ LLCoordScreen screen_pos;
+
+ if (!mWindowHandle ||
+ !GetCursorPos(&cursor_point))
+ {
+ return FALSE;
+ }
+
+ screen_pos.mX = cursor_point.x;
+ screen_pos.mY = cursor_point.y;
+
+ return convertCoords(screen_pos, position);
+}
+
+void LLWindowWin32::hideCursor()
+{
+ while (ShowCursor(FALSE) >= 0)
+ {
+ // nothing, wait for cursor to push down
+ }
+ mCursorHidden = TRUE;
+ mHideCursorPermanent = TRUE;
+}
+
+void LLWindowWin32::showCursor()
+{
+ // makes sure the cursor shows up
+ while (ShowCursor(TRUE) < 0)
+ {
+ // do nothing, wait for cursor to pop out
+ }
+ mCursorHidden = FALSE;
+ mHideCursorPermanent = FALSE;
+}
+
+void LLWindowWin32::showCursorFromMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ showCursor();
+ }
+}
+
+void LLWindowWin32::hideCursorUntilMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ hideCursor();
+ mHideCursorPermanent = FALSE;
+ }
+}
+
+BOOL LLWindowWin32::isCursorHidden()
+{
+ return mCursorHidden;
+}
+
+
+HCURSOR LLWindowWin32::loadColorCursor(LPCTSTR name)
+{
+ return (HCURSOR)LoadImage(mhInstance,
+ name,
+ IMAGE_CURSOR,
+ 0, // default width
+ 0, // default height
+ LR_DEFAULTCOLOR);
+}
+
+
+void LLWindowWin32::initCursors()
+{
+ mCursor[ UI_CURSOR_ARROW ] = LoadCursor(NULL, IDC_ARROW);
+ mCursor[ UI_CURSOR_WAIT ] = LoadCursor(NULL, IDC_WAIT);
+ mCursor[ UI_CURSOR_HAND ] = LoadCursor(NULL, IDC_HAND);
+ mCursor[ UI_CURSOR_IBEAM ] = LoadCursor(NULL, IDC_IBEAM);
+ mCursor[ UI_CURSOR_CROSS ] = LoadCursor(NULL, IDC_CROSS);
+ mCursor[ UI_CURSOR_SIZENWSE ] = LoadCursor(NULL, IDC_SIZENWSE);
+ mCursor[ UI_CURSOR_SIZENESW ] = LoadCursor(NULL, IDC_SIZENESW);
+ mCursor[ UI_CURSOR_SIZEWE ] = LoadCursor(NULL, IDC_SIZEWE);
+ mCursor[ UI_CURSOR_SIZENS ] = LoadCursor(NULL, IDC_SIZENS);
+ mCursor[ UI_CURSOR_NO ] = LoadCursor(NULL, IDC_NO);
+ mCursor[ UI_CURSOR_WORKING ] = LoadCursor(NULL, IDC_APPSTARTING);
+
+ HMODULE module = GetModuleHandle(NULL);
+ mCursor[ UI_CURSOR_TOOLGRAB ] = LoadCursor(module, TEXT("TOOLGRAB"));
+ mCursor[ UI_CURSOR_TOOLLAND ] = LoadCursor(module, TEXT("TOOLLAND"));
+ mCursor[ UI_CURSOR_TOOLFOCUS ] = LoadCursor(module, TEXT("TOOLFOCUS"));
+ mCursor[ UI_CURSOR_TOOLCREATE ] = LoadCursor(module, TEXT("TOOLCREATE"));
+ mCursor[ UI_CURSOR_ARROWDRAG ] = LoadCursor(module, TEXT("ARROWDRAG"));
+ mCursor[ UI_CURSOR_ARROWCOPY ] = LoadCursor(module, TEXT("ARROWCOPY"));
+ mCursor[ UI_CURSOR_ARROWDRAGMULTI ] = LoadCursor(module, TEXT("ARROWDRAGMULTI"));
+ mCursor[ UI_CURSOR_ARROWCOPYMULTI ] = LoadCursor(module, TEXT("ARROWCOPYMULTI"));
+ mCursor[ UI_CURSOR_NOLOCKED ] = LoadCursor(module, TEXT("NOLOCKED"));
+ mCursor[ UI_CURSOR_ARROWLOCKED ]= LoadCursor(module, TEXT("ARROWLOCKED"));
+ mCursor[ UI_CURSOR_GRABLOCKED ] = LoadCursor(module, TEXT("GRABLOCKED"));
+ mCursor[ UI_CURSOR_TOOLTRANSLATE ] = LoadCursor(module, TEXT("TOOLTRANSLATE"));
+ mCursor[ UI_CURSOR_TOOLROTATE ] = LoadCursor(module, TEXT("TOOLROTATE"));
+ mCursor[ UI_CURSOR_TOOLSCALE ] = LoadCursor(module, TEXT("TOOLSCALE"));
+ mCursor[ UI_CURSOR_TOOLCAMERA ] = LoadCursor(module, TEXT("TOOLCAMERA"));
+ mCursor[ UI_CURSOR_TOOLPAN ] = LoadCursor(module, TEXT("TOOLPAN"));
+ mCursor[ UI_CURSOR_TOOLZOOMIN ] = LoadCursor(module, TEXT("TOOLZOOMIN"));
+ mCursor[ UI_CURSOR_TOOLPICKOBJECT3 ] = LoadCursor(module, TEXT("TOOLPICKOBJECT3"));
+ mCursor[ UI_CURSOR_PIPETTE ] = LoadCursor(module, TEXT("TOOLPIPETTE"));
+
+ // Color cursors
+ mCursor[UI_CURSOR_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::setCursor(ECursorType cursor)
+{
+ if (cursor == UI_CURSOR_ARROW
+ && mBusyCount > 0)
+ {
+ cursor = UI_CURSOR_WORKING;
+ }
+
+ if( mCurrentCursor != cursor )
+ {
+ mCurrentCursor = cursor;
+ SetCursor( mCursor[cursor] );
+ }
+}
+
+ECursorType LLWindowWin32::getCursor() const
+{
+ return mCurrentCursor;
+}
+
+void LLWindowWin32::captureMouse()
+{
+ SetCapture(mWindowHandle);
+}
+
+void LLWindowWin32::releaseMouse()
+{
+ // *NOTE:Mani ReleaseCapture will spawn new windows messages...
+ // which will in turn call our MainWindowProc. It therefore requires
+ // pausing *and more importantly resumption* of the mainlooptimeout...
+ // just like DispatchMessage below.
+ mCallbacks->handlePauseWatchdog(this);
+ ReleaseCapture();
+ mCallbacks->handleResumeWatchdog(this);
+}
+
+
+void LLWindowWin32::delayInputProcessing()
+{
+ mInputProcessingPaused = TRUE;
+}
+
+void LLWindowWin32::gatherInput()
+{
+ MSG msg;
+ int msg_count = 0;
+
+ LLMemType m1(LLMemType::MTYPE_GATHER_INPUT);
+
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg_count < MAX_MESSAGE_PER_UPDATE)
+ {
+ mCallbacks->handlePingWatchdog(this, "Main:TranslateGatherInput");
+ TranslateMessage(&msg);
+
+ // turn watchdog off in here to not fail if windows is doing something wacky
+ mCallbacks->handlePauseWatchdog(this);
+ DispatchMessage(&msg);
+ mCallbacks->handleResumeWatchdog(this);
+ msg_count++;
+
+ if ( mInputProcessingPaused )
+ {
+ break;
+ }
+ /* Attempted workaround for problem where typing fast and hitting
+ return would result in only part of the text being sent. JC
+
+ BOOL key_posted = TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ msg_count++;
+
+ // If a key was translated, a WM_CHAR might have been posted to the end
+ // of the event queue. We need it immediately.
+ if (key_posted && msg.message == WM_KEYDOWN)
+ {
+ if (PeekMessage(&msg, NULL, WM_CHAR, WM_CHAR, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ msg_count++;
+ }
+ }
+ */
+ mCallbacks->handlePingWatchdog(this, "Main:AsyncCallbackGatherInput");
+ // For async host by name support. Really hacky.
+ if (gAsyncMsgCallback && (LL_WM_HOST_RESOLVED == msg.message))
+ {
+ gAsyncMsgCallback(msg);
+ }
+ }
+
+ mInputProcessingPaused = FALSE;
+
+ // clear this once we've processed all mouse messages that might have occurred after
+ // we slammed the mouse position
+ mMousePositionModified = FALSE;
+}
+
+static LLFastTimer::DeclareTimer FTM_KEYHANDLER("Handle Keyboard");
+static LLFastTimer::DeclareTimer FTM_MOUSEHANDLER("Handle Mouse");
+
+LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param)
+{
+ LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(h_wnd, GWL_USERDATA);
+
+
+ if (NULL != window_imp)
+ {
+ window_imp->mCallbacks->handleResumeWatchdog(window_imp);
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:StartWndProc");
+ // Has user provided their own window callback?
+ if (NULL != window_imp->mWndProc)
+ {
+ if (!window_imp->mWndProc(h_wnd, u_msg, w_param, l_param))
+ {
+ // user has handled window message
+ return 0;
+ }
+ }
+
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:PreSwitchWndProc");
+
+ // Juggle to make sure we can get negative positions for when
+ // mouse is outside window.
+ LLCoordWindow window_coord((S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param));
+
+ // This doesn't work, as LOWORD returns unsigned short.
+ //LLCoordWindow window_coord(LOWORD(l_param), HIWORD(l_param));
+ LLCoordGL gl_coord;
+
+ // pass along extended flag in mask
+ MASK mask = (l_param>>16 & KF_EXTENDED) ? MASK_EXTENDED : 0x0;
+ BOOL eat_keystroke = TRUE;
+
+ switch(u_msg)
+ {
+ RECT update_rect;
+ S32 update_width;
+ S32 update_height;
+
+ case WM_TIMER:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_TIMER");
+ window_imp->mCallbacks->handleTimerEvent(window_imp);
+ break;
+
+ case WM_DEVICECHANGE:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_DEVICECHANGE");
+ if (gDebugWindowProc)
+ {
+ llinfos << " WM_DEVICECHANGE: wParam=" << w_param
+ << "; lParam=" << l_param << llendl;
+ }
+ if (w_param == DBT_DEVNODES_CHANGED || w_param == DBT_DEVICEARRIVAL)
+ {
+ if (window_imp->mCallbacks->handleDeviceChange(window_imp))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_PAINT:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_PAINT");
+ GetUpdateRect(window_imp->mWindowHandle, &update_rect, FALSE);
+ update_width = update_rect.right - update_rect.left + 1;
+ update_height = update_rect.bottom - update_rect.top + 1;
+ window_imp->mCallbacks->handlePaint(window_imp, update_rect.left, update_rect.top,
+ update_width, update_height);
+ break;
+ case WM_PARENTNOTIFY:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_PARENTNOTIFY");
+ u_msg = u_msg;
+ break;
+
+ case WM_SETCURSOR:
+ // This message is sent whenever the cursor is moved in a window.
+ // You need to set the appropriate cursor appearance.
+
+ // Only take control of cursor over client region of window
+ // This allows Windows(tm) to handle resize cursors, etc.
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SETCURSOR");
+ if (LOWORD(l_param) == HTCLIENT)
+ {
+ SetCursor(window_imp->mCursor[ window_imp->mCurrentCursor] );
+ return 0;
+ }
+ break;
+
+ case WM_ENTERMENULOOP:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_ENTERMENULOOP");
+ window_imp->mCallbacks->handleWindowBlock(window_imp);
+ break;
+
+ case WM_EXITMENULOOP:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_EXITMENULOOP");
+ window_imp->mCallbacks->handleWindowUnblock(window_imp);
+ break;
+
+ case WM_ACTIVATEAPP:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_ACTIVATEAPP");
+ {
+ // This message should be sent whenever the app gains or loses focus.
+ BOOL activating = (BOOL) w_param;
+ BOOL minimized = window_imp->getMinimized();
+
+ if (gDebugWindowProc)
+ {
+ LL_INFOS("Window") << "WINDOWPROC ActivateApp "
+ << " activating " << S32(activating)
+ << " minimized " << S32(minimized)
+ << " fullscreen " << S32(window_imp->mFullscreen)
+ << LL_ENDL;
+ }
+
+ 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();
+ }
+ }
+
+ window_imp->mCallbacks->handleActivateApp(window_imp, activating);
+
+ break;
+ }
+
+ case WM_ACTIVATE:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_ACTIVATE");
+ {
+ // Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE
+ BOOL activating = (LOWORD(w_param) != WA_INACTIVE);
+
+ BOOL minimized = BOOL(HIWORD(w_param));
+
+ if (!activating && LLWinImm::isAvailable() && window_imp->mPreeditor)
+ {
+ window_imp->interruptLanguageTextInput();
+ }
+
+ // JC - I'm not sure why, but if we don't report that we handled the
+ // WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work
+ // properly when we run fullscreen.
+ if (gDebugWindowProc)
+ {
+ LL_INFOS("Window") << "WINDOWPROC Activate "
+ << " activating " << S32(activating)
+ << " minimized " << S32(minimized)
+ << LL_ENDL;
+ }
+
+ // Don't handle this.
+ break;
+ }
+
+ case WM_QUERYOPEN:
+ // TODO: use this to return a nice icon
+ break;
+
+ case WM_SYSCOMMAND:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main: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:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_CLOSE");
+ // Will the app allow the window to close?
+ if (window_imp->mCallbacks->handleCloseRequest(window_imp))
+ {
+ // Get the app to initiate cleanup.
+ window_imp->mCallbacks->handleQuit(window_imp);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ return 0;
+
+ case WM_DESTROY:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_DESTROY");
+ if (window_imp->shouldPostQuit())
+ {
+ PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0
+ }
+ return 0;
+
+ case WM_COMMAND:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_COMMAND");
+ if (!HIWORD(w_param)) // this message is from a menu
+ {
+ window_imp->mCallbacks->handleMenuSelect(window_imp, LOWORD(w_param));
+ }
+ break;
+
+ case WM_SYSKEYDOWN:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SYSKEYDOWN");
+ // allow system keys, such as ALT-F4 to be processed by Windows
+ eat_keystroke = FALSE;
+ case WM_KEYDOWN:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_KEYDOWN");
+ {
+ if (gDebugWindowProc)
+ {
+ LL_INFOS("Window") << "Debug WindowProc WM_KEYDOWN "
+ << " key " << S32(w_param)
+ << LL_ENDL;
+ }
+ if(gKeyboard->handleKeyDown(w_param, mask) && eat_keystroke)
+ {
+ return 0;
+ }
+ // pass on to windows if we didn't handle it
+ break;
+ }
+ case WM_SYSKEYUP:
+ eat_keystroke = FALSE;
+ case WM_KEYUP:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_KEYUP");
+ LLFastTimer t2(FTM_KEYHANDLER);
+
+ if (gDebugWindowProc)
+ {
+ LL_INFOS("Window") << "Debug WindowProc WM_KEYUP "
+ << " key " << S32(w_param)
+ << LL_ENDL;
+ }
+ if (gKeyboard->handleKeyUp(w_param, mask) && eat_keystroke)
+ {
+ return 0;
+ }
+
+ // pass on to windows
+ break;
+ }
+ case WM_IME_SETCONTEXT:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_SETCONTEXT");
+ if (gDebugWindowProc)
+ {
+ llinfos << "WM_IME_SETCONTEXT" << llendl;
+ }
+ if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+ {
+ l_param &= ~ISC_SHOWUICOMPOSITIONWINDOW;
+ // Invoke DefWinProc with the modified LPARAM.
+ }
+ break;
+
+ case WM_IME_STARTCOMPOSITION:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_STARTCOMPOSITION");
+ if (gDebugWindowProc)
+ {
+ llinfos << "WM_IME_STARTCOMPOSITION" << llendl;
+ }
+ if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+ {
+ window_imp->handleStartCompositionMessage();
+ return 0;
+ }
+ break;
+
+ case WM_IME_ENDCOMPOSITION:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_ENDCOMPOSITION");
+ if (gDebugWindowProc)
+ {
+ llinfos << "WM_IME_ENDCOMPOSITION" << llendl;
+ }
+ if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+ {
+ return 0;
+ }
+ break;
+
+ case WM_IME_COMPOSITION:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_COMPOSITION");
+ if (gDebugWindowProc)
+ {
+ llinfos << "WM_IME_COMPOSITION" << llendl;
+ }
+ if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+ {
+ window_imp->handleCompositionMessage(l_param);
+ return 0;
+ }
+ break;
+
+ case WM_IME_REQUEST:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_IME_REQUEST");
+ if (gDebugWindowProc)
+ {
+ llinfos << "WM_IME_REQUEST" << llendl;
+ }
+ if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+ {
+ LRESULT result = 0;
+ if (window_imp->handleImeRequests(w_param, l_param, &result))
+ {
+ return result;
+ }
+ }
+ break;
+
+ case WM_CHAR:
+ // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need
+ // to figure out how that works. - Doug
+ //
+ // ... 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
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_CHAR");
+ if (gDebugWindowProc)
+ {
+ LL_INFOS("Window") << "Debug WindowProc WM_CHAR "
+ << " key " << S32(w_param)
+ << LL_ENDL;
+ }
+ // 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_LBUTTONDOWN:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONDOWN");
+ LLFastTimer t2(FTM_MOUSEHANDLER);
+ if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+ {
+ window_imp->interruptLanguageTextInput();
+ }
+
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ // generate move event to update mouse coordinates
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ if (window_imp->mCallbacks->handleMouseDown(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ //RN: ignore right button double clicks for now
+ //case WM_RBUTTONDBLCLK:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONDBLCLK");
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ // generate move event to update mouse coordinates
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ if (window_imp->mCallbacks->handleDoubleClick(window_imp, gl_coord, mask) )
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONUP");
+ LLFastTimer t2(FTM_MOUSEHANDLER);
+ //if (gDebugClicks)
+ //{
+ // LL_INFOS("Window") << "WndProc left button up" << LL_ENDL;
+ //}
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ // generate move event to update mouse coordinates
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ if (window_imp->mCallbacks->handleMouseUp(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_RBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_RBUTTONDOWN");
+ LLFastTimer t2(FTM_MOUSEHANDLER);
+ if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+ {
+ window_imp->interruptLanguageTextInput();
+ }
+
+ // Because we move the cursor position in the llviewerapp, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ // generate move event to update mouse coordinates
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ if (window_imp->mCallbacks->handleRightMouseDown(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_RBUTTONUP:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_RBUTTONUP");
+ LLFastTimer t2(FTM_MOUSEHANDLER);
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ // generate move event to update mouse coordinates
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ if (window_imp->mCallbacks->handleRightMouseUp(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_MBUTTONDOWN:
+// case WM_MBUTTONDBLCLK:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MBUTTONDOWN");
+ LLFastTimer t2(FTM_MOUSEHANDLER);
+ if (LLWinImm::isAvailable() && window_imp->mPreeditor)
+ {
+ window_imp->interruptLanguageTextInput();
+ }
+
+ // Because we move the cursor position in tllviewerhe app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ // generate move event to update mouse coordinates
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ if (window_imp->mCallbacks->handleMiddleMouseDown(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_MBUTTONUP:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MBUTTONUP");
+ LLFastTimer t2(FTM_MOUSEHANDLER);
+ // Because we move the cursor position in the llviewer app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ // generate move event to update mouse coordinates
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ if (window_imp->mCallbacks->handleMiddleMouseUp(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_MOUSEWHEEL:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main: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)
+ {
+ window_imp->mCallbacks->handleScrollWheel(window_imp, -z_delta / WHEEL_DELTA);
+ z_delta = 0;
+ }
+ return 0;
+ }
+ /*
+ // TODO: add this after resolving _WIN32_WINNT issue
+ case WM_MOUSELEAVE:
+ {
+ window_imp->mCallbacks->handleMouseLeave(window_imp);
+
+ // TRACKMOUSEEVENT track_mouse_event;
+ // track_mouse_event.cbSize = sizeof( TRACKMOUSEEVENT );
+ // track_mouse_event.dwFlags = TME_LEAVE;
+ // track_mouse_event.hwndTrack = h_wnd;
+ // track_mouse_event.dwHoverTime = HOVER_DEFAULT;
+ // TrackMouseEvent( &track_mouse_event );
+ return 0;
+ }
+ */
+ // Handle mouse movement within the window
+ case WM_MOUSEMOVE:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MOUSEMOVE");
+ window_imp->convertCoords(window_coord, &gl_coord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ return 0;
+ }
+
+ case WM_SIZE:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SIZE");
+ S32 width = S32( LOWORD(l_param) );
+ S32 height = S32( HIWORD(l_param) );
+
+ if (gDebugWindowProc)
+ {
+ BOOL maximized = ( w_param == SIZE_MAXIMIZED );
+ BOOL restored = ( w_param == SIZE_RESTORED );
+ BOOL minimized = ( w_param == SIZE_MINIMIZED );
+
+ LL_INFOS("Window") << "WINDOWPROC Size "
+ << width << "x" << height
+ << " max " << S32(maximized)
+ << " min " << S32(minimized)
+ << " rest " << S32(restored)
+ << LL_ENDL;
+ }
+
+ // 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->mCallbacks->handleActivate(window_imp, TRUE);
+ }
+
+ // handle case of window being maximized from fully minimized state
+ if (w_param == SIZE_MAXIMIZED && window_imp->mLastSizeWParam != SIZE_MAXIMIZED)
+ {
+ window_imp->mCallbacks->handleActivate(window_imp, TRUE);
+ }
+
+ // Also handle the minimization case
+ if (w_param == SIZE_MINIMIZED && window_imp->mLastSizeWParam != SIZE_MINIMIZED)
+ {
+ window_imp->mCallbacks->handleActivate(window_imp, FALSE);
+ }
+
+ // Actually resize all of our views
+ if (w_param != SIZE_MINIMIZED)
+ {
+ // Ignore updates for minimizing and minimized "windows"
+ window_imp->mCallbacks->handleResize( window_imp,
+ LOWORD(l_param),
+ HIWORD(l_param) );
+ }
+
+ window_imp->mLastSizeWParam = w_param;
+
+ return 0;
+ }
+
+ case WM_SETFOCUS:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_SETFOCUS");
+ if (gDebugWindowProc)
+ {
+ LL_INFOS("Window") << "WINDOWPROC SetFocus" << LL_ENDL;
+ }
+ window_imp->mCallbacks->handleFocus(window_imp);
+ return 0;
+
+ case WM_KILLFOCUS:
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_KILLFOCUS");
+ if (gDebugWindowProc)
+ {
+ LL_INFOS("Window") << "WINDOWPROC KillFocus" << LL_ENDL;
+ }
+ window_imp->mCallbacks->handleFocusLost(window_imp);
+ return 0;
+
+ case WM_COPYDATA:
+ {
+ window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_COPYDATA");
+ // received a URL
+ PCOPYDATASTRUCT myCDS = (PCOPYDATASTRUCT) l_param;
+ window_imp->mCallbacks->handleDataCopy(window_imp, myCDS->dwData, myCDS->lpData);
+ };
+ return 0;
+
+ break;
+ }
+
+ window_imp->mCallbacks->handlePauseWatchdog(window_imp);
+ }
+
+
+ // pass unhandled messages down to Windows
+ return DefWindowProc(h_wnd, u_msg, w_param, l_param);
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to)
+{
+ S32 client_height;
+ RECT client_rect;
+ LLCoordWindow window_position;
+
+ if (!mWindowHandle ||
+ !GetClientRect(mWindowHandle, &client_rect) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to)
+{
+ S32 client_height;
+ RECT client_rect;
+
+ if (!mWindowHandle ||
+ !GetClientRect(mWindowHandle, &client_rect) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+{
+ POINT mouse_point;
+
+ mouse_point.x = from.mX;
+ mouse_point.y = from.mY;
+ BOOL result = ScreenToClient(mWindowHandle, &mouse_point);
+
+ if (result)
+ {
+ to->mX = mouse_point.x;
+ to->mY = mouse_point.y;
+ }
+
+ return result;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+{
+ POINT mouse_point;
+
+ mouse_point.x = from.mX;
+ mouse_point.y = from.mY;
+ BOOL result = ClientToScreen(mWindowHandle, &mouse_point);
+
+ if (result)
+ {
+ to->mX = mouse_point.x;
+ to->mY = mouse_point.y;
+ }
+
+ return result;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to)
+{
+ LLCoordWindow window_coord;
+
+ if (!mWindowHandle || (NULL == to))
+ {
+ return FALSE;
+ }
+
+ convertCoords(from, &window_coord);
+ convertCoords(window_coord, to);
+ return TRUE;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen *to)
+{
+ LLCoordWindow window_coord;
+
+ if (!mWindowHandle || (NULL == to))
+ {
+ return FALSE;
+ }
+
+ convertCoords(from, &window_coord);
+ convertCoords(window_coord, to);
+ return TRUE;
+}
+
+
+BOOL LLWindowWin32::isClipboardTextAvailable()
+{
+ return IsClipboardFormatAvailable(CF_UNICODETEXT);
+}
+
+
+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::removeCRLF(dst);
+ GlobalUnlock(h_data);
+ success = TRUE;
+ }
+ }
+ CloseClipboard();
+ }
+ }
+
+ return success;
+}
+
+
+BOOL LLWindowWin32::copyTextToClipboard(const LLWString& wstr)
+{
+ BOOL success = FALSE;
+
+ if (OpenClipboard(mWindowHandle))
+ {
+ EmptyClipboard();
+
+ // Provide a copy of the data in Unicode format.
+ LLWString sanitized_string(wstr);
+ 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 )
+{
+ 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)
+{
+ FLASHWINFO flash_info;
+
+ flash_info.cbSize = sizeof(FLASHWINFO);
+ flash_info.hwnd = mWindowHandle;
+ flash_info.dwFlags = FLASHW_TRAY;
+ flash_info.uCount = UINT(seconds / ICON_FLASH_TIME);
+ flash_info.dwTimeout = DWORD(1000.f * ICON_FLASH_TIME); // milliseconds
+ FlashWindowEx(&flash_info);
+}
+
+F32 LLWindowWin32::getGamma()
+{
+ return mCurrentGamma;
+}
+
+BOOL LLWindowWin32::restoreGamma()
+{
+ return SetDeviceGammaRamp(mhDC, mPrevGammaRamp);
+}
+
+BOOL LLWindowWin32::setGamma(const F32 gamma)
+{
+ mCurrentGamma = gamma;
+
+ 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 * 256 + i ] =
+ mCurrentGammaRamp [ 1 * 256 + i ] =
+ mCurrentGammaRamp [ 2 * 256 + i ] = ( WORD )value;
+ };
+
+ return SetDeviceGammaRamp ( mhDC, mCurrentGammaRamp );
+}
+
+void LLWindowWin32::setFSAASamples(const U32 fsaa_samples)
+{
+ mFSAASamples = fsaa_samples;
+}
+
+U32 LLWindowWin32::getFSAASamples()
+{
+ return mFSAASamples;
+}
+
+LLWindow::LLWindowResolution* LLWindowWin32::getSupportedResolutions(S32 &num_resolutions)
+{
+ if (!mSupportedResolutions)
+ {
+ mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
+ DEVMODE dev_mode;
+
+ mNumSupportedResolutions = 0;
+ for (S32 mode_num = 0; mNumSupportedResolutions < MAX_NUM_RESOLUTIONS; mode_num++)
+ {
+ if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
+ {
+ break;
+ }
+
+ if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL &&
+ dev_mode.dmPelsWidth >= 800 &&
+ dev_mode.dmPelsHeight >= 600)
+ {
+ BOOL resolution_exists = FALSE;
+ for(S32 i = 0; i < mNumSupportedResolutions; i++)
+ {
+ if (mSupportedResolutions[i].mWidth == dev_mode.dmPelsWidth &&
+ mSupportedResolutions[i].mHeight == dev_mode.dmPelsHeight)
+ {
+ resolution_exists = TRUE;
+ }
+ }
+ if (!resolution_exists)
+ {
+ mSupportedResolutions[mNumSupportedResolutions].mWidth = dev_mode.dmPelsWidth;
+ mSupportedResolutions[mNumSupportedResolutions].mHeight = dev_mode.dmPelsHeight;
+ mNumSupportedResolutions++;
+ }
+ }
+ }
+ }
+
+ num_resolutions = mNumSupportedResolutions;
+ return mSupportedResolutions;
+}
+
+
+F32 LLWindowWin32::getNativeAspectRatio()
+{
+ if (mOverrideAspectRatio > 0.f)
+ {
+ return mOverrideAspectRatio;
+ }
+ else if (mNativeAspectRatio > 0.f)
+ {
+ // we grabbed this value at startup, based on the user's desktop settings
+ return mNativeAspectRatio;
+ }
+ // RN: this hack presumes that the largest supported resolution is monitor-limited
+ // and that pixels in that mode are square, therefore defining the native aspect ratio
+ // of the monitor...this seems to work to a close approximation for most CRTs/LCDs
+ S32 num_resolutions;
+ LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
+
+ return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
+}
+
+F32 LLWindowWin32::getPixelAspectRatio()
+{
+ F32 pixel_aspect = 1.f;
+ if (getFullscreen())
+ {
+ LLCoordScreen screen_size;
+ getSize(&screen_size);
+ pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
+ }
+
+ return pixel_aspect;
+}
+
+// Change display resolution. Returns true if successful.
+// protected
+BOOL LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh)
+{
+ DEVMODE dev_mode;
+ dev_mode.dmSize = sizeof(dev_mode);
+ BOOL success = FALSE;
+
+ // Don't change anything if we don't have to
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
+ {
+ if (dev_mode.dmPelsWidth == width &&
+ dev_mode.dmPelsHeight == height &&
+ dev_mode.dmBitsPerPel == bits &&
+ dev_mode.dmDisplayFrequency == refresh )
+ {
+ // ...display mode identical, do nothing
+ return TRUE;
+ }
+ }
+
+ memset(&dev_mode, 0, sizeof(dev_mode));
+ dev_mode.dmSize = sizeof(dev_mode);
+ dev_mode.dmPelsWidth = width;
+ dev_mode.dmPelsHeight = height;
+ dev_mode.dmBitsPerPel = bits;
+ dev_mode.dmDisplayFrequency = refresh;
+ dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
+
+ // CDS_FULLSCREEN indicates that this is a temporary change to the device mode.
+ LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN);
+
+ success = (DISP_CHANGE_SUCCESSFUL == cds_result);
+
+ if (!success)
+ {
+ 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()
+{
+ SwapBuffers(mhDC);
+}
+
+
+//
+// LLSplashScreenImp
+//
+LLSplashScreenWin32::LLSplashScreenWin32()
+: mWindow(NULL)
+{
+}
+
+LLSplashScreenWin32::~LLSplashScreenWin32()
+{
+}
+
+void LLSplashScreenWin32::showImpl()
+{
+ // This appears to work. ???
+ HINSTANCE hinst = GetModuleHandle(NULL);
+
+ mWindow = CreateDialog(hinst,
+ TEXT("SPLASHSCREEN"),
+ NULL, // no parent
+ (DLGPROC) LLSplashScreenWin32::windowProc);
+ ShowWindow(mWindow, SW_SHOW);
+}
+
+
+void LLSplashScreenWin32::updateImpl(const std::string& mesg)
+{
+ if (!mWindow) return;
+
+ WCHAR w_mesg[1024];
+ mbstowcs(w_mesg, mesg.c_str(), 1024);
+
+ SendDlgItemMessage(mWindow,
+ 666, // HACK: text id
+ WM_SETTEXT,
+ FALSE,
+ (LPARAM)w_mesg);
+}
+
+
+void LLSplashScreenWin32::hideImpl()
+{
+ if (mWindow)
+ {
+ DestroyWindow(mWindow);
+ mWindow = NULL;
+ }
+}
+
+
+// static
+LRESULT CALLBACK LLSplashScreenWin32::windowProc(HWND h_wnd, UINT u_msg,
+ WPARAM w_param, LPARAM l_param)
+{
+ // Just give it to windows
+ return DefWindowProc(h_wnd, u_msg, w_param, l_param);
+}
+
+//
+// Helper Funcs
+//
+
+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;
+ }
+
+ // HACK! Doesn't properly handle wide strings!
+ int retval_win = MessageBoxA(NULL, text.c_str(), 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 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 ) };
+ sei.fMask = SEE_MASK_FLAG_DDEWAIT;
+ sei.nShow = SW_SHOWNORMAL;
+ sei.lpVerb = L"open";
+ sei.lpFile = url_utf16.c_str();
+ ShellExecuteEx( &sei );
+
+ //// TODO: LEAVING OLD CODE HERE SO I DON'T BONE OTHER MERGES
+ //// DELETE THIS ONCE THE MERGES ARE DONE
+
+ // Figure out the user's default web browser
+ // HKEY_CLASSES_ROOT\http\shell\open\command
+ /*
+ std::string reg_path_str = gURLProtocolWhitelistHandler[i] + "\\shell\\open\\command";
+ WCHAR reg_path_wstr[256];
+ mbstowcs( reg_path_wstr, reg_path_str.c_str(), LL_ARRAY_SIZE(reg_path_wstr) );
+
+ HKEY key;
+ WCHAR browser_open_wstr[1024];
+ DWORD buffer_length = 1024;
+ RegOpenKeyEx(HKEY_CLASSES_ROOT, reg_path_wstr, 0, KEY_QUERY_VALUE, &key);
+ RegQueryValueEx(key, NULL, NULL, NULL, (LPBYTE)browser_open_wstr, &buffer_length);
+ RegCloseKey(key);
+
+ // Convert to STL string
+ LLWString browser_open_wstring = utf16str_to_wstring(browser_open_wstr);
+
+ if (browser_open_wstring.length() < 2)
+ {
+ LL_WARNS("Window") << "Invalid browser executable in registry " << browser_open_wstring << LL_ENDL;
+ return;
+ }
+
+ // Extract the process that's supposed to be launched
+ LLWString browser_executable;
+ if (browser_open_wstring[0] == '"')
+ {
+ // executable is quoted, find the matching quote
+ size_t quote_pos = browser_open_wstring.find('"', 1);
+ // copy out the string including both quotes
+ browser_executable = browser_open_wstring.substr(0, quote_pos+1);
+ }
+ else
+ {
+ // executable not quoted, find a space
+ size_t space_pos = browser_open_wstring.find(' ', 1);
+ browser_executable = browser_open_wstring.substr(0, space_pos);
+ }
+
+ LL_DEBUGS("Window") << "Browser reg key: " << wstring_to_utf8str(browser_open_wstring) << LL_ENDL;
+ LL_INFOS("Window") << "Browser executable: " << wstring_to_utf8str(browser_executable) << LL_ENDL;
+
+ // Convert URL to wide string for Windows API
+ // Assume URL is UTF8, as can come from scripts
+ LLWString url_wstring = utf8str_to_wstring(escaped_url);
+ llutf16string url_utf16 = wstring_to_utf16str(url_wstring);
+
+ // Convert executable and path to wide string for Windows API
+ llutf16string browser_exec_utf16 = wstring_to_utf16str(browser_executable);
+
+ // ShellExecute returns HINSTANCE for backwards compatiblity.
+ // MS docs say to cast to int and compare to 32.
+ HWND our_window = NULL;
+ LPCWSTR directory_wstr = NULL;
+ int retval = (int) ShellExecute(our_window, // Flawfinder: ignore
+ L"open",
+ browser_exec_utf16.c_str(),
+ url_utf16.c_str(),
+ directory_wstr,
+ SW_SHOWNORMAL);
+ if (retval > 32)
+ {
+ LL_DEBUGS("Window") << "load_url success with " << retval << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS("Window") << "load_url failure with " << retval << LL_ENDL;
+ }
+ */
+}
+
+
+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()
+{
+ BringWindowToTop(mWindowHandle);
+}
+
+// set (OS) window focus back to the client
+void LLWindowWin32::focusClient()
+{
+ 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 )
+ {
+ // 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
+ {
+ // 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.set( win_pos.mX, win_pos.mY );
+ }
+
+ 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()
+{
+ if (mPreeditor)
+ {
+ if (LLWinImm::isAvailable())
+ {
+ HIMC himc = LLWinImm::getContext(mWindowHandle);
+ LLWinImm::notifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
+ LLWinImm::releaseContext(mWindowHandle, himc);
+ }
+
+ // Win32 document says there will be no composition string
+ // after NI_COMPOSITIONSTR returns. The following call to
+ // resetPreedit should be a NOP unless IME goes mad...
+ mPreeditor->resetPreedit();
+ }
+}
+
+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)
+{
+ 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)
+ {
+ 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 )
+{
+ 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(U32 request, U32 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 = FALSE;
+ 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;
+ if (preedit_length)
+ {
+ // 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
+std::vector<std::string> LLWindowWin32::getDynamicFallbackFontList()
+{
+ // Fonts previously in getFontListSans() have moved to fonts.xml.
+ return std::vector<std::string>();
+}
+
+
+#endif // LL_WINDOWS
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
index e4e9179db7..6aca31b63e 100644
--- a/indra/llwindow/llwindowwin32.h
+++ b/indra/llwindow/llwindowwin32.h
@@ -39,6 +39,8 @@
#include <windows.h>
#include "llwindow.h"
+#include "llwindowcallbacks.h"
+#include "lldragdropwin32.h"
// Hack for async host by name
#define LL_WM_HOST_RESOLVED (WM_APP + 1)
@@ -114,6 +116,8 @@ public:
/*virtual*/ void interruptLanguageTextInput();
/*virtual*/ void spawnWebBrowser(const std::string& escaped_url);
+ LLWindowCallbacks::DragNDropResult completeDragNDropRequest( const LLCoordGL gl_coord, const MASK mask, LLWindowCallbacks::DragNDropAction action, const std::string url );
+
static std::vector<std::string> getDynamicFallbackFontList();
protected:
@@ -205,6 +209,8 @@ protected:
LLPreeditor *mPreeditor;
+ LLDragDropWin32* mDragDrop;
+
friend class LLWindowManager;
};
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 5373556c20..cd116ab0d2 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -7,6 +7,7 @@ include(Boost)
include(BuildVersion)
include(DBusGlib)
include(DirectX)
+include(DragDrop)
include(ELFIO)
include(FMOD)
include(OPENAL)
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index e607c3ad15..7b486ed814 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -5516,6 +5516,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>PrimMediaDragNDrop</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable drag and drop of URLs onto prim faces</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
<key>PrimMediaMaxRetries</key>
<map>
<key>Comment</key>
@@ -10754,6 +10765,17 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>SLURLDragNDrop</key>
+ <map>
+ <key>Comment</key>
+ <string>Enable drag and drop of SLURLs onto the viewer</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>Boolean</string>
+ <key>Value</key>
+ <integer>1</integer>
+ </map>
<key>soundsbeacon</key>
<map>
<key>Comment</key>
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
index 87d101b00f..af9e791223 100644
--- a/indra/newview/llpanellogin.cpp
+++ b/indra/newview/llpanellogin.cpp
@@ -689,6 +689,23 @@ void LLPanelLogin::refreshLocation( bool force_visible )
}
// static
+void LLPanelLogin::updateLocationUI()
+{
+ if (!sInstance) return;
+
+ std::string sim_string = LLURLSimString::sInstance.mSimString;
+ if (!sim_string.empty())
+ {
+ // Replace "<Type region name>" with this region name
+ LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo");
+ combo->remove(2);
+ combo->add( sim_string );
+ combo->setTextEntry(sim_string);
+ combo->setCurrentByIndex( 2 );
+ }
+}
+
+// static
void LLPanelLogin::closePanel()
{
if (sInstance)
diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h
index 97350ce5c7..1fdc3a9361 100644
--- a/indra/newview/llpanellogin.h
+++ b/indra/newview/llpanellogin.h
@@ -71,6 +71,7 @@ public:
static void addServer(const std::string& server, S32 domain_name);
static void refreshLocation( bool force_visible );
+ static void updateLocationUI();
static void getFields(std::string *firstname, std::string *lastname,
std::string *password);
@@ -102,7 +103,7 @@ private:
static void onPassKey(LLLineEditor* caller, void* user_data);
static void onSelectServer(LLUICtrl*, void*);
static void onServerComboLostFocus(LLFocusableElement*);
-
+
private:
LLPointer<LLUIImage> mLogoImage;
boost::scoped_ptr<LLPanelLoginListener> mListener;
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index c1817496b1..52a4c82297 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -51,6 +51,7 @@
#include "llviewquery.h"
#include "llxmltree.h"
+#include "llslurl.h"
//#include "llviewercamera.h"
#include "llrender.h"
@@ -80,6 +81,9 @@
#include "timing.h"
#include "llviewermenu.h"
#include "lltooltip.h"
+#include "llmediaentry.h"
+#include "llurldispatcher.h"
+#include "llurlsimstring.h"
// newview includes
#include "llagent.h"
@@ -798,6 +802,136 @@ BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MAS
// Always handled as far as the OS is concerned.
return TRUE;
}
+
+LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data)
+{
+ LLWindowCallbacks::DragNDropResult result = LLWindowCallbacks::DND_NONE;
+
+ const bool prim_media_dnd_enabled = gSavedSettings.getBOOL("PrimMediaDragNDrop");
+ const bool slurl_dnd_enabled = gSavedSettings.getBOOL("SLURLDragNDrop");
+
+ if ( prim_media_dnd_enabled || slurl_dnd_enabled )
+ {
+ switch(action)
+ {
+ // Much of the handling for these two cases is the same.
+ case LLWindowCallbacks::DNDA_TRACK:
+ case LLWindowCallbacks::DNDA_DROPPED:
+ case LLWindowCallbacks::DNDA_START_TRACKING:
+ {
+ bool drop = (LLWindowCallbacks::DNDA_DROPPED == action);
+
+ if (slurl_dnd_enabled)
+ {
+ // special case SLURLs
+ if ( LLSLURL::isSLURL( data ) )
+ {
+ if (drop)
+ {
+ LLURLSimString::setStringRaw( LLSLURL::stripProtocol( data ) );
+ LLPanelLogin::refreshLocation( true );
+ LLPanelLogin::updateLocationUI();
+ }
+ return LLWindowCallbacks::DND_MOVE;
+ };
+ }
+
+ if (prim_media_dnd_enabled)
+ {
+ LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY, TRUE /*BOOL pick_transparent*/ );
+
+ LLUUID object_id = pick_info.getObjectID();
+ S32 object_face = pick_info.mObjectFace;
+ std::string url = data;
+
+ lldebugs << "Object: picked at " << pos.mX << ", " << pos.mY << " - face = " << object_face << " - URL = " << url << llendl;
+
+ LLVOVolume *obj = dynamic_cast<LLVOVolume*>(static_cast<LLViewerObject*>(pick_info.getObject()));
+
+ if (obj && obj->permModify() && !obj->getRegion()->getCapability("ObjectMedia").empty())
+ {
+ LLTextureEntry *te = obj->getTE(object_face);
+ if (te)
+ {
+ if (drop)
+ {
+ if (! te->hasMedia())
+ {
+ // Create new media entry
+ LLSD media_data;
+ // XXX Should we really do Home URL too?
+ media_data[LLMediaEntry::HOME_URL_KEY] = url;
+ media_data[LLMediaEntry::CURRENT_URL_KEY] = url;
+ media_data[LLMediaEntry::AUTO_PLAY_KEY] = true;
+ obj->syncMediaData(object_face, media_data, true, true);
+ // XXX This shouldn't be necessary, should it ?!?
+ if (obj->getMediaImpl(object_face))
+ obj->getMediaImpl(object_face)->navigateReload();
+ obj->sendMediaDataUpdate();
+
+ result = LLWindowCallbacks::DND_COPY;
+ }
+ else {
+ // Check the whitelist
+ if (te->getMediaData()->checkCandidateUrl(url))
+ {
+ // just navigate to the URL
+ if (obj->getMediaImpl(object_face))
+ {
+ obj->getMediaImpl(object_face)->navigateTo(url);
+ }
+ else {
+ // This is very strange. Navigation should
+ // happen via the Impl, but we don't have one.
+ // This sends it to the server, which /should/
+ // trigger us getting it. Hopefully.
+ LLSD media_data;
+ media_data[LLMediaEntry::CURRENT_URL_KEY] = url;
+ obj->syncMediaData(object_face, media_data, true, true);
+ obj->sendMediaDataUpdate();
+ }
+ result = LLWindowCallbacks::DND_LINK;
+ }
+ }
+ LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
+ mDragHoveredObject = NULL;
+
+ }
+ else {
+ // Check the whitelist, if there's media (otherwise just show it)
+ if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url))
+ {
+ if ( obj != mDragHoveredObject)
+ {
+ // Highlight the dragged object
+ LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
+ mDragHoveredObject = obj;
+ LLSelectMgr::getInstance()->highlightObjectOnly(mDragHoveredObject);
+ }
+ result = (! te->hasMedia()) ? LLWindowCallbacks::DND_COPY : LLWindowCallbacks::DND_LINK;
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case LLWindowCallbacks::DNDA_STOP_TRACKING:
+ // The cleanup case below will make sure things are unhilighted if necessary.
+ break;
+ }
+
+ if (prim_media_dnd_enabled &&
+ result == LLWindowCallbacks::DND_NONE && !mDragHoveredObject.isNull())
+ {
+ LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject);
+ mDragHoveredObject = NULL;
+ }
+ }
+
+ return result;
+}
BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
{
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
index b488276a18..782ef96841 100644
--- a/indra/newview/llviewerwindow.h
+++ b/indra/newview/llviewerwindow.h
@@ -169,7 +169,8 @@ public:
/*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*/ void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ LLWindowCallbacks::DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data);
+ void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask);
/*virtual*/ void handleMouseLeave(LLWindow *window);
/*virtual*/ void handleResize(LLWindow *window, S32 x, S32 y);
/*virtual*/ void handleFocus(LLWindow *window);
@@ -475,6 +476,10 @@ protected:
static std::string sSnapshotDir;
static std::string sMovieBaseName;
+
+private:
+ // Object temporarily hovered over while dragging
+ LLPointer<LLViewerObject> mDragHoveredObject;
};
void toggle_flying(void*);