Codejock Forums Homepage
Forum Home Forum Home > Codejock Products > Visual C++ MFC > Docking Pane
  New Posts New Posts RSS Feed - Cannot drag panels to other monitor with other DPI
  FAQ FAQ  Forum Search   Events   Register Register  Login Login

Cannot drag panels to other monitor with other DPI

 Post Reply Post Reply
Author
Message
andredevries View Drop Down
Newbie
Newbie


Joined: 22 June 2018
Location: Netherlands
Status: Offline
Points: 5
Post Options Post Options   Thanks (0) Thanks(0)   Quote andredevries Quote  Post ReplyReply Direct Link To This Post Topic: Cannot drag panels to other monitor with other DPI
    Posted: 22 June 2018 at 8:55am
Multiple 4K screens, some with 100% scaling, others with scaling other than 100. Can't move any of the windows across to the other screens if the scaling is different. They get stuck; they jump to a different location on the screen it comes from. See this short video showing the issue:
https://www.youtube.com/watch?v=IFiAtYSNl30

This happens when the "main" monitor is to the right of the "second" monitor, so the second monitor will have negative horizontal co-ordinates. Now, if the two monitors have different DPI scale settings then none of our "Floating Tool Panel" windows, that are based on a CodeJock control for dockable floating panels, can be dragged into the left panel (i.e the second monitor with negative co-ordinates).

Note: When you drag the floating panel towards the left monitor, Windows will send a notification to it when more than 50% of the panel is in the left monitor (WM_DPICHANGED). The notification is to tell the panel that it is now dragged into a monitor that has a different DPI Scale. It seems that the CodeJock Docking panel should respond to this notification in some way and it seems it is not doing that, or incorrectly?

I reproduced the issue using Codejock MFC 18.3.0.
I hope this problem can be solved or someone can tell me how to fix it myself. Thanks.

Back to Top
olebed View Drop Down
Admin Group
Admin Group


Joined: 01 July 2014
Location: Ukraine
Status: Offline
Points: 841
Post Options Post Options   Thanks (0) Thanks(0)   Quote olebed Quote  Post ReplyReply Direct Link To This Post Posted: 22 June 2018 at 11:50am
Hello,

I think this problem related to new macros XTP_POINT_FROM_LPARAM
It constructs wrong CPoint if .x or .y from lparam are negative.
I have investigated that negative points are only in method CXTPCommandBarsFrameHook::OnHookMessage()

So as workaround you can delete this macros from CXTPCommandBarsFrameHook::OnHookMessage 
because CPoint has own constructor with lparam as parameter CPoint(LPARAM dwPoint) throw();

CPoint point = XTP_POINT_FROM_LPARAM(lParam);
>
CPoint point(lParam);

Regards,
Oleksandr Lebed  
Back to Top
olebed View Drop Down
Admin Group
Admin Group


Joined: 01 July 2014
Location: Ukraine
Status: Offline
Points: 841
Post Options Post Options   Thanks (0) Thanks(0)   Quote olebed Quote  Post ReplyReply Direct Link To This Post Posted: 22 June 2018 at 12:11pm
However if you say that bug with DockingPane control,  than I can say that bug the same as with XTP_POINT_FROM_LPARAM - signed/unsigned casting.
Back to Top
Sven View Drop Down
Senior Member
Senior Member


Joined: 21 August 2003
Location: Germany
Status: Offline
Points: 127
Post Options Post Options   Thanks (0) Thanks(0)   Quote Sven Quote  Post ReplyReply Direct Link To This Post Posted: 23 June 2018 at 2:18am
I have changed XTP_POINT_FROM_LPARAM to

#define XTP_POINT_FROM_LPARAM(lParam) CPoint(static_cast<short>(LOWORD(lParam)), static_cast<short>(HIWORD(lParam)))

The original non-typecast macro is also the reason, that buttons in themed frames (minimize, maximize, restore) can't be clicked if the main window is on the left monitor with negative coordinates.
Back to Top
olebed View Drop Down
Admin Group
Admin Group


Joined: 01 July 2014
Location: Ukraine
Status: Offline
Points: 841
Post Options Post Options   Thanks (0) Thanks(0)   Quote olebed Quote  Post ReplyReply Direct Link To This Post Posted: 23 June 2018 at 4:41am
Sven, yes hit test for frame buttons doesn't work due to signed-unsigned cast. But CPoint class has own constructor with initialization with LParam.
Back to Top
andredevries View Drop Down
Newbie
Newbie


Joined: 22 June 2018
Location: Netherlands
Status: Offline
Points: 5
Post Options Post Options   Thanks (0) Thanks(0)   Quote andredevries Quote  Post ReplyReply Direct Link To This Post Posted: 25 June 2018 at 4:47am
Thanks for your responses.
However, I still have difficulty to come to a solution. It sounds plausible that it may be a signed/unsigned casting issue, and it might be a conversion issue between LPARAM and Point. But when I search in the sources on XTP_POINT_FROM_LPARAM, I find nothing. Where is that macro defined? I could not find anything similar that may be a problem. Any more ideas?

Back to Top
olebed View Drop Down
Admin Group
Admin Group


Joined: 01 July 2014
Location: Ukraine
Status: Offline
Points: 841
Post Options Post Options   Thanks (0) Thanks(0)   Quote olebed Quote  Post ReplyReply Direct Link To This Post Posted: 25 June 2018 at 5:37am
XTP_POINT_FROM_LPARAM introduced in v18.4.

Unfortunately I can't reproduce your problem. Can you provide sample ?
I have monitors configuration of work station as you described except 4k (only fullHD).
Back to Top
andredevries View Drop Down
Newbie
Newbie


Joined: 22 June 2018
Location: Netherlands
Status: Offline
Points: 5
Post Options Post Options   Thanks (0) Thanks(0)   Quote andredevries Quote  Post ReplyReply Direct Link To This Post Posted: 25 June 2018 at 8:17am
Aha, I am using v18.3. I just tested the DockingPane samples, they work fine. Seems like I am doing something wrong here.
Unfortunately it is not so easy to isolate sources to provide an example (and I am not that familiar with it).
Could you tell me what gets triggered when moving a docking pane to a screen with other DPI?
Back to Top
olebed View Drop Down
Admin Group
Admin Group


Joined: 01 July 2014
Location: Ukraine
Status: Offline
Points: 841
Post Options Post Options   Thanks (0) Thanks(0)   Quote olebed Quote  Post ReplyReply Direct Link To This Post Posted: 25 June 2018 at 9:03am
see method  CXTPDockingPaneMiniWnd::UpdatePosition()

also for drawing used 
ToolkitPro1840UD.dll!CXTPDockingPaneOffice2013Theme::DrawFloatingFrame(CDC * pDC, CXTPDockingPaneMiniWnd * pPane, CRect rc) Line 298 C++
ToolkitPro1840UD.dll!CXTPDockingPaneMiniWnd::OnNcPaint() Line 699 C++
Back to Top
andredevries View Drop Down
Newbie
Newbie


Joined: 22 June 2018
Location: Netherlands
Status: Offline
Points: 5
Post Options Post Options   Thanks (0) Thanks(0)   Quote andredevries Quote  Post ReplyReply Direct Link To This Post Posted: 25 June 2018 at 10:58am
Thanks, but does not help so far. Debugger does not stop at any of those methods.
Will have to search further myself I guess. Thanks anyway.
Back to Top
Cedric_ar View Drop Down
Groupie
Groupie


Joined: 04 September 2012
Status: Offline
Points: 19
Post Options Post Options   Thanks (0) Thanks(0)   Quote Cedric_ar Quote  Post ReplyReply Direct Link To This Post Posted: 31 October 2018 at 1:23pm
hi,

I have the same issue. it's realy hard to switch pane to a different monitor with a different DPI. I have a monitor in 125% and another in 100%. 

I try to remove XTPWM_DOCKINGPANE_NOTIFY to test if the problem is on my side, but i have the same issue. the mainframe inherited from 

class CMainFrame:public CXTPMDIFrameWnd, CXTPCommandBarsFrameHook

i initialize the dockingpane manager like this

kPaneManager = new CXTPDockingPaneManager();
kPaneManager->InstallDockingPanes( AfxGetApp()->m_pMainWnd );
kPaneManager->SetTheme( /*xtpPaneThemeVisualStudio2005Beta1*/xtpPaneThemeVisualStudio2015 );
kPaneManager->SetAlphaDockingContext(TRUE);
kPaneManager->SetShowDockingContextStickers(/*TRUE*/(XTPDockingContextStickerStyle)xtpPaneStickerStyleVisualStudio2015Dark);

i'm on 18.5.0.
Back to Top
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 891
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Posted: 23 July 2019 at 4:33pm
olebed,

is this the issue I mentioned here:

http://forum.codejock.com/forum_posts.asp?TID=23877&title=docking-pane-drag-is-wrong-with-this-setup

I have the same problem. Here is my setup. I have 3 monitors. A 2k one on the left. My middle monitor is my main monitor, a 4k monitor scaled 200%. My third monitor is a 2k monitor to the right of my main monitor.

I can drag a pane from the main monitor to the left one. But when I cross the boundary, the pane "separates" from the cursor. The closer to the top of the monitor I am, the closer the blue window being dragged is to the cursor. Move down and it separates at a 2X rate.

Now move towards the right monitor. When it reaches the halfway point, it quits moving. That's because CJ thinks the cursor is off the desktop.

The virtual size of the monitor does not match the physical size on the two 2k monitors. GetDeviceCaps and GetMonitorInfoEx shows the difference and also clues on in on how to offset and scale the window rect calculations.
Back to Top
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 891
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Posted: 15 April 2020 at 12:13pm
So, what is the fix here? Even with 19.0 I still have the problem. We really could use a fix.
Back to Top
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 891
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Posted: 29 July 2020 at 9:32am
andredevries and oled,

I can guess why one might not be able to duplicate the issue. If an application is built with per monitor aware DPI settings (usually set in the manifest), the issue will not appear regardless of the monitor setup, including 4k > 100%).

If built with system aware DPI setting, the issue easily shows up. I have not tried "none" for the DPI setting in the VS Manifest setting. I'm not where my 4k monitor is or I would try setting the settings on a sample.

For our app, I have removed the DPI setting via manifest and instead set it programmatically after reading a registry setting I use to determine which of the DPI settings I want to try.

I would not be surprised if the OS version also has an effect. My 4k setup has a box with OS Version 1803. I know Microsoft has made some changes in 1903 and put out a hot fix for that OS and supposedly has rolled the change into the latest updates. That fix had to do with resizing a window where the dynamic rectangle could fail to show up on the correct monitor and could fail to show up anywhere on the desktop when not using per monitor aware.
Back to Top
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 891
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Posted: 29 July 2020 at 9:55am
Don't know if this helps but MFC has an issue drawing a frame rect around a toolbar depending on the monitors involved and I obtained the following code that showed one way to fix it. But, we don't build MFC and we found setting per monitor aware allows an MFC app with a toolbar to work just fine. In any case, here is some code that simulates a resizing rectangle that worked on my desktop with 3 monitors including the 4k set to 200%. The magic is in VirtualToPhysical. I'd upload the project but don't see an easy way to do so. It is a single source file though.

// DrawToScreenSample.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

#include <windowsx.h>
#include "DrawToScreenSample.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING];                  // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING];            // the main window class name

const WCHAR dummyMoveWindowClass[] = L"DummyMoveWindow";
using SetThreadDpiAwarenessContextFunc = DPI_AWARENESS_CONTEXT (WINAPI*)(_In_ DPI_AWARENESS_CONTEXT dpiContext);
SetThreadDpiAwarenessContextFunc g_setThreadDpiAwarenessContext;

HWND g_windowToTrack;
bool m_movingWindow;
RECT g_lastRect;
POINT g_moveStartPoint;
POINT g_moveEndPoint;
bool g_moveWindow;
HWND g_dummyWindowToMove;

// Forward declarations of functions included in this code module:
ATOM                MyRegisterClass(HINSTANCE hInstance);
ATOM                RegisterDummyMoveWindowClass(HINSTANCE hInstance);

BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
void DrawToScreen(HWND selectedWindow);
RECT VirtualToPhysical(const RECT &virtualRect);

INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // Initialize global strings
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_DRAWTOSCREENSAMPLE, szWindowClass, MAX_LOADSTRING);

    if (!MyRegisterClass(hInstance))
    {
        return FALSE;
    }

    // Perform application initialization:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HMODULE user32Dll = GetModuleHandle(L"User32.dll");
    if (!user32Dll)
    {
        return FALSE;
    }

    g_setThreadDpiAwarenessContext = reinterpret_cast<SetThreadDpiAwarenessContextFunc>(GetProcAddress(user32Dll, "SetThreadDpiAwarenessContext"));
   
    if (!g_setThreadDpiAwarenessContext && GetLastError() != ERROR_PROC_NOT_FOUND)
    {
        return FALSE;
    }

    if (g_setThreadDpiAwarenessContext && !RegisterDummyMoveWindowClass(hInstance))
    {
        return FALSE;
    }

    //g_setThreadDpiAwarenessContext = nullptr;
   
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DRAWTOSCREENSAMPLE));

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return static_cast<int>(msg.wParam);
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DRAWTOSCREENSAMPLE));
    wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
    wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_DRAWTOSCREENSAMPLE);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

ATOM RegisterDummyMoveWindowClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex = {};
    wcex.hInstance = hInstance;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.lpfnWndProc = [](
        _In_ HWND hWnd,
        _In_ UINT message,
        _In_ WPARAM wParam,
        _In_ LPARAM lParam) ->LRESULT
    {
        switch (message)
        {
        case WM_DPICHANGED:
            {
                RECT* const prcNewWindow = reinterpret_cast<RECT*>(lParam);
                SetWindowPos(hWnd,
                    nullptr,
                    prcNewWindow->left,
                    prcNewWindow->top,
                    prcNewWindow->right - prcNewWindow->left,
                    prcNewWindow->bottom - prcNewWindow->top,
                    SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
                break;
            }
        }

        return DefWindowProc(hWnd, message, wParam, lParam);
    };

    wcex.lpszClassName = dummyMoveWindowClass;
    wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);

    return RegisterClassExW(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    hInst = hInstance; // Store instance handle in our global variable

    HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

    if (!hWnd)
    {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_COMMAND  - process the application menu
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_LBUTTONDOWN:
        SetCapture(hWnd);
        return DefWindowProc(hWnd, message, wParam, lParam);
    case WM_MOUSEMOVE:
        {
            DPI_AWARENESS_CONTEXT oldAwareness = nullptr;
            if (g_setThreadDpiAwarenessContext)
            {
                oldAwareness = g_setThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
            }

            if (GetCapture() == hWnd)
            {
                DWORD postion = GetMessagePos();
                POINT pt;
                pt.x = GET_X_LPARAM(postion);
                pt.y = GET_Y_LPARAM(postion);

                if (!m_movingWindow)
                {
                    HWND window = WindowFromPoint(pt);
                    HWND topLevelWindow = GetAncestor(window, GA_ROOT);
                    if ((g_windowToTrack || topLevelWindow) && (g_windowToTrack != topLevelWindow))
                    {
                        DrawToScreen(topLevelWindow);
                        g_windowToTrack = topLevelWindow;
                    }

                    // if the user wants to move the top-level window
                    if (topLevelWindow && (wParam & MK_CONTROL))
                    {
                        m_movingWindow = true;
                        g_moveStartPoint = pt;
                        g_moveWindow = false;

                        if (g_setThreadDpiAwarenessContext)
                        {
                            RECT physicalRect;
                            if (GetWindowRect(g_windowToTrack, &physicalRect))
                            {
                                g_dummyWindowToMove = CreateWindow(dummyMoveWindowClass, nullptr, WS_POPUP/* | WS_VISIBLE*/, physicalRect.left, physicalRect.top, physicalRect.right - physicalRect.left, physicalRect.bottom - physicalRect.top, nullptr, nullptr, hInst, nullptr);
                            }
                        }
                    }
                }
                else
                {
                    if (!(wParam & MK_CONTROL))
                    {
                        ReleaseCapture();
                    }
                    else
                    {
                        DrawToScreen(nullptr);
                    }
                }
            }

            if (g_setThreadDpiAwarenessContext)
            {
                g_setThreadDpiAwarenessContext(oldAwareness);
            }
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
    case WM_LBUTTONUP:
        {
            if (m_movingWindow)
            {
                if (wParam & MK_CONTROL)
                {
                    DPI_AWARENESS_CONTEXT oldAwareness = nullptr;
                    if (g_setThreadDpiAwarenessContext)
                    {
                        oldAwareness = g_setThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
                    }

                    DrawToScreen(nullptr);

                    if (g_setThreadDpiAwarenessContext)
                    {
                        g_setThreadDpiAwarenessContext(oldAwareness);
                    }

                    DWORD postion = GetMessagePos();
                    g_moveEndPoint.x = GET_X_LPARAM(postion);
                    g_moveEndPoint.y = GET_Y_LPARAM(postion);
                    g_moveWindow = true;
                }
            }
            ReleaseCapture();
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
    case WM_CAPTURECHANGED:
        if (m_movingWindow)
        {
            m_movingWindow = false;
            DrawToScreen(nullptr);

            if (g_moveWindow)
            {
                RECT virtualRect;

                if (g_dummyWindowToMove)
                {
                    if (GetWindowRect(g_dummyWindowToMove, &virtualRect))
                    {
                        SetWindowPos(g_windowToTrack, nullptr, virtualRect.left, virtualRect.top, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE);
                    }
                }
                else
                {
                    if (GetWindowRect(g_windowToTrack, &virtualRect))
                    {
                        OffsetRect(&virtualRect, (g_moveEndPoint.x - g_moveStartPoint.x), (g_moveEndPoint.y - g_moveStartPoint.y));
                        SetWindowPos(g_windowToTrack, nullptr, virtualRect.left, virtualRect.top, 0, 0, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE);
                    }
                }
            }

            if (g_dummyWindowToMove)
            {
                DestroyWindow(g_dummyWindowToMove);
                g_dummyWindowToMove = nullptr;
            }

            g_windowToTrack = nullptr;
        }
        else if (g_windowToTrack)
        {
            DrawToScreen(nullptr);
            g_windowToTrack = nullptr;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // Parse the menu selections:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

void DrawToScreen(HWND selectedWindow)
{
    HDC dc = GetDC(nullptr);
    if (dc)
    {
        if (SetROP2(dc, R2_XORPEN))
        {
            WORD grayPattern[8];
            for (int i = 0; i < 8; i++)
            {
                grayPattern = WORD(0x5555 << (i & 1));
            }

            HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern);
            if (grayBitmap)
            {
                LOGBRUSH logBrush = {};
                logBrush.lbStyle = BS_PATTERN;
                logBrush.lbHatch = reinterpret_cast<LONG_PTR>(grayBitmap);
                HPEN pen = ExtCreatePen(PS_GEOMETRIC, 0, &logBrush, 0, nullptr);
                if (pen)
                {
                    HGDIOBJ oldPen = SelectObject(dc, pen);
                    if (oldPen)
                    {
                        HGDIOBJ oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
                        if (oldBrush)
                        {
                            if (g_windowToTrack)
                            {
                                Rectangle(dc, g_lastRect.left, g_lastRect.top, g_lastRect.right, g_lastRect.bottom);
                            }

                            RECT calculatedPhysicalRect = {};

                            if (m_movingWindow)
                            {
                                DWORD postion = GetMessagePos();
                                POINT pt;
                                pt.x = GET_X_LPARAM(postion);
                                pt.y = GET_Y_LPARAM(postion);

                                RECT virtualRect;
                                if (GetWindowRect(g_windowToTrack, &virtualRect))
                                {
                                    OffsetRect(&virtualRect, (pt.x - g_moveStartPoint.x), (pt.y - g_moveStartPoint.y));
                                    if (g_dummyWindowToMove)
                                    {
                                        if (SetWindowPos(g_dummyWindowToMove, nullptr, virtualRect.left, virtualRect.top, virtualRect.right - virtualRect.left, virtualRect.bottom - virtualRect.top,
                                            SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE))
                                        {
                                            if (!GetWindowRect(g_dummyWindowToMove, &calculatedPhysicalRect))
                                            {
                                                SetRectEmpty(&calculatedPhysicalRect);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        calculatedPhysicalRect = VirtualToPhysical(virtualRect);
                                    }
                                }
                            }
                            else if (selectedWindow)
                            {
                                if (g_setThreadDpiAwarenessContext)
                                {
                                    if (!GetWindowRect(selectedWindow, &calculatedPhysicalRect))
                                    {
                                        SetRectEmpty(&calculatedPhysicalRect);
                                    }
                                }
                                else
                                {
                                    RECT virtualRect;
                                    if (GetWindowRect(selectedWindow, &virtualRect))
                                    {
                                        calculatedPhysicalRect = VirtualToPhysical(virtualRect);
                                    }
                                }
                            }

                            if (!IsRectEmpty(&calculatedPhysicalRect))
                            {
                                if (Rectangle(dc, calculatedPhysicalRect.left, calculatedPhysicalRect.top, calculatedPhysicalRect.right, calculatedPhysicalRect.bottom))
                                {
                                    g_lastRect = calculatedPhysicalRect;
                                }
                            }

                            SelectObject(dc, oldBrush);
                        }
                        SelectObject(dc, oldPen);
                    }
                    DeleteObject(pen);
                }
                DeleteObject(grayBitmap);
            }
        }
        ReleaseDC(nullptr, dc);
    }
}

RECT VirtualToPhysical(const RECT &virtualRect)
{
    RECT calculatedPhysicalRect = {};

    POINT centerPoint;
    centerPoint.x = virtualRect.left + (virtualRect.right  - virtualRect.left) / 2;
    centerPoint.y = virtualRect.top  + (virtualRect.bottom - virtualRect.top)  / 2;
    HMONITOR monitor = MonitorFromPoint(centerPoint, MONITOR_DEFAULTTONEAREST);
    if (monitor)
    {
        MONITORINFOEX monitorInfo = {};
        monitorInfo.cbSize = sizeof(MONITORINFOEX);
        if (GetMonitorInfo(monitor, &monitorInfo))
        {
            HDC monitorDC = CreateDC(nullptr, monitorInfo.szDevice, nullptr, nullptr);
            if (monitorDC)
            {
                int screenWidth = GetDeviceCaps(monitorDC, DESKTOPHORZRES);
                int virtualizedScreenWidth = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;

                calculatedPhysicalRect.left   = (virtualRect.left   - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.left;
                calculatedPhysicalRect.top    = (virtualRect.top    - monitorInfo.rcMonitor.top)  * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.top;
                calculatedPhysicalRect.right  = (virtualRect.right  - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.left;
                calculatedPhysicalRect.bottom = (virtualRect.bottom - monitorInfo.rcMonitor.top)  * screenWidth / virtualizedScreenWidth + monitorInfo.rcMonitor.top;

                DeleteDC(monitorDC);
            }
        }
    }

    return calculatedPhysicalRect;
}

//void DrawMoving()
//{
//    HDC dc = GetDC(nullptr);
//
//    SetROP2(dc, R2_XORPEN);
//
//    WORD grayPattern[8];
//    for (int i = 0; i < 8; i++)
//    {
//        grayPattern = (WORD)(0x5555 << (i & 1));
//    }
//
//    HBITMAP grayBitmap = CreateBitmap(8, 8, 1, 1, &grayPattern);
//
//    LOGBRUSH logBrush = {};
//    logBrush.lbStyle = BS_PATTERN;
//    logBrush.lbHatch = (LONG_PTR)grayBitmap;
//    HPEN pen = ExtCreatePen(PS_GEOMETRIC, 0, &logBrush, 0, nullptr);
//
//    SelectObject(dc, pen);
//    SelectObject(dc, GetStockObject(NULL_BRUSH));
//
//    if (g_windowToTrack)
//    {
//        Rectangle(dc, g_lastRect.left, g_lastRect.top, g_lastRect.right, g_lastRect.bottom);
//    }
//   
//    if (m_movingWindow) //
//    {
//        DWORD postion = GetMessagePos(); //
//        POINT pt; //
//        pt.x = GET_X_LPARAM(postion); //
//        pt.y = GET_Y_LPARAM(postion); //
//
//        RECT virtualRect;
//        GetWindowRect(g_windowToTrack, &virtualRect);
//        OffsetRect(&virtualRect, (pt.x - g_moveStartPoint.x), (pt.y - g_moveStartPoint.y)); //
//
//        RECT calculatedPhysicalRect = {};
//
//        if (g_dummyWindowToMove) //
//        {
//            SetWindowPos(g_dummyWindowToMove, nullptr, virtualRect.left, virtualRect.top, 0, 0, SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOSIZE); //
//
//            DPI_AWARENESS_CONTEXT oldAwareness = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
//            GetWindowRect(g_dummyWindowToMove, &calculatedPhysicalRect); //
//            SetThreadDpiAwarenessContext(oldAwareness);
//        }
//        else
//        {
//            POINT centerPoint;
//            centerPoint.x = virtualRect.left + (virtualRect.right - virtualRect.left) / 2;
//            centerPoint.y = virtualRect.top + (virtualRect.bottom - virtualRect.top) / 2;
//            HMONITOR monitor = MonitorFromPoint(centerPoint, MONITOR_DEFAULTTONEAREST);
//            MONITORINFOEX monitorInfo = {};
//            monitorInfo.cbSize = sizeof(MONITORINFOEX);
//            GetMonitorInfo(monitor, &monitorInfo);
//
//            HDC monitorDC = CreateDC(nullptr, monitorInfo.szDevice, nullptr, nullptr);
//            int screenWidth = GetDeviceCaps(monitorDC, DESKTOPHORZRES);
//            int virtualizedScreenWidth = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
//           
//            calculatedPhysicalRect.left = ((virtualRect.left - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.left;
//            calculatedPhysicalRect.top = ((virtualRect.top - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.top;
//            calculatedPhysicalRect.right = ((virtualRect.right - monitorInfo.rcMonitor.left) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.left;
//            calculatedPhysicalRect.bottom = ((virtualRect.bottom - monitorInfo.rcMonitor.top) * screenWidth / virtualizedScreenWidth) + monitorInfo.rcMonitor.top;
//        }
//
//        Rectangle(dc, calculatedPhysicalRect.left, calculatedPhysicalRect.top, calculatedPhysicalRect.right, calculatedPhysicalRect.bottom);
//       
//        g_lastRect = calculatedPhysicalRect;
//    }
//
//    DeleteObject(pen);
//    DeleteObject(grayBitmap);
//
//    ReleaseDC(nullptr, dc);
//}

// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

Back to Top
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 891
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Posted: 11 November 2021 at 10:38am
Any fix at all? Running with per monitor aware causes the windows (frame and others) to scale way up on my non-4k monitors when my 4k is primary and is at 200%. If the primary is not the 4k and isn't scaled and the 4k is scaled, there are issues with that desktop topology too.
Back to Top
Fredrik View Drop Down
Senior Member
Senior Member


Joined: 22 June 2005
Status: Offline
Points: 235
Post Options Post Options   Thanks (0) Thanks(0)   Quote Fredrik Quote  Post ReplyReply Direct Link To This Post Posted: 24 November 2021 at 10:12am
I just checked with the 20.2 release, but I still cannot drag docking panes to any of my secondary windows Unhappy
Windows 10, Visual Studio 20157, Toolkit Pro 18.3.0
Back to Top
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 891
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Posted: 04 April 2022 at 4:14pm
I have found that CXTPDockingPaneContext::Move is calling XTPMultiMonitor()->GetWorkArea. This call is what results in getting "stuck" crossing monitors. I use the debugger to bypass that code and always set m_rectDragFrame to m_rectDragFrameScreen. Now, I can cross monitor boundaries. The window "sticks" on the monitor boundary because the rect isn't being updated and m_rectLast never changes. This prevents the call to SetWindowPos from occurring.

I have three monitors with my 4k at 175% being the main monitor with a non-4k to the left and another to the right. When I change that code I also see the following:

If I move the window when it is on my main monitor, I have to drag slowly. As long as I do, the drag point and the cursor position are close together and the window moves. If I move too quickly and the cursor gets outside the drag window, the drag window jumps as ptOffset changes radically.

I am monitoring the point GetMessagePos returns and I see it is always the same as the message.pt obtained in Track. But, as soon as I move quickly, so the cursor gets "ahead" of the window being moved, the coordinates I am getting when on my main (4k) monitor change - they are no longer scaled by my DPI scale factor.

Oh, and this hooker talk in this thread - when I break into the Track code, no hooker is on the call stack. So, is that really an issue?

Once the cursor and the window gets separated, I can see the window pos and the cursor converging if I move towards the left size of my main monitor (x == 0) and I see it diverge more an more as I move to the right side of that monitor due to the point not being scaled. I have not detected why the message point changes when the window pos doesn't keep up with the cursor pos.

And, when they two do separate, if I move the cursor to either the left or right monitor, the window pos syncs back up to the cursor pos and movement is fine on those monitors.

Right now, I think this monitor work area intersection test is just plain wrong. In the very least, the code needs to enumerate each monitor and check for intersection with each one.

Now, if I can figure out why the message point x and y values seem to depend on whether the actual cursor is "on" the floating pane or not, then I expect the tracking on a system aware DPI setting will work as long as I fix the "intersect with monitor" code.
Back to Top
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 891
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Posted: 04 April 2022 at 5:22pm
I left out some important info! In order to even get it to start moving correct when on the main/4k/scaled monitor, I also have to set the DPM's m_bShowContentsWhileDragging to true. That way, the window actually moves with the cursor. Otherwise, the layered "move context window" moves and as soon as the cursor gets out of the actual pane's window area, I get the "jump" as the message point is no longer scaled.
Back to Top
rdhd View Drop Down
Senior Member
Senior Member
Avatar

Joined: 13 August 2007
Location: United States
Status: Offline
Points: 891
Post Options Post Options   Thanks (0) Thanks(0)   Quote rdhd Quote  Post ReplyReply Direct Link To This Post Posted: 06 April 2022 at 3:20pm
So once the code to fix the issue with monitor boundaries is fixed (or eliminated), I can move across the monitor. But, if I move "fast" on the scaled monitor so the cursor gets off the window being dragged, including the blue "context" window, the OS no longer scales the mouse point. So, here's the fix for that - Add WS_EX_NOACTIVATE to the created context window:

void CXTPDockingPaneContext::CreateContextWindow(CXTPDockingPaneContextAlphaWnd* pWnd)
{
    ASSERT(m_bUseAlphaContext);

    if (pWnd->GetSafeHwnd())
        return;

    pWnd->CreateEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE,
                   AfxRegisterWndClass(NULL, AfxGetApp()->LoadStandardCursor(IDC_ARROW)), 0,
                   WS_POPUP, CRect(0, 0, 0, 0), nullptr/*m_pManager->GetSite()*/, 0);

    if (m_pfnSetLayeredWindowAttributes)
    {
        ((PFNSETLAYEREDWINDOWATTRIBUTES)m_pfnSetLayeredWindowAttributes)(pWnd->m_hWnd, 0, 100,
                                                                         LWA_ALPHA);
    }
}
Once you do that, you can drag across monitors and the context window doesn't separate from the cursor and jump (due to the instant change from scaled points to non-scaled points.

If you are calling SetShowContentsWhileDragging(TRUE) so the blue context window doesn't show up, the issue with the scaling of the message point will still occur unless you add WS_EX_NOACTIVATE to the docking pane mini window (or some dialog in it). Once I did that to my floating panes and fixed the monitor boundary issue, I can now drag floating panes with the DPI awareness context set to system aware.

I'm not sure I'm comfortable setting that style on all our floating panes as no telling what regression I might end up with. But, it is perfectly safe to do so on the context window. I'm thinking I might check for that style when starting the drag and if it is not there, add it and remove it when the drag is done. In any case, so far my testing is showing me these two issues are fixed for me (monitor boundary sticking and window jumping if the cursor moves too fast).

What I also plan to do is modify the CJ code I commented out in CXTPDockingPaneContext::Move. Instead of not calling XTPMultiMonitor()->GetWorkArea() and intersecting the rect of the window being moved with it, just enum the monitors and intersect with the work area of each. That too will take a bit of work as the monitor info does NOT return scaled sizes. They are physical so a physical to virtual transform will be needed. Or perhaps just cheat a bit and call this to get the rect:

void GetDesktopRectForMultipleMonitors(LPRECT pDesktopRect)
{
    if(pDesktopRect)
    {
        pDesktopRect->left = GetSystemMetrics(SM_XVIRTUALSCREEN);
        pDesktopRect->top = GetSystemMetrics(SM_YVIRTUALSCREEN);
        int w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
        pDesktopRect->right = pDesktopRect->left + w;
        int h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
        pDesktopRect->bottom =  pDesktopRect->top + h;
    }
}

Then intersect with that rect.
Back to Top
 Post Reply Post Reply
  Share Topic   

Forum Jump Forum Permissions View Drop Down

Forum Software by Web Wiz Forums® version 12.04
Copyright ©2001-2021 Web Wiz Ltd.

This page was generated in 0.297 seconds.