https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f3d03760e9202c18ce2ebc115ffb539f39fc2e3d

commit f3d03760e9202c18ce2ebc115ffb539f39fc2e3d
Author:     Whindmar Saksit <whinds...@proton.me>
AuthorDate: Sun Aug 11 20:21:58 2024 +0200
Commit:     GitHub <nore...@github.com>
CommitDate: Sun Aug 11 20:21:58 2024 +0200

    [NTUSER] Rewrite Window Snap handling (#5705)
    
    Fixes many Window Snap related bugs and uses the 
WS_EX2_VERTICALLYMAXIMIZED* styles to remember which edge it is snapped to.
    
    The most significant change is that GetWindowPlacement lies about the 
normal position when it is snapped, just like Windows.
    
    CORE-19160 CORE-19165 CORE-19166
---
 win32ss/user/ntuser/defwnd.c    | 110 +++++----------
 win32ss/user/ntuser/nonclient.c | 288 ++++++++++++++++++++++------------------
 win32ss/user/ntuser/winpos.c    | 150 ++++++++++++++++++++-
 win32ss/user/ntuser/winpos.h    |  31 +++++
 4 files changed, 370 insertions(+), 209 deletions(-)

diff --git a/win32ss/user/ntuser/defwnd.c b/win32ss/user/ntuser/defwnd.c
index 33911c5ea5b..368105afc42 100644
--- a/win32ss/user/ntuser/defwnd.c
+++ b/win32ss/user/ntuser/defwnd.c
@@ -788,112 +788,66 @@ IntDefWindowProc(
          }
          if (g_bWindowSnapEnabled && (IS_KEY_DOWN(gafAsyncKeyState, VK_LWIN) 
|| IS_KEY_DOWN(gafAsyncKeyState, VK_RWIN)))
          {
-            BOOL IsTaskBar;
-            DWORD StyleTB;
-            DWORD ExStyleTB;
             HWND hwndTop = UserGetForegroundWindow();
             PWND topWnd = UserGetWindowObject(hwndTop);
+            BOOL allowSnap;
 
             // MS Doc: foreground window can be NULL, e.g. when window is 
losing activation
             if (!topWnd)
                return 0;
 
-            // We want to forbid snapping operations on the TaskBar
-            // We use a heuristic for detecting the TaskBar Wnd by its typical 
Style & ExStyle Values
-            ExStyleTB = (topWnd->ExStyle & WS_EX_TOOLWINDOW);
-            StyleTB = (topWnd->style & (WS_POPUP | WS_VISIBLE | 
WS_CLIPSIBLINGS | WS_CLIPCHILDREN));
-            IsTaskBar = (StyleTB == (WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | 
WS_CLIPCHILDREN))
-                        && (ExStyleTB == WS_EX_TOOLWINDOW);
-            TRACE("ExStyle=%x Style=%x IsTaskBar=%d\n", ExStyleTB, StyleTB, 
IsTaskBar);
+            allowSnap = IntIsSnapAllowedForWindow(topWnd);
+            /* Allow the minimize action if it has a minimize button, even if 
the window cannot be snapped (e.g. Calc.exe) */
+            if (!allowSnap && (topWnd->style & (WS_MINIMIZEBOX|WS_THICKFRAME)) 
== WS_MINIMIZEBOX)
+                allowSnap = wParam == VK_DOWN;
 
-            if (!IsTaskBar)
+            if (allowSnap)
             {
-               if ((topWnd->style & WS_THICKFRAME) == 0)
-                  return 0;
+               UINT snapped = IntGetWindowSnapEdge(topWnd);
 
                if (wParam == VK_DOWN)
                {
                    if (topWnd->style & WS_MAXIMIZE)
-                   {
-                       co_IntSendMessage(hwndTop, WM_SYSCOMMAND, SC_RESTORE, 
lParam);
-
-                       /* "Normal size" must be erased after restoring, 
otherwise it will block next side snap actions */
-                       RECTL_vSetEmptyRect(&topWnd->InternalPos.NormalRect);
-                   }
+                       co_IntSendMessage(hwndTop, WM_SYSCOMMAND, SC_RESTORE, 
MAKELONG(0, 1));
+                   else if (snapped)
+                       co_IntUnsnapWindow(topWnd);
                    else
-                   {
-                       co_IntSendMessage(hwndTop, WM_SYSCOMMAND, SC_MINIMIZE, 
lParam);
-                   }
+                       co_IntSendMessage(hwndTop, WM_SYSCOMMAND, SC_MINIMIZE, 
MAKELONG(0, 1));
                }
                else if (wParam == VK_UP)
                {
-                  RECT currentRect;
-                  if ((topWnd->InternalPos.NormalRect.right == 
topWnd->InternalPos.NormalRect.left) ||
-                      (topWnd->InternalPos.NormalRect.top == 
topWnd->InternalPos.NormalRect.bottom))
-                  {
-                      currentRect = topWnd->rcWindow;
-                  }
-                  else
-                  {
-                      currentRect = topWnd->InternalPos.NormalRect;
-                  }
-                  co_IntSendMessage(hwndTop, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
-
-                  // save normal rect if maximazing snapped window
-                  topWnd->InternalPos.NormalRect = currentRect;
+                   if (topWnd->style & WS_MINIMIZE)
+                       co_IntSendMessage(hwndTop, WM_SYSCOMMAND, SC_RESTORE, 
MAKELONG(0, 1));
+                   else
+                       co_IntSendMessage(hwndTop, WM_SYSCOMMAND, SC_MAXIMIZE, 
MAKELONG(0, 1));
                }
                else if (wParam == VK_LEFT || wParam == VK_RIGHT)
                {
-                  RECT snapRect, normalRect, windowRect;
-                  BOOL snapped;
-                  normalRect = topWnd->InternalPos.NormalRect;
-                  snapped = (normalRect.left != 0 && normalRect.right != 0 &&
-                             normalRect.top != 0 && normalRect.bottom != 0);
+                  UINT edge = wParam == VK_LEFT ? HTLEFT : HTRIGHT;
+                  UINT otherEdge = edge == HTLEFT ? HTRIGHT : HTLEFT;
 
                   if (topWnd->style & WS_MAXIMIZE)
                   {
-                     co_IntSendMessage(hwndTop, WM_SYSCOMMAND, SC_RESTORE, 
lParam);
-                     snapped = FALSE;
-                  }
-                  windowRect = topWnd->rcWindow;
-
-                  UserSystemParametersInfo(SPI_GETWORKAREA, 0, &snapRect, 0);
-                  if (wParam == VK_LEFT)
-                  {
-                     snapRect.right = (snapRect.left + snapRect.right) / 2;
+                     /* SC_RESTORE + Snap causes the window to visually move 
twice, place it manually in the snap position */
+                     RECT normalRect = topWnd->InternalPos.NormalRect;
+                     co_IntCalculateSnapPosition(topWnd, edge, 
&topWnd->InternalPos.NormalRect); /* Calculate edge position */
+                     IntSetSnapEdge(topWnd, edge); /* Tell everyone the edge 
we are snapped to */
+                     co_IntSendMessage(hwndTop, WM_SYSCOMMAND, SC_RESTORE, 
MAKELONG(0, 1));
+                     IntSetSnapInfo(topWnd, edge, &normalRect); /* Reset the 
real place to unsnap to */
+                     snapped = HTNOWHERE; /* Force snap */
                   }
-                  else // VK_RIGHT
+#if 0 /* Windows 8 does this but is it a good feature? */
+                  else if (snapped == edge)
                   {
-                     snapRect.left = (snapRect.left + snapRect.right) / 2;
+                     /* Already snapped to this edge, snap to the opposite 
side */
+                     edge = otherEdge;
                   }
+#endif
 
-                  if (snapped)
-                  {
-                     // if window was snapped but moved to other location - 
restore normal size
-                     if (!IntEqualRect(&snapRect, &windowRect))
-                     {
-                        RECT empty = {0, 0, 0, 0};
-                        co_WinPosSetWindowPos(topWnd,
-                                              0,
-                                              normalRect.left,
-                                              normalRect.top,
-                                              normalRect.right - 
normalRect.left,
-                                              normalRect.bottom - 
normalRect.top,
-                                              0);
-                        topWnd->InternalPos.NormalRect = empty;
-                     }
-                  }
+                  if (snapped == otherEdge)
+                     co_IntUnsnapWindow(topWnd);
                   else
-                  {
-                     co_WinPosSetWindowPos(topWnd,
-                                           0,
-                                           snapRect.left,
-                                           snapRect.top,
-                                           snapRect.right - snapRect.left,
-                                           snapRect.bottom - snapRect.top,
-                                           0);
-                     topWnd->InternalPos.NormalRect = windowRect;
-                  }
+                     co_IntSnapWindow(topWnd, edge);
                }
             }
          }
diff --git a/win32ss/user/ntuser/nonclient.c b/win32ss/user/ntuser/nonclient.c
index 2324cc04afd..f3a0cdb8492 100644
--- a/win32ss/user/ntuser/nonclient.c
+++ b/win32ss/user/ntuser/nonclient.c
@@ -136,6 +136,18 @@ NC_GetSysPopupPos(PWND Wnd, RECT *Rect)
     }
 }
 
+static UINT
+GetSnapActivationPoint(PWND Wnd, POINT pt)
+{
+    RECT wa;
+    UserSystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0); /* FIXME: MultiMon 
of PWND */
+
+    if (pt.x <= wa.left) return HTLEFT;
+    if (pt.x >= wa.right-1) return HTRIGHT;
+    if (pt.y <= wa.top) return HTTOP; /* Maximize */
+    return HTNOWHERE;
+}
+
 LONG FASTCALL
 DefWndStartSizeMove(PWND Wnd, WPARAM wParam, POINT *capturePoint)
 {
@@ -239,13 +251,15 @@ VOID FASTCALL
 DefWndDoSizeMove(PWND pwnd, WORD wParam)
 {
    MSG msg;
-   RECT sizingRect, mouseRect, origRect, unmodRect;
+   RECT sizingRect, mouseRect, origRect, unmodRect, snapPreviewRect;
+   PRECT pFrameRect = &sizingRect;
    HDC hdc;
    LONG hittest = (LONG)(wParam & 0x0f);
    PCURICON_OBJECT DragCursor = NULL, OldCursor = NULL;
    POINT minTrack, maxTrack;
    POINT capturePoint, pt;
    ULONG Style, ExStyle;
+   UINT orgSnap = IntGetWindowSnapEdge(pwnd), snap = orgSnap;
    BOOL thickframe;
    BOOL iconic;
    BOOL moved = FALSE;
@@ -262,6 +276,8 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
    iconic = (Style & WS_MINIMIZE) != 0;
 
    if (((Style & WS_MAXIMIZE) && syscommand != SC_MOVE) || 
!IntIsWindowVisible(pwnd)) return;
+   if ((Style & (WS_MAXIMIZE | WS_CHILD)) == WS_MAXIMIZE)
+       orgSnap = snap = HTTOP;
 
    thickframe = UserHasThickFrameStyle(Style, ExStyle) && !iconic;
 
@@ -294,7 +310,7 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
       {
           co_UserSetCapture(UserHMGetHandle(pwnd));
           hittest = DefWndStartSizeMove(pwnd, wParam, &capturePoint);
-         if (!hittest)
+          if (!hittest)
           {
               IntReleaseCapture();
               return;
@@ -402,56 +418,20 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
       else if (g_bWindowSnapEnabled && (msg.message == WM_LBUTTONUP ||
               (msg.message == WM_MOUSEMOVE && (msg.wParam & MK_LBUTTON) == 0)))
       { // If WindowSnapEnabled: Decide whether to snap before exiting
-         DWORD ExStyleTB, StyleTB;
-         BOOL IsTaskBar;
-
-         // We want to forbid snapping operations on the TaskBar
-         // We use a heuristic for detecting the TaskBar Wnd by its typical 
Style & ExStyle Values
-         ExStyleTB = (ExStyle & WS_EX_TOOLWINDOW);
-         StyleTB = (Style & (WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | 
WS_CLIPCHILDREN));
-         IsTaskBar = (StyleTB == (WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | 
WS_CLIPCHILDREN))
-                     && (ExStyleTB == WS_EX_TOOLWINDOW);
-         TRACE("ExStyle=%x Style=%x IsTaskBar=%d\n", ExStyleTB, StyleTB, 
IsTaskBar);
-
-         // check for snapping if was moved by caption
-         if (!IsTaskBar && hittest == HTCAPTION && thickframe && (ExStyle & 
WS_EX_MDICHILD) == 0)
+         if (hittest == HTCAPTION && thickframe && /* Check for snapping if 
was moved by caption */
+             IntIsSnapAllowedForWindow(pwnd) && (ExStyle & WS_EX_MDICHILD) == 
0)
          {
-            RECT snapRect;
-            BOOL doSideSnap = FALSE;
-            UserSystemParametersInfo(SPI_GETWORKAREA, 0, &snapRect, 0);
-
-            // snap to left
-            if (pt.x <= snapRect.left)
-            {
-               snapRect.right = (snapRect.right - snapRect.left) / 2 + 
snapRect.left;
-               doSideSnap = TRUE;
-            }
-            // snap to right
-            if (pt.x >= snapRect.right-1)
+            BOOLEAN wasSnap = IntIsWindowSnapped(pwnd); /* Need the live snap 
state, not orgSnap nor maximized state */
+            UINT snapTo = iconic ? HTNOWHERE : GetSnapActivationPoint(pwnd, 
pt);
+            if (snapTo)
             {
-               snapRect.left = (snapRect.right - snapRect.left) / 2 + 
snapRect.left;
-               doSideSnap = TRUE;
-            }
-
-            if (doSideSnap)
-            {
-               co_WinPosSetWindowPos(pwnd,
-                                     NULL,
-                                     snapRect.left,
-                                     snapRect.top,
-                                     snapRect.right - snapRect.left,
-                                     snapRect.bottom - snapRect.top,
-                                     SWP_NOACTIVATE);
-               pwnd->InternalPos.NormalRect = origRect;
-            }
-            else
-            {
-               // maximize if on dragged to top
-               if (pt.y <= snapRect.top)
-               {
-                  co_IntSendMessage(UserHMGetHandle(pwnd), WM_SYSCOMMAND, 
SC_MAXIMIZE, 0);
-                  pwnd->InternalPos.NormalRect = origRect;
-               }
+                if (DragFullWindows)
+                {
+                    co_IntSnapWindow(pwnd, snapTo);
+                    if (!wasSnap)
+                        pwnd->InternalPos.NormalRect = origRect;
+                }
+                snap = snapTo;
             }
          }
          break;
@@ -468,10 +448,10 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
 
       if (msg.message == WM_KEYDOWN) switch(msg.wParam)
       {
-       case VK_UP:    pt.y -= 8; break;
-       case VK_DOWN:  pt.y += 8; break;
-       case VK_LEFT:  pt.x -= 8; break;
-       case VK_RIGHT: pt.x += 8; break;
+      case VK_UP:    pt.y -= 8; break;
+      case VK_DOWN:  pt.y += 8; break;
+      case VK_LEFT:  pt.x -= 8; break;
+      case VK_RIGHT: pt.x += 8; break;
       }
 
       pt.x = max( pt.x, mouseRect.left );
@@ -484,63 +464,99 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
 
       if (dx || dy)
       {
-         if ( !moved )
-         {
-             moved = TRUE;
-          if ( iconic ) /* ok, no system popup tracking */
+      if (!moved)
+      {
+          moved = TRUE;
+          if (iconic) /* ok, no system popup tracking */
           {
               OldCursor = UserSetCursor(DragCursor, FALSE);
-              UserShowCursor( TRUE );
+              UserShowCursor(TRUE);
           }
-          else if(!DragFullWindows)
-             UserDrawMovingFrame( hdc, &sizingRect, thickframe );
-         }
+          else if (!DragFullWindows)
+             UserDrawMovingFrame(hdc, &sizingRect, thickframe);
+      }
 
-         if (msg.message == WM_KEYDOWN) UserSetCursorPos(pt.x, pt.y, 0, 0, 
FALSE);
-         else
-         {
-             RECT newRect = unmodRect;
+      if (msg.message == WM_KEYDOWN)
+      {
+          UserSetCursorPos(pt.x, pt.y, 0, 0, FALSE);
+      }
+      else
+      {
+          RECT newRect = unmodRect;
 
-             if (!iconic && !DragFullWindows) UserDrawMovingFrame( hdc, 
&sizingRect, thickframe );
+          if (!iconic && !DragFullWindows)
+          {
+              UserDrawMovingFrame(hdc, pFrameRect, thickframe);
+              pFrameRect = &sizingRect;
+          }
           if (hittest == HTCAPTION)
           {
               /* Restore window size if it is snapped */
-              if (!RECTL_bIsEmptyRect(&pwnd->InternalPos.NormalRect) &&
-                  !IntEqualRect(&pwnd->InternalPos.NormalRect, 
&pwnd->rcWindow))
+              PRECT pr = &newRect;
+              LONG width, height, capcy, snapTo;
+              if (snap && syscommand == SC_MOVE && !iconic &&
+                  !RECTL_bIsEmptyRect(&pwnd->InternalPos.NormalRect))
               {
-                  UserSetCursorPos(max(0, pwnd->InternalPos.NormalRect.left) + 
pt.x, pwnd->InternalPos.NormalRect.top + pt.y, 0, 0, FALSE);
-
-                  /* Save normal size - it required when window unsnapped from 
one side and snapped to another holding mouse down */
-                  origRect = pwnd->InternalPos.NormalRect;
-
-                  /* Restore from maximized state */
-                  if (Style & WS_MAXIMIZE)
+                  *pr = pwnd->InternalPos.NormalRect;
+                  origRect = *pr; /* Save normal size - is required when 
window unsnapped from one side and snapped to another holding mouse down */
+
+                  /* Try to position the center of the caption where the mouse 
is horizontally */
+                  capcy = UserGetSystemMetrics((ExStyle & WS_EX_TOPMOST) ? 
SM_CYSMCAPTION : SM_CYCAPTION); /* No border, close enough */
+                  width = pr->right - pr->left;
+                  height = pr->bottom - pr->top;
+                  pr->left = pt.x - width / 2;
+                  pr->right = pr->left + width;
+                  pr->top = mouseRect.top;
+                  pr->bottom = pr->top + height;
+                  if (pr->left < mouseRect.left)
                   {
-                      co_IntSendMessage(UserHMGetHandle(pwnd), WM_SYSCOMMAND, 
SC_RESTORE, 0);
+                      pr->left = mouseRect.left;
+                      pr->right = pr->left + width;
                   }
-                  /* Restore snapped to left/right place */
-                  else
+                  if ((pwnd->ExStyle & WS_EX_LAYOUTRTL) && pr->right > 
mouseRect.right)
                   {
-                      co_WinPosSetWindowPos(pwnd,
-                                            NULL,
-                                            pwnd->InternalPos.NormalRect.left,
-                                            pwnd->InternalPos.NormalRect.top,
-                                            pwnd->InternalPos.NormalRect.right 
- pwnd->InternalPos.NormalRect.left,
-                                            
pwnd->InternalPos.NormalRect.bottom - pwnd->InternalPos.NormalRect.top,
-                                            0);
+                      pr->left = mouseRect.right - width;
+                      pr->right = pr->left + width;
+                  }
+                  UserSetCursorPos(pt.x, pr->top + capcy / 2, 0, 0, FALSE);
+                  snap = FALSE;
+                  dx = dy = 0; /* Don't offset this move */
+                  if (DragFullWindows)
+                  {
+                      IntSetStyle(pwnd, 0, WS_MAXIMIZE);
+                      IntSetSnapEdge(pwnd, HTNOWHERE);
+
+                      /* Have to move and size it now because we don't want 
SWP_NOSIZE */
+                      co_WinPosSetWindowPos(pwnd, HWND_TOP, pr->left, pr->top, 
width, height, SWP_NOACTIVATE);
+                  }
+              }
+              else if (!snap && syscommand == SC_MOVE && !iconic)
+              {
+                  if ((snapTo = GetSnapActivationPoint(pwnd, pt)) != 0)
+                  {
+                      co_IntCalculateSnapPosition(pwnd, snapTo, 
&snapPreviewRect);
+                      if (DragFullWindows)
+                      {
+                          /* TODO: Show preview of snap */
+                      }
+                      else
+                      {
+                          pFrameRect = &snapPreviewRect;
+                          UserDrawMovingFrame(hdc, pFrameRect, thickframe);
+                          continue;
+                      }
                   }
-                  RECTL_vSetEmptyRect(&pwnd->InternalPos.NormalRect);
-                  continue;
               }
 
               /* regular window moving */
               RECTL_vOffsetRect(&newRect, dx, dy);
           }
-             if (ON_LEFT_BORDER(hittest)) newRect.left += dx;
-             else if (ON_RIGHT_BORDER(hittest)) newRect.right += dx;
-             if (ON_TOP_BORDER(hittest)) newRect.top += dy;
-             else if (ON_BOTTOM_BORDER(hittest)) newRect.bottom += dy;
-             capturePoint = pt;
+          if (ON_LEFT_BORDER(hittest)) newRect.left += dx;
+          else if (ON_RIGHT_BORDER(hittest)) newRect.right += dx;
+          if (ON_TOP_BORDER(hittest)) newRect.top += dy;
+          else if (ON_BOTTOM_BORDER(hittest)) newRect.bottom += dy;
+
+          capturePoint = pt;
 
               //
               //  Save the new position to the unmodified rectangle. This 
allows explorer task bar
@@ -549,7 +565,7 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
               //
               unmodRect = newRect;
 
-             /* determine the hit location */
+              /* Determine the hit location */
               if (syscommand == SC_SIZE)
               {
                   WPARAM wpSizingHit = 0;
@@ -561,7 +577,7 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
               else
                   co_IntSendMessage( UserHMGetHandle(pwnd), WM_MOVING, 0, 
(LPARAM)&newRect );
 
-             if (!iconic)
+              if (!iconic)
               {
                  if (!DragFullWindows)
                      UserDrawMovingFrame( hdc, &newRect, thickframe );
@@ -609,7 +625,7 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
                  }
               }
               sizingRect = newRect;
-         }
+        }
       }
    }
 
@@ -631,8 +647,12 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
        */
       if (OldCursor) UserDereferenceObject(OldCursor);
    }
-   else if ( moved && !DragFullWindows )
-      UserDrawMovingFrame( hdc, &sizingRect, thickframe );
+   else
+   {
+      UINT eraseFinalFrame = moved && !DragFullWindows;
+      if (eraseFinalFrame)
+         UserDrawMovingFrame(hdc, pFrameRect, thickframe); // Undo the XOR 
drawing
+   }
 
    UserReleaseDC(NULL, hdc, FALSE);
 
@@ -656,49 +676,57 @@ DefWndDoSizeMove(PWND pwnd, WORD wParam)
    /* window moved or resized */
    if (moved)
    {
+      BOOL forceSizing = !iconic && hittest == HTCAPTION && (!!orgSnap != 
!!snap);
+      UINT swp = (!forceSizing && hittest == HTCAPTION) ? SWP_NOSIZE : 0;
+
       /* if the moving/resizing isn't canceled call SetWindowPos
        * with the new position or the new size of the window
        */
       if (!((msg.message == WM_KEYDOWN) && (msg.wParam == VK_ESCAPE)) )
       {
-         /* NOTE: SWP_NOACTIVATE prevents document window activation in Word 6 
*/
-         if (!DragFullWindows || iconic )
-         {
-           co_WinPosSetWindowPos( pwnd,
-                                  0,
-                                  sizingRect.left,
-                                  sizingRect.top,
-                                  sizingRect.right - sizingRect.left,
-                                  sizingRect.bottom - sizingRect.top,
-                                 ( hittest == HTCAPTION ) ? SWP_NOSIZE : 0 );
-          }
+         /* NOTE: SWP_NOACTIVATE prevents document window activation in Word 6 
*/
+         if (!DragFullWindows || iconic)
+         {
+            if (snap)
+            {
+               co_IntSnapWindow(pwnd, snap);
+            }
+            else
+            {
+               if (orgSnap && !snap)
+               {
+                  IntSetStyle(pwnd, 0, WS_MAXIMIZE);
+                  IntSetSnapInfo(pwnd, HTNOWHERE, NULL);
+               }
+               co_WinPosSetWindowPos(pwnd, HWND_TOP, sizingRect.left, 
sizingRect.top,
+                                     sizingRect.right - sizingRect.left,
+                                     sizingRect.bottom - sizingRect.top, swp);
+            }
+         }
       }
       else
-      { /* restore previous size/position */
-       if ( DragFullWindows )
-       {
-         co_WinPosSetWindowPos( pwnd,
-                                0,
-                                origRect.left,
-                                origRect.top,
-                                origRect.right - origRect.left,
-                                origRect.bottom - origRect.top,
-                               ( hittest == HTCAPTION ) ? SWP_NOSIZE : 0 );
-        }
+      {
+         /* restore previous size/position */
+         if (orgSnap)
+         {
+            co_IntSnapWindow(pwnd, orgSnap);
+         }
+         else if (DragFullWindows)
+         {
+            co_WinPosSetWindowPos(pwnd, HWND_TOP, origRect.left, origRect.top,
+                                  origRect.right - origRect.left,
+                                  origRect.bottom - origRect.top, swp);
+         }
       }
    }
 
-   if ( IntIsWindow(UserHMGetHandle(pwnd)) )
+   if (IntIsWindow(UserHMGetHandle(pwnd)))
    {
-     if ( iconic )
-     {
-       /* Single click brings up the system menu when iconized */
-       if ( !moved )
-        {
-           if( Style & WS_SYSMENU )
-             co_IntSendMessage( UserHMGetHandle(pwnd), WM_SYSCOMMAND, 
SC_MOUSEMENU + HTSYSMENU, MAKELONG(pt.x,pt.y));
-        }
-     }
+      /* Single click brings up the system menu when iconized */
+      if (iconic && !moved && (Style & WS_SYSMENU))
+      {
+         co_IntSendMessage(UserHMGetHandle(pwnd), WM_SYSCOMMAND, SC_MOUSEMENU 
+ HTSYSMENU, MAKELONG(pt.x, pt.y));
+      }
    }
 }
 
diff --git a/win32ss/user/ntuser/winpos.c b/win32ss/user/ntuser/winpos.c
index 227b03e8a5c..9c55ef7c0d9 100644
--- a/win32ss/user/ntuser/winpos.c
+++ b/win32ss/user/ntuser/winpos.c
@@ -549,7 +549,13 @@ WinPosInitInternalPos(PWND Wnd, RECTL *RestoreRect)
    }
    else
    {
-      Wnd->InternalPos.NormalRect = Rect;
+      /* Lie about the snap; Windows does this so applications don't save their
+       * position as a snap but rather the unsnapped "real" position. */
+      if (!IntIsWindowSnapped(Wnd) ||
+          RECTL_bIsEmptyRect(&Wnd->InternalPos.NormalRect))
+      {
+         Wnd->InternalPos.NormalRect = Rect;
+      }
    }
 }
 
@@ -2487,6 +2493,7 @@ co_WinPosMinMaximize(PWND Wnd, UINT ShowFlag, RECT* 
NewPos)
          case SW_MAXIMIZE:
             {
                //ERR("MinMaximize Maximize\n");
+               IntSetSnapEdge(Wnd, HTNOWHERE); /* Mark as not snapped (for 
Win+Left,Up,Down) */
                if ((Wnd->style & WS_MAXIMIZE) && (Wnd->style & WS_VISIBLE))
                {
                   SwpFlags = SWP_NOSIZE | SWP_NOMOVE;
@@ -2535,6 +2542,12 @@ co_WinPosMinMaximize(PWND Wnd, UINT ShowFlag, RECT* 
NewPos)
                   else
                   {
                      *NewPos = wpl.rcNormalPosition;
+                     if (ShowFlag != SW_SHOWNORMAL && ShowFlag != 
SW_SHOWDEFAULT)
+                     {
+                        UINT edge = IntGetWindowSnapEdge(Wnd);
+                        if (edge)
+                           co_IntCalculateSnapPosition(Wnd, edge, NewPos);
+                     }
                      NewPos->right -= NewPos->left;
                      NewPos->bottom -= NewPos->top;
                      break;
@@ -3863,4 +3876,139 @@ NtUserWindowFromPoint(LONG X, LONG Y)
    return Ret;
 }
 
+/* Windows 10 (1903?)
+BOOL APIENTRY
+NtUserIsWindowArranged(HWND hWnd)
+{
+    PWND pwnd = UserGetWindowObject(hWnd);
+    return pwnd && IntIsWindowSnapped(pwnd);
+}
+*/
+
+UINT FASTCALL
+IntGetWindowSnapEdge(PWND Wnd)
+{
+    if (Wnd->ExStyle2 & WS_EX2_VERTICALLYMAXIMIZEDLEFT) return HTLEFT;
+    if (Wnd->ExStyle2 & WS_EX2_VERTICALLYMAXIMIZEDRIGHT) return HTRIGHT;
+    return HTNOWHERE;
+}
+
+VOID FASTCALL
+co_IntCalculateSnapPosition(PWND Wnd, UINT Edge, OUT RECT *Pos)
+{
+    POINT maxs, mint, maxt;
+    UINT width, height;
+    UserSystemParametersInfo(SPI_GETWORKAREA, 0, Pos, 0); /* FIXME: MultiMon 
of PWND */
+
+    co_WinPosGetMinMaxInfo(Wnd, &maxs, NULL, &mint, &maxt);
+    width = Pos->right - Pos->left;
+    width = min(min(max(width / 2, mint.x), maxt.x), width);
+    height = Pos->bottom - Pos->top;
+    height = min(max(height, mint.y), maxt.y);
+
+    switch (Edge)
+    {
+    case HTTOP: /* Maximized (Calculate RECT snap preview for SC_MOVE) */
+        height = min(Pos->bottom - Pos->top, maxs.y);
+        break; 
+    case HTLEFT:
+        Pos->right = width;
+        break;
+    case HTRIGHT:
+        Pos->left = Pos->right - width;
+        break;
+    default:
+        ERR("Unexpected snap edge %#x\n", Edge);
+    }
+    Pos->bottom = Pos->top + height;
+}
+
+VOID FASTCALL
+co_IntSnapWindow(PWND Wnd, UINT Edge)
+{
+    RECT newPos;
+    BOOLEAN wasSnapped = IntIsWindowSnapped(Wnd);
+    UINT normal = !(Wnd->style & (WS_MAXIMIZE | WS_MINIMIZE));
+    USER_REFERENCE_ENTRY ref;
+    BOOLEAN hasRef = FALSE;
+
+    if (Edge == HTTOP)
+    {
+        co_IntSendMessage(UserHMGetHandle(Wnd), WM_SYSCOMMAND, SC_MAXIMIZE, 0);
+        return;
+    }
+    else if (Edge)
+    {
+        UserRefObjectCo(Wnd, &ref);
+        hasRef = TRUE;
+        co_IntCalculateSnapPosition(Wnd, Edge, &newPos);
+        IntSetSnapInfo(Wnd, Edge, (wasSnapped || !normal) ? NULL : 
&Wnd->rcWindow);
+    }
+    else if (wasSnapped)
+    {
+        if (!normal)
+        {
+            IntSetSnapEdge(Wnd, HTNOWHERE);
+            return;
+        }
+        newPos = Wnd->InternalPos.NormalRect;
+        IntSetSnapInfo(Wnd, HTNOWHERE, NULL);
+    }
+    else
+    {
+        return; /* Already unsnapped, do nothing */
+    }
+
+    TRACE("WindowSnap: %d->%d\n", IntGetWindowSnapEdge(Wnd), Edge);
+    co_WinPosSetWindowPos(Wnd, HWND_TOP,
+                          newPos.left,
+                          newPos.top,
+                          newPos.right - newPos.left,
+                          newPos.bottom - newPos.top,
+                          0);
+    if (hasRef)
+        UserDerefObjectCo(Wnd);
+}
+
+VOID FASTCALL
+IntSetSnapEdge(PWND Wnd, UINT Edge)
+{
+    UINT styleMask = WS_EX2_VERTICALLYMAXIMIZEDLEFT | 
WS_EX2_VERTICALLYMAXIMIZEDRIGHT;
+    UINT style = 0;
+    switch (Edge)
+    {
+    case HTNOWHERE:
+        style = 0;
+        break;
+    case HTTOP: /* Maximize throws away the snap */
+        style = 0;
+        break;
+    case HTLEFT:
+        style = WS_EX2_VERTICALLYMAXIMIZEDLEFT;
+        break;
+    case HTRIGHT:
+        style = WS_EX2_VERTICALLYMAXIMIZEDRIGHT;
+        break;
+    default:
+        ERR("Unexpected snap edge %#x\n", Edge);
+    }
+    Wnd->ExStyle2 = (Wnd->ExStyle2 & ~styleMask) | style;
+}
+
+VOID FASTCALL
+IntSetSnapInfo(PWND Wnd, UINT Edge, IN const RECT *Pos OPTIONAL)
+{
+    RECT r;
+    IntSetSnapEdge(Wnd, Edge);
+    if (Edge != HTNOWHERE)
+    {
+        RECTL_vSetEmptyRect(&r);
+        Pos = (Wnd->style & WS_MINIMIZE) ? NULL : &r;
+    }
+    if (Pos)
+    {
+        Wnd->InternalPos.NormalRect = *Pos;
+    }
+}
+
 /* EOF */
diff --git a/win32ss/user/ntuser/winpos.h b/win32ss/user/ntuser/winpos.h
index 03e1cd8923b..17d1c49cf33 100644
--- a/win32ss/user/ntuser/winpos.h
+++ b/win32ss/user/ntuser/winpos.h
@@ -71,3 +71,34 @@ BOOL FASTCALL IntClientToScreen(PWND,LPPOINT);
 BOOL FASTCALL IntGetWindowRect(PWND,RECTL*);
 BOOL UserHasWindowEdge(DWORD,DWORD);
 VOID UserGetWindowBorders(DWORD,DWORD,SIZE*,BOOL);
+
+UINT FASTCALL IntGetWindowSnapEdge(PWND Wnd);
+VOID FASTCALL co_IntCalculateSnapPosition(PWND Wnd, UINT Edge, OUT RECT *Pos);
+VOID FASTCALL co_IntSnapWindow(PWND Wnd, UINT Edge);
+VOID FASTCALL IntSetSnapEdge(PWND Wnd, UINT Edge);
+VOID FASTCALL IntSetSnapInfo(PWND Wnd, UINT Edge, IN const RECT *Pos OPTIONAL);
+
+FORCEINLINE VOID
+co_IntUnsnapWindow(PWND Wnd)
+{
+    co_IntSnapWindow(Wnd, 0);
+}
+
+FORCEINLINE BOOLEAN
+IntIsWindowSnapped(PWND Wnd)
+{
+    return (Wnd->ExStyle2 & (WS_EX2_VERTICALLYMAXIMIZEDLEFT | 
WS_EX2_VERTICALLYMAXIMIZEDRIGHT)) != 0;
+}
+
+FORCEINLINE BOOLEAN
+IntIsSnapAllowedForWindow(PWND Wnd)
+{
+    /* We want to forbid snapping operations on the TaskBar and on child 
windows.
+     * We use a heuristic for detecting the TaskBar by its typical Style & 
ExStyle. */
+    const UINT style = Wnd->style;
+    const UINT tbws = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | 
WS_CLIPCHILDREN;
+    const UINT tbes = WS_EX_TOOLWINDOW;
+    BOOLEAN istb = (style & tbws) == tbws && (Wnd->ExStyle & (tbes | 
WS_EX_APPWINDOW)) == tbes;
+    BOOLEAN thickframe = (style & WS_THICKFRAME) && (style & (WS_DLGFRAME | 
WS_BORDER)) != WS_DLGFRAME;
+    return thickframe && !(style & WS_CHILD) && !istb;
+}

Reply via email to