    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <com/sun/star/lang/XMultiServiceFactory.hpp>
    #include <com/sun/star/container/XIndexAccess.hpp>
    #include <com/sun/star/beans/XPropertySet.hpp>
    #include <com/sun/star/awt/Rectangle.hpp>
    #include <unotools/misccfg.hxx>

    #include <officecfg/Office/Common.hxx>

    #include <memory>
    #include <string.h>
    #include <limits.h>

    #include <svsys.h>

    #include <comphelper/windowserrorstring.hxx>

    #include <rtl/string.h>
    #include <rtl/ustring.h>
    #include <sal/log.hxx>

    #include <osl/module.h>

    #include <tools/debug.hxx>
    #include <o3tl/enumarray.hxx>
    #include <o3tl/char16_t2wchar_t.hxx>

    #include <vcl/sysdata.hxx>
    #include <vcl/timer.hxx>
    #include <vcl/settings.hxx>
    #include <vcl/keycodes.hxx>
    #include <vcl/window.hxx>
    #include <vcl/wrkwin.hxx>
    #include <vcl/svapp.hxx>

    #include <win/wincomp.hxx>
    #include <win/salids.hrc>
    #include <win/saldata.hxx>
    #include <win/salinst.h>
    #include <win/salbmp.h>
    #include <win/salgdi.h>
    #include <win/salsys.h>
    #include <win/salframe.h>
    #include <win/salvd.h>
    #include <win/salmenu.h>
    #include <win/salobj.h>
    #include <win/saltimer.h>

    #include <helpwin.hxx>
    #include <window.h>
    #include <sallayout.hxx>

    #define COMPILE_MULTIMON_STUBS
    #pragma warning(push)
    #pragma warning(disable:4996) // 'GetVersionExA': was declared deprecated
    #include <multimon.h>
    #pragma warning(pop)
    #include <vector>

    #include <com/sun/star/uno/Exception.hpp>

    #include <oleacc.h>
    #include <com/sun/star/accessibility/XMSAAService.hpp>
    #ifndef WM_GETOBJECT // TESTME does this ever happen ?
    #  define WM_GETOBJECT  0x003D
    #endif

    #include <time.h>

    #if !defined WIN32_LEAN_AND_MEAN
    # define WIN32_LEAN_AND_MEAN
    #endif
    #include <windows.h>
    #include <shobjidl.h>
    #include <propkey.h>
    #include <propvarutil.h>
    #include <shellapi.h>

    using namespace ::com::sun::star;
    using namespace ::com::sun::star::uno;
    using namespace ::com::sun::star::lang;
    using namespace ::com::sun::star::container;
    using namespace ::com::sun::star::beans;

    #ifndef SPI_GETWHEELSCROLLCHARS
    # define SPI_GETWHEELSCROLLCHARS   0x006C
    #endif
    #ifndef SPI_SETWHEELSCROLLCHARS
    # define SPI_SETWHEELSCROLLCHARS   0x006D
    #endif
    #ifndef WM_MOUSEHWHEEL
    # define WM_MOUSEHWHEEL 0x020E
    #endif
    #ifndef IDC_PEN
    # define IDC_PEN MAKEINTRESOURCE(32631)
    #endif

    const unsigned int WM_USER_SYSTEM_WINDOW_ACTIVATED = RegisterWindowMessageW(L"SYSTEM_WINDOW_ACTIVATED");

    bool WinSalFrame::mbInReparent = false;

    // Macros for support of WM_UNICHAR & Keyman 6.0
    //#define Uni_UTF32ToSurrogate1(ch)   (((unsigned long) (ch) - 0x10000) / 0x400 + 0xD800)
    #define Uni_UTF32ToSurrogate2(ch)   ((static_cast<unsigned long>(ch) - 0x10000) % 0x400 + 0xDC00)
    #define Uni_SupplementaryPlanesStart    0x10000

    static void UpdateFrameGeometry( HWND hWnd, WinSalFrame* pFrame );
    static void SetMaximizedFrameGeometry( HWND hWnd, WinSalFrame* pFrame, RECT* pParentRect = nullptr );

    static void ImplSaveFrameState( WinSalFrame* pFrame )
    {
        // save position, size and state for GetWindowState()
        if ( !pFrame->mbFullScreen )
        {
            bool bVisible = (GetWindowStyle( pFrame->mhWnd ) & WS_VISIBLE) != 0;
            if ( IsIconic( pFrame->mhWnd ) )
            {
                pFrame->maState.mnState |= WindowStateState::Minimized;
                if ( bVisible )
                    pFrame->mnShowState = SW_SHOWMAXIMIZED;
            }
            else if ( IsZoomed( pFrame->mhWnd ) )
            {
                pFrame->maState.mnState &= ~WindowStateState::Minimized;
                pFrame->maState.mnState |= WindowStateState::Maximized;
                if ( bVisible )
                    pFrame->mnShowState = SW_SHOWMAXIMIZED;
                pFrame->mbRestoreMaximize = true;

                WINDOWPLACEMENT aPlacement;
                aPlacement.length = sizeof(aPlacement);
                if( GetWindowPlacement( pFrame->mhWnd, &aPlacement ) )
                {
                    RECT aRect = aPlacement.rcNormalPosition;
                    RECT aRect2 = aRect;
                    AdjustWindowRectEx( &aRect2, GetWindowStyle( pFrame->mhWnd ),
                                        FALSE,  GetWindowExStyle( pFrame->mhWnd ) );
                    long nTopDeco = abs( aRect.top - aRect2.top );
                    long nLeftDeco = abs( aRect.left - aRect2.left );
                    long nBottomDeco = abs( aRect.bottom - aRect2.bottom );
                    long nRightDeco = abs( aRect.right - aRect2.right );

                    pFrame->maState.mnX      = aRect.left + nLeftDeco;
                    pFrame->maState.mnY      = aRect.top + nTopDeco;
                    pFrame->maState.mnWidth  = aRect.right - aRect.left - nLeftDeco - nRightDeco;
                    pFrame->maState.mnHeight = aRect.bottom - aRect.top - nTopDeco - nBottomDeco;
                }
            }
            else
            {
                RECT aRect;
                GetWindowRect( pFrame->mhWnd, &aRect );

                // to be consistent with Unix, the frame state is without(!) decoration
                RECT aRect2 = aRect;
                AdjustWindowRectEx( &aRect2, GetWindowStyle( pFrame->mhWnd ),
                                FALSE,     GetWindowExStyle( pFrame->mhWnd ) );
                long nTopDeco = abs( aRect.top - aRect2.top );
                long nLeftDeco = abs( aRect.left - aRect2.left );
                long nBottomDeco = abs( aRect.bottom - aRect2.bottom );
                long nRightDeco = abs( aRect.right - aRect2.right );

                pFrame->maState.mnState &= ~WindowStateState(WindowStateState::Minimized | WindowStateState::Maximized);
                // subtract decoration
                pFrame->maState.mnX      = aRect.left+nLeftDeco;
                pFrame->maState.mnY      = aRect.top+nTopDeco;
                pFrame->maState.mnWidth  = aRect.right-aRect.left-nLeftDeco-nRightDeco;
                pFrame->maState.mnHeight = aRect.bottom-aRect.top-nTopDeco-nBottomDeco;
                if ( bVisible )
                    pFrame->mnShowState = SW_SHOWNORMAL;
                pFrame->mbRestoreMaximize = false;
            }
        }
    }

    // if pParentRect is set, the workarea of the monitor that contains pParentRect is returned
    void ImplSalGetWorkArea( HWND hWnd, RECT *pRect, const RECT *pParentRect )
    {
        // check if we or our parent is fullscreen, then the taskbar should be ignored
        bool bIgnoreTaskbar = false;
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if( pFrame )
        {
            vcl::Window *pWin = pFrame->GetWindow();
            while( pWin )
            {
                WorkWindow *pWorkWin = (pWin->GetType() == WindowType::WORKWINDOW) ? static_cast<WorkWindow *>(pWin) : nullptr;
                if( pWorkWin && pWorkWin->ImplGetWindowImpl()->mbReallyVisible && pWorkWin->IsFullScreenMode() )
                {
                    bIgnoreTaskbar = true;
                    break;
                }
                else
                    pWin = pWin->ImplGetWindowImpl()->mpParent;
            }
        }

        // calculates the work area taking multiple monitors into account
        static int nMonitors = GetSystemMetrics( SM_CMONITORS );
        if( nMonitors == 1 )
        {
            if( bIgnoreTaskbar )
            {
                pRect->left = pRect->top = 0;
                pRect->right   = GetSystemMetrics( SM_CXSCREEN );
                pRect->bottom  = GetSystemMetrics( SM_CYSCREEN );
            }
            else
                SystemParametersInfoW( SPI_GETWORKAREA, 0, pRect, 0 );
        }
        else
        {
            if( pParentRect != nullptr )
            {
                // return the size of the monitor where pParentRect lives
                HMONITOR hMonitor;
                MONITORINFO mi;

                // get the nearest monitor to the passed rect.
                hMonitor = MonitorFromRect(pParentRect, MONITOR_DEFAULTTONEAREST);

                // get the work area or entire monitor rect.
                mi.cbSize = sizeof(mi);
                GetMonitorInfo(hMonitor, &mi);
                if( !bIgnoreTaskbar )
                    *pRect = mi.rcWork;
                else
                    *pRect = mi.rcMonitor;
            }
            else
            {
                // return the union of all monitors
                pRect->left = GetSystemMetrics( SM_XVIRTUALSCREEN );
                pRect->top = GetSystemMetrics( SM_YVIRTUALSCREEN );
                pRect->right = pRect->left + GetSystemMetrics( SM_CXVIRTUALSCREEN );
                pRect->bottom = pRect->top + GetSystemMetrics( SM_CYVIRTUALSCREEN );

                // virtualscreen does not take taskbar into account, so use the corresponding
                // diffs between screen and workarea from the default screen
                // however, this is still not perfect: the taskbar might not be on the primary screen
                if( !bIgnoreTaskbar )
                {
                    RECT wRect, scrRect;
                    SystemParametersInfoW( SPI_GETWORKAREA, 0, &wRect, 0 );
                    scrRect.left = 0;
                    scrRect.top = 0;
                    scrRect.right = GetSystemMetrics( SM_CXSCREEN );
                    scrRect.bottom = GetSystemMetrics( SM_CYSCREEN );

                    pRect->left += wRect.left;
                    pRect->top += wRect.top;
                    pRect->right -= scrRect.right - wRect.right;
                    pRect->bottom -= scrRect.bottom - wRect.bottom;
                }
            }
        }
    }

    SalFrame* ImplSalCreateFrame( WinSalInstance* pInst,
                                  HWND hWndParent, SalFrameStyleFlags nSalFrameStyle )
    {
        WinSalFrame*   pFrame = new WinSalFrame;
        HWND        hWnd;
        DWORD       nSysStyle = 0;
        DWORD       nExSysStyle = 0;
        bool        bSubFrame = false;

        static const char* pEnvSynchronize = getenv("SAL_SYNCHRONIZE");
        if ( pEnvSynchronize )   // no buffering of drawing commands
            GdiSetBatchLimit( 1 );

        static const char* pEnvTransparentFloats = getenv("SAL_TRANSPARENT_FLOATS" );

        // determine creation data
        if ( nSalFrameStyle & (SalFrameStyleFlags::PLUG | SalFrameStyleFlags::SYSTEMCHILD) )
        {
            nSysStyle |= WS_CHILD;
            if( nSalFrameStyle & SalFrameStyleFlags::SYSTEMCHILD )
                nSysStyle |= WS_CLIPSIBLINGS;
        }
        else
        {
            // #i87402# commenting out WS_CLIPCHILDREN
            // this breaks SalFrameStyleFlags::SYSTEMCHILD handling, which is not
            // used currently. Probably SalFrameStyleFlags::SYSTEMCHILD should be
            // removed again.

            // nSysStyle  |= WS_CLIPCHILDREN;
            if ( hWndParent )
            {
                nSysStyle |= WS_POPUP;
                bSubFrame = true;
                pFrame->mbNoIcon = true;
            }
            else
            {
                // Only with WS_OVRLAPPED we get a useful default position/size
                if ( (nSalFrameStyle & (SalFrameStyleFlags::SIZEABLE | SalFrameStyleFlags::MOVEABLE)) ==
                     (SalFrameStyleFlags::SIZEABLE | SalFrameStyleFlags::MOVEABLE) )
                    nSysStyle |= WS_OVERLAPPED;
                else
                {
                    nSysStyle |= WS_POPUP;
                    if ( !(nSalFrameStyle & SalFrameStyleFlags::MOVEABLE) )
                        nExSysStyle |= WS_EX_TOOLWINDOW;    // avoid taskbar appearance, for eg splash screen
                }
            }

            if ( nSalFrameStyle & SalFrameStyleFlags::MOVEABLE )
            {
                pFrame->mbCaption = true;
                nSysStyle |= WS_SYSMENU | WS_CAPTION;
                if ( !hWndParent )
                    nSysStyle |= WS_SYSMENU | WS_MINIMIZEBOX;
                else
                    nExSysStyle |= WS_EX_DLGMODALFRAME;

                if ( nSalFrameStyle & SalFrameStyleFlags::SIZEABLE )
                {
                    pFrame->mbSizeBorder = true;
                    nSysStyle |= WS_THICKFRAME;
                    if ( !hWndParent )
                        nSysStyle |= WS_MAXIMIZEBOX;
                }
                else
                    pFrame->mbFixBorder = true;

                if ( nSalFrameStyle & SalFrameStyleFlags::DEFAULT )
                    nExSysStyle |= WS_EX_APPWINDOW;
            }
            if( nSalFrameStyle & SalFrameStyleFlags::TOOLWINDOW
                // #100656# toolwindows lead to bad alt-tab behaviour, if they have the focus
                // you must press it twice to leave the application
                // so toolwindows are only used for non sizeable windows
                // which are typically small, so a small caption makes sense

                // #103578# looked too bad - above changes reverted
                /* && !(nSalFrameStyle & SalFrameStyleFlags::SIZEABLE) */ )
            {
                pFrame->mbNoIcon = true;
                nExSysStyle |= WS_EX_TOOLWINDOW;
                if ( pEnvTransparentFloats /*&& !(nSalFrameStyle & SalFrameStyleFlags::MOVEABLE) */)
                    nExSysStyle |= WS_EX_LAYERED;
            }
        }
        if ( nSalFrameStyle & SalFrameStyleFlags::FLOAT )
        {
            nExSysStyle |= WS_EX_TOOLWINDOW;
            pFrame->mbFloatWin = true;

            if (pEnvTransparentFloats)
                nExSysStyle |= WS_EX_LAYERED;

        }
        if (nSalFrameStyle & SalFrameStyleFlags::TOOLTIP)
            nExSysStyle |= WS_EX_TOPMOST;

        // init frame data
        pFrame->mnStyle = nSalFrameStyle;

        // determine show style
        pFrame->mnShowState = SW_SHOWNORMAL;
        if ( (nSysStyle & (WS_POPUP | WS_MAXIMIZEBOX | WS_THICKFRAME)) == (WS_MAXIMIZEBOX | WS_THICKFRAME) )
        {
            if ( GetSystemMetrics( SM_CXSCREEN ) <= 1024 )
                pFrame->mnShowState = SW_SHOWMAXIMIZED;
            else
            {
                if ( nSalFrameStyle & SalFrameStyleFlags::DEFAULT )
                {
                    SalData* pSalData = GetSalData();
                    pFrame->mnShowState = pSalData->mnCmdShow;
                    if ( (pFrame->mnShowState != SW_SHOWMINIMIZED) &&
                         (pFrame->mnShowState != SW_MINIMIZE) &&
                         (pFrame->mnShowState != SW_SHOWMINNOACTIVE) )
                    {
                        if ( (pFrame->mnShowState == SW_SHOWMAXIMIZED) ||
                             (pFrame->mnShowState == SW_MAXIMIZE) )
                            pFrame->mbOverwriteState = false;
                        pFrame->mnShowState = SW_SHOWMAXIMIZED;
                    }
                    else
                        pFrame->mbOverwriteState = false;
                }
                else
                {
                    // Document Windows are also maximized, if the current Document Window
                    // is also maximized
                    HWND hWnd2 = GetForegroundWindow();
                    if ( hWnd2 && IsMaximized( hWnd2 ) &&
                         (GetWindowInstance( hWnd2 ) == pInst->mhInst) &&
                         ((GetWindowStyle( hWnd2 ) & (WS_POPUP | WS_MAXIMIZEBOX | WS_THICKFRAME)) == (WS_MAXIMIZEBOX | WS_THICKFRAME)) )
                        pFrame->mnShowState = SW_SHOWMAXIMIZED;
                }
            }
        }

        // create frame
        LPCWSTR pClassName;
        if ( bSubFrame )
        {
            if ( nSalFrameStyle & (SalFrameStyleFlags::MOVEABLE|SalFrameStyleFlags::NOSHADOW) ) // check if shadow not wanted
                pClassName = SAL_SUBFRAME_CLASSNAMEW;
            else
                pClassName = SAL_TMPSUBFRAME_CLASSNAMEW;    // undecorated floaters will get shadow on XP
        }
        else
        {
            if ( nSalFrameStyle & SalFrameStyleFlags::MOVEABLE )
                pClassName = SAL_FRAME_CLASSNAMEW;
            else
                pClassName = SAL_TMPSUBFRAME_CLASSNAMEW;
        }
        hWnd = CreateWindowExW( nExSysStyle, pClassName, L"", nSysStyle,
                                CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
                                hWndParent, nullptr, pInst->mhInst, pFrame );
        SAL_WARN_IF(!hWnd, "vcl", "CreateWindowExW failed: " << WindowsErrorString(GetLastError()));

    #if OSL_DEBUG_LEVEL > 1
        // set transparency value
        if( GetWindowExStyle( hWnd ) & WS_EX_LAYERED )
            SetLayeredWindowAttributes( hWnd, 0, 230, 0x00000002 /*LWA_ALPHA*/ );
    #endif
        if ( !hWnd )
        {
            delete pFrame;
            return nullptr;
        }

        // If we have a Window with an Caption Bar and without
        // an MaximizeBox, we change the SystemMenu
        if ( (nSysStyle & (WS_CAPTION | WS_MAXIMIZEBOX)) == (WS_CAPTION) )
        {
            HMENU hSysMenu = GetSystemMenu( hWnd, FALSE );
            if ( hSysMenu )
            {
                if ( !(nSysStyle & (WS_MINIMIZEBOX | WS_MAXIMIZEBOX)) )
                    DeleteMenu( hSysMenu, SC_RESTORE, MF_BYCOMMAND );
                else
                    EnableMenuItem( hSysMenu, SC_RESTORE, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED );
                if ( !(nSysStyle & WS_MINIMIZEBOX) )
                    DeleteMenu( hSysMenu, SC_MINIMIZE, MF_BYCOMMAND );
                if ( !(nSysStyle & WS_MAXIMIZEBOX) )
                    DeleteMenu( hSysMenu, SC_MAXIMIZE, MF_BYCOMMAND );
                if ( !(nSysStyle & WS_THICKFRAME) )
                    DeleteMenu( hSysMenu, SC_SIZE, MF_BYCOMMAND );
            }
        }
        if ( (nSysStyle & WS_SYSMENU) && !(nSalFrameStyle & SalFrameStyleFlags::CLOSEABLE) )
        {
            HMENU hSysMenu = GetSystemMenu( hWnd, FALSE );
            if ( hSysMenu )
                EnableMenuItem( hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED );
        }

        // reset input context
        pFrame->mhDefIMEContext = ImmAssociateContext( hWnd, nullptr );

        // determine output size and state
        RECT aRect;
        GetClientRect( hWnd, &aRect );
        pFrame->mnWidth  = aRect.right;
        pFrame->mnHeight = aRect.bottom;
        ImplSaveFrameState( pFrame );
        pFrame->mbDefPos = true;

        UpdateFrameGeometry( hWnd, pFrame );

        if( pFrame->mnShowState == SW_SHOWMAXIMIZED )
        {
            // #96084 set a useful internal window size because
            // the window will not be maximized (and the size updated) before show()

            SetMaximizedFrameGeometry( hWnd, pFrame );
        }

        return pFrame;
    }

    // helper that only creates the HWND
    // to allow for easy reparenting of system windows, (i.e. destroy and create new)
    HWND ImplSalReCreateHWND( HWND hWndParent, HWND oldhWnd, bool bAsChild )
    {
        HINSTANCE hInstance = GetSalData()->mhInst;
        sal_uLong nSysStyle     = GetWindowLongW( oldhWnd, GWL_STYLE );
        sal_uLong nExSysStyle   = GetWindowLongW( oldhWnd, GWL_EXSTYLE );

        if( bAsChild )
        {
            nSysStyle = WS_CHILD;
            nExSysStyle = 0;
        }

        LPCWSTR pClassName = SAL_SUBFRAME_CLASSNAMEW;
        return CreateWindowExW( nExSysStyle, pClassName, L"", nSysStyle,
                                CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
                                hWndParent, nullptr, hInstance, GetWindowPtr( oldhWnd ) );
    }

    // translation table from System keycodes into StartView keycodes
    #define KEY_TAB_SIZE     146

    static const sal_uInt16 aImplTranslateKeyTab[KEY_TAB_SIZE] =
    {
        // StarView-Code      System-Code                         Index
        0,                    //                                  0
        0,                    // VK_LBUTTON                       1
        0,                    // VK_RBUTTON                       2
        0,                    // VK_CANCEL                        3
        0,                    // VK_MBUTTON                       4
        0,                    //                                  5
        0,                    //                                  6
        0,                    //                                  7
        KEY_BACKSPACE,        // VK_BACK                          8
        KEY_TAB,              // VK_TAB                           9
        0,                    //                                  10
        0,                    //                                  11
        0,                    // VK_CLEAR                         12
        KEY_RETURN,           // VK_RETURN                        13
        0,                    //                                  14
        0,                    //                                  15
        0,                    // VK_SHIFT                         16
        0,                    // VK_CONTROL                       17
        0,                    // VK_MENU                          18
        0,                    // VK_PAUSE                         19
        0,                    // VK_CAPITAL                       20
        0,                    // VK_HANGUL                        21
        0,                    //                                  22
        0,                    //                                  23
        0,                    //                                  24
        KEY_HANGUL_HANJA,     // VK_HANJA                         25
        0,                    //                                  26
        KEY_ESCAPE,           // VK_ESCAPE                        27
        0,                    //                                  28
        0,                    //                                  29
        0,                    //                                  30
        0,                    //                                  31
        KEY_SPACE,            // VK_SPACE                         32
        KEY_PAGEUP,           // VK_PRIOR                         33
        KEY_PAGEDOWN,         // VK_NEXT                          34
        KEY_END,              // VK_END                           35
        KEY_HOME,             // VK_HOME                          36
        KEY_LEFT,             // VK_LEFT                          37
        KEY_UP,               // VK_UP                            38
        KEY_RIGHT,            // VK_RIGHT                         39
        KEY_DOWN,             // VK_DOWN                          40
        0,                    // VK_SELECT                        41
        0,                    // VK_PRINT                         42
        0,                    // VK_EXECUTE                       43
        0,                    // VK_SNAPSHOT                      44
        KEY_INSERT,           // VK_INSERT                        45
        KEY_DELETE,           // VK_DELETE                        46
        KEY_HELP,             // VK_HELP                          47
        KEY_0,                //                                  48
        KEY_1,                //                                  49
        KEY_2,                //                                  50
        KEY_3,                //                                  51
        KEY_4,                //                                  52
        KEY_5,                //                                  53
        KEY_6,                //                                  54
        KEY_7,                //                                  55
        KEY_8,                //                                  56
        KEY_9,                //                                  57
        0,                    //                                  58
        0,                    //                                  59
        0,                    //                                  60
        0,                    //                                  61
        0,                    //                                  62
        0,                    //                                  63
        0,                    //                                  64
        KEY_A,                //                                  65
        KEY_B,                //                                  66
        KEY_C,                //                                  67
        KEY_D,                //                                  68
        KEY_E,                //                                  69
        KEY_F,                //                                  70
        KEY_G,                //                                  71
        KEY_H,                //                                  72
        KEY_I,                //                                  73
        KEY_J,                //                                  74
        KEY_K,                //                                  75
        KEY_L,                //                                  76
        KEY_M,                //                                  77
        KEY_N,                //                                  78
        KEY_O,                //                                  79
        KEY_P,                //                                  80
        KEY_Q,                //                                  81
        KEY_R,                //                                  82
        KEY_S,                //                                  83
        KEY_T,                //                                  84
        KEY_U,                //                                  85
        KEY_V,                //                                  86
        KEY_W,                //                                  87
        KEY_X,                //                                  88
        KEY_Y,                //                                  89
        KEY_Z,                //                                  90
        0,                    // VK_LWIN                          91
        0,                    // VK_RWIN                          92
        KEY_CONTEXTMENU,      // VK_APPS                          93
        0,                    //                                  94
        0,                    //                                  95
        KEY_0,                // VK_NUMPAD0                       96
        KEY_1,                // VK_NUMPAD1                       97
        KEY_2,                // VK_NUMPAD2                       98
        KEY_3,                // VK_NUMPAD3                       99
        KEY_4,                // VK_NUMPAD4                      100
        KEY_5,                // VK_NUMPAD5                      101
        KEY_6,                // VK_NUMPAD6                      102
        KEY_7,                // VK_NUMPAD7                      103
        KEY_8,                // VK_NUMPAD8                      104
        KEY_9,                // VK_NUMPAD9                      105
        KEY_MULTIPLY,         // VK_MULTIPLY                     106
        KEY_ADD,              // VK_ADD                          107
        KEY_DECIMAL,          // VK_SEPARATOR                    108
        KEY_SUBTRACT,         // VK_SUBTRACT                     109
        KEY_DECIMAL,          // VK_DECIMAL                      110
        KEY_DIVIDE,           // VK_DIVIDE                       111
        KEY_F1,               // VK_F1                           112
        KEY_F2,               // VK_F2                           113
        KEY_F3,               // VK_F3                           114
        KEY_F4,               // VK_F4                           115
        KEY_F5,               // VK_F5                           116
        KEY_F6,               // VK_F6                           117
        KEY_F7,               // VK_F7                           118
        KEY_F8,               // VK_F8                           119
        KEY_F9,               // VK_F9                           120
        KEY_F10,              // VK_F10                          121
        KEY_F11,              // VK_F11                          122
        KEY_F12,              // VK_F12                          123
        KEY_F13,              // VK_F13                          124
        KEY_F14,              // VK_F14                          125
        KEY_F15,              // VK_F15                          126
        KEY_F16,              // VK_F16                          127
        KEY_F17,              // VK_F17                          128
        KEY_F18,              // VK_F18                          129
        KEY_F19,              // VK_F19                          130
        KEY_F20,              // VK_F20                          131
        KEY_F21,              // VK_F21                          132
        KEY_F22,              // VK_F22                          133
        KEY_F23,              // VK_F23                          134
        KEY_F24,              // VK_F24                          135
        0,                    //                                 136
        0,                    //                                 137
        0,                    //                                 138
        0,                    //                                 139
        0,                    //                                 140
        0,                    //                                 141
        0,                    //                                 142
        0,                    //                                 143
        0,                    // NUMLOCK                         144
        0                     // SCROLLLOCK                      145
    };

    static UINT ImplSalGetWheelScrollLines()
    {
        UINT nScrLines = 0;
        HWND hWndMsWheel = FindWindowW( MSH_WHEELMODULE_CLASS, MSH_WHEELMODULE_TITLE );
        if ( hWndMsWheel )
        {
            UINT nGetScrollLinesMsgId = RegisterWindowMessageW( MSH_SCROLL_LINES );
            nScrLines = static_cast<UINT>(SendMessageW( hWndMsWheel, nGetScrollLinesMsgId, 0, 0 ));
        }

        if ( !nScrLines )
            if( !SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &nScrLines, 0 ) )
                nScrLines = 0 ;

        if ( !nScrLines )
            nScrLines = 3;

        return nScrLines;
    }

    static UINT ImplSalGetWheelScrollChars()
    {
        UINT nScrChars = 0;
        if( !SystemParametersInfoW( SPI_GETWHEELSCROLLCHARS, 0, &nScrChars, 0 ) )
        {
            return 3;
        }

        // system settings successfully read
        return nScrChars;
    }

    static void ImplSalAddBorder( const WinSalFrame* pFrame, int& width, int& height )
    {
        // transform client size into window size
        RECT    aWinRect;
        aWinRect.left   = 0;
        aWinRect.right  = width-1;
        aWinRect.top    = 0;
        aWinRect.bottom = height-1;
        AdjustWindowRectEx( &aWinRect, GetWindowStyle( pFrame->mhWnd ),
                            FALSE,     GetWindowExStyle( pFrame->mhWnd ) );
        width  = aWinRect.right - aWinRect.left + 1;
        height = aWinRect.bottom - aWinRect.top + 1;
    }

    static void ImplSalCalcFullScreenSize( const WinSalFrame* pFrame,
                                           int& rX, int& rY, int& rDX, int& rDY )
    {
        // set window to screen size
        int nFrameX;
        int nFrameY;
        int nCaptionY;
        int nScreenX = 0;
        int nScreenY = 0;
        int nScreenDX = 0;
        int nScreenDY = 0;

        if ( pFrame->mbSizeBorder )
        {
            nFrameX = GetSystemMetrics( SM_CXSIZEFRAME );
            nFrameY = GetSystemMetrics( SM_CYSIZEFRAME );
        }
        else if ( pFrame->mbFixBorder )
        {
            nFrameX = GetSystemMetrics( SM_CXFIXEDFRAME );
            nFrameY = GetSystemMetrics( SM_CYFIXEDFRAME );
        }
        else if ( pFrame->mbBorder )
        {
            nFrameX = GetSystemMetrics( SM_CXBORDER );
            nFrameY = GetSystemMetrics( SM_CYBORDER );
        }
        else
        {
            nFrameX = 0;
            nFrameY = 0;
        }
        if ( pFrame->mbCaption )
            nCaptionY = GetSystemMetrics( SM_CYCAPTION );
        else
            nCaptionY = 0;

        try
        {
            sal_Int32 nMonitors = Application::GetScreenCount();
            if( (pFrame->mnDisplay >= 0) && (pFrame->mnDisplay < nMonitors) )
            {
                tools::Rectangle aRect = Application::GetScreenPosSizePixel( pFrame->mnDisplay );
                nScreenX = aRect.Left();
                nScreenY = aRect.Top();
                nScreenDX = aRect.getWidth()+1;  // difference between java/awt convention and vcl
                nScreenDY = aRect.getHeight()+1; // difference between java/awt convention and vcl
            }
            else
            {
                tools::Rectangle aCombined = Application::GetScreenPosSizePixel( 0 );
                for( sal_Int32 i = 1 ; i < nMonitors ; i++ )
                {
                    aCombined.Union( Application::GetScreenPosSizePixel( i ) );
                }
                nScreenX  = aCombined.Left();
                nScreenY  = aCombined.Top();
                nScreenDX = aCombined.getWidth();
                nScreenDY = aCombined.getHeight();
            }
        }
        catch( Exception& )
        {
        }

        if( !nScreenDX || !nScreenDY )
        {
            nScreenDX   = GetSystemMetrics( SM_CXSCREEN );
            nScreenDY   = GetSystemMetrics( SM_CYSCREEN );
        }

        rX  = nScreenX -nFrameX;
        rY  = nScreenY -(nFrameY+nCaptionY);
        rDX = nScreenDX+(nFrameX*2);
        rDY = nScreenDY+(nFrameY*2)+nCaptionY;
    }

    static void ImplSalFrameFullScreenPos( WinSalFrame* pFrame, bool bAlways = false )
    {
        if ( bAlways || !IsIconic( pFrame->mhWnd ) )
        {
            // set window to screen size
            int nX;
            int nY;
            int nWidth;
            int nHeight;
            ImplSalCalcFullScreenSize( pFrame, nX, nY, nWidth, nHeight );
            SetWindowPos( pFrame->mhWnd, nullptr,
                          nX, nY, nWidth, nHeight,
                          SWP_NOZORDER | SWP_NOACTIVATE );
        }
    }

    namespace {

    void SetForegroundWindow_Impl(HWND hwnd)
    {
        static bool bUseForegroundWindow = !std::getenv("VCL_HIDE_WINDOWS");
        if (bUseForegroundWindow)
            SetForegroundWindow(hwnd);
    }

    }

    WinSalFrame::WinSalFrame()
    {
        SalData* pSalData = GetSalData();

        mhWnd               = nullptr;
        mhCursor            = LoadCursor( nullptr, IDC_ARROW );
        mhDefIMEContext     = nullptr;
        mpLocalGraphics     = nullptr;
        mpThreadGraphics    = nullptr;
        mnShowState         = SW_SHOWNORMAL;
        mnWidth             = 0;
        mnHeight            = 0;
        mnMinWidth          = 0;
        mnMinHeight         = 0;
        mnMaxWidth          = SHRT_MAX;
        mnMaxHeight         = SHRT_MAX;
        mnInputLang         = 0;
        mnInputCodePage     = 0;
        mbGraphics          = false;
        mbCaption           = false;
        mbBorder            = false;
        mbFixBorder         = false;
        mbSizeBorder        = false;
        mbFullScreen        = false;
        mbPresentation      = false;
        mbInShow            = false;
        mbRestoreMaximize   = false;
        mbInMoveMsg         = false;
        mbInSizeMsg         = false;
        mbFullScreenToolWin = false;
        mbDefPos            = true;
        mbOverwriteState    = true;
        mbIME               = false;
        mbHandleIME         = false;
        mbSpezIME           = false;
        mbAtCursorIME       = false;
        mbCandidateMode     = false;
        mbFloatWin          = false;
        mbNoIcon            = false;
        mSelectedhMenu      = nullptr;
        mLastActivatedhMenu = nullptr;
        mpClipRgnData       = nullptr;
        mbFirstClipRect     = true;
        mpNextClipRect      = nullptr;
        mnDisplay           = 0;
        mbPropertiesStored  = false;

        memset( &maState, 0, sizeof( SalFrameState ) );
        maSysData.nSize     = sizeof( SystemEnvData );

        memset( &maGeometry, 0, sizeof( maGeometry ) );

        // get data, when making 1st frame
        if ( !pSalData->mpFirstFrame )
        {
            if ( !aSalShlData.mnWheelScrollLines )
                aSalShlData.mnWheelScrollLines = ImplSalGetWheelScrollLines();
            if ( !aSalShlData.mnWheelScrollChars )
                aSalShlData.mnWheelScrollChars = ImplSalGetWheelScrollChars();
        }

        // insert frame in framelist
        mpNextFrame = pSalData->mpFirstFrame;
        pSalData->mpFirstFrame = this;
    }

    void WinSalFrame::updateScreenNumber()
    {
        if( mnDisplay == -1 ) // spans all monitors
            return;
        WinSalSystem* pSys = static_cast<WinSalSystem*>(ImplGetSalSystem());
        if( pSys )
        {
            const std::vector<WinSalSystem::DisplayMonitor>& rMonitors =
                pSys->getMonitors();
            Point aPoint( maGeometry.nX, maGeometry.nY );
            size_t nMon = rMonitors.size();
            for( size_t i = 0; i < nMon; i++ )
            {
                if( rMonitors[i].m_aArea.IsInside( aPoint ) )
                {
                    mnDisplay = static_cast<sal_Int32>(i);
                    maGeometry.nDisplayScreenNumber = static_cast<unsigned int>(i);
                }
            }
        }
    }

    bool WinSalFrame::ReleaseFrameGraphicsDC( WinSalGraphics* pGraphics )
    {
        assert( pGraphics );
        SalData* pSalData = GetSalData();
        HDC hDC = pGraphics->getHDC();
        if ( !hDC )
            return false;
        if ( pGraphics->getDefPal() )
            SelectPalette( hDC, pGraphics->getDefPal(), TRUE );
        pGraphics->DeInitGraphics();
        SendMessageW( pSalData->mpInstance->mhComWnd, SAL_MSG_RELEASEDC,
            reinterpret_cast<WPARAM>(mhWnd), reinterpret_cast<LPARAM>(hDC) );
        if ( pGraphics == mpThreadGraphics )
            pSalData->mnCacheDCInUse--;
        pGraphics->setHDC(nullptr);
        return true;
    }

    WinSalFrame::~WinSalFrame()
    {
        SalData* pSalData = GetSalData();

        if( mpClipRgnData )
            delete [] reinterpret_cast<BYTE*>(mpClipRgnData);

        // remove frame from framelist
        WinSalFrame** ppFrame = &pSalData->mpFirstFrame;
        for(; (*ppFrame != this) && *ppFrame; ppFrame = &(*ppFrame)->mpNextFrame );
        if( *ppFrame )
            *ppFrame = mpNextFrame;
        mpNextFrame = nullptr;

        // destroy the thread SalGraphics
        if ( mpThreadGraphics )
        {
            ReleaseFrameGraphicsDC( mpThreadGraphics );
            delete mpThreadGraphics;
            mpThreadGraphics = nullptr;
        }

        // destroy the local SalGraphics
        if ( mpLocalGraphics )
        {
            ReleaseFrameGraphicsDC( mpLocalGraphics );
            delete mpLocalGraphics;
            mpLocalGraphics = nullptr;
        }

        if ( mhWnd )
        {
            // reset mouse leave data
            if ( pSalData->mhWantLeaveMsg == mhWnd )
            {
                pSalData->mhWantLeaveMsg = nullptr;
                if ( pSalData->mpMouseLeaveTimer )
                {
                    delete pSalData->mpMouseLeaveTimer;
                    pSalData->mpMouseLeaveTimer = nullptr;
                }
            }

            // remove windows properties
            if ( mbPropertiesStored )
                SetApplicationID( OUString() );

            // destroy system frame
            if ( !DestroyWindow( mhWnd ) )
                SetWindowPtr( mhWnd, nullptr );

            mhWnd = nullptr;
        }
    }

    bool WinSalFrame::InitFrameGraphicsDC( WinSalGraphics *pGraphics, HDC hDC, HWND hWnd )
    {
        SalData* pSalData = GetSalData();
        assert( pGraphics );
        pGraphics->setHWND( hWnd );

        HDC hCurrentDC = pGraphics->getHDC();
        assert( !hCurrentDC || (hCurrentDC == hDC) );
        if ( hCurrentDC )
            return true;
        pGraphics->setHDC( hDC );

        if ( !hDC )
            return false;

        if ( pSalData->mhDitherPal )
        {
            pGraphics->setDefPal(SelectPalette( hDC, pSalData->mhDitherPal, TRUE ));
            RealizePalette( hDC );
        }
        pGraphics->InitGraphics();

        if ( pGraphics == mpThreadGraphics )
            pSalData->mnCacheDCInUse++;
        return true;
    }

    SalGraphics* WinSalFrame::AcquireGraphics()
    {
        if ( mbGraphics || !mhWnd )
            return nullptr;

        SalData* pSalData = GetSalData();
        WinSalGraphics *pGraphics = nullptr;
        HDC hDC = nullptr;

        // Other threads get an own DC, because Windows modify in the
        // other case our DC (changing clip region), when they send a
        // WM_ERASEBACKGROUND message
        if ( !pSalData->mpInstance->IsMainThread() )
        {
            // We use only three CacheDC's for all threads, because W9x is limited
            // to max. 5 Cache DC's per thread
            if ( pSalData->mnCacheDCInUse >= 3 )
                return nullptr;

            if ( !mpThreadGraphics )
                mpThreadGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, true, mhWnd, this);
            pGraphics = mpThreadGraphics;
            assert( !pGraphics->getHDC() );
            hDC = reinterpret_cast<HDC>(static_cast<sal_IntPtr>(SendMessageW( pSalData->mpInstance->mhComWnd,
                                        SAL_MSG_GETCACHEDDC, reinterpret_cast<WPARAM>(mhWnd), 0 )));
        }
        else
        {
            if ( !mpLocalGraphics )
                mpLocalGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, true, mhWnd, this);
            pGraphics = mpLocalGraphics;
            hDC = pGraphics->getHDC();
            if ( !hDC )
                hDC = GetDC( mhWnd );
        }

        mbGraphics = InitFrameGraphicsDC( pGraphics, hDC, mhWnd );
        return mbGraphics ? pGraphics : nullptr;
    }

    void WinSalFrame::ReleaseGraphics( SalGraphics* pGraphics )
    {
        if ( mpThreadGraphics == pGraphics )
            ReleaseFrameGraphicsDC( mpThreadGraphics );
        mbGraphics = false;
    }

    bool WinSalFrame::PostEvent(ImplSVEvent* pData)
    {
        BOOL const ret = PostMessageW(mhWnd, SAL_MSG_USEREVENT, 0, reinterpret_cast<LPARAM>(pData));
        SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
        return static_cast<bool>(ret);
    }

    void WinSalFrame::SetTitle( const OUString& rTitle )
    {
        static_assert( sizeof( WCHAR ) == sizeof( sal_Unicode ), "must be the same size" );

        SetWindowTextW( mhWnd, o3tl::toW(rTitle.getStr()) );
    }

    void WinSalFrame::SetIcon( sal_uInt16 nIcon )
    {
        // If we have a window without an Icon (for example a dialog), ignore this call
        if ( mbNoIcon )
            return;

        // 0 means default (class) icon
        HICON hIcon = nullptr, hSmIcon = nullptr;
        if ( !nIcon )
            nIcon = 1;

        ImplLoadSalIcon( nIcon, hIcon, hSmIcon );

        SAL_WARN_IF( !hIcon , "vcl",   "WinSalFrame::SetIcon(): Could not load large icon !" );
        SAL_WARN_IF( !hSmIcon , "vcl", "WinSalFrame::SetIcon(): Could not load small icon !" );

        SendMessageW( mhWnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon) );
        SendMessageW( mhWnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hSmIcon) );
    }

    void WinSalFrame::SetMenu( SalMenu* pSalMenu )
    {
        WinSalMenu* pWMenu = static_cast<WinSalMenu*>(pSalMenu);
        if( pSalMenu && pWMenu->mbMenuBar )
            ::SetMenu( mhWnd, pWMenu->mhMenu );
    }

    void WinSalFrame::DrawMenuBar()
    {
        ::DrawMenuBar( mhWnd );
    }

    HWND ImplGetParentHwnd( HWND hWnd )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if( !pFrame || !pFrame->GetWindow())
            return ::GetParent( hWnd );
        vcl::Window *pRealParent = pFrame->GetWindow()->ImplGetWindowImpl()->mpRealParent;
        if( pRealParent )
            return static_cast<WinSalFrame*>(pRealParent->ImplGetWindowImpl()->mpFrame)->mhWnd;
        else
            return ::GetParent( hWnd );

    }

    SalFrame* WinSalFrame::GetParent() const
    {
        return GetWindowPtr( ImplGetParentHwnd( mhWnd ) );
    }

    static void ImplSalShow( HWND hWnd, bool bVisible, bool bNoActivate )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( !pFrame )
            return;

        if ( bVisible )
        {
            pFrame->mbDefPos = false;
            pFrame->mbOverwriteState = true;
            pFrame->mbInShow = true;

            // #i4715, save position
            RECT aRectPreMatrox, aRectPostMatrox;
            GetWindowRect( hWnd, &aRectPreMatrox );

            vcl::DeletionListener aDogTag( pFrame );
            if( bNoActivate )
                ShowWindow( hWnd, SW_SHOWNOACTIVATE );
            else
                ShowWindow( hWnd, pFrame->mnShowState );
            if( aDogTag.isDeleted() )
                return;

            if (pFrame->mbFloatWin && !(pFrame->mnStyle & SalFrameStyleFlags::NOSHADOW))
            {
                // erase the window immediately to improve XP shadow effect
                // otherwise the shadow may appears long time before the rest of the window
                // especially when accessibility is on
                HDC dc = GetDC( hWnd );
                RECT aRect;
                GetClientRect( hWnd, &aRect );
                FillRect( dc, &aRect, reinterpret_cast<HBRUSH>(COLOR_MENU+1) ); // choose the menucolor, because its mostly noticeable for menus
                ReleaseDC( hWnd, dc );
            }

            // #i4715, matrox centerpopup might have changed our position
            // reposition popups without caption (menus, dropdowns, tooltips)
            GetWindowRect( hWnd, &aRectPostMatrox );
            if( (GetWindowStyle( hWnd ) & WS_POPUP) &&
                !pFrame->mbCaption &&
                (aRectPreMatrox.left != aRectPostMatrox.left || aRectPreMatrox.top != aRectPostMatrox.top) )
                SetWindowPos( hWnd, nullptr, aRectPreMatrox.left, aRectPreMatrox.top, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE  );

            if( aDogTag.isDeleted() )
                return;
            vcl::Window *pClientWin = pFrame->GetWindow()->ImplGetClientWindow();
            if ( pFrame->mbFloatWin || ( pClientWin && (pClientWin->GetStyle() & WB_SYSTEMFLOATWIN) ) )
                pFrame->mnShowState = SW_SHOWNOACTIVATE;
            else
                pFrame->mnShowState = SW_SHOW;
            // hide toolbar for W98
            if ( pFrame->mbPresentation )
            {
                HWND hWndParent = ::GetParent( hWnd );
                if ( hWndParent )
                    SetForegroundWindow_Impl( hWndParent );
                SetForegroundWindow_Impl( hWnd );
            }

            pFrame->mbInShow = false;
            pFrame->updateScreenNumber();

            // Direct Paint only, if we get the SolarMutex
            if ( ImplSalYieldMutexTryToAcquire() )
            {
                UpdateWindow( hWnd );
                ImplSalYieldMutexRelease();
            }
        }
        else
        {
            ShowWindow( hWnd, SW_HIDE );
        }
    }

    void WinSalFrame::SetExtendedFrameStyle( SalExtStyle )
    {
    }

    void WinSalFrame::Show( bool bVisible, bool bNoActivate )
    {
        // Post this Message to the window, because this only works
        // in the thread of the window, which has create this window.
        // We post this message to avoid deadlocks
        if ( GetSalData()->mnAppThreadId != GetCurrentThreadId() )
        {
            BOOL const ret = PostMessageW(mhWnd, SAL_MSG_SHOW, WPARAM(bVisible), LPARAM(bNoActivate));
            SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
        }
        else
            ImplSalShow( mhWnd, bVisible, bNoActivate );
    }

    void WinSalFrame::SetMinClientSize( long nWidth, long nHeight )
    {
        mnMinWidth  = nWidth;
        mnMinHeight = nHeight;
    }

    void WinSalFrame::SetMaxClientSize( long nWidth, long nHeight )
    {
        mnMaxWidth  = nWidth;
        mnMaxHeight = nHeight;
    }

    void WinSalFrame::SetPosSize( long nX, long nY, long nWidth, long nHeight,
                                                       sal_uInt16 nFlags )
    {
        bool bVisible = (GetWindowStyle( mhWnd ) & WS_VISIBLE) != 0;
        if ( !bVisible )
        {
            vcl::Window *pClientWin = GetWindow()->ImplGetClientWindow();
            if ( mbFloatWin || ( pClientWin && (pClientWin->GetStyle() & WB_SYSTEMFLOATWIN) ) )
                    mnShowState = SW_SHOWNOACTIVATE;
            else
                    mnShowState = SW_SHOWNORMAL;
        }
        else
        {
            if ( IsIconic( mhWnd ) || IsZoomed( mhWnd ) )
                    ShowWindow( mhWnd, SW_RESTORE );
        }

        SalEvent nEvent = SalEvent::NONE;
        UINT    nPosSize = 0;
        RECT    aClientRect, aWindowRect;
        GetClientRect( mhWnd, &aClientRect );   // x,y always 0,0, but width and height without border
        GetWindowRect( mhWnd, &aWindowRect );   // x,y in screen coordinates, width and height with border

        if ( !(nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y)) )
            nPosSize |= SWP_NOMOVE;
        else
        {
            //SAL_WARN_IF( !nX || !nY, "vcl", " Windowposition of (0,0) requested!" );
            nEvent = SalEvent::Move;
        }
        if ( !(nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT)) )
            nPosSize |= SWP_NOSIZE;
        else
            nEvent = (nEvent == SalEvent::Move) ? SalEvent::MoveResize : SalEvent::Resize;

        if ( !(nFlags & SAL_FRAME_POSSIZE_X) )
            nX = aWindowRect.left;
        if ( !(nFlags & SAL_FRAME_POSSIZE_Y) )
            nY = aWindowRect.top;
        if ( !(nFlags & SAL_FRAME_POSSIZE_WIDTH) )
            nWidth = aClientRect.right-aClientRect.left;
        if ( !(nFlags & SAL_FRAME_POSSIZE_HEIGHT) )
            nHeight = aClientRect.bottom-aClientRect.top;

        // Calculate window size including the border
        RECT    aWinRect;
        aWinRect.left   = 0;
        aWinRect.right  = static_cast<int>(nWidth)-1;
        aWinRect.top    = 0;
        aWinRect.bottom = static_cast<int>(nHeight)-1;
        AdjustWindowRectEx( &aWinRect, GetWindowStyle( mhWnd ),
                            FALSE,     GetWindowExStyle( mhWnd ) );
        nWidth  = aWinRect.right - aWinRect.left + 1;
        nHeight = aWinRect.bottom - aWinRect.top + 1;

        if ( !(nPosSize & SWP_NOMOVE) && ::GetParent( mhWnd ) )
        {
                RECT aParentRect;
                GetClientRect( ImplGetParentHwnd( mhWnd ), &aParentRect );
                if( AllSettings::GetLayoutRTL() )
                    nX = (aParentRect.right - aParentRect.left) - nWidth-1 - nX;

                //#110386#, do not transform coordinates for system child windows
                if( !(GetWindowStyle( mhWnd ) & WS_CHILD) )
                {
                    POINT aPt;
                    aPt.x = nX;
                    aPt.y = nY;

                    HWND parentHwnd = ImplGetParentHwnd( mhWnd );
                    WinSalFrame* pParentFrame = GetWindowPtr( parentHwnd );
                    if ( pParentFrame && pParentFrame->mnShowState == SW_SHOWMAXIMIZED )
                    {
                        // #i42485#: parent will be shown maximized in which case
                        // a ClientToScreen uses the wrong coordinates (i.e. those from the restore pos)
                        // so use the (already updated) frame geometry for the transformation
                        aPt.x +=  pParentFrame->maGeometry.nX;
                        aPt.y +=  pParentFrame->maGeometry.nY;
                    }
                    else
                        ClientToScreen( parentHwnd, &aPt );

                    nX = aPt.x;
                    nY = aPt.y;

                    // the position is set
                    mbDefPos = false;
                }
        }

        // #i3338# to be conformant to UNIX we must position the client window, ie without the decoration
        // #i43250# if the position was read from the system (GetWindowRect(), see above), it must not be modified
        if ( nFlags & SAL_FRAME_POSSIZE_X )
            nX += aWinRect.left;
        if ( nFlags & SAL_FRAME_POSSIZE_Y )
            nY += aWinRect.top;

        int     nScreenX;
        int     nScreenY;
        int     nScreenWidth;
        int     nScreenHeight;

        RECT aRect;
        ImplSalGetWorkArea( mhWnd, &aRect, nullptr );
        nScreenX        = aRect.left;
        nScreenY        = aRect.top;
        nScreenWidth    = aRect.right-aRect.left;
        nScreenHeight   = aRect.bottom-aRect.top;

        if ( mbDefPos && (nPosSize & SWP_NOMOVE)) // we got no positioning request, so choose default position
        {
            // center window

            HWND hWndParent = ::GetParent( mhWnd );
            // Search for TopLevel Frame
            while ( hWndParent && (GetWindowStyle( hWndParent ) & WS_CHILD) )
                hWndParent = ::GetParent( hWndParent );
            // if the Window has a Parent, than center the window to
            // the parent, in the other case to the screen
            if ( hWndParent && !IsIconic( hWndParent ) &&
                 (GetWindowStyle( hWndParent ) & WS_VISIBLE) )
            {
                RECT aParentRect;
                GetWindowRect( hWndParent, &aParentRect );
                int nParentWidth    = aParentRect.right-aParentRect.left;
                int nParentHeight   = aParentRect.bottom-aParentRect.top;

                // We don't center, when Parent is smaller than our window
                if ( (nParentWidth-GetSystemMetrics( SM_CXFIXEDFRAME ) <= nWidth) &&
                     (nParentHeight-GetSystemMetrics( SM_CYFIXEDFRAME ) <= nHeight) )
                {
                    int nOff = GetSystemMetrics( SM_CYSIZEFRAME ) + GetSystemMetrics( SM_CYCAPTION );
                    nX = aParentRect.left+nOff;
                    nY = aParentRect.top+nOff;
                }
                else
                {
                    nX = (nParentWidth-nWidth)/2 + aParentRect.left;
                    nY = (nParentHeight-nHeight)/2 + aParentRect.top;
                }
            }
            else
            {
                POINT pt;
                GetCursorPos( &pt );
                RECT aRect2;
                aRect2.left = pt.x;
                aRect2.top = pt.y;
                aRect2.right = pt.x+2;
                aRect2.bottom = pt.y+2;

                // dualmonitor support:
                // Get screensize of the monitor with the mouse pointer
                ImplSalGetWorkArea( mhWnd, &aRect2, &aRect2 );

                nX = ((aRect2.right-aRect2.left)-nWidth)/2 + aRect2.left;
                nY = ((aRect2.bottom-aRect2.top)-nHeight)/2 + aRect2.top;
            }

            //if ( bVisible )
            //    mbDefPos = FALSE;

            mbDefPos = false;   // center only once
            nPosSize &= ~SWP_NOMOVE;        // activate positioning
            nEvent = SalEvent::MoveResize;
        }

        // Adjust Window in the screen
        bool bCheckOffScreen = true;

        // but don't do this for floaters or ownerdraw windows that are currently moved interactively
        if( (mnStyle & SalFrameStyleFlags::FLOAT) && !(mnStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) )
            bCheckOffScreen = false;

        if( mnStyle & SalFrameStyleFlags::OWNERDRAWDECORATION )
        {
            // may be the window is currently being moved (mouse is captured), then no check is required
            if( mhWnd == ::GetCapture() )
                bCheckOffScreen = false;
            else
                bCheckOffScreen = true;
        }

        if( bCheckOffScreen )
        {
            if ( nX+nWidth > nScreenX+nScreenWidth )
                nX = (nScreenX+nScreenWidth) - nWidth;
            if ( nY+nHeight > nScreenY+nScreenHeight )
                nY = (nScreenY+nScreenHeight) - nHeight;
            if ( nX < nScreenX )
                nX = nScreenX;
            if ( nY < nScreenY )
                nY = nScreenY;
        }

        UINT nPosFlags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | nPosSize;
        // bring floating windows always to top
        if( !(mnStyle & SalFrameStyleFlags::FLOAT) )
            nPosFlags |= SWP_NOZORDER; // do not change z-order

        SetWindowPos( mhWnd, HWND_TOP, nX, nY, static_cast<int>(nWidth), static_cast<int>(nHeight), nPosFlags  );

        UpdateFrameGeometry( mhWnd, this );

        // Notification -- really ???
        if( nEvent != SalEvent::NONE )
            CallCallback( nEvent, nullptr );
    }

    void WinSalFrame::ImplSetParentFrame( HWND hNewParentWnd, bool bAsChild )
    {
        // save hwnd, will be overwritten in WM_CREATE during createwindow
        HWND hWndOld = mhWnd;
        HWND hWndOldParent = ::GetParent( hWndOld );
        SalData* pSalData = GetSalData();

        if( hNewParentWnd == hWndOldParent )
            return;

        ::std::vector< WinSalFrame* > children;
        ::std::vector< WinSalObject* > systemChildren;

        // search child windows
        WinSalFrame *pFrame = pSalData->mpFirstFrame;
        while( pFrame )
        {
            HWND hWndParent = ::GetParent( pFrame->mhWnd );
            if( mhWnd == hWndParent )
                children.push_back( pFrame );
            pFrame = pFrame->mpNextFrame;
        }

        // search system child windows (plugins etc.)
        WinSalObject *pObject = pSalData->mpFirstObject;
        while( pObject )
        {
            HWND hWndParent = ::GetParent( pObject->mhWnd );
            if( mhWnd == hWndParent )
                systemChildren.push_back( pObject );
            pObject = pObject->mpNextObject;
        }

        // to recreate the DCs, if they were destroyed
        bool bHadLocalGraphics = false, bHadThreadGraphics = false;

        HFONT   hFont   = nullptr;
        HPEN    hPen    = nullptr;
        HBRUSH  hBrush  = nullptr;

        int oldCount = pSalData->mnCacheDCInUse;

        // release the thread DC
        if ( mpThreadGraphics )
        {
            // save current gdi objects before hdc is gone
            HDC hDC = mpThreadGraphics->getHDC();
            if ( hDC )
            {
                hFont  = static_cast<HFONT>(GetCurrentObject( hDC, OBJ_FONT ));
                hPen   = static_cast<HPEN>(GetCurrentObject( hDC, OBJ_PEN ));
                hBrush = static_cast<HBRUSH>(GetCurrentObject( hDC, OBJ_BRUSH ));
            }

            bHadThreadGraphics = ReleaseFrameGraphicsDC( mpThreadGraphics );
            assert( (bHadThreadGraphics && hDC) || (!bHadThreadGraphics && !hDC) );
        }

        // release the local DC
        if ( mpLocalGraphics )
            bHadLocalGraphics = ReleaseFrameGraphicsDC( mpLocalGraphics );

        // create a new hwnd with the same styles
        HWND hWndParent = hNewParentWnd;
        // forward to main thread
        HWND hWnd = reinterpret_cast<HWND>(static_cast<sal_IntPtr>(SendMessageW( pSalData->mpInstance->mhComWnd,
                                            bAsChild ? SAL_MSG_RECREATECHILDHWND : SAL_MSG_RECREATEHWND,
                                            reinterpret_cast<WPARAM>(hWndParent), reinterpret_cast<LPARAM>(mhWnd) )));

        // succeeded ?
        SAL_WARN_IF( !IsWindow( hWnd ), "vcl", "WinSalFrame::SetParent not successful");

        // re-create thread DC
        if( bHadThreadGraphics )
        {
            HDC hDC = reinterpret_cast<HDC>(static_cast<sal_IntPtr>(
                        SendMessageW( pSalData->mpInstance->mhComWnd,
                            SAL_MSG_GETCACHEDDC, reinterpret_cast<WPARAM>(hWnd), 0 )));
            InitFrameGraphicsDC( mpThreadGraphics, hDC, hWnd );
            if ( hDC )
            {
                // re-select saved gdi objects
                if( hFont )
                    SelectObject( hDC, hFont );
                if( hPen )
                    SelectObject( hDC, hPen );
                if( hBrush )
                    SelectObject( hDC, hBrush );

                SAL_WARN_IF( oldCount != pSalData->mnCacheDCInUse, "vcl", "WinSalFrame::SetParent() hDC count corrupted");
            }
        }

        // re-create local DC
        if( bHadLocalGraphics )
            InitFrameGraphicsDC( mpLocalGraphics, GetDC( hWnd ), hWnd );

        // TODO: add SetParent() call for SalObjects
        SAL_WARN_IF( !systemChildren.empty(), "vcl", "WinSalFrame::SetParent() parent of living system child window will be destroyed!");

        // reparent children before old parent is destroyed
        for (auto & child : children)
            child->ImplSetParentFrame( hWnd, false );

        children.clear();
        systemChildren.clear();

        // Now destroy original HWND in the thread where it was created.
        SendMessageW( GetSalData()->mpInstance->mhComWnd,
                         SAL_MSG_DESTROYHWND, WPARAM(0), reinterpret_cast<LPARAM>(hWndOld));
    }

    void WinSalFrame::SetParent( SalFrame* pNewParent )
    {
        WinSalFrame::mbInReparent = true;
        ImplSetParentFrame( static_cast<WinSalFrame*>(pNewParent)->mhWnd, false );
        WinSalFrame::mbInReparent = false;
    }

    bool WinSalFrame::SetPluginParent( SystemParentData* pNewParent )
    {
        if ( pNewParent->hWnd == nullptr )
        {
            pNewParent->hWnd = GetDesktopWindow();
        }

        WinSalFrame::mbInReparent = true;
        ImplSetParentFrame( pNewParent->hWnd, true );
        WinSalFrame::mbInReparent = false;
        return true;
    }

    void WinSalFrame::GetWorkArea( tools::Rectangle &rRect )
    {
        RECT aRect;
        ImplSalGetWorkArea( mhWnd, &aRect, nullptr );
        rRect.SetLeft( aRect.left );
        rRect.SetRight( aRect.right-1 );
        rRect.SetTop( aRect.top );
        rRect.SetBottom( aRect.bottom-1 );
    }

    void WinSalFrame::GetClientSize( long& rWidth, long& rHeight )
    {
        rWidth  = maGeometry.nWidth;
        rHeight = maGeometry.nHeight;
    }

    void WinSalFrame::SetWindowState( const SalFrameState* pState )
    {
        // Check if the window fits into the screen, in case the screen
        // resolution changed
        int     nX;
        int     nY;
        int     nWidth;
        int     nHeight;
        int     nScreenX;
        int     nScreenY;
        int     nScreenWidth;
        int     nScreenHeight;

        RECT aRect;
        ImplSalGetWorkArea( mhWnd, &aRect, nullptr );
        // #102500# allow some overlap, the window could have been made a little larger than the physical screen
        nScreenX        = aRect.left-10;
        nScreenY        = aRect.top-10;
        nScreenWidth    = aRect.right-aRect.left+20;
        nScreenHeight   = aRect.bottom-aRect.top+20;

        UINT    nPosSize    = 0;
        RECT    aWinRect;
        GetWindowRect( mhWnd, &aWinRect );

        // to be consistent with Unix, the frame state is without(!) decoration
        // ->add the decoration
        RECT aRect2 = aWinRect;
        AdjustWindowRectEx( &aRect2, GetWindowStyle( mhWnd ),
                        FALSE,     GetWindowExStyle( mhWnd ) );
        long nTopDeco = abs( aWinRect.top - aRect2.top );
        long nLeftDeco = abs( aWinRect.left - aRect2.left );
        long nBottomDeco = abs( aWinRect.bottom - aRect2.bottom );
        long nRightDeco = abs( aWinRect.right - aRect2.right );

        // adjust window position/size to fit the screen
        if ( !(pState->mnMask & (WindowStateMask::X | WindowStateMask::Y)) )
            nPosSize |= SWP_NOMOVE;
        if ( !(pState->mnMask & (WindowStateMask::Width | WindowStateMask::Height)) )
            nPosSize |= SWP_NOSIZE;
        if ( pState->mnMask & WindowStateMask::X )
            nX = static_cast<int>(pState->mnX) - nLeftDeco;
        else
            nX = aWinRect.left;
        if ( pState->mnMask & WindowStateMask::Y )
            nY = static_cast<int>(pState->mnY) - nTopDeco;
        else
            nY = aWinRect.top;
        if ( pState->mnMask & WindowStateMask::Width )
            nWidth = static_cast<int>(pState->mnWidth) + nLeftDeco + nRightDeco;
        else
            nWidth = aWinRect.right-aWinRect.left;
        if ( pState->mnMask & WindowStateMask::Height )
            nHeight = static_cast<int>(pState->mnHeight) + nTopDeco + nBottomDeco;
        else
            nHeight = aWinRect.bottom-aWinRect.top;

        // Adjust Window in the screen:
        // if it does not fit into the screen do nothing, ie default pos/size will be used
        // if there is an overlap with the screen border move the window while keeping its size

        if( nWidth > nScreenWidth || nHeight > nScreenHeight )
            nPosSize |= (SWP_NOMOVE | SWP_NOSIZE);

        if ( nX+nWidth > nScreenX+nScreenWidth )
            nX = (nScreenX+nScreenWidth) - nWidth;
        if ( nY+nHeight > nScreenY+nScreenHeight )
            nY = (nScreenY+nScreenHeight) - nHeight;
        if ( nX < nScreenX )
            nX = nScreenX;
        if ( nY < nScreenY )
            nY = nScreenY;

        // set Restore-Position
        WINDOWPLACEMENT aPlacement;
        aPlacement.length = sizeof( aPlacement );
        GetWindowPlacement( mhWnd, &aPlacement );

        // set State
        bool bVisible = (GetWindowStyle( mhWnd ) & WS_VISIBLE) != 0;
        bool bUpdateHiddenFramePos = false;
        if ( !bVisible )
        {
            aPlacement.showCmd = SW_HIDE;

            if ( mbOverwriteState )
            {
                if ( pState->mnMask & WindowStateMask::State )
                {
                    if ( pState->mnState & WindowStateState::Minimized )
                        mnShowState = SW_SHOWMINIMIZED;
                    else if ( pState->mnState & WindowStateState::Maximized )
                    {
                        mnShowState = SW_SHOWMAXIMIZED;
                        bUpdateHiddenFramePos = true;
                    }
                    else if ( pState->mnState & WindowStateState::Normal )
                        mnShowState = SW_SHOWNORMAL;
                }
            }
        }
        else
        {
            if ( pState->mnMask & WindowStateMask::State )
            {
                if ( pState->mnState & WindowStateState::Minimized )
                {
                    if ( pState->mnState & WindowStateState::Maximized )
                        aPlacement.flags |= WPF_RESTORETOMAXIMIZED;
                    aPlacement.showCmd = SW_SHOWMINIMIZED;
                }
                else if ( pState->mnState & WindowStateState::Maximized )
                    aPlacement.showCmd = SW_SHOWMAXIMIZED;
                else if ( pState->mnState & WindowStateState::Normal )
                    aPlacement.showCmd = SW_RESTORE;
            }
        }

        // if a window is neither minimized nor maximized or need not be
        // positioned visibly (that is in visible state), do not use
        // SetWindowPlacement since it calculates including the TaskBar
        if ( !IsIconic( mhWnd ) && !IsZoomed( mhWnd ) &&
             (!bVisible || (aPlacement.showCmd == SW_RESTORE)) )
        {
            if( bUpdateHiddenFramePos )
            {
                RECT aStateRect;
                aStateRect.left   = nX;
                aStateRect.top    = nY;
                aStateRect.right  = nX+nWidth;
                aStateRect.bottom = nY+nHeight;
                // #96084 set a useful internal window size because
                // the window will not be maximized (and the size updated) before show()
                SetMaximizedFrameGeometry( mhWnd, this, &aStateRect );
                SetWindowPos( mhWnd, nullptr,
                              maGeometry.nX, maGeometry.nY, maGeometry.nWidth, maGeometry.nHeight,
                              SWP_NOZORDER | SWP_NOACTIVATE | nPosSize );
            }
            else
                SetWindowPos( mhWnd, nullptr,
                          nX, nY, nWidth, nHeight,
                          SWP_NOZORDER | SWP_NOACTIVATE | nPosSize );
        }
        else
        {
            if( !(nPosSize & (SWP_NOMOVE|SWP_NOSIZE)) )
            {
                aPlacement.rcNormalPosition.left    = nX-nScreenX;
                aPlacement.rcNormalPosition.top     = nY-nScreenY;
                aPlacement.rcNormalPosition.right   = nX+nWidth-nScreenX;
                aPlacement.rcNormalPosition.bottom  = nY+nHeight-nScreenY;
            }
            SetWindowPlacement( mhWnd, &aPlacement );
        }

        if( !(nPosSize & SWP_NOMOVE) )
            mbDefPos = false; // window was positioned
    }

    bool WinSalFrame::GetWindowState( SalFrameState* pState )
    {
        if ( maState.mnWidth && maState.mnHeight )
        {
            *pState = maState;
            // #94144# allow Minimize again, should be masked out when read from configuration
            // 91625 - Don't save minimize
            //if ( !(pState->mnState & WindowStateState::Maximized) )
            if ( !(pState->mnState & (WindowStateState::Minimized | WindowStateState::Maximized)) )
                pState->mnState |= WindowStateState::Normal;
            return true;
        }

        return false;
    }

    void WinSalFrame::SetScreenNumber( unsigned int nNewScreen )
    {
        WinSalSystem* pSys = static_cast<WinSalSystem*>(ImplGetSalSystem());
        if( pSys )
        {
            const std::vector<WinSalSystem::DisplayMonitor>& rMonitors =
                pSys->getMonitors();
            size_t nMon = rMonitors.size();
            if( nNewScreen < nMon )
            {
                Point aOldMonPos, aNewMonPos( rMonitors[nNewScreen].m_aArea.TopLeft() );
                Point aCurPos( maGeometry.nX, maGeometry.nY );
                for( size_t i = 0; i < nMon; i++ )
                {
                    if( rMonitors[i].m_aArea.IsInside( aCurPos ) )
                    {
                        aOldMonPos = rMonitors[i].m_aArea.TopLeft();
                        break;
                    }
                }
                mnDisplay = nNewScreen;
                maGeometry.nDisplayScreenNumber = nNewScreen;
                SetPosSize( aNewMonPos.X() + (maGeometry.nX - aOldMonPos.X()),
                            aNewMonPos.Y() + (maGeometry.nY - aOldMonPos.Y()),
                            0, 0,
                            SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y );
            }
        }
    }

    void WinSalFrame::SetApplicationID( const OUString &rApplicationID )
    {
        // http://msdn.microsoft.com/en-us/library/windows/desktop/dd378430(v=vs.85).aspx
        // A window's properties must be removed before the window is closed.

        typedef HRESULT ( WINAPI *SHGETPROPERTYSTOREFORWINDOW )( HWND, REFIID, void ** );
        SHGETPROPERTYSTOREFORWINDOW pSHGetPropertyStoreForWindow;
        pSHGetPropertyStoreForWindow = reinterpret_cast<SHGETPROPERTYSTOREFORWINDOW>(GetProcAddress(
                                       GetModuleHandleW (L"shell32.dll"), "SHGetPropertyStoreForWindow" ));

        if( pSHGetPropertyStoreForWindow )
        {
            IPropertyStore *pps;
            HRESULT hr = pSHGetPropertyStoreForWindow ( mhWnd, IID_PPV_ARGS(&pps) );
            if ( SUCCEEDED(hr) )
            {
                PROPVARIANT pv;
                if ( !rApplicationID.isEmpty() )
                {
                    hr = InitPropVariantFromString( o3tl::toW(rApplicationID.getStr()), &pv );
                    mbPropertiesStored = true;
                }
                else
                    // if rApplicationID we remove the property from the window, if present
                    PropVariantInit( &pv );

                if ( SUCCEEDED(hr) )
                {
                    hr = pps->SetValue( PKEY_AppUserModel_ID, pv );
                    PropVariantClear( &pv );
                }
                pps->Release();
            }
        }
    }

    void WinSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nDisplay )
    {
        if ( (mbFullScreen == bFullScreen) && (!bFullScreen || (mnDisplay == nDisplay)) )
            return;

        mbFullScreen = bFullScreen;
        mnDisplay = nDisplay;

        if ( bFullScreen )
        {
            // to hide the Windows taskbar
            DWORD nExStyle = GetWindowExStyle( mhWnd );
            if ( nExStyle & WS_EX_TOOLWINDOW )
            {
                mbFullScreenToolWin = true;
                nExStyle &= ~WS_EX_TOOLWINDOW;
                SetWindowExStyle( mhWnd, nExStyle );
            }
            // save old position
            GetWindowRect( mhWnd, &maFullScreenRect );

            // save show state
            mnFullScreenShowState = mnShowState;
            if ( !(GetWindowStyle( mhWnd ) & WS_VISIBLE) )
                mnShowState = SW_SHOW;

            // set window to screen size
            ImplSalFrameFullScreenPos( this, true );
        }
        else
        {
            // when the ShowState has to be reset, hide the window first to
            // reduce flicker
            bool bVisible = (GetWindowStyle( mhWnd ) & WS_VISIBLE) != 0;
            if ( bVisible && (mnShowState != mnFullScreenShowState) )
                ShowWindow( mhWnd, SW_HIDE );

            if ( mbFullScreenToolWin )
                SetWindowExStyle( mhWnd, GetWindowExStyle( mhWnd ) | WS_EX_TOOLWINDOW );
            mbFullScreenToolWin = false;

            SetWindowPos( mhWnd, nullptr,
                          maFullScreenRect.left,
                          maFullScreenRect.top,
                          maFullScreenRect.right-maFullScreenRect.left,
                          maFullScreenRect.bottom-maFullScreenRect.top,
                          SWP_NOZORDER | SWP_NOACTIVATE );

            // restore show state
            if ( mnShowState != mnFullScreenShowState )
            {
                mnShowState = mnFullScreenShowState;
                if ( bVisible )
                {
                    mbInShow = true;
                    ShowWindow( mhWnd, mnShowState );
                    mbInShow = false;
                    UpdateWindow( mhWnd );
                }
            }
        }
    }

    void WinSalFrame::StartPresentation( bool bStart )
    {
        if ( mbPresentation == bStart )
            return;

        mbPresentation = bStart;

        SalData* pSalData = GetSalData();
        if ( bStart )
        {
            // turn off screen-saver when in Presentation mode
            SystemParametersInfoW( SPI_GETSCREENSAVEACTIVE, 0,
                                  &(pSalData->mbScrSvrEnabled), 0 );
            if ( pSalData->mbScrSvrEnabled )
                SystemParametersInfoW( SPI_SETSCREENSAVEACTIVE, FALSE, nullptr, 0 );
        }
        else
        {
            // turn on screen-saver
            if ( pSalData->mbScrSvrEnabled )
                SystemParametersInfoW( SPI_SETSCREENSAVEACTIVE, pSalData->mbScrSvrEnabled, nullptr, 0 );
        }
    }

    void WinSalFrame::SetAlwaysOnTop( bool bOnTop )
    {
        HWND hWnd;
        if ( bOnTop )
            hWnd = HWND_TOPMOST;
        else
            hWnd = HWND_NOTOPMOST;
        SetWindowPos( mhWnd, hWnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
    }

    static void ImplSalToTop( HWND hWnd, SalFrameToTop nFlags )
    {
        WinSalFrame* pToTopFrame = GetWindowPtr( hWnd );
        if( pToTopFrame && (pToTopFrame->mnStyle & SalFrameStyleFlags::SYSTEMCHILD) )
            BringWindowToTop( hWnd );

        if ( nFlags & SalFrameToTop::ForegroundTask )
        {
            // This magic code is necessary to connect the input focus of the
            // current window thread and the thread which owns the window that
            // should be the new foreground window.
            HWND   hCurrWnd     = GetForegroundWindow();
            DWORD  myThreadID   = GetCurrentThreadId();
            DWORD  currThreadID = GetWindowThreadProcessId(hCurrWnd,nullptr);
            AttachThreadInput(myThreadID, currThreadID,TRUE);
            SetForegroundWindow_Impl(hWnd);
            AttachThreadInput(myThreadID,currThreadID,FALSE);
        }

        if ( nFlags & SalFrameToTop::RestoreWhenMin )
        {
            HWND hIconicWnd = hWnd;
            while ( hIconicWnd )
            {
                if ( IsIconic( hIconicWnd ) )
                {
                    WinSalFrame* pFrame = GetWindowPtr( hIconicWnd );
                    if ( pFrame )
                    {
                        if ( GetWindowPtr( hWnd )->mbRestoreMaximize )
                            ShowWindow( hIconicWnd, SW_MAXIMIZE );
                        else
                            ShowWindow( hIconicWnd, SW_RESTORE );
                    }
                    else
                        ShowWindow( hIconicWnd, SW_RESTORE );
                }

                hIconicWnd = ::GetParent( hIconicWnd );
            }
        }

        if ( !IsIconic( hWnd ) && IsWindowVisible( hWnd ) )
        {
            SetFocus( hWnd );

            // Windows sometimes incorrectly reports to have the focus;
            // thus make sure to really get the focus
            if ( ::GetFocus() == hWnd )
                SetForegroundWindow_Impl( hWnd );
        }
    }

    void WinSalFrame::ToTop( SalFrameToTop nFlags )
    {
        nFlags &= ~SalFrameToTop::GrabFocus;   // this flag is not needed on win32
        // Post this Message to the window, because this only works
        // in the thread of the window, which has create this window.
        // We post this message to avoid deadlocks
        if ( GetSalData()->mnAppThreadId != GetCurrentThreadId() )
        {
            BOOL const ret = PostMessageW( mhWnd, SAL_MSG_TOTOP, static_cast<WPARAM>(nFlags), 0 );
            SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
        }
        else
            ImplSalToTop( mhWnd, nFlags );
    }

    void WinSalFrame::SetPointer( PointerStyle ePointerStyle )
    {
        struct ImplPtrData
        {
            HCURSOR         mhCursor;
            LPCTSTR         mnSysId;
            UINT            mnOwnId;
        };

        static o3tl::enumarray<PointerStyle, ImplPtrData> aImplPtrTab =
        {
        ImplPtrData{ nullptr, IDC_ARROW, 0 },                       // POINTER_ARROW
        { nullptr, nullptr, SAL_RESID_POINTER_NULL },               // POINTER_NULL
        { nullptr, IDC_WAIT, 0 },                                   // POINTER_WAIT
        { nullptr, IDC_IBEAM, 0 },                                  // POINTER_TEXT
        { nullptr, IDC_HELP, 0 },                                   // POINTER_HELP
        { nullptr, IDC_CROSS, 0 },                                  // POINTER_CROSS
        { nullptr, IDC_SIZEALL, 0 },                                // POINTER_MOVE
        { nullptr, IDC_SIZENS, 0 },                                 // POINTER_NSIZE
        { nullptr, IDC_SIZENS, 0 },                                 // POINTER_SSIZE
        { nullptr, IDC_SIZEWE, 0 },                                 // POINTER_WSIZE
        { nullptr, IDC_SIZEWE, 0 },                                 // POINTER_ESIZE
        { nullptr, IDC_SIZENWSE, 0 },                               // POINTER_NWSIZE
        { nullptr, IDC_SIZENESW, 0 },                               // POINTER_NESIZE
        { nullptr, IDC_SIZENESW, 0 },                               // POINTER_SWSIZE
        { nullptr, IDC_SIZENWSE, 0 },                               // POINTER_SESIZE
        { nullptr, IDC_SIZENS, 0 },                                 // POINTER_WINDOW_NSIZE
        { nullptr, IDC_SIZENS, 0 },                                 // POINTER_WINDOW_SSIZE
        { nullptr, IDC_SIZEWE, 0 },                                 // POINTER_WINDOW_WSIZE
        { nullptr, IDC_SIZEWE, 0 },                                 // POINTER_WINDOW_ESIZE
        { nullptr, IDC_SIZENWSE, 0 },                               // POINTER_WINDOW_NWSIZE
        { nullptr, IDC_SIZENESW, 0 },                               // POINTER_WINDOW_NESIZE
        { nullptr, IDC_SIZENESW, 0 },                               // POINTER_WINDOW_SWSIZE
        { nullptr, IDC_SIZENWSE, 0 },                               // POINTER_WINDOW_SESIZE
        { nullptr, IDC_SIZEWE, 0 },                                 // POINTER_HSPLIT
        { nullptr, IDC_SIZENS, 0 },                                 // POINTER_VSPLIT
        { nullptr, IDC_SIZEWE, 0 },                                 // POINTER_HSIZEBAR
        { nullptr, IDC_SIZENS, 0 },                                 // POINTER_VSIZEBAR
        { nullptr, IDC_HAND, 0 },                                   // POINTER_HAND
        { nullptr, IDC_HAND, 0 },                                   // POINTER_REFHAND
        { nullptr, IDC_PEN, 0 },                                    // POINTER_PEN
        { nullptr, nullptr, SAL_RESID_POINTER_MAGNIFY },            // POINTER_MAGNIFY
        { nullptr, nullptr, SAL_RESID_POINTER_FILL },               // POINTER_FILL
        { nullptr, nullptr, SAL_RESID_POINTER_ROTATE },             // POINTER_ROTATE
        { nullptr, nullptr, SAL_RESID_POINTER_HSHEAR },             // POINTER_HSHEAR
        { nullptr, nullptr, SAL_RESID_POINTER_VSHEAR },             // POINTER_VSHEAR
        { nullptr, nullptr, SAL_RESID_POINTER_MIRROR },             // POINTER_MIRROR
        { nullptr, nullptr, SAL_RESID_POINTER_CROOK },              // POINTER_CROOK
        { nullptr, nullptr, SAL_RESID_POINTER_CROP },               // POINTER_CROP
        { nullptr, nullptr, SAL_RESID_POINTER_MOVEPOINT },          // POINTER_MOVEPOINT
        { nullptr, nullptr, SAL_RESID_POINTER_MOVEBEZIERWEIGHT },   // POINTER_MOVEBEZIERWEIGHT
        { nullptr, nullptr, SAL_RESID_POINTER_MOVEDATA },           // POINTER_MOVEDATA
        { nullptr, nullptr, SAL_RESID_POINTER_COPYDATA },           // POINTER_COPYDATA
        { nullptr, nullptr, SAL_RESID_POINTER_LINKDATA },           // POINTER_LINKDATA
        { nullptr, nullptr, SAL_RESID_POINTER_MOVEDATALINK },       // POINTER_MOVEDATALINK
        { nullptr, nullptr, SAL_RESID_POINTER_COPYDATALINK },       // POINTER_COPYDATALINK
        { nullptr, nullptr, SAL_RESID_POINTER_MOVEFILE },           // POINTER_MOVEFILE
        { nullptr, nullptr, SAL_RESID_POINTER_COPYFILE },           // POINTER_COPYFILE
        { nullptr, nullptr, SAL_RESID_POINTER_LINKFILE },           // POINTER_LINKFILE
        { nullptr, nullptr, SAL_RESID_POINTER_MOVEFILELINK },       // POINTER_MOVEFILELINK
        { nullptr, nullptr, SAL_RESID_POINTER_COPYFILELINK },       // POINTER_COPYFILELINK
        { nullptr, nullptr, SAL_RESID_POINTER_MOVEFILES },          // POINTER_MOVEFILES
        { nullptr, nullptr, SAL_RESID_POINTER_COPYFILES },          // POINTER_COPYFILES
        { nullptr, IDC_NO, 0 },                                     // POINTER_NOTALLOWED
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_LINE },          // POINTER_DRAW_LINE
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_RECT },          // POINTER_DRAW_RECT
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_POLYGON },       // POINTER_DRAW_POLYGON
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_BEZIER },        // POINTER_DRAW_BEZIER
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_ARC },           // POINTER_DRAW_ARC
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_PIE },           // POINTER_DRAW_PIE
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_CIRCLECUT },     // POINTER_DRAW_CIRCLECUT
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_ELLIPSE },       // POINTER_DRAW_ELLIPSE
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_FREEHAND },      // POINTER_DRAW_FREEHAND
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_CONNECT },       // POINTER_DRAW_CONNECT
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_TEXT },          // POINTER_DRAW_TEXT
        { nullptr, nullptr, SAL_RESID_POINTER_DRAW_CAPTION },       // POINTER_DRAW_CAPTION
        { nullptr, nullptr, SAL_RESID_POINTER_CHART },              // POINTER_CHART
        { nullptr, nullptr, SAL_RESID_POINTER_DETECTIVE },          // POINTER_DETECTIVE
        { nullptr, nullptr, SAL_RESID_POINTER_PIVOT_COL },          // POINTER_PIVOT_COL
        { nullptr, nullptr, SAL_RESID_POINTER_PIVOT_ROW },          // POINTER_PIVOT_ROW
        { nullptr, nullptr, SAL_RESID_POINTER_PIVOT_FIELD },        // POINTER_PIVOT_FIELD
        { nullptr, nullptr, SAL_RESID_POINTER_CHAIN },              // POINTER_CHAIN
        { nullptr, nullptr, SAL_RESID_POINTER_CHAIN_NOTALLOWED },   // POINTER_CHAIN_NOTALLOWED
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_N },       // POINTER_AUTOSCROLL_N
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_S },       // POINTER_AUTOSCROLL_S
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_W },       // POINTER_AUTOSCROLL_W
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_E },       // POINTER_AUTOSCROLL_E
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NW },      // POINTER_AUTOSCROLL_NW
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NE },      // POINTER_AUTOSCROLL_NE
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_SW },      // POINTER_AUTOSCROLL_SW
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_SE },      // POINTER_AUTOSCROLL_SE
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NS },      // POINTER_AUTOSCROLL_NS
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_WE },      // POINTER_AUTOSCROLL_WE
        { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NSWE },    // POINTER_AUTOSCROLL_NSWE
        { nullptr, nullptr, SAL_RESID_POINTER_TEXT_VERTICAL },      // POINTER_TEXT_VERTICAL
        { nullptr, nullptr, SAL_RESID_POINTER_PIVOT_DELETE },       // POINTER_PIVOT_DELETE

         // #i32329#
        { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_S },       // POINTER_TAB_SELECT_S
        { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_E },       // POINTER_TAB_SELECT_E
        { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_SE },      // POINTER_TAB_SELECT_SE
        { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_W },       // POINTER_TAB_SELECT_W
        { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_SW },      // POINTER_TAB_SELECT_SW

        { nullptr, nullptr, SAL_RESID_POINTER_HIDEWHITESPACE },     // POINTER_HIDEWHITESPACE
        { nullptr, nullptr, SAL_RESID_POINTER_SHOWWHITESPACE }      // POINTER_UNHIDEWHITESPACE
        };

        // Mousepointer loaded ?
        if ( !aImplPtrTab[ePointerStyle].mhCursor )
        {
            if ( aImplPtrTab[ePointerStyle].mnOwnId )
                aImplPtrTab[ePointerStyle].mhCursor = ImplLoadSalCursor( aImplPtrTab[ePointerStyle].mnOwnId );
            else
                aImplPtrTab[ePointerStyle].mhCursor = LoadCursor( nullptr, aImplPtrTab[ePointerStyle].mnSysId );
        }

        // change the mouse pointer if different
        if ( mhCursor != aImplPtrTab[ePointerStyle].mhCursor )
        {
            mhCursor = aImplPtrTab[ePointerStyle].mhCursor;
            SetCursor( mhCursor );
        }
    }

    void WinSalFrame::CaptureMouse( bool bCapture )
    {
        // Send this Message to the window, because CaptureMouse() only work
        // in the thread of the window, which has create this window
        int nMsg;
        if ( bCapture )
            nMsg = SAL_MSG_CAPTUREMOUSE;
        else
            nMsg = SAL_MSG_RELEASEMOUSE;
        SendMessageW( mhWnd, nMsg, 0, 0 );
    }

    void WinSalFrame::SetPointerPos( long nX, long nY )
    {
        POINT aPt;
        aPt.x = static_cast<int>(nX);
        aPt.y = static_cast<int>(nY);
        ClientToScreen( mhWnd, &aPt );
        SetCursorPos( aPt.x, aPt.y );
    }

    void WinSalFrame::Flush()
    {
        GdiFlush();
    }

    static void ImplSalFrameSetInputContext( HWND hWnd, const SalInputContext* pContext )
    {
        WinSalFrame*   pFrame = GetWindowPtr( hWnd );
        bool           bIME(pContext->mnOptions & InputContextFlags::Text);
        if ( bIME )
        {
            if ( !pFrame->mbIME )
            {
                pFrame->mbIME = true;

                if ( pFrame->mhDefIMEContext )
                {
                    ImmAssociateContext( pFrame->mhWnd, pFrame->mhDefIMEContext );
                    UINT nImeProps = ImmGetProperty( GetKeyboardLayout( 0 ), IGP_PROPERTY );
                    pFrame->mbSpezIME = (nImeProps & IME_PROP_SPECIAL_UI) != 0;
                    pFrame->mbAtCursorIME = (nImeProps & IME_PROP_AT_CARET) != 0;
                    pFrame->mbHandleIME = !pFrame->mbSpezIME;
                }
            }

            // When the application can't handle IME messages, then the
            // System should handle the IME handling
            if ( !(pContext->mnOptions & InputContextFlags::ExtText) )
                pFrame->mbHandleIME = false;

            // Set the Font for IME Handling
            if ( pContext->mpFont )
            {
                HIMC hIMC = ImmGetContext( pFrame->mhWnd );
                if ( hIMC )
                {
                    LOGFONTW aLogFont;
                    HDC hDC = GetDC( pFrame->mhWnd );
                    // In case of vertical writing, always append a '@' to the
                    // Windows font name, not only if such a Windows font really is
                    // available (bTestVerticalAvail == false in the below call):
                    // The Windows IME's candidates window seems to always use a
                    // font that has all necessary glyphs, not necessarily the one
                    // specified by this font name; but it seems to decide whether
                    // to use that font's horizontal or vertical variant based on a
                    // '@' in front of this font name.
                    ImplGetLogFontFromFontSelect(hDC, pContext->mpFont->GetFontSelectPattern(),
                                                 nullptr, aLogFont);
                    ReleaseDC( pFrame->mhWnd, hDC );
                    ImmSetCompositionFontW( hIMC, &aLogFont );
                    ImmReleaseContext( pFrame->mhWnd, hIMC );
                }
            }
        }
        else
        {
            if ( pFrame->mbIME )
            {
                pFrame->mbIME = false;
                pFrame->mbHandleIME = false;
                ImmAssociateContext( pFrame->mhWnd, nullptr );
            }
        }
    }

    void WinSalFrame::SetInputContext( SalInputContext* pContext )
    {
        // Must be called in the main thread!
        SendMessageW( mhWnd, SAL_MSG_SETINPUTCONTEXT, 0, reinterpret_cast<LPARAM>(pContext) );
    }

    static void ImplSalFrameEndExtTextInput( HWND hWnd, EndExtTextInputFlags nFlags )
    {
        HIMC hIMC = ImmGetContext( hWnd );
        if ( hIMC )
        {
            DWORD nIndex;
            if ( nFlags & EndExtTextInputFlags::Complete )
                nIndex = CPS_COMPLETE;
            else
                nIndex = CPS_CANCEL;

            ImmNotifyIME( hIMC, NI_COMPOSITIONSTR, nIndex, 0 );
            ImmReleaseContext( hWnd, hIMC );
        }
    }

    void WinSalFrame::EndExtTextInput( EndExtTextInputFlags nFlags )
    {
        // Must be called in the main thread!
        SendMessageW( mhWnd, SAL_MSG_ENDEXTTEXTINPUT, static_cast<WPARAM>(nFlags), 0 );
    }

    static void ImplGetKeyNameText( LONG lParam, sal_Unicode* pBuf,
                                    UINT& rCount, UINT nMaxSize,
                                    const sal_Char* pReplace )
    {
        static_assert( sizeof( WCHAR ) == sizeof( sal_Unicode ), "must be the same size" );

        static const int nMaxKeyLen = 350;
        WCHAR aKeyBuf[ nMaxKeyLen ];
        int nKeyLen = 0;
        if ( lParam )
        {
            OUString aLang = Application::GetSettings().GetUILanguageTag().getLanguage();
            OUString aRet;

            aRet = ::vcl_sal::getKeysReplacementName( aLang, lParam );
            if( aRet.isEmpty() )
            {
                nKeyLen = GetKeyNameTextW( lParam, aKeyBuf, nMaxKeyLen );
                SAL_WARN_IF( nKeyLen > nMaxKeyLen, "vcl", "Invalid key name length!" );
                if( nKeyLen > nMaxKeyLen )
                    nKeyLen = 0;
                else if( nKeyLen > 0 )
                {
                    // Capitalize just the first letter of key names
                    CharLowerBuffW( aKeyBuf, nKeyLen );

                    bool bUpper = true;
                    for( WCHAR *pW=aKeyBuf, *pE=pW+nKeyLen; pW < pE; ++pW )
                    {
                        if( bUpper )
                            CharUpperBuffW( pW, 1 );
                        bUpper = (*pW=='+') || (*pW=='-') || (*pW==' ') || (*pW=='.');
                    }
                }
            }
            else
            {
                nKeyLen = aRet.getLength();
                wcscpy( aKeyBuf, o3tl::toW( aRet.getStr() ));
            }
        }

        if ( (nKeyLen > 0) || pReplace )
        {
            if( (rCount > 0) && (rCount < nMaxSize) )
            {
                pBuf[rCount] = '+';
                rCount++;
            }

            if( nKeyLen > 0 )
            {
                WCHAR *pW = aKeyBuf, *pE = aKeyBuf + nKeyLen;
                while ((pW < pE) && *pW && (rCount < nMaxSize))
                    pBuf[rCount++] = *pW++;
            }
            else // fall back to provided default name
            {
                while( *pReplace && (rCount < nMaxSize) )
                {
                    pBuf[rCount] = *pReplace;
                    rCount++;
                    pReplace++;
                }
            }
        }
        else
            rCount = 0;
    }

    OUString WinSalFrame::GetKeyName( sal_uInt16 nKeyCode )
    {
        static const UINT nMaxKeyLen = 350;
        sal_Unicode aKeyBuf[ nMaxKeyLen ];
        UINT        nKeyBufLen = 0;
        UINT        nSysCode = 0;

        if ( nKeyCode & KEY_MOD1 )
        {
            nSysCode = MapVirtualKeyW( VK_CONTROL, 0 );
            nSysCode = (nSysCode << 16) | ((sal_uLong(1)) << 25);
            ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, "Ctrl" );
        }

        if ( nKeyCode & KEY_MOD2 )
        {
            nSysCode = MapVirtualKeyW( VK_MENU, 0 );
            nSysCode = (nSysCode << 16) | ((sal_uLong(1)) << 25);
            ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, "Alt" );
        }

        if ( nKeyCode & KEY_SHIFT )
        {
            nSysCode = MapVirtualKeyW( VK_SHIFT, 0 );
            nSysCode = (nSysCode << 16) | ((sal_uLong(1)) << 25);
            ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, "Shift" );
        }

        sal_uInt16      nCode = nKeyCode & 0x0FFF;
        sal_uLong       nSysCode2 = 0;
        const sal_Char*   pReplace = nullptr;
        sal_Unicode cSVCode = 0;
        sal_Char    aFBuf[4];
        nSysCode = 0;

        if ( (nCode >= KEY_0) && (nCode <= KEY_9) )
            cSVCode = '0' + (nCode - KEY_0);
        else if ( (nCode >= KEY_A) && (nCode <= KEY_Z) )
            cSVCode = 'A' + (nCode - KEY_A);
        else if ( (nCode >= KEY_F1) && (nCode <= KEY_F26) )
        {
            nSysCode = VK_F1 + (nCode - KEY_F1);
            aFBuf[0] = 'F';
            if ( (nCode >= KEY_F1) && (nCode <= KEY_F9) )
            {
                aFBuf[1] = sal::static_int_cast<sal_Char>('1' + (nCode - KEY_F1));
                aFBuf[2] = 0;
            }
            else if ( (nCode >= KEY_F10) && (nCode <= KEY_F19) )
            {
                aFBuf[1] = '1';
                aFBuf[2] = sal::static_int_cast<sal_Char>('0' + (nCode - KEY_F10));
                aFBuf[3] = 0;
            }
            else
            {
                aFBuf[1] = '2';
                aFBuf[2] = sal::static_int_cast<sal_Char>('0' + (nCode - KEY_F20));
                aFBuf[3] = 0;
            }
            pReplace = aFBuf;
        }
        else
        {
            switch ( nCode )
            {
                case KEY_DOWN:
                    nSysCode = VK_DOWN;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "Down";
                    break;
                case KEY_UP:
                    nSysCode = VK_UP;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "Up";
                    break;
                case KEY_LEFT:
                    nSysCode = VK_LEFT;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "Left";
                    break;
                case KEY_RIGHT:
                    nSysCode = VK_RIGHT;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "Right";
                    break;
                case KEY_HOME:
                    nSysCode = VK_HOME;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "Home";
                    break;
                case KEY_END:
                    nSysCode = VK_END;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "End";
                    break;
                case KEY_PAGEUP:
                    nSysCode = VK_PRIOR;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "Page Up";
                    break;
                case KEY_PAGEDOWN:
                    nSysCode = VK_NEXT;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "Page Down";
                    break;
                case KEY_RETURN:
                    nSysCode = VK_RETURN;
                    pReplace = "Enter";
                    break;
                case KEY_ESCAPE:
                    nSysCode = VK_ESCAPE;
                    pReplace = "Escape";
                    break;
                case KEY_TAB:
                    nSysCode = VK_TAB;
                    pReplace = "Tab";
                    break;
                case KEY_BACKSPACE:
                    nSysCode = VK_BACK;
                    pReplace = "Backspace";
                    break;
                case KEY_SPACE:
                    nSysCode = VK_SPACE;
                    pReplace = "Space";
                    break;
                case KEY_INSERT:
                    nSysCode = VK_INSERT;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "Insert";
                    break;
                case KEY_DELETE:
                    nSysCode = VK_DELETE;
                    nSysCode2 = ((sal_uLong(1)) << 24);
                    pReplace = "Delete";
                    break;

                case KEY_ADD:
                    cSVCode  = '+';
                    break;
                case KEY_SUBTRACT:
                    cSVCode  = '-';
                    break;
                case KEY_MULTIPLY:
                    cSVCode  = '*';
                    break;
                case KEY_DIVIDE:
                    cSVCode  = '/';
                    break;
                case KEY_POINT:
                    cSVCode  = '.';
                    break;
                case KEY_COMMA:
                    cSVCode  = ',';
                    break;
                case KEY_LESS:
                    cSVCode  = '<';
                    break;
                case KEY_GREATER:
                    cSVCode  = '>';
                    break;
                case KEY_EQUAL:
                    cSVCode  = '=';
                    break;
                case KEY_SEMICOLON:
                    cSVCode = ';';
                    break;
                case KEY_QUOTERIGHT:
                    cSVCode = '\'';
                    break;
                case KEY_BRACKETLEFT:
                    cSVCode = '[';
                    break;
                case KEY_BRACKETRIGHT:
                    cSVCode = ']';
                    break;
            }
        }

        if ( nSysCode )
        {
            nSysCode = MapVirtualKeyW( nSysCode, 0 );
            if ( nSysCode )
                nSysCode = (nSysCode << 16) | nSysCode2;
            ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, pReplace );
        }
        else
        {
            if ( cSVCode )
            {
                if ( nKeyBufLen > 0 )
                    aKeyBuf[ nKeyBufLen++ ] = '+';
                if( nKeyBufLen < nMaxKeyLen )
                    aKeyBuf[ nKeyBufLen++ ] = cSVCode;
            }
        }

        if( !nKeyBufLen )
            return OUString();

        return OUString( aKeyBuf, sal::static_int_cast< sal_uInt16 >(nKeyBufLen) );
    }

    inline Color ImplWinColorToSal( COLORREF nColor )
    {
        return Color( GetRValue( nColor ), GetGValue( nColor ), GetBValue( nColor ) );
    }

    static void ImplSalUpdateStyleFontW( HDC hDC, const LOGFONTW& rLogFont, vcl::Font& rFont )
    {
        ImplSalLogFontToFontW( hDC, rLogFont, rFont );

        // On Windows 9x, Windows NT we get sometimes very small sizes
        // (for example for the small Caption height).
        // So if it is MS Sans Serif, a none scalable font we use
        // 8 Point as the minimum control height, in all other cases
        // 6 Point is the smallest one
        if ( rFont.GetFontHeight() < 8 )
        {
            if ( rtl_ustr_compareIgnoreAsciiCase( o3tl::toU(rLogFont.lfFaceName), o3tl::toU(L"MS Sans Serif") ) == 0 )
                rFont.SetFontHeight( 8 );
            else if ( rFont.GetFontHeight() < 6 )
                rFont.SetFontHeight( 6 );
        }
    }

    static long ImplW2I( const wchar_t* pStr )
    {
        long    n = 0;
        int     nSign = 1;

        if ( *pStr == '-' )
        {
            nSign = -1;
            pStr++;
        }

        while( (*pStr >= 48) && (*pStr <= 57) )
        {
            n *= 10;
            n += ((*pStr) - 48);
            pStr++;
        }

        n *= nSign;

        return n;
    }

    void WinSalFrame::UpdateSettings( AllSettings& rSettings )
    {
        MouseSettings aMouseSettings = rSettings.GetMouseSettings();
        aMouseSettings.SetDoubleClickTime( GetDoubleClickTime() );
        aMouseSettings.SetDoubleClickWidth( GetSystemMetrics( SM_CXDOUBLECLK ) );
        aMouseSettings.SetDoubleClickHeight( GetSystemMetrics( SM_CYDOUBLECLK ) );
        long nDragWidth = GetSystemMetrics( SM_CXDRAG );
        long nDragHeight = GetSystemMetrics( SM_CYDRAG );
        if ( nDragWidth )
            aMouseSettings.SetStartDragWidth( nDragWidth );
        if ( nDragHeight )
            aMouseSettings.SetStartDragHeight( nDragHeight );
        HKEY hRegKey;
        if ( RegOpenKeyW( HKEY_CURRENT_USER,
                          L"Control Panel\\Desktop",
                          &hRegKey ) == ERROR_SUCCESS )
        {
            wchar_t aValueBuf[10];
            DWORD   nValueSize = sizeof( aValueBuf );
            DWORD   nType;
            if ( RegQueryValueExW( hRegKey, L"MenuShowDelay", nullptr,
                                   &nType, reinterpret_cast<LPBYTE>(aValueBuf), &nValueSize ) == ERROR_SUCCESS )
            {
                if ( nType == REG_SZ )
                    aMouseSettings.SetMenuDelay( static_cast<sal_uLong>(ImplW2I( aValueBuf )) );
            }

            RegCloseKey( hRegKey );
        }

        StyleSettings aStyleSettings = rSettings.GetStyleSettings();

        aStyleSettings.SetScrollBarSize( GetSystemMetrics( SM_CXVSCROLL ) );
        aStyleSettings.SetSpinSize( GetSystemMetrics( SM_CXVSCROLL ) );
        UINT blinkTime = GetCaretBlinkTime();
        aStyleSettings.SetCursorBlinkTime(
            blinkTime == 0 || blinkTime == INFINITE // 0 indicates error
            ? STYLE_CURSOR_NOBLINKTIME : blinkTime );
        aStyleSettings.SetFloatTitleHeight( GetSystemMetrics( SM_CYSMCAPTION ) );
        aStyleSettings.SetTitleHeight( GetSystemMetrics( SM_CYCAPTION ) );
        aStyleSettings.SetActiveBorderColor( ImplWinColorToSal( GetSysColor( COLOR_ACTIVEBORDER ) ) );
        aStyleSettings.SetDeactiveBorderColor( ImplWinColorToSal( GetSysColor( COLOR_INACTIVEBORDER ) ) );
        aStyleSettings.SetDeactiveColor( ImplWinColorToSal( GetSysColor( COLOR_GRADIENTINACTIVECAPTION ) ) );
        aStyleSettings.SetFaceColor( ImplWinColorToSal( GetSysColor( COLOR_3DFACE ) ) );
        aStyleSettings.SetInactiveTabColor( aStyleSettings.GetFaceColor() );
        aStyleSettings.SetLightColor( ImplWinColorToSal( GetSysColor( COLOR_3DHILIGHT ) ) );
        aStyleSettings.SetLightBorderColor( ImplWinColorToSal( GetSysColor( COLOR_3DLIGHT ) ) );
        aStyleSettings.SetShadowColor( ImplWinColorToSal( GetSysColor( COLOR_3DSHADOW ) ) );
        aStyleSettings.SetDarkShadowColor( ImplWinColorToSal( GetSysColor( COLOR_3DDKSHADOW ) ) );
        aStyleSettings.SetHelpColor( ImplWinColorToSal( GetSysColor( COLOR_INFOBK ) ) );
        aStyleSettings.SetHelpTextColor( ImplWinColorToSal( GetSysColor( COLOR_INFOTEXT ) ) );

        aStyleSettings.SetDialogColor( aStyleSettings.GetFaceColor() );

        aStyleSettings.SetDialogTextColor( aStyleSettings.GetButtonTextColor() );
        aStyleSettings.SetButtonTextColor( ImplWinColorToSal( GetSysColor( COLOR_BTNTEXT ) ) );
        aStyleSettings.SetButtonRolloverTextColor( aStyleSettings.GetButtonTextColor() );
        aStyleSettings.SetButtonPressedRolloverTextColor( aStyleSettings.GetButtonTextColor() );
        aStyleSettings.SetTabTextColor( aStyleSettings.GetButtonTextColor() );
        aStyleSettings.SetTabRolloverTextColor( aStyleSettings.GetButtonTextColor() );
        aStyleSettings.SetTabHighlightTextColor( aStyleSettings.GetButtonTextColor() );
        aStyleSettings.SetRadioCheckTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) );
        aStyleSettings.SetGroupTextColor( aStyleSettings.GetRadioCheckTextColor() );
        aStyleSettings.SetLabelTextColor( aStyleSettings.GetRadioCheckTextColor() );
        aStyleSettings.SetWindowColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOW ) ) );
        aStyleSettings.SetActiveTabColor( aStyleSettings.GetWindowColor() );
        aStyleSettings.SetWindowTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) );
        aStyleSettings.SetToolTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) );
        aStyleSettings.SetFieldColor( aStyleSettings.GetWindowColor() );
        aStyleSettings.SetFieldTextColor( aStyleSettings.GetWindowTextColor() );
        aStyleSettings.SetFieldRolloverTextColor( aStyleSettings.GetFieldTextColor() );
        aStyleSettings.SetHighlightColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHT ) ) );
        aStyleSettings.SetHighlightTextColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHTTEXT ) ) );
        aStyleSettings.SetMenuHighlightColor( aStyleSettings.GetHighlightColor() );
        aStyleSettings.SetMenuHighlightTextColor( aStyleSettings.GetHighlightTextColor() );

        ImplSVData* pSVData = ImplGetSVData();
        pSVData->maNWFData.mnMenuFormatBorderX = 0;
        pSVData->maNWFData.mnMenuFormatBorderY = 0;
        pSVData->maNWFData.maMenuBarHighlightTextColor = COL_TRANSPARENT;
        GetSalData()->mbThemeMenuSupport = false;
        if (officecfg::Office::Common::Accessibility::AutoDetectSystemHC::get())
        {
            aStyleSettings.SetShadowColor( ImplWinColorToSal( GetSysColor( COLOR_ACTIVEBORDER ) ) );
            aStyleSettings.SetWorkspaceColor( ImplWinColorToSal( GetSysColor( COLOR_MENU ) ) );
        }
        aStyleSettings.SetMenuColor( ImplWinColorToSal( GetSysColor( COLOR_MENU ) ) );
        aStyleSettings.SetMenuBarColor( aStyleSettings.GetMenuColor() );
        aStyleSettings.SetMenuBarRolloverColor( aStyleSettings.GetHighlightColor() );
        aStyleSettings.SetMenuBorderColor( aStyleSettings.GetLightBorderColor() ); // overridden below for flat menus
        aStyleSettings.SetUseFlatBorders( false );
        aStyleSettings.SetUseFlatMenus( false );
        aStyleSettings.SetMenuTextColor( ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ) );
        if ( boost::optional<Color> aColor = aStyleSettings.GetPersonaMenuBarTextColor() )
        {
            aStyleSettings.SetMenuBarTextColor( *aColor );
            aStyleSettings.SetMenuBarRolloverTextColor( *aColor );
        }
        else
        {
            aStyleSettings.SetMenuBarTextColor( ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ) );
            aStyleSettings.SetMenuBarRolloverTextColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHTTEXT ) ) );
        }
        aStyleSettings.SetMenuBarHighlightTextColor(aStyleSettings.GetMenuHighlightTextColor());
        aStyleSettings.SetActiveColor( ImplWinColorToSal( GetSysColor( COLOR_ACTIVECAPTION ) ) );
        aStyleSettings.SetActiveTextColor( ImplWinColorToSal( GetSysColor( COLOR_CAPTIONTEXT ) ) );
        aStyleSettings.SetDeactiveColor( ImplWinColorToSal( GetSysColor( COLOR_INACTIVECAPTION ) ) );
        aStyleSettings.SetDeactiveTextColor( ImplWinColorToSal( GetSysColor( COLOR_INACTIVECAPTIONTEXT ) ) );
        BOOL bFlatMenus = FALSE;
        SystemParametersInfoW( SPI_GETFLATMENU, 0, &bFlatMenus, 0);
        if( bFlatMenus )
        {
            aStyleSettings.SetUseFlatMenus( true );
            aStyleSettings.SetMenuBarColor( ImplWinColorToSal( GetSysColor( COLOR_MENUBAR ) ) );
            aStyleSettings.SetMenuHighlightColor( ImplWinColorToSal( GetSysColor( COLOR_MENUHILIGHT ) ) );
            aStyleSettings.SetMenuBarRolloverColor( ImplWinColorToSal( GetSysColor( COLOR_MENUHILIGHT ) ) );
            aStyleSettings.SetMenuBorderColor( ImplWinColorToSal( GetSysColor( COLOR_3DSHADOW ) ) );

            // flat borders for our controls etc. as well in this mode (ie, no 3d borders)
            // this is not active in the classic style appearance
            aStyleSettings.SetUseFlatBorders( true );
        }
        aStyleSettings.SetCheckedColorSpecialCase( );

        // caret width
        DWORD nCaretWidth = 2;
        if( SystemParametersInfoW( SPI_GETCARETWIDTH, 0, &nCaretWidth, 0 ) )
            aStyleSettings.SetCursorSize( nCaretWidth );

        // High contrast
        HIGHCONTRAST hc;
        hc.cbSize = sizeof( HIGHCONTRAST );
        if( SystemParametersInfoW( SPI_GETHIGHCONTRAST, hc.cbSize, &hc, 0 )
                && (hc.dwFlags & HCF_HIGHCONTRASTON) )
            aStyleSettings.SetHighContrastMode( true );
        else
            aStyleSettings.SetHighContrastMode( false );

        // Query Fonts
        vcl::Font    aMenuFont = aStyleSettings.GetMenuFont();
        vcl::Font    aTitleFont = aStyleSettings.GetTitleFont();
        vcl::Font    aFloatTitleFont = aStyleSettings.GetFloatTitleFont();
        vcl::Font    aHelpFont = aStyleSettings.GetHelpFont();
        vcl::Font    aAppFont = aStyleSettings.GetAppFont();
        vcl::Font    aIconFont = aStyleSettings.GetIconFont();
        HDC     hDC = GetDC( nullptr );
        NONCLIENTMETRICSW aNonClientMetrics;
        aNonClientMetrics.cbSize = sizeof( aNonClientMetrics );
        if ( SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, sizeof( aNonClientMetrics ), &aNonClientMetrics, 0 ) )
        {
            ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfMenuFont, aMenuFont );
            ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfCaptionFont, aTitleFont );
            ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfSmCaptionFont, aFloatTitleFont );
            ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfStatusFont, aHelpFont );
            ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfMessageFont, aAppFont );

            LOGFONTW aLogFont;
            if ( SystemParametersInfoW( SPI_GETICONTITLELOGFONT, 0, &aLogFont, 0 ) )
                ImplSalUpdateStyleFontW( hDC, aLogFont, aIconFont );
        }

        ReleaseDC( nullptr, hDC );

        aStyleSettings.SetToolbarIconSize(ToolbarIconSize::Large);

        aStyleSettings.BatchSetFonts( aAppFont, aAppFont );

        aStyleSettings.SetMenuFont( aMenuFont );
        aStyleSettings.SetTitleFont( aTitleFont );
        aStyleSettings.SetFloatTitleFont( aFloatTitleFont );
        aStyleSettings.SetHelpFont( aHelpFont );
        aStyleSettings.SetIconFont( aIconFont );
        // We prefer Arial in the russian version, because MS Sans Serif
        // is to wide for the dialogs
        if ( rSettings.GetLanguageTag().getLanguageType() == LANGUAGE_RUSSIAN )
        {
            OUString aFontName = aAppFont.GetFamilyName();
            OUString aFirstName = aFontName.getToken( 0, ';' );
            if ( aFirstName.equalsIgnoreAsciiCase( "MS Sans Serif" ) )
            {
                aFontName = "Arial;" + aFontName;
                aAppFont.SetFamilyName( aFontName );
            }
        }

        if ( aAppFont.GetWeight() > WEIGHT_NORMAL )
            aAppFont.SetWeight( WEIGHT_NORMAL );
        aStyleSettings.SetToolFont( aAppFont );
        aStyleSettings.SetTabFont( aAppFont );

        BOOL bDragFull;
        if ( SystemParametersInfoW( SPI_GETDRAGFULLWINDOWS, 0, &bDragFull, 0 ) )
        {
            DragFullOptions nDragFullOptions = aStyleSettings.GetDragFullOptions();
            if ( bDragFull )
                nDragFullOptions |= DragFullOptions::WindowMove | DragFullOptions::WindowSize | DragFullOptions::Docking | DragFullOptions::Split;
            else
                nDragFullOptions &= ~DragFullOptions(DragFullOptions::WindowMove | DragFullOptions::WindowSize | DragFullOptions::Docking | DragFullOptions::Split);
            aStyleSettings.SetDragFullOptions( nDragFullOptions );
        }

        if ( RegOpenKeyW( HKEY_CURRENT_USER,
                          L"Control Panel\\International\\Calendars\\TwoDigitYearMax",
                          &hRegKey ) == ERROR_SUCCESS )
        {
            wchar_t aValueBuf[10];
            DWORD   nValue;
            DWORD   nValueSize = sizeof( aValueBuf );
            DWORD   nType;
            if ( RegQueryValueExW( hRegKey, L"1", nullptr,
                                   &nType, reinterpret_cast<LPBYTE>(aValueBuf), &nValueSize ) == ERROR_SUCCESS )
            {
                if ( nType == REG_SZ )
                {
                    nValue = static_cast<sal_uLong>(ImplW2I( aValueBuf ));
                    if ( (nValue > 1000) && (nValue < 10000) )
                    {
                        MiscSettings aMiscSettings = rSettings.GetMiscSettings();
                        utl::MiscCfg().SetYear2000( static_cast<sal_Int32>(nValue-99) );
                        rSettings.SetMiscSettings( aMiscSettings );
                    }
                }
            }

            RegCloseKey( hRegKey );
        }

        rSettings.SetMouseSettings( aMouseSettings );
        rSettings.SetStyleSettings( aStyleSettings );

        // now apply the values from theming, if available
        WinSalGraphics::updateSettingsNative( rSettings );
    }

    const SystemEnvData* WinSalFrame::GetSystemData() const
    {
        return &maSysData;
    }

    void WinSalFrame::Beep()
    {
        // a simple beep
        MessageBeep( 0 );
    }

    SalFrame::SalPointerState WinSalFrame::GetPointerState()
    {
        SalPointerState aState;
        aState.mnState = 0;

        if ( GetKeyState( VK_LBUTTON ) & 0x8000 )
            aState.mnState |= MOUSE_LEFT;
        if ( GetKeyState( VK_MBUTTON ) & 0x8000 )
            aState.mnState |= MOUSE_MIDDLE;
        if ( GetKeyState( VK_RBUTTON ) & 0x8000 )
            aState.mnState |= MOUSE_RIGHT;
        if ( GetKeyState( VK_SHIFT ) & 0x8000 )
            aState.mnState |= KEY_SHIFT;
        if ( GetKeyState( VK_CONTROL ) & 0x8000 )
            aState.mnState |= KEY_MOD1;
        if ( GetKeyState( VK_MENU ) & 0x8000 )
            aState.mnState |= KEY_MOD2;

        POINT pt;
        GetCursorPos( &pt );

        aState.maPos = Point( pt.x - maGeometry.nX, pt.y - maGeometry.nY );
        return aState;
    }

    KeyIndicatorState WinSalFrame::GetIndicatorState()
    {
        KeyIndicatorState aState = KeyIndicatorState::NONE;
        if (::GetKeyState(VK_CAPITAL))
            aState |= KeyIndicatorState::CAPSLOCK;

        if (::GetKeyState(VK_NUMLOCK))
            aState |= KeyIndicatorState::NUMLOCK;

        if (::GetKeyState(VK_SCROLL))
            aState |= KeyIndicatorState::SCROLLLOCK;

        return aState;
    }

    void WinSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode )
    {
        BYTE nVKey = 0;
        switch (nKeyCode)
        {
            case KEY_CAPSLOCK:
                nVKey = VK_CAPITAL;
            break;
        }

        if (nVKey > 0 && nVKey < 255)
        {
            ::keybd_event(nVKey, 0x45, KEYEVENTF_EXTENDEDKEY, 0);
            ::keybd_event(nVKey, 0x45, KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP, 0);
        }
    }

    void WinSalFrame::ResetClipRegion()
    {
        SetWindowRgn( mhWnd, nullptr, TRUE );
    }

    void WinSalFrame::BeginSetClipRegion( sal_uLong nRects )
    {
        if( mpClipRgnData )
            delete [] reinterpret_cast<BYTE*>(mpClipRgnData);
        sal_uLong nRectBufSize = sizeof(RECT)*nRects;
        mpClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+nRectBufSize]);
        mpClipRgnData->rdh.dwSize     = sizeof( RGNDATAHEADER );
        mpClipRgnData->rdh.iType      = RDH_RECTANGLES;
        mpClipRgnData->rdh.nCount     = nRects;
        mpClipRgnData->rdh.nRgnSize  = nRectBufSize;
        SetRectEmpty( &(mpClipRgnData->rdh.rcBound) );
        mpNextClipRect        = reinterpret_cast<RECT*>(&(mpClipRgnData->Buffer));
        mbFirstClipRect       = true;
    }

    void WinSalFrame::UnionClipRegion( long nX, long nY, long nWidth, long nHeight )
    {
        if( ! mpClipRgnData )
            return;

        RECT*       pRect = mpNextClipRect;
        RECT*       pBoundRect = &(mpClipRgnData->rdh.rcBound);
        long        nRight = nX + nWidth;
        long        nBottom = nY + nHeight;

        if ( mbFirstClipRect )
        {
            pBoundRect->left    = nX;
            pBoundRect->top     = nY;
            pBoundRect->right   = nRight;
            pBoundRect->bottom  = nBottom;
            mbFirstClipRect = false;
        }
        else
        {
            if ( nX < pBoundRect->left )
                pBoundRect->left = static_cast<int>(nX);

            if ( nY < pBoundRect->top )
                pBoundRect->top = static_cast<int>(nY);

            if ( nRight > pBoundRect->right )
                pBoundRect->right = static_cast<int>(nRight);

            if ( nBottom > pBoundRect->bottom )
                pBoundRect->bottom = static_cast<int>(nBottom);
        }

        pRect->left     = static_cast<int>(nX);
        pRect->top      = static_cast<int>(nY);
        pRect->right    = static_cast<int>(nRight);
        pRect->bottom   = static_cast<int>(nBottom);
        if( (mpNextClipRect  - reinterpret_cast<RECT*>(&mpClipRgnData->Buffer)) < static_cast<int>(mpClipRgnData->rdh.nCount) )
            mpNextClipRect++;
    }

    void WinSalFrame::EndSetClipRegion()
    {
        if( ! mpClipRgnData )
            return;

        HRGN hRegion;

        // create region from accumulated rectangles
        if ( mpClipRgnData->rdh.nCount == 1 )
        {
            RECT* pRect = &(mpClipRgnData->rdh.rcBound);
            hRegion = CreateRectRgn( pRect->left, pRect->top,
                                     pRect->right, pRect->bottom );
        }
        else
        {
            sal_uLong nSize = mpClipRgnData->rdh.nRgnSize+sizeof(RGNDATAHEADER);
            hRegion = ExtCreateRegion( nullptr, nSize, mpClipRgnData );
        }
        delete [] reinterpret_cast<BYTE*>(mpClipRgnData);
        mpClipRgnData = nullptr;

        SAL_WARN_IF( !hRegion, "vcl", "WinSalFrame::EndSetClipRegion() - Can't create ClipRegion" );
        if( hRegion )
        {
            RECT aWindowRect;
            GetWindowRect( mhWnd, &aWindowRect );
            POINT aPt;
            aPt.x=0;
            aPt.y=0;
            ClientToScreen( mhWnd, &aPt );
            OffsetRgn( hRegion, aPt.x - aWindowRect.left, aPt.y - aWindowRect.top );

            if( SetWindowRgn( mhWnd, hRegion, TRUE ) == 0 )
                DeleteObject( hRegion );
        }
    }

    static bool ImplHandleMouseMsg( HWND hWnd, UINT nMsg,
                                    WPARAM wParam, LPARAM lParam )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( !pFrame )
            return false;

        if( nMsg == WM_LBUTTONDOWN || nMsg == WM_MBUTTONDOWN || nMsg == WM_RBUTTONDOWN )
        {
            // #103168# post again if async focus has not arrived yet
            // hopefully we will not receive the corresponding button up before this
            // button down arrives again
            vcl::Window *pWin = pFrame->GetWindow();
            if( pWin && pWin->ImplGetWindowImpl()->mpFrameData->mnFocusId )
            {
                BOOL const ret = PostMessageW( hWnd, nMsg, wParam, lParam );
                SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
                return true;
            }
        }
        SalMouseEvent   aMouseEvt;
        bool            nRet;
        SalEvent        nEvent = SalEvent::NONE;
        bool            bCall = true;

        aMouseEvt.mnX       = static_cast<short>(LOWORD( lParam ));
        aMouseEvt.mnY       = static_cast<short>(HIWORD( lParam ));
        aMouseEvt.mnCode    = 0;
        aMouseEvt.mnTime    = GetMessageTime();

        // Use GetKeyState(), as some Logitech mouse drivers do not check
        // KeyState when simulating double-click with center mouse button

        if ( GetKeyState( VK_LBUTTON ) & 0x8000 )
            aMouseEvt.mnCode |= MOUSE_LEFT;
        if ( GetKeyState( VK_MBUTTON ) & 0x8000 )
            aMouseEvt.mnCode |= MOUSE_MIDDLE;
        if ( GetKeyState( VK_RBUTTON ) & 0x8000 )
            aMouseEvt.mnCode |= MOUSE_RIGHT;
        if ( GetKeyState( VK_SHIFT ) & 0x8000 )
            aMouseEvt.mnCode |= KEY_SHIFT;
        if ( GetKeyState( VK_CONTROL ) & 0x8000 )
            aMouseEvt.mnCode |= KEY_MOD1;
        if ( GetKeyState( VK_MENU ) & 0x8000 )
            aMouseEvt.mnCode |= KEY_MOD2;

        switch ( nMsg )
        {
            case WM_MOUSEMOVE:
                {
                // As the mouse events are not collected correctly when
                // pressing modifier keys (as interrupted by KeyEvents)
                // we do this here ourselves
                if ( aMouseEvt.mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2) )
                {
                    MSG aTempMsg;
                    if ( PeekMessageW( &aTempMsg, hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE | PM_NOYIELD ) )
                    {
                        if ( (aTempMsg.message == WM_MOUSEMOVE) &&
                             (aTempMsg.wParam == wParam) )
                            return true;
                    }
                }

                SalData* pSalData = GetSalData();
                // Test for MouseLeave
                if ( pSalData->mhWantLeaveMsg && (pSalData->mhWantLeaveMsg != hWnd) )
                    SendMessageW( pSalData->mhWantLeaveMsg, SAL_MSG_MOUSELEAVE, 0, GetMessagePos() );

                pSalData->mhWantLeaveMsg = hWnd;
                // Start MouseLeave-Timer
                if ( !pSalData->mpMouseLeaveTimer )
                {
                    pSalData->mpMouseLeaveTimer = new AutoTimer;
                    pSalData->mpMouseLeaveTimer->SetDebugName( "ImplHandleMouseMsg SalData::mpMouseLeaveTimer" );
                    pSalData->mpMouseLeaveTimer->SetTimeout( SAL_MOUSELEAVE_TIMEOUT );
                    pSalData->mpMouseLeaveTimer->Start();
                    // We don't need to set a timeout handler, because we test
                    // for mouseleave in the timeout callback
                }
                aMouseEvt.mnButton = 0;
                nEvent = SalEvent::MouseMove;
                }
                break;

            case WM_NCMOUSEMOVE:
            case SAL_MSG_MOUSELEAVE:
                {
                SalData* pSalData = GetSalData();
                if ( pSalData->mhWantLeaveMsg == hWnd )
                {
                    // Mouse-Coordinates are relative to the screen
                    POINT aPt;
                    aPt.x = static_cast<short>(LOWORD(lParam));
                    aPt.y = static_cast<short>(HIWORD(lParam));
                    ScreenToClient(hWnd, &aPt);
                    if (const auto& pHelpWin = ImplGetSVData()->maHelpData.mpHelpWin)
                    {
                        const tools::Rectangle& rHelpRect = pHelpWin->GetHelpArea();
                        if (rHelpRect.IsInside(Point(aPt.x, aPt.y)))
                        {
                            // We have entered a tooltip (help window). Don't call the handler here; it
                            // would launch the sequence "Mouse leaves the Control->Control redraws->
                            // Help window gets destroyed->Mouse enters the Control->Control redraws",
                            // which takes CPU and may flicker. Just destroy the help window and pretend
                            // we are still over the original window.
                            ImplDestroyHelpWindow(true);
                            bCall = false;
                            break;
                        }
                    }
                    pSalData->mhWantLeaveMsg = nullptr;
                    if ( pSalData->mpMouseLeaveTimer )
                    {
                        delete pSalData->mpMouseLeaveTimer;
                        pSalData->mpMouseLeaveTimer = nullptr;
                    }
                    aMouseEvt.mnX = aPt.x;
                    aMouseEvt.mnY = aPt.y;
                    aMouseEvt.mnButton = 0;
                    nEvent = SalEvent::MouseLeave;
                }
                else
                    bCall = false;
                }
                break;

            case WM_LBUTTONDOWN:
                aMouseEvt.mnButton = MOUSE_LEFT;
                nEvent = SalEvent::MouseButtonDown;
                break;

            case WM_MBUTTONDOWN:
                aMouseEvt.mnButton = MOUSE_MIDDLE;
                nEvent = SalEvent::MouseButtonDown;
                break;

            case WM_RBUTTONDOWN:
                aMouseEvt.mnButton = MOUSE_RIGHT;
                nEvent = SalEvent::MouseButtonDown;
                break;

            case WM_LBUTTONUP:
                aMouseEvt.mnButton = MOUSE_LEFT;
                nEvent = SalEvent::MouseButtonUp;
                break;

            case WM_MBUTTONUP:
                aMouseEvt.mnButton = MOUSE_MIDDLE;
                nEvent = SalEvent::MouseButtonUp;
                break;

            case WM_RBUTTONUP:
                aMouseEvt.mnButton = MOUSE_RIGHT;
                nEvent = SalEvent::MouseButtonUp;
                break;
        }

        // check if this window was destroyed - this might happen if we are the help window
        // and sent a mouse leave message to the application which killed the help window, ie ourselves
        if( !IsWindow( hWnd ) )
            return false;

        if ( bCall )
        {
            if ( nEvent == SalEvent::MouseButtonDown )
                UpdateWindow( hWnd );

            if( AllSettings::GetLayoutRTL() )
                aMouseEvt.mnX = pFrame->maGeometry.nWidth-1-aMouseEvt.mnX;

            nRet = pFrame->CallCallback( nEvent, &aMouseEvt );
            if ( nMsg == WM_MOUSEMOVE )
                SetCursor( pFrame->mhCursor );
        }
        else
            nRet = false;

        return nRet;
    }

    static bool ImplHandleMouseActivateMsg( HWND hWnd )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( !pFrame )
            return false;

        if ( pFrame->mbFloatWin )
            return true;

        return pFrame->CallCallback( SalEvent::MouseActivate, nullptr );
    }

    static bool ImplHandleWheelMsg( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
    {
        DBG_ASSERT( nMsg == WM_MOUSEWHEEL ||
                    nMsg == WM_MOUSEHWHEEL,
                    "ImplHandleWheelMsg() called with no wheel mouse event" );

        ImplSalYieldMutexAcquireWithWait();

        bool nRet = false;
        WinSalFrame*   pFrame = GetWindowPtr( hWnd );
        if ( pFrame )
        {
            WORD    nWinModCode = LOWORD( wParam );
            POINT   aWinPt;
            aWinPt.x    = static_cast<short>(LOWORD( lParam ));
            aWinPt.y    = static_cast<short>(HIWORD( lParam ));
            ScreenToClient( hWnd, &aWinPt );

            SalWheelMouseEvent aWheelEvt;
            aWheelEvt.mnTime        = GetMessageTime();
            aWheelEvt.mnX           = aWinPt.x;
            aWheelEvt.mnY           = aWinPt.y;
            aWheelEvt.mnCode        = 0;
            aWheelEvt.mnDelta       = static_cast<short>(HIWORD( wParam ));
            aWheelEvt.mnNotchDelta  = aWheelEvt.mnDelta/WHEEL_DELTA;
            if( aWheelEvt.mnNotchDelta == 0 )
            {
                if( aWheelEvt.mnDelta > 0 )
                    aWheelEvt.mnNotchDelta = 1;
                else if( aWheelEvt.mnDelta < 0 )
                    aWheelEvt.mnNotchDelta = -1;
            }

            if( nMsg == WM_MOUSEWHEEL )
            {
                if ( aSalShlData.mnWheelScrollLines == WHEEL_PAGESCROLL )
                    aWheelEvt.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL;
                else
                    aWheelEvt.mnScrollLines = aSalShlData.mnWheelScrollLines;
                aWheelEvt.mbHorz        = false;
            }
            else
            {
                aWheelEvt.mnScrollLines = aSalShlData.mnWheelScrollChars;
                aWheelEvt.mbHorz        = true;

                // fdo#36380 - seems horiz scrolling has swapped direction
                aWheelEvt.mnDelta *= -1;
                aWheelEvt.mnNotchDelta *= -1;
            }

            if ( nWinModCode & MK_SHIFT )
                aWheelEvt.mnCode |= KEY_SHIFT;
            if ( nWinModCode & MK_CONTROL )
                aWheelEvt.mnCode |= KEY_MOD1;
            if ( GetKeyState( VK_MENU ) & 0x8000 )
                aWheelEvt.mnCode |= KEY_MOD2;

            if( AllSettings::GetLayoutRTL() )
                aWheelEvt.mnX = pFrame->maGeometry.nWidth-1-aWheelEvt.mnX;

            nRet = pFrame->CallCallback( SalEvent::WheelMouse, &aWheelEvt );
        }

        ImplSalYieldMutexRelease();

        return nRet;
    }

    static sal_uInt16 ImplSalGetKeyCode( WPARAM wParam )
    {
        sal_uInt16 nKeyCode;

        // convert KeyCode
        if ( wParam < KEY_TAB_SIZE )
            nKeyCode = aImplTranslateKeyTab[wParam];
        else
        {
            SalData* pSalData = GetSalData();
            std::map< UINT, sal_uInt16 >::const_iterator it = pSalData->maVKMap.find( static_cast<UINT>(wParam) );
            if( it != pSalData->maVKMap.end() )
                nKeyCode = it->second;
            else
                nKeyCode = 0;
        }

        return nKeyCode;
    }

    static void ImplUpdateInputLang( WinSalFrame* pFrame )
    {
        UINT nLang = LOWORD( GetKeyboardLayout( 0 ) );
        if ( nLang && nLang != pFrame->mnInputLang )
        {
            // keep input lang up-to-date
            pFrame->mnInputLang = nLang;
        }

        // We are on Windows NT so we use Unicode FrameProcs and get
        // Unicode charcodes directly from Windows no need to set up a
        // code page
        return;
    }

    static sal_Unicode ImplGetCharCode( WinSalFrame* pFrame, WPARAM nCharCode )
    {
        ImplUpdateInputLang( pFrame );

        // We are on Windows NT so we use Unicode FrameProcs and we
        // get Unicode charcodes directly from Windows
        return static_cast<sal_Unicode>(nCharCode);
    }

    LanguageType WinSalFrame::GetInputLanguage()
    {
        if( !mnInputLang )
            ImplUpdateInputLang( this );

        if( !mnInputLang )
            return LANGUAGE_DONTKNOW;
        else
            return LanguageType(mnInputLang);
    }

    bool WinSalFrame::MapUnicodeToKeyCode( sal_Unicode aUnicode, LanguageType aLangType, vcl::KeyCode& rKeyCode )
    {
        bool bRet = false;
        sal_IntPtr nLangType = static_cast<sal_uInt16>(aLangType);
        // just use the passed language identifier, do not try to load additional keyboard support
        HKL hkl = reinterpret_cast<HKL>(nLangType);

        if( hkl )
        {
            SHORT scan = VkKeyScanExW( aUnicode, hkl );
            if( LOWORD(scan) == 0xFFFF )
                // keyboard not loaded or key cannot be mapped
                bRet = false;
            else
            {
                BYTE vkeycode   = LOBYTE(scan);
                BYTE shiftstate = HIBYTE(scan);

                // Last argument is set to false, because there's no decision made
                // yet which key should be assigned to MOD3 modifier on Windows.
                // Windows key - user's can be confused, because it should display
                //               Windows menu (applies to both left/right key)
                // Menu key    - this key is used to display context menu
                // AltGr key   - probably it has no sense
                rKeyCode = vcl::KeyCode( ImplSalGetKeyCode( vkeycode ),
                    (shiftstate & 0x01) != 0,     // shift
                    (shiftstate & 0x02) != 0,     // ctrl
                    (shiftstate & 0x04) != 0,     // alt
                    false );
                bRet = true;
            }
        }

        return bRet;
    }

    static bool ImplHandleKeyMsg( HWND hWnd, UINT nMsg,
                                  WPARAM wParam, LPARAM lParam, LRESULT& rResult )
    {
        static bool         bIgnoreCharMsg  = false;
        static WPARAM       nDeadChar       = 0;
        static WPARAM       nLastVKChar     = 0;
        static sal_uInt16   nLastChar       = 0;
        static ModKeyFlags  nLastModKeyCode = ModKeyFlags::NONE;
        static bool         bWaitForModKeyRelease = false;
        sal_uInt16          nRepeat         = LOWORD( lParam )-1;
        sal_uInt16          nModCode        = 0;

        // this key might have been relayed by SysChild and thus
        // may not be processed twice
        GetSalData()->mnSalObjWantKeyEvt = 0;

        if ( nMsg == WM_DEADCHAR )
        {
            nDeadChar = wParam;
            return false;
        }

        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( !pFrame )
            return false;

        // reset the background mode for each text input,
        // as some tools such as RichWin may have changed it
        if ( pFrame->mpLocalGraphics &&
             pFrame->mpLocalGraphics->getHDC() )
            SetBkMode( pFrame->mpLocalGraphics->getHDC(), TRANSPARENT );

        // determine modifiers
        if ( GetKeyState( VK_SHIFT ) & 0x8000 )
            nModCode |= KEY_SHIFT;
        if ( GetKeyState( VK_CONTROL ) & 0x8000 )
            nModCode |= KEY_MOD1;
        if (GetKeyState(VK_MENU) & 0x8000)
            nModCode |= KEY_MOD2;

        if ( (nMsg == WM_CHAR) || (nMsg == WM_SYSCHAR) )
        {
            nDeadChar = 0;

            if ( bIgnoreCharMsg )
            {
                bIgnoreCharMsg = false;
                // #101635# if zero is returned here for WM_SYSCHAR (ALT+<key>) Windows will beep
                // because this 'hotkey' was not processed -> better return 1
                // except for Alt-SPACE which should always open the sysmenu (#104616#)

                // also return zero if a system menubar is available that might process this hotkey
                // this also applies to the OLE inplace embedding where we are a child window
                if( (GetWindowStyle( hWnd ) & WS_CHILD) || GetMenu( hWnd ) || (wParam == 0x20) )
                    return false;
                else
                    return true;
            }

            // ignore backspace as a single key, so that
            // we do not get problems for combinations w/ a DeadKey
            if ( wParam == 0x08 )    // BACKSPACE
                return false;

            // only "free flying" WM_CHAR messages arrive here, that are
            // created by typing a ALT-NUMPAD combination
            SalKeyEvent aKeyEvt;

            if ( (wParam >= '0') && (wParam <= '9') )
                aKeyEvt.mnCode = sal::static_int_cast<sal_uInt16>(KEYGROUP_NUM + wParam - '0');
            else if ( (wParam >= 'A') && (wParam <= 'Z') )
                aKeyEvt.mnCode = sal::static_int_cast<sal_uInt16>(KEYGROUP_ALPHA + wParam - 'A');
            else if ( (wParam >= 'a') && (wParam <= 'z') )
                aKeyEvt.mnCode = sal::static_int_cast<sal_uInt16>(KEYGROUP_ALPHA + wParam - 'a');
            else if ( wParam == 0x0D )    // RETURN
                aKeyEvt.mnCode = KEY_RETURN;
            else if ( wParam == 0x1B )    // ESCAPE
                aKeyEvt.mnCode = KEY_ESCAPE;
            else if ( wParam == 0x09 )    // TAB
                aKeyEvt.mnCode = KEY_TAB;
            else if ( wParam == 0x20 )    // SPACE
                aKeyEvt.mnCode = KEY_SPACE;
            else
                aKeyEvt.mnCode = 0;

            aKeyEvt.mnCode     |= nModCode;
            aKeyEvt.mnCharCode  = ImplGetCharCode( pFrame, wParam );
            aKeyEvt.mnRepeat    = nRepeat;
            nLastChar = 0;
            nLastVKChar = 0;
            bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
            pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
            return nRet;
        }
         // #i11583#, MCD, 2003-01-13, Support for WM_UNICHAR & Keyman 6.0; addition begins
        else if( nMsg == WM_UNICHAR )
         {
             // If Windows is asking if we accept WM_UNICHAR, return TRUE
             if(wParam == UNICODE_NOCHAR)
            {
                rResult = TRUE; // ssa: this will actually return TRUE to windows
                return true;    // ...but this will only avoid calling the defwindowproc
            }

             SalKeyEvent aKeyEvt;
             aKeyEvt.mnCode     = nModCode; // Or should it be 0? - as this is always a character returned
             aKeyEvt.mnRepeat   = 0;

            if( wParam >= Uni_SupplementaryPlanesStart )
            {
                // character is supplementary char in UTF-32 format - must be converted to UTF-16 supplementary pair
                // sal_Unicode ch = (sal_Unicode) Uni_UTF32ToSurrogate1(wParam);
                 nLastChar = 0;
                 nLastVKChar = 0;
                 pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
                 pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
                wParam = static_cast<sal_Unicode>(Uni_UTF32ToSurrogate2( wParam ));
             }

             aKeyEvt.mnCharCode = static_cast<sal_Unicode>(wParam);

             nLastChar = 0;
             nLastVKChar = 0;
             bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
             pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );

             return nRet;
         }
         // MCD, 2003-01-13, Support for WM_UNICHAR & Keyman 6.0; addition ends
        else
        {
            // for shift, control and menu we issue a KeyModChange event
            if ( (wParam == VK_SHIFT) || (wParam == VK_CONTROL) || (wParam == VK_MENU) )
            {
                SalKeyModEvent aModEvt;
                aModEvt.mbDown = false; // auto-accelerator feature not supported here.
                aModEvt.mnCode = nModCode;
                aModEvt.mnModKeyCode = ModKeyFlags::NONE;   // no command events will be sent if this member is 0

                ModKeyFlags tmpCode = ModKeyFlags::NONE;
                if( GetKeyState( VK_LSHIFT )  & 0x8000 )
                    tmpCode |= ModKeyFlags::LeftShift;
                if( GetKeyState( VK_RSHIFT )  & 0x8000 )
                    tmpCode |= ModKeyFlags::RightShift;
                if( GetKeyState( VK_LCONTROL ) & 0x8000 )
                    tmpCode |= ModKeyFlags::LeftMod1;
                if( GetKeyState( VK_RCONTROL ) & 0x8000 )
                    tmpCode |= ModKeyFlags::RightMod1;
                if( GetKeyState( VK_LMENU )  & 0x8000 )
                    tmpCode |= ModKeyFlags::LeftMod2;
                if( GetKeyState( VK_RMENU )  & 0x8000 )
                    tmpCode |= ModKeyFlags::RightMod2;

                if( tmpCode < nLastModKeyCode )
                {
                    aModEvt.mnModKeyCode = nLastModKeyCode;
                    nLastModKeyCode = ModKeyFlags::NONE;
                    bWaitForModKeyRelease = true;
                }
                else
                {
                    if( !bWaitForModKeyRelease )
                        nLastModKeyCode = tmpCode;
                }

                if( tmpCode == ModKeyFlags::NONE )
                    bWaitForModKeyRelease = false;

                return pFrame->CallCallback( SalEvent::KeyModChange, &aModEvt );
            }
            else
            {
                SalKeyEvent     aKeyEvt;
                SalEvent        nEvent;
                MSG             aCharMsg;
                BOOL            bCharPeek = FALSE;
                UINT            nCharMsg = WM_CHAR;
                bool            bKeyUp = (nMsg == WM_KEYUP) || (nMsg == WM_SYSKEYUP);

                nLastModKeyCode = ModKeyFlags::NONE; // make sure no modkey messages are sent if they belong to a hotkey (see above)
                aKeyEvt.mnCharCode = 0;
                aKeyEvt.mnCode = 0;

                aKeyEvt.mnCode = ImplSalGetKeyCode( wParam );
                if ( !bKeyUp )
                {
                    // check for charcode
                    // Get the related WM_CHAR message using PeekMessage, if available.
                    // The WM_CHAR message is always at the beginning of the
                    // message queue. Also it is made certain that there is always only
                    // one WM_CHAR message in the queue.
                    bCharPeek = PeekMessageW( &aCharMsg, hWnd,
                                                 WM_CHAR, WM_CHAR, PM_NOREMOVE | PM_NOYIELD );
                    if ( bCharPeek && (nDeadChar == aCharMsg.wParam) )
                    {
                        bCharPeek = FALSE;
                        nDeadChar = 0;

                        if ( wParam == VK_BACK )
                        {
                            PeekMessageW( &aCharMsg, hWnd,
                                             nCharMsg, nCharMsg, PM_REMOVE | PM_NOYIELD );
                            return false;
                        }
                    }
                    else
                    {
                        if ( !bCharPeek )
                        {
                            bCharPeek = PeekMessageW( &aCharMsg, hWnd,
                                                        WM_SYSCHAR, WM_SYSCHAR, PM_NOREMOVE | PM_NOYIELD );
                            nCharMsg = WM_SYSCHAR;
                        }
                    }
                    if ( bCharPeek )
                        aKeyEvt.mnCharCode = ImplGetCharCode( pFrame, aCharMsg.wParam );
                    else
                        aKeyEvt.mnCharCode = 0;

                    nLastChar = aKeyEvt.mnCharCode;
                    nLastVKChar = wParam;
                }
                else
                {
                    if ( wParam == nLastVKChar )
                    {
                        aKeyEvt.mnCharCode = nLastChar;
                        nLastChar = 0;
                        nLastVKChar = 0;
                    }
                }

                if ( aKeyEvt.mnCode || aKeyEvt.mnCharCode )
                {
                    if ( bKeyUp )
                        nEvent = SalEvent::KeyUp;
                    else
                        nEvent = SalEvent::KeyInput;

                    aKeyEvt.mnCode     |= nModCode;
                    aKeyEvt.mnRepeat    = nRepeat;

                    if ((nModCode & (KEY_MOD1 | KEY_MOD2)) == (KEY_MOD1 | KEY_MOD2) &&
                        aKeyEvt.mnCharCode)
                    {
                        // this is actually AltGr and should not be handled as Alt
                        aKeyEvt.mnCode &= ~(KEY_MOD1 | KEY_MOD2);
                    }

                    bIgnoreCharMsg = bCharPeek;
                    bool nRet = pFrame->CallCallback( nEvent, &aKeyEvt );
                    // independent part only reacts on keyup but Windows does not send
                    // keyup for VK_HANJA
                    if( aKeyEvt.mnCode == KEY_HANGUL_HANJA )
                        nRet = pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );

                    bIgnoreCharMsg = false;

                    // char-message, than remove or ignore
                    if ( bCharPeek )
                    {
                        nDeadChar = 0;
                        if ( nRet )
                        {
                            PeekMessageW( &aCharMsg, hWnd,
                                             nCharMsg, nCharMsg, PM_REMOVE | PM_NOYIELD );
                        }
                        else
                            bIgnoreCharMsg = true;
                    }

                    return nRet;
                }
                else
                    return false;
            }
        }
    }

    bool ImplHandleSalObjKeyMsg( HWND hWnd, UINT nMsg,
                                 WPARAM wParam, LPARAM lParam )
    {
        if ( (nMsg == WM_KEYDOWN) || (nMsg == WM_KEYUP) )
        {
            WinSalFrame* pFrame = GetWindowPtr( hWnd );
            if ( !pFrame )
                return false;

            sal_uInt16  nRepeat     = LOWORD( lParam )-1;
            sal_uInt16  nModCode    = 0;

            // determine modifiers
            if ( GetKeyState( VK_SHIFT ) & 0x8000 )
                nModCode |= KEY_SHIFT;
            if ( GetKeyState( VK_CONTROL ) & 0x8000 )
                nModCode |= KEY_MOD1;
            if ( GetKeyState( VK_MENU ) & 0x8000 )
                nModCode |= KEY_MOD2;

            if ( (wParam != VK_SHIFT) && (wParam != VK_CONTROL) && (wParam != VK_MENU) )
            {
                SalKeyEvent     aKeyEvt;
                SalEvent        nEvent;
                bool            bKeyUp = (nMsg == WM_KEYUP) || (nMsg == WM_SYSKEYUP);

                // convert KeyCode
                aKeyEvt.mnCode      = ImplSalGetKeyCode( wParam );
                aKeyEvt.mnCharCode  = 0;

                if ( aKeyEvt.mnCode )
                {
                    if ( bKeyUp )
                        nEvent = SalEvent::KeyUp;
                    else
                        nEvent = SalEvent::KeyInput;

                    aKeyEvt.mnCode     |= nModCode;
                    aKeyEvt.mnRepeat    = nRepeat;
                    bool nRet = pFrame->CallCallback( nEvent, &aKeyEvt );
                    return nRet;
                }
                else
                    return false;
            }
        }

        return false;
    }

    bool ImplHandleSalObjSysCharMsg( HWND hWnd, WPARAM wParam, LPARAM lParam )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( !pFrame )
            return false;

        sal_uInt16  nRepeat     = LOWORD( lParam )-1;
        sal_uInt16  nModCode    = 0;
        sal_uInt16  cKeyCode    = static_cast<sal_uInt16>(wParam);

        // determine modifiers
        if ( GetKeyState( VK_SHIFT ) & 0x8000 )
            nModCode |= KEY_SHIFT;
        if ( GetKeyState( VK_CONTROL ) & 0x8000 )
            nModCode |= KEY_MOD1;
        nModCode |= KEY_MOD2;

        // assemble KeyEvent
        SalKeyEvent aKeyEvt;
        if ( (cKeyCode >= 48) && (cKeyCode <= 57) )
            aKeyEvt.mnCode = KEY_0+(cKeyCode-48);
        else if ( (cKeyCode >= 65) && (cKeyCode <= 90) )
            aKeyEvt.mnCode = KEY_A+(cKeyCode-65);
        else  if ( (cKeyCode >= 97) && (cKeyCode <= 122) )
            aKeyEvt.mnCode = KEY_A+(cKeyCode-97);
        else
            aKeyEvt.mnCode = 0;
        aKeyEvt.mnCode     |= nModCode;
        aKeyEvt.mnCharCode  = ImplGetCharCode( pFrame, cKeyCode );
        aKeyEvt.mnRepeat    = nRepeat;
        bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
        pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
        return nRet;
    }

    enum class DeferPolicy
    {
        Blocked,
        Allowed
    };

    // Remember to release the solar mutex on success!
    static inline WinSalFrame* ProcessOrDeferMessage( HWND hWnd, INT nMsg, WPARAM pWParam = 0,
                                                      DeferPolicy eCanDefer = DeferPolicy::Allowed )
    {
        bool bFailedCondition = false, bGotMutex = false;
        WinSalFrame* pFrame = nullptr;

        if ( DeferPolicy::Blocked == eCanDefer )
            assert( (DeferPolicy::Blocked == eCanDefer) && (nMsg == 0) && (pWParam == 0) );
        else
            assert( (DeferPolicy::Allowed == eCanDefer) && (nMsg != 0) );

        if ( DeferPolicy::Blocked == eCanDefer )
        {
            ImplSalYieldMutexAcquireWithWait();
            bGotMutex = true;
        }
        else if ( !(bGotMutex = ImplSalYieldMutexTryToAcquire()) )
            bFailedCondition = true;

        if ( !bFailedCondition )
        {
            pFrame = GetWindowPtr( hWnd );
            bFailedCondition = pFrame == nullptr;
        }

        if ( bFailedCondition )
        {
            if ( bGotMutex )
                ImplSalYieldMutexRelease();
            if ( DeferPolicy::Allowed == eCanDefer )
            {
                BOOL const ret = PostMessageW(hWnd, nMsg, pWParam, 0);
                SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
            }
        }

        return pFrame;
    }

    enum class PostedState
    {
        IsPosted,
        IsInitial
    };

    static bool ImplHandlePostPaintMsg( HWND hWnd, RECT* pRect,
                                        PostedState eProcessed = PostedState::IsPosted )
    {
        RECT* pMsgRect;
        if ( PostedState::IsInitial == eProcessed )
        {
            pMsgRect = new RECT;
            CopyRect( pMsgRect, pRect );
        }
        else
            pMsgRect = pRect;

        WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTPAINT,
                                                     reinterpret_cast<WPARAM>(pMsgRect) );
        if ( pFrame )
        {
            SalPaintEvent aPEvt( pRect->left, pRect->top, pRect->right-pRect->left, pRect->bottom-pRect->top );
            pFrame->CallCallback( SalEvent::Paint, &aPEvt );
            ImplSalYieldMutexRelease();
            if ( PostedState::IsPosted == eProcessed )
                delete pRect;
        }

        return (pFrame != nullptr);
    }

    static bool ImplHandlePaintMsg( HWND hWnd )
    {
        bool bPaintSuccessful = false;

        // even without the Yield mutex, we can still change the clip region,
        // because other threads don't use the Yield mutex
        // --> see AcquireGraphics()

        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( pFrame )
        {
            // clip region must be set, as we don't get a proper
            // bounding rectangle otherwise
            WinSalGraphics *pGraphics = pFrame->mpLocalGraphics;
            bool bHasClipRegion = pGraphics &&
                pGraphics->getHDC() && pGraphics->getRegion();
            if ( bHasClipRegion )
                SelectClipRgn( pGraphics->getHDC(), nullptr );

            // according to Windows documentation one shall check first if
            // there really is a paint-region
            RECT aUpdateRect;
            PAINTSTRUCT aPs;
            bool bHasPaintRegion = GetUpdateRect( hWnd, nullptr, FALSE );
            if ( bHasPaintRegion )
            {
                // call BeginPaint/EndPaint to query the paint rect and use
                // this information in the (deferred) paint
                BeginPaint( hWnd, &aPs );
                CopyRect( &aUpdateRect, &aPs.rcPaint );
            }

            // reset clip region
            if ( bHasClipRegion )
                SelectClipRgn( pGraphics->getHDC(), pGraphics->getRegion() );

            // try painting
            if ( bHasPaintRegion )
            {
                bPaintSuccessful = ImplHandlePostPaintMsg(
                    hWnd, &aUpdateRect, PostedState::IsInitial );
                EndPaint( hWnd, &aPs );
            }
            else // if there is nothing to paint, the paint is successful
                bPaintSuccessful = true;
        }

        return bPaintSuccessful;
    }

    static void SetMaximizedFrameGeometry( HWND hWnd, WinSalFrame* pFrame, RECT* pParentRect )
    {
        // calculate and set frame geometry of a maximized window - useful if the window is still hidden

        // dualmonitor support:
        // Get screensize of the monitor with the mouse pointer

        RECT aRectMouse;
        if( ! pParentRect )
        {
            POINT pt;
            GetCursorPos( &pt );
            aRectMouse.left = pt.x;
            aRectMouse.top = pt.y;
            aRectMouse.right = pt.x+2;
            aRectMouse.bottom = pt.y+2;
            pParentRect = &aRectMouse;
        }

        RECT aRect;
        ImplSalGetWorkArea( hWnd, &aRect, pParentRect );

        // a maximized window has no other borders than the caption
        pFrame->maGeometry.nLeftDecoration = pFrame->maGeometry.nRightDecoration = pFrame->maGeometry.nBottomDecoration = 0;
        pFrame->maGeometry.nTopDecoration = pFrame->mbCaption ? GetSystemMetrics( SM_CYCAPTION ) : 0;

        aRect.top += pFrame->maGeometry.nTopDecoration;
        pFrame->maGeometry.nX = aRect.left;
        pFrame->maGeometry.nY = aRect.top;
        pFrame->maGeometry.nWidth = aRect.right - aRect.left;
        pFrame->maGeometry.nHeight = aRect.bottom - aRect.top;
    }

    static void UpdateFrameGeometry( HWND hWnd, WinSalFrame* pFrame )
    {
        if( !pFrame )
            return;

        RECT aRect;
        GetWindowRect( hWnd, &aRect );
        memset(&pFrame->maGeometry, 0, sizeof(SalFrameGeometry) );

        if ( IsIconic( hWnd ) )
            return;

        POINT aPt;
        aPt.x=0;
        aPt.y=0;
        ClientToScreen(hWnd, &aPt);
        int cx = aPt.x - aRect.left;
        pFrame->maGeometry.nTopDecoration = aPt.y - aRect.top;

        pFrame->maGeometry.nLeftDecoration = cx;
        pFrame->maGeometry.nRightDecoration = cx;

        pFrame->maGeometry.nX = aPt.x;
        pFrame->maGeometry.nY = aPt.y;

        RECT aInnerRect;
        GetClientRect( hWnd, &aInnerRect );
        if( aInnerRect.right )
        {
            // improve right decoration
            aPt.x=aInnerRect.right;
            aPt.y=aInnerRect.top;
            ClientToScreen(hWnd, &aPt);
            pFrame->maGeometry.nRightDecoration = aRect.right - aPt.x;
        }
        if( aInnerRect.bottom ) // may be zero if window was not shown yet
            pFrame->maGeometry.nBottomDecoration += aRect.bottom - aPt.y - aInnerRect.bottom;
        else
            // bottom border is typically the same as left/right
            pFrame->maGeometry.nBottomDecoration = pFrame->maGeometry.nLeftDecoration;

        int nWidth  = aRect.right - aRect.left
            - pFrame->maGeometry.nRightDecoration - pFrame->maGeometry.nLeftDecoration;
        int nHeight = aRect.bottom - aRect.top
            - pFrame->maGeometry.nBottomDecoration - pFrame->maGeometry.nTopDecoration;
        // clamp to zero
        pFrame->maGeometry.nHeight = nHeight < 0 ? 0 : nHeight;
        pFrame->maGeometry.nWidth = nWidth < 0 ? 0 : nWidth;
        pFrame->updateScreenNumber();
    }

    static void ImplCallMoveHdl( HWND hWnd )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( pFrame )
        {
            pFrame->CallCallback( SalEvent::Move, nullptr );
            // to avoid doing Paint twice by VCL and SAL
            //if ( IsWindowVisible( hWnd ) && !pFrame->mbInShow )
            //    UpdateWindow( hWnd );
        }
    }

    static void ImplCallClosePopupsHdl( HWND hWnd )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( pFrame )
        {
            pFrame->CallCallback( SalEvent::ClosePopups, nullptr );
        }
    }

    static void ImplHandleMoveMsg( HWND hWnd )
    {
        WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTMOVE );
            if ( pFrame )
            {
                UpdateFrameGeometry( hWnd, pFrame );

                if ( GetWindowStyle( hWnd ) & WS_VISIBLE )
                    pFrame->mbDefPos = false;

                // protect against recursion
                if ( !pFrame->mbInMoveMsg )
                {
                    // adjust window again for FullScreenMode
                    pFrame->mbInMoveMsg = true;
                    if ( pFrame->mbFullScreen )
                        ImplSalFrameFullScreenPos( pFrame );
                    pFrame->mbInMoveMsg = false;
                }

                // save state
                ImplSaveFrameState( pFrame );

                // Call Hdl
                //#93851 if we call this handler, VCL floating windows are not updated correctly
                ImplCallMoveHdl( hWnd );

                ImplSalYieldMutexRelease();
            }
    }

    static void ImplCallSizeHdl( HWND hWnd )
    {
        // as Windows can send these messages also, we have to use
        // the Solar semaphore
        WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTCALLSIZE );
            if ( pFrame )
            {
                pFrame->CallCallback( SalEvent::Resize, nullptr );
                // to avoid double Paints by VCL and SAL
                if ( IsWindowVisible( hWnd ) && !pFrame->mbInShow )
                    UpdateWindow( hWnd );

                ImplSalYieldMutexRelease();
            }
    }

    static void ImplHandleSizeMsg( HWND hWnd, WPARAM wParam, LPARAM lParam )
    {
        if ( (wParam != SIZE_MAXSHOW) && (wParam != SIZE_MAXHIDE) )
        {
            WinSalFrame* pFrame = GetWindowPtr( hWnd );
            if ( pFrame )
            {
                UpdateFrameGeometry( hWnd, pFrame );

                pFrame->mnWidth  = static_cast<int>(LOWORD(lParam));
                pFrame->mnHeight = static_cast<int>(HIWORD(lParam));
                // save state
                ImplSaveFrameState( pFrame );
                // Call Hdl
                ImplCallSizeHdl( hWnd );

                WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
                if ( pTimer )
                    pTimer->SetForceRealTimer( true );
            }
        }
    }

    static void ImplHandleFocusMsg( HWND hWnd )
    {
        WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTFOCUS );
        if ( pFrame )
        {
            if ( !WinSalFrame::mbInReparent )
            {
                bool bGotFocus = ::GetFocus() == hWnd;
                if ( bGotFocus )
                {
                    if ( IsWindowVisible( hWnd ) && !pFrame->mbInShow )
                        UpdateWindow( hWnd );

                    // do we support IME?
                    if ( pFrame->mbIME && pFrame->mhDefIMEContext )
                    {
                        UINT nImeProps = ImmGetProperty( GetKeyboardLayout( 0 ), IGP_PROPERTY );

                        pFrame->mbSpezIME = (nImeProps & IME_PROP_SPECIAL_UI) != 0;
                        pFrame->mbAtCursorIME = (nImeProps & IME_PROP_AT_CARET) != 0;
                        pFrame->mbHandleIME = !pFrame->mbSpezIME;
                    }
                }
                pFrame->CallCallback( bGotFocus ? SalEvent::GetFocus : SalEvent::LoseFocus, nullptr );
            }
            ImplSalYieldMutexRelease();
        }
    }

    static void ImplHandleCloseMsg( HWND hWnd )
    {
        WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, WM_CLOSE );
            if ( pFrame )
            {
                pFrame->CallCallback( SalEvent::Close, nullptr );
                ImplSalYieldMutexRelease();
            }
    }

    static bool ImplHandleShutDownMsg( HWND hWnd )
    {
        bool nRet = false;
        WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, 0, 0, DeferPolicy::Blocked );
        if ( pFrame )
        {
            nRet = pFrame->CallCallback( SalEvent::Shutdown, nullptr );
            ImplSalYieldMutexRelease();
        }
        return nRet;
    }

    static void ImplHandleSettingsChangeMsg( HWND hWnd, UINT nMsg,
                                             WPARAM wParam, LPARAM lParam )
    {
        SalEvent nSalEvent = SalEvent::SettingsChanged;

        if ( nMsg == WM_DEVMODECHANGE )
            nSalEvent = SalEvent::PrinterChanged;
        else if ( nMsg == WM_DISPLAYCHANGE )
            nSalEvent = SalEvent::DisplayChanged;
        else if ( nMsg == WM_FONTCHANGE )
            nSalEvent = SalEvent::FontChanged;
        else if ( nMsg == WM_WININICHANGE )
        {
            if ( lParam )
            {
                if ( ImplSalWICompareAscii( reinterpret_cast<const wchar_t*>(lParam), "devices" ) == 0 )
                    nSalEvent = SalEvent::PrinterChanged;
            }
        }

        if ( nMsg == WM_SETTINGCHANGE )
        {
            if ( wParam == SPI_SETWHEELSCROLLLINES )
                aSalShlData.mnWheelScrollLines = ImplSalGetWheelScrollLines();
            else if( wParam == SPI_SETWHEELSCROLLCHARS )
                aSalShlData.mnWheelScrollChars = ImplSalGetWheelScrollChars();
        }

        if ( WM_SYSCOLORCHANGE == nMsg && GetSalData()->mhDitherPal )
            ImplUpdateSysColorEntries();

        WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, 0, 0, DeferPolicy::Blocked );
        if ( pFrame )
        {
            if ( (nMsg == WM_DISPLAYCHANGE) || (nMsg == WM_WININICHANGE) )
            {
                if ( pFrame->mbFullScreen )
                    ImplSalFrameFullScreenPos( pFrame );
            }

            pFrame->CallCallback( nSalEvent, nullptr );
            ImplSalYieldMutexRelease();
        }
    }

    static void ImplHandleUserEvent( HWND hWnd, LPARAM lParam )
    {
        WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, 0, 0, DeferPolicy::Blocked );
        if ( pFrame )
        {
            pFrame->CallCallback( SalEvent::UserEvent, reinterpret_cast<void*>(lParam) );
            ImplSalYieldMutexRelease();
        }
    }

    static void ImplHandleForcePalette( HWND hWnd )
    {
        SalData*    pSalData = GetSalData();
        HPALETTE    hPal = pSalData->mhDitherPal;
        if ( hPal )
        {
            WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_FORCEPALETTE );
            if ( pFrame && pFrame->mpLocalGraphics && pFrame->mpLocalGraphics->getHDC() )
            {
                WinSalGraphics* pGraphics = pFrame->mpLocalGraphics;
                if ( pGraphics && pGraphics->getDefPal() )
                {
                    SelectPalette( pGraphics->getHDC(), hPal, FALSE );
                    if ( RealizePalette( pGraphics->getHDC() ) )
                    {
                        InvalidateRect( hWnd, nullptr, FALSE );
                        UpdateWindow( hWnd );
                        pFrame->CallCallback( SalEvent::DisplayChanged, nullptr );
                    }
                }
            }
            if ( pFrame )
                ImplSalYieldMutexRelease();
        }
    }

    static LRESULT ImplHandlePalette( bool bFrame, HWND hWnd, UINT nMsg,
                                      WPARAM wParam, LPARAM lParam, bool& rDef )
    {
        SalData*    pSalData = GetSalData();
        HPALETTE    hPal = pSalData->mhDitherPal;
        if ( !hPal )
            return 0;

        rDef = false;
        if ( pSalData->mbInPalChange )
            return 0;

        if ( (nMsg == WM_PALETTECHANGED) || (nMsg == SAL_MSG_POSTPALCHANGED) )
        {
            if ( reinterpret_cast<HWND>(wParam) == hWnd )
                return 0;
        }

        bool bReleaseMutex = false;
        if ( (nMsg == WM_QUERYNEWPALETTE) || (nMsg == WM_PALETTECHANGED) )
        {
            // as Windows can send these messages also, we have to use
            // the Solar semaphore
            if ( ImplSalYieldMutexTryToAcquire() )
                bReleaseMutex = true;
            else if ( nMsg == WM_QUERYNEWPALETTE )
            {
                BOOL const ret = PostMessageW(hWnd, SAL_MSG_POSTQUERYNEWPAL, wParam, lParam);
                SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
            }
            else /* ( nMsg == WM_PALETTECHANGED ) */
            {
                BOOL const ret = PostMessageW(hWnd, SAL_MSG_POSTPALCHANGED, wParam, lParam);
                SAL_WARN_IF(0 == ret, "vcl", "ERROR: PostMessage() failed!");
            }
        }

        WinSalVirtualDevice*pTempVD;
        WinSalFrame*        pTempFrame;
        WinSalGraphics*     pGraphics;
        HDC                 hDC;
        HPALETTE            hOldPal;
        UINT                nCols;
        bool                bStdDC;
        bool                bUpdate;

        pSalData->mbInPalChange = true;

        // reset all palettes in VirDevs and Frames
        pTempVD = pSalData->mpFirstVD;
        while ( pTempVD )
        {
            pGraphics = pTempVD->getGraphics();
            if ( pGraphics->getDefPal() )
            {
                SelectPalette( pGraphics->getHDC(),
                               pGraphics->getDefPal(),
                               TRUE );
            }
            pTempVD = pTempVD->getNext();
        }
        pTempFrame = pSalData->mpFirstFrame;
        while ( pTempFrame )
        {
            pGraphics = pTempFrame->mpLocalGraphics;
            if ( pGraphics && pGraphics->getHDC() && pGraphics->getDefPal() )
            {
                SelectPalette( pGraphics->getHDC(),
                               pGraphics->getDefPal(),
                               TRUE );
            }
            pTempFrame = pTempFrame->mpNextFrame;
        }

        // re-initialize palette
        WinSalFrame* pFrame = nullptr;
        if ( bFrame )
            pFrame = GetWindowPtr( hWnd );
        if ( pFrame && pFrame->mpLocalGraphics && pFrame->mpLocalGraphics->getHDC() )
        {
            hDC = pFrame->mpLocalGraphics->getHDC();
            bStdDC = true;
        }
        else
        {
            hDC = GetDC( hWnd );
            bStdDC = false;
        }
        UnrealizeObject( hPal );
        hOldPal = SelectPalette( hDC, hPal, TRUE );
        nCols = RealizePalette( hDC );
        bUpdate = nCols != 0;
        if ( !bStdDC )
        {
            SelectPalette( hDC, hOldPal, TRUE );
            ReleaseDC( hWnd, hDC );
        }

        // reset all palettes in VirDevs and Frames
        pTempVD = pSalData->mpFirstVD;
        while ( pTempVD )
        {
            pGraphics = pTempVD->getGraphics();
            if ( pGraphics->getDefPal() )
            {
                SelectPalette( pGraphics->getHDC(), hPal, TRUE );
                RealizePalette( pGraphics->getHDC() );
            }
            pTempVD = pTempVD->getNext();
        }
        pTempFrame = pSalData->mpFirstFrame;
        while ( pTempFrame )
        {
            if ( pTempFrame != pFrame )
            {
                pGraphics = pTempFrame->mpLocalGraphics;
                if ( pGraphics && pGraphics->getHDC() && pGraphics->getDefPal() )
                {
                    SelectPalette( pGraphics->getHDC(), hPal, TRUE );
                    if ( RealizePalette( pGraphics->getHDC() ) )
                        bUpdate = true;
                }
            }
            pTempFrame = pTempFrame->mpNextFrame;
        }

        // if colors changed, update the window
        if ( bUpdate )
        {
            pTempFrame = pSalData->mpFirstFrame;
            while ( pTempFrame )
            {
                pGraphics = pTempFrame->mpLocalGraphics;
                if ( pGraphics && pGraphics->getHDC() && pGraphics->getDefPal() )
                {
                    InvalidateRect( pTempFrame->mhWnd, nullptr, FALSE );
                    UpdateWindow( pTempFrame->mhWnd );
                    pTempFrame->CallCallback( SalEvent::DisplayChanged, nullptr );
                }
                pTempFrame = pTempFrame->mpNextFrame;
            }
        }

        pSalData->mbInPalChange = false;

        if ( bReleaseMutex )
            ImplSalYieldMutexRelease();

        if ( nMsg == WM_PALETTECHANGED )
            return 0;
        else
            return nCols;
    }

    static bool ImplHandleMinMax( HWND hWnd, LPARAM lParam )
    {
        bool bRet = false;

        if ( ImplSalYieldMutexTryToAcquire() )
        {
            WinSalFrame* pFrame = GetWindowPtr( hWnd );
            if ( pFrame )
            {
                MINMAXINFO* pMinMax = reinterpret_cast<MINMAXINFO*>(lParam);

                if ( pFrame->mbFullScreen )
                {
                    int         nX;
                    int         nY;
                    int         nDX;
                    int         nDY;
                    ImplSalCalcFullScreenSize( pFrame, nX, nY, nDX, nDY );

                    if ( pMinMax->ptMaxPosition.x > nX )
                        pMinMax->ptMaxPosition.x = nX;
                    if ( pMinMax->ptMaxPosition.y > nY )
                        pMinMax->ptMaxPosition.y = nY;

                    if ( pMinMax->ptMaxSize.x < nDX )
                        pMinMax->ptMaxSize.x = nDX;
                    if ( pMinMax->ptMaxSize.y < nDY )
                        pMinMax->ptMaxSize.y = nDY;
                    if ( pMinMax->ptMaxTrackSize.x < nDX )
                        pMinMax->ptMaxTrackSize.x = nDX;
                    if ( pMinMax->ptMaxTrackSize.y < nDY )
                        pMinMax->ptMaxTrackSize.y = nDY;

                    pMinMax->ptMinTrackSize.x = nDX;
                    pMinMax->ptMinTrackSize.y = nDY;

                    bRet = true;
                }

                if ( pFrame->mnMinWidth || pFrame->mnMinHeight )
                {
                    int nWidth   = pFrame->mnMinWidth;
                    int nHeight  = pFrame->mnMinHeight;

                    ImplSalAddBorder( pFrame, nWidth, nHeight );

                    if ( pMinMax->ptMinTrackSize.x < nWidth )
                         pMinMax->ptMinTrackSize.x = nWidth;
                    if ( pMinMax->ptMinTrackSize.y < nHeight )
                         pMinMax->ptMinTrackSize.y = nHeight;
                }

                if ( pFrame->mnMaxWidth || pFrame->mnMaxHeight )
                {
                    int nWidth   = pFrame->mnMaxWidth;
                    int nHeight  = pFrame->mnMaxHeight;

                    ImplSalAddBorder( pFrame, nWidth, nHeight );

                    if( nWidth > 0 && nHeight > 0 ) // protect against int overflow due to INT_MAX initialisation
                    {
                        if ( pMinMax->ptMaxTrackSize.x > nWidth )
                            pMinMax->ptMaxTrackSize.x = nWidth;
                        if ( pMinMax->ptMaxTrackSize.y > nHeight )
                            pMinMax->ptMaxTrackSize.y = nHeight;
                    }
                }
            }

            ImplSalYieldMutexRelease();
        }

        return bRet;
    }

    // retrieves the SalMenuItem pointer from a hMenu
    // the pointer is stored in every item, so if no position
    // is specified we just use the first item (ie, pos=0)
    // if bByPosition is false then nPos denotes a menu id instead of a position
    static WinSalMenuItem* ImplGetSalMenuItem( HMENU hMenu, UINT nPos, bool bByPosition=true )
    {
        MENUITEMINFOW mi;
        memset(&mi, 0, sizeof(mi));
        mi.cbSize = sizeof( mi );
        mi.fMask = MIIM_DATA;
        if( !GetMenuItemInfoW( hMenu, nPos, bByPosition, &mi) )
            SAL_WARN("vcl", "GetMenuItemInfoW failed: " << WindowsErrorString(GetLastError()));

        return reinterpret_cast<WinSalMenuItem *>(mi.dwItemData);
    }

    // returns the index of the currently selected item if any or -1
    static int ImplGetSelectedIndex( HMENU hMenu )
    {
        MENUITEMINFOW mi;
        memset(&mi, 0, sizeof(mi));
        mi.cbSize = sizeof( mi );
        mi.fMask = MIIM_STATE;
        int n = GetMenuItemCount( hMenu );
        if( n != -1 )
        {
            for(int i=0; i<n; i++ )
            {
                if( !GetMenuItemInfoW( hMenu, i, TRUE, &mi) )
                    SAL_WARN( "vcl", "GetMenuItemInfoW faled: " << WindowsErrorString( GetLastError() ) );
                else
                {
                    if( mi.fState & MFS_HILITE )
                        return i;
                }
            }
        }
        return -1;
    }

    static LRESULT ImplMenuChar( HWND, WPARAM wParam, LPARAM lParam )
    {
        LRESULT nRet = MNC_IGNORE;
        HMENU hMenu = reinterpret_cast<HMENU>(lParam);
        OUString aMnemonic( "&" + OUStringLiteral1(static_cast<sal_Unicode>(LOWORD(wParam))) );
        aMnemonic = aMnemonic.toAsciiLowerCase();   // we only have ascii mnemonics

        // search the mnemonic in the current menu
        int nItemCount = GetMenuItemCount( hMenu );
        int nFound = 0;
        int idxFound = -1;
        int idxSelected = ImplGetSelectedIndex( hMenu );
        int idx = idxSelected != -1 ? idxSelected+1 : 0;    // if duplicate mnemonics cycle through menu
        for( int i=0; i< nItemCount; i++, idx++ )
        {
            WinSalMenuItem* pSalMenuItem = ImplGetSalMenuItem( hMenu, idx % nItemCount );
            if( !pSalMenuItem )
                continue;
            OUString aStr = pSalMenuItem->mText;
            aStr = aStr.toAsciiLowerCase();
            if( aStr.indexOf( aMnemonic ) != -1 )
            {
                if( idxFound == -1 )
                    idxFound = idx % nItemCount;
                if( nFound++ )
                    break;  // duplicate found
            }
        }
        if( nFound == 1 )
            nRet = MAKELRESULT( idxFound, MNC_EXECUTE );
        else
            // duplicate mnemonics, just select the next occurrence
            nRet = MAKELRESULT( idxFound, MNC_SELECT );

        return nRet;
    }

    static LRESULT ImplMeasureItem( HWND hWnd, WPARAM wParam, LPARAM lParam )
    {
        LRESULT nRet = 0;
        if( !wParam )
        {
            // request was sent by a menu
            nRet = 1;
            MEASUREITEMSTRUCT *pMI = reinterpret_cast<LPMEASUREITEMSTRUCT>(lParam);
            if( pMI->CtlType != ODT_MENU )
                return 0;

            WinSalMenuItem *pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(pMI->itemData);
            if( !pSalMenuItem )
                return 0;

            HDC hdc = GetDC( hWnd );
            SIZE strSize;

            NONCLIENTMETRICSW ncm;
            memset( &ncm, 0, sizeof(ncm) );
            ncm.cbSize = sizeof( ncm );
            SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );

            // Assume every menu item can be default and printed bold
            //ncm.lfMenuFont.lfWeight = FW_BOLD;

            HFONT hfntOld = static_cast<HFONT>(SelectObject(hdc, CreateFontIndirectW( &ncm.lfMenuFont )));

            // menu text and accelerator
            OUString aStr(pSalMenuItem->mText);
            if( pSalMenuItem->mAccelText.getLength() )
            {
                aStr += " ";
                aStr += pSalMenuItem->mAccelText;
            }
            GetTextExtentPoint32W( hdc, o3tl::toW(aStr.getStr()),
                                    aStr.getLength(), &strSize );

            // image
            Size bmpSize( 16, 16 );
            //if( !!pSalMenuItem->maBitmap )
            //    bmpSize = pSalMenuItem->maBitmap.GetSizePixel();

            // checkmark
            Size checkSize( GetSystemMetrics( SM_CXMENUCHECK ), GetSystemMetrics( SM_CYMENUCHECK ) );

            pMI->itemWidth = checkSize.Width() + 3 + bmpSize.Width() + 3 + strSize.cx;
            pMI->itemHeight = std::max( std::max( checkSize.Height(), bmpSize.Height() ), strSize.cy );
            pMI->itemHeight += 4;

            DeleteObject( SelectObject(hdc, hfntOld) );
            ReleaseDC( hWnd, hdc );
        }

        return nRet;
    }

    static LRESULT ImplDrawItem(HWND, WPARAM wParam, LPARAM lParam )
    {
        LRESULT nRet = 0;
        if( !wParam )
        {
            // request was sent by a menu
            nRet = 1;
            DRAWITEMSTRUCT *pDI = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
            if( pDI->CtlType != ODT_MENU )
                return 0;

            WinSalMenuItem *pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(pDI->itemData);
            if( !pSalMenuItem )
                return 0;

            COLORREF clrPrevText, clrPrevBkgnd;
            HFONT hfntOld;
            HBRUSH hbrOld;
            bool    fChecked = (pDI->itemState & ODS_CHECKED);
            bool    fSelected = (pDI->itemState & ODS_SELECTED);
            bool    fDisabled = (pDI->itemState & (ODS_DISABLED | ODS_GRAYED));

            // Set the appropriate foreground and background colors.
            RECT aRect = pDI->rcItem;

            clrPrevBkgnd = SetBkColor( pDI->hDC, GetSysColor( COLOR_MENU ) );

            if ( fDisabled )
                clrPrevText = SetTextColor( pDI->hDC, GetSysColor( COLOR_GRAYTEXT ) );
            else
                clrPrevText = SetTextColor( pDI->hDC, GetSysColor( fSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT ) );

            DWORD colBackground = GetSysColor( fSelected ? COLOR_HIGHLIGHT : COLOR_MENU );
            clrPrevBkgnd = SetBkColor( pDI->hDC, colBackground );

            hbrOld = static_cast<HBRUSH>(SelectObject( pDI->hDC, CreateSolidBrush( GetBkColor( pDI->hDC ) ) ));

            // Fill background
            if(!PatBlt( pDI->hDC, aRect.left, aRect.top, aRect.right-aRect.left, aRect.bottom-aRect.top, PATCOPY ))
                SAL_WARN("vcl", "PatBlt failed: " << WindowsErrorString(GetLastError()));

            int lineHeight = aRect.bottom-aRect.top;

            int x = aRect.left;
            int y = aRect.top;

            int checkWidth  = GetSystemMetrics( SM_CXMENUCHECK );
            int checkHeight = GetSystemMetrics( SM_CYMENUCHECK );
            if( fChecked )
            {
                RECT r;
                r.left = 0;
                r.top = 0;
                r.right = checkWidth;
                r.bottom = checkWidth;
                HDC memDC = CreateCompatibleDC( pDI->hDC );
                HBITMAP memBmp = CreateCompatibleBitmap( pDI->hDC, checkWidth, checkHeight );
                HBITMAP hOldBmp = static_cast<HBITMAP>(SelectObject( memDC, memBmp ));
                DrawFrameControl( memDC, &r, DFC_MENU, DFCS_MENUCHECK );
                BitBlt( pDI->hDC, x, y+(lineHeight-checkHeight)/2, checkWidth, checkHeight, memDC, 0, 0, SRCAND );
                DeleteObject( SelectObject( memDC, hOldBmp ) );
                DeleteDC( memDC );
            }
            x += checkWidth+3;

            //Size bmpSize = aBitmap.GetSizePixel();
            Size bmpSize(16, 16);
            if( !!pSalMenuItem->maBitmap )
            {
                Bitmap aBitmap( pSalMenuItem->maBitmap );

                // set transparent pixels to background color
                if( fDisabled )
                    colBackground = RGB(255,255,255);
                aBitmap.Replace( COL_LIGHTMAGENTA,
                    Color( GetRValue(colBackground),GetGValue(colBackground),GetBValue(colBackground) ));

                WinSalBitmap* pSalBmp = static_cast<WinSalBitmap*>(aBitmap.ImplGetSalBitmap().get());
                HGLOBAL hDrawDIB = pSalBmp->ImplGethDIB();

                if( hDrawDIB )
                {
                    PBITMAPINFO         pBI = static_cast<PBITMAPINFO>(GlobalLock( hDrawDIB ));
                    PBYTE               pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize +
                                                WinSalBitmap::ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD );

                    HBITMAP hBmp = CreateDIBitmap( pDI->hDC, &pBI->bmiHeader, CBM_INIT, pBits, pBI, DIB_RGB_COLORS );
                    GlobalUnlock( hDrawDIB );

                    HBRUSH hbrIcon = CreateSolidBrush( GetSysColor( COLOR_GRAYTEXT ) );
                    DrawStateW( pDI->hDC, hbrIcon, nullptr, reinterpret_cast<LPARAM>(hBmp), WPARAM(0),
                        x, y+(lineHeight-bmpSize.Height())/2, bmpSize.Width(), bmpSize.Height(),
                         DST_BITMAP | (fDisabled ? (fSelected ? DSS_MONO : DSS_DISABLED) : DSS_NORMAL) );

                    DeleteObject( hbrIcon );
                    DeleteObject( hBmp );
                }

            }
            x += bmpSize.Width() + 3;
            aRect.left = x;

            NONCLIENTMETRICSW ncm;
            memset( &ncm, 0, sizeof(ncm) );
            ncm.cbSize = sizeof( ncm );
            SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );

            // Print default menu entry with bold font
            //if ( pDI->itemState & ODS_DEFAULT )
            //    ncm.lfMenuFont.lfWeight = FW_BOLD;

            hfntOld = static_cast<HFONT>(SelectObject(pDI->hDC, CreateFontIndirectW( &ncm.lfMenuFont )));

            SIZE strSize;
            OUString aStr( pSalMenuItem->mText );
            GetTextExtentPoint32W( pDI->hDC, o3tl::toW(aStr.getStr()),
                                    aStr.getLength(), &strSize );

            if(!DrawStateW( pDI->hDC, nullptr, nullptr,
                reinterpret_cast<LPARAM>(aStr.getStr()),
                WPARAM(0), aRect.left, aRect.top + (lineHeight - strSize.cy)/2, 0, 0,
                DST_PREFIXTEXT | (fDisabled && !fSelected ? DSS_DISABLED : DSS_NORMAL) ) )
                SAL_WARN("vcl", "DrawStateW failed: " << WindowsErrorString(GetLastError()));

            if( pSalMenuItem->mAccelText.getLength() )
            {
                SIZE strSizeA;
                aStr = pSalMenuItem->mAccelText;
                GetTextExtentPoint32W( pDI->hDC, o3tl::toW(aStr.getStr()),
                                        aStr.getLength(), &strSizeA );
                TEXTMETRICW tm;
                GetTextMetricsW( pDI->hDC, &tm );

                // position the accelerator string to the right but leave space for the
                // (potential) submenu arrow (tm.tmMaxCharWidth)
                if(!DrawStateW( pDI->hDC, nullptr, nullptr,
                    reinterpret_cast<LPARAM>(aStr.getStr()),
                    WPARAM(0), aRect.right-strSizeA.cx-tm.tmMaxCharWidth, aRect.top + (lineHeight - strSizeA.cy)/2, 0, 0,
                    DST_TEXT | (fDisabled && !fSelected ? DSS_DISABLED : DSS_NORMAL) ) )
                    SAL_WARN("vcl", "DrawStateW failed: " << WindowsErrorString(GetLastError()));
            }

            // Restore the original font and colors.
            DeleteObject( SelectObject( pDI->hDC, hbrOld ) );
            DeleteObject( SelectObject( pDI->hDC, hfntOld) );
            SetTextColor(pDI->hDC, clrPrevText);
            SetBkColor(pDI->hDC, clrPrevBkgnd);
        }
        return nRet;
    }

    static bool ImplHandleMenuActivate( HWND hWnd, WPARAM wParam, LPARAM )
    {
        // Menu activation
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( !pFrame )
            return false;

        HMENU hMenu = reinterpret_cast<HMENU>(wParam);
        // WORD nPos = LOWORD (lParam);
        // bool bWindowMenu = (bool) HIWORD(lParam);

        // Send activate and deactivate together, so we have not keep track of opened menus
        // this will be enough to have the menus updated correctly
        SalMenuEvent aMenuEvt;
        WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( hMenu, 0 );
        if( pSalMenuItem )
            aMenuEvt.mpMenu = pSalMenuItem->mpMenu;
        else
            aMenuEvt.mpMenu = nullptr;

        bool nRet = pFrame->CallCallback( SalEvent::MenuActivate, &aMenuEvt );
        if( nRet )
            nRet = pFrame->CallCallback( SalEvent::MenuDeactivate, &aMenuEvt );
        if( nRet )
            pFrame->mLastActivatedhMenu = hMenu;

        return nRet;
    }

    static bool ImplHandleMenuSelect( HWND hWnd, WPARAM wParam, LPARAM lParam )
    {
        // Menu selection
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( !pFrame )
            return false;

        WORD nId = LOWORD(wParam);      // menu item or submenu index
        WORD nFlags = HIWORD(wParam);
        HMENU hMenu = reinterpret_cast<HMENU>(lParam);

        // check if we have to process the message
        if( !GetSalData()->IsKnownMenuHandle( hMenu ) )
            return false;

        bool bByPosition = false;
        if( nFlags & MF_POPUP )
            bByPosition = true;

        bool nRet = false;
        if ( hMenu && !pFrame->mLastActivatedhMenu )
        {
            // we never activated a menu (ie, no WM_INITMENUPOPUP has occurred yet)
            // which means this must be the menubar -> send activation/deactivation
            SalMenuEvent aMenuEvt;
            WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( hMenu, nId, bByPosition );
            if( pSalMenuItem )
                aMenuEvt.mpMenu = pSalMenuItem->mpMenu;
            else
                aMenuEvt.mpMenu = nullptr;

            nRet = pFrame->CallCallback( SalEvent::MenuActivate, &aMenuEvt );
            if( nRet )
                nRet = pFrame->CallCallback( SalEvent::MenuDeactivate, &aMenuEvt );
            if( nRet )
                pFrame->mLastActivatedhMenu = hMenu;
        }

        if( !hMenu && nFlags == 0xFFFF )
        {
            // all menus are closed, reset activation logic
            pFrame->mLastActivatedhMenu = nullptr;
        }

        if( hMenu )
        {
            // hMenu must be saved, as it is not passed in WM_COMMAND which always occurs after a selection
            // if a menu is closed due to a command selection then hMenu is NULL, but WM_COMMAND comes later
            // so we must not overwrite it in this case
            pFrame->mSelectedhMenu = hMenu;

            // send highlight event
            if( nFlags & MF_POPUP )
            {
                // submenu selected
                // wParam now carries an index instead of an id -> retrieve id
                MENUITEMINFOW mi;
                memset(&mi, 0, sizeof(mi));
                mi.cbSize = sizeof( mi );
                mi.fMask = MIIM_ID;
                if( GetMenuItemInfoW( hMenu, LOWORD(wParam), TRUE, &mi) )
                    nId = sal::static_int_cast<WORD>(mi.wID);
            }

            SalMenuEvent aMenuEvt;
            aMenuEvt.mnId   = nId;
            WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( hMenu, nId, false );
            if( pSalMenuItem )
                aMenuEvt.mpMenu = pSalMenuItem->mpMenu;
            else
                aMenuEvt.mpMenu = nullptr;

            nRet = pFrame->CallCallback( SalEvent::MenuHighlight, &aMenuEvt );
        }

        return nRet;
    }

    static bool ImplHandleCommand( HWND hWnd, WPARAM wParam, LPARAM )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( !pFrame )
            return false;

        bool nRet = false;
        if( !HIWORD(wParam) )
        {
            // Menu command
            WORD nId = LOWORD(wParam);
            if( nId )   // zero for separators
            {
                SalMenuEvent aMenuEvt;
                aMenuEvt.mnId   = nId;
                WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( pFrame->mSelectedhMenu, nId, false );
                if( pSalMenuItem )
                    aMenuEvt.mpMenu = pSalMenuItem->mpMenu;
                else
                    aMenuEvt.mpMenu = nullptr;

                nRet = pFrame->CallCallback( SalEvent::MenuCommand, &aMenuEvt );
            }
        }
        return nRet;
    }

    static bool ImplHandleSysCommand( HWND hWnd, WPARAM wParam, LPARAM lParam )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( !pFrame )
            return false;

        WPARAM nCommand = wParam & 0xFFF0;

        if ( pFrame->mbFullScreen )
        {
            BOOL    bMaximize = IsZoomed( pFrame->mhWnd );
            BOOL    bMinimize = IsIconic( pFrame->mhWnd );
            if ( (nCommand == SC_SIZE) ||
                 (!bMinimize && (nCommand == SC_MOVE)) ||
                 (!bMaximize && (nCommand == SC_MAXIMIZE)) ||
                 (bMaximize && (nCommand == SC_RESTORE)) )
            {
                return true;
            }
        }

        if ( nCommand == SC_MOVE )
        {
            WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
            if ( pTimer )
                pTimer->SetForceRealTimer( true );
        }

        if ( nCommand == SC_KEYMENU )
        {
            // do not process SC_KEYMENU if we have a native menu
            // Windows should handle this
            if( GetMenu( hWnd ) )
                return false;

            // Process here KeyMenu events only for Alt to activate the MenuBar,
            // or if a SysChild window is in focus, as Alt-key-combinations are
            // only processed via this event
            if ( !LOWORD( lParam ) )
            {
                // Only trigger if no other key is pressed.
                // Contrary to Docu the CharCode is delivered with the x-coordinate
                // that is pressed in addition.
                // Also 32 for space, 99 for c, 100 for d, ...
                // As this is not documented, we check the state of the space-bar
                if ( GetKeyState( VK_SPACE ) & 0x8000 )
                    return false;

                // to avoid activating the MenuBar for Alt+MouseKey
                if ( (GetKeyState( VK_LBUTTON ) & 0x8000) ||
                     (GetKeyState( VK_RBUTTON ) & 0x8000) ||
                     (GetKeyState( VK_MBUTTON ) & 0x8000) ||
                     (GetKeyState( VK_SHIFT )   & 0x8000) )
                    return true;

                SalKeyEvent aKeyEvt;
                aKeyEvt.mnCode      = KEY_MENU;
                aKeyEvt.mnCharCode  = 0;
                aKeyEvt.mnRepeat    = 0;
                bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
                pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
                return nRet;
            }
            else
            {
                // check if a SysChild is in focus
                HWND hFocusWnd = ::GetFocus();
                if ( hFocusWnd && ImplFindSalObject( hFocusWnd ) )
                {
                    char cKeyCode = static_cast<char>(static_cast<unsigned char>(LOWORD( lParam )));
                    // LowerCase
                    if ( (cKeyCode >= 65) && (cKeyCode <= 90) )
                        cKeyCode += 32;
                    // We only accept 0-9 and A-Z; all other keys have to be
                    // processed by the SalObj hook
                    if ( ((cKeyCode >= 48) && (cKeyCode <= 57)) ||
                         ((cKeyCode >= 97) && (cKeyCode <= 122)) )
                    {
                        sal_uInt16 nModCode = 0;
                        if ( GetKeyState( VK_SHIFT ) & 0x8000 )
                            nModCode |= KEY_SHIFT;
                        if ( GetKeyState( VK_CONTROL ) & 0x8000 )
                            nModCode |= KEY_MOD1;
                        nModCode |= KEY_MOD2;

                        SalKeyEvent aKeyEvt;
                        if ( (cKeyCode >= 48) && (cKeyCode <= 57) )
                            aKeyEvt.mnCode = KEY_0+(cKeyCode-48);
                        else
                            aKeyEvt.mnCode = KEY_A+(cKeyCode-97);
                        aKeyEvt.mnCode     |= nModCode;
                        aKeyEvt.mnCharCode  = cKeyCode;
                        aKeyEvt.mnRepeat    = 0;
                        bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
                        pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
                        return nRet;
                    }
                }
            }
        }

        return false;
    }

    static void ImplHandleInputLangChange( HWND hWnd, WPARAM, LPARAM lParam )
    {
        ImplSalYieldMutexAcquireWithWait();

        // check if we support IME
        WinSalFrame* pFrame = GetWindowPtr( hWnd );

        if ( !pFrame )
            return;

        if ( pFrame->mbIME && pFrame->mhDefIMEContext )
        {
            HKL     hKL = reinterpret_cast<HKL>(lParam);
            UINT    nImeProps = ImmGetProperty( hKL, IGP_PROPERTY );

            pFrame->mbSpezIME = (nImeProps & IME_PROP_SPECIAL_UI) != 0;
            pFrame->mbAtCursorIME = (nImeProps & IME_PROP_AT_CARET) != 0;
            pFrame->mbHandleIME = !pFrame->mbSpezIME;
        }

        // trigger input language and codepage update
        UINT nLang = pFrame->mnInputLang;
        ImplUpdateInputLang( pFrame );

        // notify change
        if( nLang != pFrame->mnInputLang )
            pFrame->CallCallback( SalEvent::InputLanguageChange, nullptr );

        // reinit spec. keys
        GetSalData()->initKeyCodeMap();

        ImplSalYieldMutexRelease();
    }

    static void ImplUpdateIMECursorPos( WinSalFrame* pFrame, HIMC hIMC )
    {
        COMPOSITIONFORM aForm;
        memset( &aForm, 0, sizeof( aForm ) );

        // get cursor position and from it calculate default position
        // for the composition window
        SalExtTextInputPosEvent aPosEvt;
        pFrame->CallCallback( SalEvent::ExtTextInputPos, &aPosEvt );
        if ( (aPosEvt.mnX == -1) && (aPosEvt.mnY == -1) )
            aForm.dwStyle |= CFS_DEFAULT;
        else
        {
            aForm.dwStyle          |= CFS_POINT;
            aForm.ptCurrentPos.x    = aPosEvt.mnX;
            aForm.ptCurrentPos.y    = aPosEvt.mnY;
        }
        ImmSetCompositionWindow( hIMC, &aForm );

        // Because not all IME's use this values, we create
        // a Windows caret to force the Position from the IME
        if ( GetFocus() == pFrame->mhWnd )
        {
            CreateCaret( pFrame->mhWnd, nullptr,
                         aPosEvt.mnWidth, aPosEvt.mnHeight );
            SetCaretPos( aPosEvt.mnX, aPosEvt.mnY );
        }
    }

    static bool ImplHandleIMEStartComposition( HWND hWnd )
    {
        bool bDef = true;

        ImplSalYieldMutexAcquireWithWait();

        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( pFrame )
        {
            HIMC hIMC = ImmGetContext( hWnd );
            if ( hIMC )
            {
                ImplUpdateIMECursorPos( pFrame, hIMC );
                ImmReleaseContext( hWnd, hIMC );
            }

            if ( pFrame->mbHandleIME )
            {
                if ( pFrame->mbAtCursorIME )
                    bDef = false;
            }
        }

        ImplSalYieldMutexRelease();

        return bDef;
    }

    static bool ImplHandleIMECompositionInput( WinSalFrame* pFrame,
                                                   HIMC hIMC, LPARAM lParam )
    {
        bool bDef = true;

        // Init Event
        SalExtTextInputEvent    aEvt;
        aEvt.mpTextAttr         = nullptr;
        aEvt.mnCursorPos        = 0;
        aEvt.mnCursorFlags      = 0;

        // If we get a result string, then we handle this input
        if ( lParam & GCS_RESULTSTR )
        {
            bDef = false;

            LONG nTextLen = ImmGetCompositionStringW( hIMC, GCS_RESULTSTR, nullptr, 0 ) / sizeof( WCHAR );
            if ( nTextLen >= 0 )
            {
                auto pTextBuf = std::unique_ptr<WCHAR[]>(new WCHAR[nTextLen]);
                ImmGetCompositionStringW( hIMC, GCS_RESULTSTR, pTextBuf.get(), nTextLen*sizeof( WCHAR ) );
                aEvt.maText = OUString( o3tl::toU(pTextBuf.get()), static_cast<sal_Int32>(nTextLen) );
            }

            aEvt.mnCursorPos = aEvt.maText.getLength();
            pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt );
            pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
            ImplUpdateIMECursorPos( pFrame, hIMC );
        }

        // If the IME doesn't support OnSpot input, then there is nothing to do
        if ( !pFrame->mbAtCursorIME )
            return !bDef;

        // If we get new Composition data, then we handle this new input
        if ( (lParam & (GCS_COMPSTR | GCS_COMPATTR)) ||
             ((lParam & GCS_CURSORPOS) && !(lParam & GCS_RESULTSTR)) )
        {
            bDef = false;

            ExtTextInputAttr* pSalAttrAry = nullptr;
            LONG    nTextLen = ImmGetCompositionStringW( hIMC, GCS_COMPSTR, nullptr, 0 ) / sizeof( WCHAR );
            if ( nTextLen > 0 )
            {
                {
                    auto pTextBuf = std::unique_ptr<WCHAR[]>(new WCHAR[nTextLen]);
                    ImmGetCompositionStringW( hIMC, GCS_COMPSTR, pTextBuf.get(), nTextLen*sizeof( WCHAR ) );
                    aEvt.maText = OUString( o3tl::toU(pTextBuf.get()), static_cast<sal_Int32>(nTextLen) );
                }

                std::unique_ptr<BYTE[]> pAttrBuf;
                LONG        nAttrLen = ImmGetCompositionStringW( hIMC, GCS_COMPATTR, nullptr, 0 );
                if ( nAttrLen > 0 )
                {
                    pAttrBuf.reset(new BYTE[nAttrLen]);
                    ImmGetCompositionStringW( hIMC, GCS_COMPATTR, pAttrBuf.get(), nAttrLen );
                }

                if ( pAttrBuf )
                {
                    sal_Int32 nTextLen2 = aEvt.maText.getLength();
                    pSalAttrAry = new ExtTextInputAttr[nTextLen2];
                    memset( pSalAttrAry, 0, nTextLen2*sizeof( sal_uInt16 ) );
                    for( sal_Int32 i = 0; (i < nTextLen2) && (i < nAttrLen); i++ )
                    {
                        BYTE nWinAttr = pAttrBuf.get()[i];
                        ExtTextInputAttr   nSalAttr;
                        if ( nWinAttr == ATTR_TARGET_CONVERTED )
                        {
                            nSalAttr = ExtTextInputAttr::BoldUnderline;
                            aEvt.mnCursorFlags |= EXTTEXTINPUT_CURSOR_INVISIBLE;
                        }
                        else if ( nWinAttr == ATTR_CONVERTED )
                            nSalAttr = ExtTextInputAttr::DashDotUnderline;
                        else if ( nWinAttr == ATTR_TARGET_NOTCONVERTED )
                            nSalAttr = ExtTextInputAttr::Highlight;
                        else if ( nWinAttr == ATTR_INPUT_ERROR )
                            nSalAttr = ExtTextInputAttr::RedText | ExtTextInputAttr::DottedUnderline;
                        else /* ( nWinAttr == ATTR_INPUT ) */
                            nSalAttr = ExtTextInputAttr::DottedUnderline;
                        pSalAttrAry[i] = nSalAttr;
                    }

                    aEvt.mpTextAttr = pSalAttrAry;
                }
            }

            // Only when we get new composition data, we must send this event
            if ( (nTextLen > 0) || !(lParam & GCS_RESULTSTR) )
            {
                // End the mode, if the last character is deleted
                if ( !nTextLen && !pFrame->mbCandidateMode )
                {
                    pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt );
                    pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
                }
                else
                {
                    // Because Cursor-Position and DeltaStart never updated
                    // from the korean input engine, we must handle this here
                    if ( lParam & CS_INSERTCHAR )
                    {
                        aEvt.mnCursorPos = nTextLen;
                        if ( aEvt.mnCursorPos && (lParam & CS_NOMOVECARET) )
                            aEvt.mnCursorPos--;
                    }
                    else
                        aEvt.mnCursorPos = LOWORD( ImmGetCompositionStringW( hIMC, GCS_CURSORPOS, nullptr, 0 ) );

                    if ( pFrame->mbCandidateMode )
                        aEvt.mnCursorFlags |= EXTTEXTINPUT_CURSOR_INVISIBLE;
                    if ( lParam & CS_NOMOVECARET )
                        aEvt.mnCursorFlags |= EXTTEXTINPUT_CURSOR_OVERWRITE;

                    pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt );
                }
                ImplUpdateIMECursorPos( pFrame, hIMC );
            }

            if ( pSalAttrAry )
                delete [] pSalAttrAry;
        }

        return !bDef;
    }

    static bool ImplHandleIMEComposition( HWND hWnd, LPARAM lParam )
    {
        bool bDef = true;
        ImplSalYieldMutexAcquireWithWait();

        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( pFrame && (!lParam || (lParam & GCS_RESULTSTR)) )
        {
            // reset the background mode for each text input,
            // as some tools such as RichWin may have changed it
            if ( pFrame->mpLocalGraphics &&
                 pFrame->mpLocalGraphics->getHDC() )
                SetBkMode( pFrame->mpLocalGraphics->getHDC(), TRANSPARENT );
        }

        if ( pFrame && pFrame->mbHandleIME )
        {
            if ( !lParam )
            {
                SalExtTextInputEvent aEvt;
                aEvt.mpTextAttr         = nullptr;
                aEvt.mnCursorPos        = 0;
                aEvt.mnCursorFlags      = 0;
                pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt );
                pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
            }
            else if ( lParam & (GCS_RESULTSTR | GCS_COMPSTR | GCS_COMPATTR | GCS_CURSORPOS) )
            {
                HIMC hIMC = ImmGetContext( hWnd );
                if ( hIMC )
                {
                    if ( ImplHandleIMECompositionInput( pFrame, hIMC, lParam ) )
                        bDef = false;

                    ImmReleaseContext( hWnd, hIMC );
                }
            }
        }

        ImplSalYieldMutexRelease();
        return bDef;
    }

    static bool ImplHandleIMEEndComposition( HWND hWnd )
    {
        bool bDef = true;

        ImplSalYieldMutexAcquireWithWait();

        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        if ( pFrame && pFrame->mbHandleIME )
        {
            if ( pFrame->mbAtCursorIME )
                bDef = false;
        }

        ImplSalYieldMutexRelease();

        return bDef;
    }

    static bool ImplHandleAppCommand( HWND hWnd, LPARAM lParam, LRESULT & nRet )
    {
        MediaCommand nCommand;
        switch( GET_APPCOMMAND_LPARAM(lParam) )
        {
        case APPCOMMAND_MEDIA_CHANNEL_DOWN:         nCommand = MediaCommand::ChannelDown; break;
        case APPCOMMAND_MEDIA_CHANNEL_UP:           nCommand = MediaCommand::ChannelUp; break;
        case APPCOMMAND_MEDIA_NEXTTRACK:            nCommand = MediaCommand::NextTrack; break;
        case APPCOMMAND_MEDIA_PAUSE:                nCommand = MediaCommand::Pause; break;
        case APPCOMMAND_MEDIA_PLAY:                 nCommand = MediaCommand::Play; break;
        case APPCOMMAND_MEDIA_PLAY_PAUSE:           nCommand = MediaCommand::PlayPause; break;
        case APPCOMMAND_MEDIA_PREVIOUSTRACK:        nCommand = MediaCommand::PreviousTrack; break;
        case APPCOMMAND_MEDIA_RECORD:               nCommand = MediaCommand::Record; break;
        case APPCOMMAND_MEDIA_REWIND:               nCommand = MediaCommand::Rewind; break;
        case APPCOMMAND_MEDIA_STOP:                 nCommand = MediaCommand::Stop; break;
        case APPCOMMAND_MIC_ON_OFF_TOGGLE:          nCommand = MediaCommand::MicOnOffToggle; break;
        case APPCOMMAND_MICROPHONE_VOLUME_DOWN:     nCommand = MediaCommand::MicrophoneVolumeDown; break;
        case APPCOMMAND_MICROPHONE_VOLUME_MUTE:     nCommand = MediaCommand::MicrophoneVolumeMute; break;
        case APPCOMMAND_MICROPHONE_VOLUME_UP:       nCommand = MediaCommand::MicrophoneVolumeUp; break;
        case APPCOMMAND_VOLUME_DOWN:                nCommand = MediaCommand::VolumeDown; break;
        case APPCOMMAND_VOLUME_MUTE:                nCommand = MediaCommand::VolumeMute; break;
        case APPCOMMAND_VOLUME_UP:                  nCommand = MediaCommand::VolumeUp; break;
        default:
            return false;
        }

        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        vcl::Window *pWindow = pFrame ? pFrame->GetWindow() : nullptr;

        if( pWindow )
        {
            const Point aPoint;
            CommandMediaData aMediaData(nCommand);
            CommandEvent aCEvt( aPoint, CommandEventId::Media, false, &aMediaData );
            NotifyEvent aNCmdEvt( MouseNotifyEvent::COMMAND, pWindow, &aCEvt );

            if ( !ImplCallPreNotify( aNCmdEvt ) )
            {
                pWindow->Command( aCEvt );
                nRet = 1;
                return !aMediaData.GetPassThroughToOS();
            }
        }

        return false;
    }

    static void ImplHandleIMENotify( HWND hWnd, WPARAM wParam )
    {
        if ( wParam == WPARAM(IMN_OPENCANDIDATE) )
        {
            ImplSalYieldMutexAcquireWithWait();

            WinSalFrame* pFrame = GetWindowPtr( hWnd );
            if ( pFrame && pFrame->mbHandleIME &&
                 pFrame->mbAtCursorIME )
            {
                // we want to hide the cursor
                pFrame->mbCandidateMode = true;
                ImplHandleIMEComposition( hWnd, GCS_CURSORPOS );

                HWND hWnd2 = pFrame->mhWnd;
                HIMC hIMC = ImmGetContext( hWnd2 );
                if ( hIMC )
                {
                    LONG nBufLen = ImmGetCompositionStringW( hIMC, GCS_COMPSTR, nullptr, 0 );
                    if ( nBufLen >= 1 )
                    {
                        SalExtTextInputPosEvent aPosEvt;
                        pFrame->CallCallback( SalEvent::ExtTextInputPos, &aPosEvt );

                        // Vertical !!!
                        CANDIDATEFORM aForm;
                        aForm.dwIndex           = 0;
                        aForm.dwStyle           = CFS_EXCLUDE;
                        aForm.ptCurrentPos.x    = aPosEvt.mnX;
                        aForm.ptCurrentPos.y    = aPosEvt.mnY+1;
                        aForm.rcArea.left       = aPosEvt.mnX;
                        aForm.rcArea.top        = aPosEvt.mnY;
                        aForm.rcArea.right      = aForm.rcArea.left+aPosEvt.mnExtWidth+1;
                        aForm.rcArea.bottom     = aForm.rcArea.top+aPosEvt.mnHeight+1;
                        ImmSetCandidateWindow( hIMC, &aForm );
                    }

                    ImmReleaseContext( hWnd2, hIMC );
                }
            }

            ImplSalYieldMutexRelease();
        }
        else if ( wParam == WPARAM(IMN_CLOSECANDIDATE) )
        {
            ImplSalYieldMutexAcquireWithWait();
            WinSalFrame* pFrame = GetWindowPtr( hWnd );
            if ( pFrame )
                pFrame->mbCandidateMode = false;
            ImplSalYieldMutexRelease();
        }
    }

    static bool
    ImplHandleGetObject(HWND hWnd, LPARAM lParam, WPARAM wParam, LRESULT & nRet)
    {
        // IA2 should be enabled automatically
        AllSettings aSettings = Application::GetSettings();
        MiscSettings aMisc = aSettings.GetMiscSettings();
        aMisc.SetEnableATToolSupport( true );
        aSettings.SetMiscSettings( aMisc );
        Application::SetSettings( aSettings );

        if (!Application::GetSettings().GetMiscSettings().GetEnableATToolSupport())
            return false; // locked down somehow ?

        ImplSVData* pSVData = ImplGetSVData();

        // Make sure to launch Accessibility only the following criteria are satisfied
        // to avoid RFT interrupts regular accessibility processing
        if ( !pSVData->mxAccessBridge.is() )
        {
            if( !InitAccessBridge() )
                return false;
        }

        uno::Reference< accessibility::XMSAAService > xMSAA( pSVData->mxAccessBridge, uno::UNO_QUERY );
        if ( xMSAA.is() )
        {
            sal_Int32 lParam32 = static_cast<sal_Int32>(lParam);
            sal_uInt32 wParam32 = static_cast<sal_uInt32>(wParam);

            // mhOnSetTitleWnd not set to reasonable value anywhere...
            if ( lParam32 == OBJID_CLIENT )
            {
                nRet = xMSAA->getAccObjectPtr(
                        reinterpret_cast<sal_Int64>(hWnd), lParam32, wParam32);
                if (nRet != 0)
                    return true;
            }
        }
        return false;
    }

    static LRESULT ImplHandleIMEReconvertString( HWND hWnd, LPARAM lParam )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        LPRECONVERTSTRING pReconvertString = reinterpret_cast<LPRECONVERTSTRING>(lParam);
        LRESULT nRet = 0;
        SalSurroundingTextRequestEvent aEvt;
        aEvt.maText.clear();
        aEvt.mnStart = aEvt.mnEnd = 0;

        UINT nImeProps = ImmGetProperty( GetKeyboardLayout( 0 ), IGP_SETCOMPSTR );
        if( (nImeProps & SCS_CAP_SETRECONVERTSTRING) == 0 )
        {
        // This IME does not support reconversion.
        return 0;
        }

        if( !pReconvertString )
        {
        // The first call for reconversion.
        pFrame->CallCallback( SalEvent::StartReconversion, nullptr );

        // Retrieve the surrounding text from the focused control.
        pFrame->CallCallback( SalEvent::SurroundingTextRequest, &aEvt );

        if( aEvt.maText.isEmpty())
        {
            return 0;
        }

        nRet = sizeof(RECONVERTSTRING) + (aEvt.maText.getLength() + 1) * sizeof(WCHAR);
        }
        else
        {
        // The second call for reconversion.

        // Retrieve the surrounding text from the focused control.
        pFrame->CallCallback( SalEvent::SurroundingTextRequest, &aEvt );
        nRet = sizeof(RECONVERTSTRING) + (aEvt.maText.getLength() + 1) * sizeof(WCHAR);

        pReconvertString->dwStrOffset = sizeof(RECONVERTSTRING);
        pReconvertString->dwStrLen = aEvt.maText.getLength();
        pReconvertString->dwCompStrOffset = aEvt.mnStart * sizeof(WCHAR);
        pReconvertString->dwCompStrLen = aEvt.mnEnd - aEvt.mnStart;
        pReconvertString->dwTargetStrOffset = pReconvertString->dwCompStrOffset;
        pReconvertString->dwTargetStrLen = pReconvertString->dwCompStrLen;

        memcpy( pReconvertString + 1, aEvt.maText.getStr(), (aEvt.maText.getLength() + 1) * sizeof(WCHAR) );
        }

        // just return the required size of buffer to reconvert.
        return nRet;
    }

    static LRESULT ImplHandleIMEConfirmReconvertString( HWND hWnd, LPARAM lParam )
    {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        LPRECONVERTSTRING pReconvertString = reinterpret_cast<LPRECONVERTSTRING>(lParam);
        SalSurroundingTextRequestEvent aEvt;
        aEvt.maText.clear();
        aEvt.mnStart = aEvt.mnEnd = 0;

        pFrame->CallCallback( SalEvent::SurroundingTextRequest, &aEvt );

        sal_uLong nTmpStart = pReconvertString->dwCompStrOffset / sizeof(WCHAR);
        sal_uLong nTmpEnd = nTmpStart + pReconvertString->dwCompStrLen;

        if( nTmpStart != aEvt.mnStart || nTmpEnd != aEvt.mnEnd )
        {
        SalSurroundingTextSelectionChangeEvent aSelEvt;
        aSelEvt.mnStart = nTmpStart;
        aSelEvt.mnEnd = nTmpEnd;

        pFrame->CallCallback( SalEvent::SurroundingTextSelectionChange, &aSelEvt );
        }

        return TRUE;
    }

    static LRESULT ImplHandleIMEQueryCharPosition( HWND hWnd, LPARAM lParam ) {
        WinSalFrame* pFrame = GetWindowPtr( hWnd );
        PIMECHARPOSITION pQueryCharPosition = reinterpret_cast<PIMECHARPOSITION>(lParam);
        if ( pQueryCharPosition->dwSize < sizeof(IMECHARPOSITION) )
            return FALSE;

        SalQueryCharPositionEvent aEvt;
        aEvt.mbValid = false;
        aEvt.mnCharPos = pQueryCharPosition->dwCharPos;

        pFrame->CallCallback( SalEvent::QueryCharPosition, &aEvt );

        if ( !aEvt.mbValid )
            return FALSE;

        if ( aEvt.mbVertical )
        {
            // For vertical writing, the base line is left edge of the rectangle
            // and the target position is top-right corner.
            pQueryCharPosition->pt.x = aEvt.mnCursorBoundX + aEvt.mnCursorBoundWidth;
            pQueryCharPosition->pt.y = aEvt.mnCursorBoundY;
            pQueryCharPosition->cLineHeight = aEvt.mnCursorBoundWidth;
        }
        else
        {
            // For horizontal writing, the base line is the bottom edge of the rectangle.
            // and the target position is top-left corner.
            pQueryCharPosition->pt.x = aEvt.mnCursorBoundX;
            pQueryCharPosition->pt.y = aEvt.mnCursorBoundY;
            pQueryCharPosition->cLineHeight = aEvt.mnCursorBoundHeight;
        }

        // Currently not supported but many IMEs usually ignore them.
        pQueryCharPosition->rcDocument.left = 0;
        pQueryCharPosition->rcDocument.top = 0;
        pQueryCharPosition->rcDocument.right = 0;
        pQueryCharPosition->rcDocument.bottom = 0;

        return TRUE;
    }

    void SalTestMouseLeave()
    {
        SalData* pSalData = GetSalData();

        if ( pSalData->mhWantLeaveMsg && !::GetCapture() )
        {
            POINT aPt;
            GetCursorPos( &aPt );
            if ( pSalData->mhWantLeaveMsg != WindowFromPoint( aPt ) )
                SendMessageW( pSalData->mhWantLeaveMsg, SAL_MSG_MOUSELEAVE, 0, MAKELPARAM( aPt.x, aPt.y ) );
        }
    }

    static bool ImplSalWheelMousePos( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ,
                                     LRESULT& rResult )
    {
        POINT aPt;
        POINT aScreenPt;
        aScreenPt.x = static_cast<short>(LOWORD( lParam ));
        aScreenPt.y = static_cast<short>(HIWORD( lParam ));
        // find child window that is at this position
        HWND hChildWnd;
        HWND hWheelWnd = hWnd;
        do
        {
            hChildWnd = hWheelWnd;
            aPt = aScreenPt;
            ScreenToClient( hChildWnd, &aPt );
            hWheelWnd = ChildWindowFromPointEx( hChildWnd, aPt, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT );
        }
        while ( hWheelWnd && (hWheelWnd != hChildWnd) );
        if ( hWheelWnd && (hWheelWnd != hWnd) &&
             (hWheelWnd != ::GetFocus()) && IsWindowEnabled( hWheelWnd ) )
        {
            rResult = SendMessageW( hWheelWnd, nMsg, wParam, lParam );
            return false;
        }

        return true;
    }

    LRESULT CALLBACK SalFrameWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef )
    {
        LRESULT     nRet = 0;
        static bool  bInWheelMsg = false;
        static bool  bInQueryEnd = false;

        SAL_INFO("vcl.gdi.wndproc", "SalFrameWndProc(nMsg=" << nMsg << ", wParam=" << wParam << ", lParam=" << lParam << ")");

        // By WM_CREATE we connect the frame with the window handle
        if ( nMsg == WM_CREATE )
        {
            // Save Window-Instance in Windowhandle
            // Can also be used for the A-Version, because the struct
            // to access lpCreateParams is the same structure
            CREATESTRUCTW* pStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
            WinSalFrame* pFrame = static_cast<WinSalFrame*>(pStruct->lpCreateParams);
            if ( pFrame != nullptr )
            {
                SetWindowPtr( hWnd, pFrame );
                // Set HWND already here, as data might be used already
                // when messages are being sent by CreateWindow()
                pFrame->mhWnd = hWnd;
                pFrame->maSysData.hWnd = hWnd;
            }
            return 0;
        }

        ImplSVData* pSVData = ImplGetSVData();
        // #i72707# TODO: the mbDeInit check will not be needed
        // once all windows that are not properly closed on exit got fixed
        if( pSVData->mbDeInit )
            return 0;

        if ( WM_USER_SYSTEM_WINDOW_ACTIVATED == nMsg )
        {
            ImplHideSplash();
            return 0;
        }

        switch( nMsg )
        {
            case WM_MOUSEMOVE:
            case WM_LBUTTONDOWN:
            case WM_MBUTTONDOWN:
            case WM_RBUTTONDOWN:
            case WM_LBUTTONUP:
            case WM_MBUTTONUP:
            case WM_RBUTTONUP:
            case WM_NCMOUSEMOVE:
            case SAL_MSG_MOUSELEAVE:
                ImplSalYieldMutexAcquireWithWait();
                rDef = !ImplHandleMouseMsg( hWnd, nMsg, wParam, lParam );
                ImplSalYieldMutexRelease();
                break;

            case WM_NCLBUTTONDOWN:
            case WM_NCMBUTTONDOWN:
            case WM_NCRBUTTONDOWN:
                ImplSalYieldMutexAcquireWithWait();
                ImplCallClosePopupsHdl( hWnd );   // close popups...
                ImplSalYieldMutexRelease();
                break;

            case WM_MOUSEACTIVATE:
                if ( LOWORD( lParam ) == HTCLIENT )
                {
                    ImplSalYieldMutexAcquireWithWait();
                    nRet = LRESULT(ImplHandleMouseActivateMsg( hWnd ));
                    ImplSalYieldMutexRelease();
                    if ( nRet )
                    {
                        nRet = MA_NOACTIVATE;
                        rDef = false;
                    }
                }
                break;

            case WM_KEYDOWN:
            case WM_KEYUP:
            case WM_DEADCHAR:
            case WM_CHAR:
            case WM_UNICHAR:    // MCD, 2003-01-13, Support for WM_UNICHAR & Keyman 6.0
            case WM_SYSKEYDOWN:
            case WM_SYSKEYUP:
            case WM_SYSCHAR:
                ImplSalYieldMutexAcquireWithWait();
                rDef = !ImplHandleKeyMsg( hWnd, nMsg, wParam, lParam, nRet );
                ImplSalYieldMutexRelease();
                break;

            case WM_MOUSEWHEEL:
                // FALLTHROUGH intended
            case WM_MOUSEHWHEEL:
                // protect against recursion, in case the message is returned
                // by IE or the external window
                if ( !bInWheelMsg )
                {
                    bInWheelMsg = true;
                    rDef = !ImplHandleWheelMsg( hWnd, nMsg, wParam, lParam );
                    // If we did not process the message, re-check if here is a
                    // connected (?) window that we have to notify.
                    if ( rDef )
                        rDef = ImplSalWheelMousePos( hWnd, nMsg, wParam, lParam, nRet );
                    bInWheelMsg = false;
                }
                break;

            case WM_COMMAND:
                ImplSalYieldMutexAcquireWithWait();
                rDef = !ImplHandleCommand( hWnd, wParam, lParam );
                ImplSalYieldMutexRelease();
                break;

            case WM_INITMENUPOPUP:
                ImplSalYieldMutexAcquireWithWait();
                rDef = !ImplHandleMenuActivate( hWnd, wParam, lParam );
                ImplSalYieldMutexRelease();
                break;

            case WM_MENUSELECT:
                ImplSalYieldMutexAcquireWithWait();
                rDef = !ImplHandleMenuSelect( hWnd, wParam, lParam );
                ImplSalYieldMutexRelease();
                break;

            case WM_SYSCOMMAND:
                ImplSalYieldMutexAcquireWithWait();
                nRet = LRESULT(ImplHandleSysCommand( hWnd, wParam, lParam ));
                ImplSalYieldMutexRelease();
                if ( nRet )
                    rDef = false;
                break;

            case WM_MENUCHAR:
                nRet = ImplMenuChar( hWnd, wParam, lParam );
                if( nRet )
                    rDef = false;
                break;

            case WM_MEASUREITEM:
                nRet = ImplMeasureItem(hWnd, wParam, lParam);
                if( nRet )
                    rDef = false;
                break;

            case WM_DRAWITEM:
                nRet = ImplDrawItem(hWnd, wParam, lParam);
                if( nRet )
                    rDef = false;
                break;

            case WM_MOVE:
            case SAL_MSG_POSTMOVE:
                ImplHandleMoveMsg( hWnd );
                rDef = false;
                break;
            case WM_SIZE:
                ImplHandleSizeMsg( hWnd, wParam, lParam );
                rDef = false;
                break;
            case SAL_MSG_POSTCALLSIZE:
                ImplCallSizeHdl( hWnd );
                rDef = false;
                break;

            case WM_GETMINMAXINFO:
                if ( ImplHandleMinMax( hWnd, lParam ) )
                    rDef = false;
                break;

            case WM_ERASEBKGND:
                nRet = 1;
                rDef = false;
                break;
            case WM_PAINT:
                ImplHandlePaintMsg( hWnd );
                rDef = false;
                break;
            case SAL_MSG_POSTPAINT:
                ImplHandlePostPaintMsg( hWnd, reinterpret_cast<RECT*>(wParam) );
                rDef = false;
                break;

            case SAL_MSG_FORCEPALETTE:
                ImplHandleForcePalette( hWnd );
                rDef = false;
                break;

            case WM_QUERYNEWPALETTE:
            case SAL_MSG_POSTQUERYNEWPAL:
                nRet = ImplHandlePalette( true, hWnd, nMsg, wParam, lParam, rDef );
                break;

            case WM_ACTIVATE:
                // Getting activated, we also want to set our palette.
                // We do this in Activate, so that other external child windows
                // can overwrite our palette. Thus our palette is set only once
                // and not recursively, as at all other places it is set only as
                // the background palette.
                if ( LOWORD( wParam ) != WA_INACTIVE )
                    SendMessageW( hWnd, SAL_MSG_FORCEPALETTE, 0, 0 );
                break;

            case WM_ENABLE:
                // #95133# a system dialog is opened/closed, using our app window as parent
                {
                    WinSalFrame* pFrame = GetWindowPtr( hWnd );
                    vcl::Window *pWin = nullptr;
                    if( pFrame )
                        pWin = pFrame->GetWindow();

                    if( !wParam )
                    {
                        pSVData->maAppData.mnModalMode++;

                        ImplHideSplash();
                        if( pWin )
                        {
                            pWin->EnableInput( false, nullptr );
                            pWin->IncModalCount();  // #106303# support frame based modal count
                        }
                    }
                    else
                    {
                        ImplGetSVData()->maAppData.mnModalMode--;
                        if( pWin )
                        {
                            pWin->EnableInput( true, nullptr );
                            pWin->DecModalCount();  // #106303# support frame based modal count
                        }
                    }
                }
                break;

            case WM_KILLFOCUS:
                DestroyCaret();
                SAL_FALLTHROUGH;
            case WM_SETFOCUS:
            case SAL_MSG_POSTFOCUS:
                ImplHandleFocusMsg( hWnd );
                rDef = false;
                break;

            case WM_CLOSE:
                ImplHandleCloseMsg( hWnd );
                rDef = false;
                break;

            case WM_QUERYENDSESSION:
                if( !bInQueryEnd )
                {
                    // handle queryendsession only once
                    bInQueryEnd = true;
                    nRet = LRESULT(!ImplHandleShutDownMsg( hWnd ));
                    rDef = false;

                    // Issue #16314#: ImplHandleShutDownMsg causes a PostMessage in case of allowing shutdown.
                    // This posted message was never processed and cause Windows XP to hang after log off
                    // if there are multiple sessions and the current session wasn't the first one started.
                    // So if shutdown is allowed we assume that a post message was done and retrieve all
                    // messages in the message queue and dispatch them before we return control to the system.

                    if ( nRet )
                        while ( Application::Reschedule( true ) );
                }
                else
                {
                    ImplSalYieldMutexAcquireWithWait();
                    ImplSalYieldMutexRelease();
                    rDef = true;
                }
                break;

            case WM_ENDSESSION:
                if( !wParam )
                    bInQueryEnd = false; // no shutdown: allow query again
                nRet = FALSE;
                rDef = false;
                break;

            case WM_DISPLAYCHANGE:
            case WM_SETTINGCHANGE:
            case WM_DEVMODECHANGE:
            case WM_FONTCHANGE:
            case WM_SYSCOLORCHANGE:
            case WM_TIMECHANGE:
                ImplHandleSettingsChangeMsg( hWnd, nMsg, wParam, lParam );
                break;

            case WM_THEMECHANGED:
                GetSalData()->mbThemeChanged = true;
                break;

            case SAL_MSG_USEREVENT:
                ImplHandleUserEvent( hWnd, lParam );
                rDef = false;
                break;

            case SAL_MSG_CAPTUREMOUSE:
                SetCapture( hWnd );
                rDef = false;
                break;
            case SAL_MSG_RELEASEMOUSE:
                if ( ::GetCapture() == hWnd )
                    ReleaseCapture();
                rDef = false;
                break;
            case SAL_MSG_TOTOP:
                ImplSalToTop( hWnd, static_cast<SalFrameToTop>(wParam) );
                rDef = false;
                break;
            case SAL_MSG_SHOW:
                ImplSalShow( hWnd, static_cast<bool>(wParam), static_cast<bool>(lParam) );
                rDef = false;
                break;
            case SAL_MSG_SETINPUTCONTEXT:
                ImplSalFrameSetInputContext( hWnd, reinterpret_cast<const SalInputContext*>(lParam) );
                rDef = false;
                break;
            case SAL_MSG_ENDEXTTEXTINPUT:
                ImplSalFrameEndExtTextInput( hWnd, static_cast<EndExtTextInputFlags>(wParam) );
                rDef = false;
                break;

            case WM_INPUTLANGCHANGE:
                ImplHandleInputLangChange( hWnd, wParam, lParam );
                break;

            case WM_IME_CHAR:
                // #103487#, some IMEs (eg, those that do not work onspot)
                //           may send WM_IME_CHAR instead of WM_IME_COMPOSITION
                // we just handle it like a WM_CHAR message - seems to work fine
                ImplSalYieldMutexAcquireWithWait();
                rDef = !ImplHandleKeyMsg( hWnd, WM_CHAR, wParam, lParam, nRet );
                ImplSalYieldMutexRelease();
                break;

             case WM_IME_STARTCOMPOSITION:
                rDef = ImplHandleIMEStartComposition( hWnd );
                break;

            case WM_IME_COMPOSITION:
                rDef = ImplHandleIMEComposition( hWnd, lParam );
                break;

            case WM_IME_ENDCOMPOSITION:
                rDef = ImplHandleIMEEndComposition( hWnd );
                break;

            case WM_IME_NOTIFY:
                ImplHandleIMENotify( hWnd, wParam );
                break;

            case WM_GETOBJECT:
                ImplSalYieldMutexAcquireWithWait();
                if ( ImplHandleGetObject( hWnd, lParam, wParam, nRet ) )
                {
                    rDef = false;
                }
                ImplSalYieldMutexRelease();
                break;

            case WM_APPCOMMAND:
                if( ImplHandleAppCommand( hWnd, lParam, nRet ) )
                {
                    rDef = false;
                }
                break;
            case WM_IME_REQUEST:
                if ( static_cast<sal_uIntPtr>(wParam) == IMR_RECONVERTSTRING )
                {
                    nRet = ImplHandleIMEReconvertString( hWnd, lParam );
                    rDef = false;
                }
                else if( static_cast<sal_uIntPtr>(wParam) == IMR_CONFIRMRECONVERTSTRING )
                {
                    nRet = ImplHandleIMEConfirmReconvertString( hWnd, lParam );
                    rDef = false;
                }
                else if ( static_cast<sal_uIntPtr>(wParam) == IMR_QUERYCHARPOSITION )
                {
                    if ( ImplSalYieldMutexTryToAcquire() )
                    {
                        nRet = ImplHandleIMEQueryCharPosition( hWnd, lParam );
                        ImplSalYieldMutexRelease();
                    }
                    else
                        nRet = FALSE;
                    rDef = false;
                }
                break;
        }

        return nRet;
    }

    LRESULT CALLBACK SalFrameWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
    {
        bool bDef = true;
        LRESULT nRet = 0;
        __try
        {
            nRet = SalFrameWndProc( hWnd, nMsg, wParam, lParam, bDef );
        }
        __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
        {
        }

        if ( bDef )
            nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
        return nRet;
    }

    bool ImplHandleGlobalMsg( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, LRESULT& rlResult )
    {
        // handle all messages concerning all frames so they get processed only once
        // Must work for Unicode and none Unicode
        bool bResult = false;
        if ( (nMsg == WM_PALETTECHANGED) || (nMsg == SAL_MSG_POSTPALCHANGED) )
        {
            bResult = true;
            rlResult = ImplHandlePalette( false, hWnd, nMsg, wParam, lParam, bResult );
        }
        else if( nMsg == WM_DISPLAYCHANGE )
        {
            WinSalSystem* pSys = static_cast<WinSalSystem*>(ImplGetSalSystem());
            if( pSys )
                pSys->clearMonitors();
            bResult = (pSys != nullptr);
        }
        return bResult;
    }

    #ifdef _WIN32
    bool HasAtHook()
    {
        BOOL bIsRunning = FALSE;
        // pvParam must be BOOL
        return SystemParametersInfoW(SPI_GETSCREENREADER, 0, &bIsRunning, 0)
            && bIsRunning;
    }
    #endif

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: maFullScreenRect, mnFullScreenShowState, mnStyle.

↑ V547 Expression '!aEvt.mbValid' is always true.

↑ V547 Expression 'nTextLen >= 0' is always true.

↑ V560 A part of conditional expression is always true: (cKeyCode >= 48).

↑ V560 A part of conditional expression is always true: pGraphics.

↑ V560 A part of conditional expression is always false: (nMsg == 0x0105).

↑ V519 The 'aKeyEvt.mnCode' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 3484, 3486.

↑ V519 The 'nLastVKChar' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 3416, 3425.

↑ V519 The 'nLastChar' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 3415, 3424.

↑ V560 A part of conditional expression is always true: nVKey < 255.

↑ V560 A part of conditional expression is always true.

↑ V560 A part of conditional expression is always true.

↑ V519 The 'mbInShow' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1880, 1882.

↑ V519 The 'clrPrevBkgnd' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 4490, 4498.

↑ V519 The 'bIgnoreCharMsg' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 3552, 3559.