diff options
Diffstat (limited to 'indra/llwindow/llwindowwin32.cpp')
-rw-r--r-- | indra/llwindow/llwindowwin32.cpp | 7386 |
1 files changed, 3693 insertions, 3693 deletions
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index ed39786b37..57a4921d92 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -1,3693 +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 "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
+/** + * @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 |