Hi,

> > > I have some EDA tools running on a Linux machine and display on my Windows
> > > PC using xorg-server-21.1.3 XWin multiwindow mode
> > > Sometimes the application window flickers forever for an unknown reason.
> > > The problem became more severe after my PC upgrade to Windows10.
> > >
> > > Knowing the root cause, I am now able to demonstrate the issue with a 
> > > small
> > > test case as well as a patch that works for me. Both are attached below.
> >
> > Thanks very much for taking the time to look into this, and writing a patch.
> > But I'd like to add a bit more commentary, which perhaps you can supply,
> > about the analysis you did to determine:
> >
> > 1. how the code is misbehaving
> > 2. how this change remedies that
> > 3. how this change doesn't effect anything else
> >
> > What your fix does is examine the queue of pending WM messages to
> > determine if there are any more pending for the window receiving a
> > WM_WM_CHANGE_STATE message, and if there are, ignore it.
> >
> > It seems to me that is error prone, in a couple of ways:
> >
> > 1. it should check the message type as well, as e.g. a pending message
> > of a different type could cause a WM_WM_CHANGE_STATE message to be ignored.
> >
> > 2. even then, assuming that successive WM_WM_CHANGE_STATE messages
> > cancel out each other isn't always true e.g. consider a sequence of
> > _NET_WM_STATE_ADD _NET_WM_STATE_MAXIMIZED_VERT followed by
> > _NET_WM_STATE_ADD _NET_WM_STATE_MAXIMIZED_HORZ.
> >

> Here I have some details on the problem:
> In following, WWCS means WM_WM_CHANGE_STATE
> There exists a message self-generating loop
> I.e., a WWCS message may generate another WWCS.
>
> winSendMessageToWM(WWCS) --> MultiWindowWMProc(WWCS)
> --> UpdateState() --> ShowWindow() --> MultiWindowProc(WM_SIZE)
> --> winAdjustXWindowState() --> winSendMessageToWM(WWCS)
>
> In normal case, there is a conditional code inside UpdateState() that
> could break the loop.
>
>             if (current_state == state)
>                 return;
>
> Normally after maximizing the window, the WWCS just
> to be self-generated once. On processing the 2nd WWCS,
> the current window state matches WWCS target state
> and ShowWindow() won't be called and the loop could end.
>
> Initial
>     Window state normal
>     queue : | WWCS(max1) |
> --> process WWCS(max1) :
>     Winow state (norm) != target state (max2)
>     Set Window state to max and generate WWCS(max2)
>     queue : | WWCS(max2) |
> --> Process WWCS(max2) :
>     Winow state (max) == target state (max)
>     break
>
> where WWCS(max1) means WWCS msg with target state max
> and 1 means first generated/inserted WWCS in queue.
>
> However, there is case triggering this loop running forever.
> Assuming intitially there are two WWCS message exists
> where one is to maximize window and one is to normal window
> The condition of state compare in UpdateState() would never match.
> And WWCS(max) and WWCS(norm) is going to self-generate in turn.
>
> Initial
>     Window state normal
>     queue : | WWCS(max1) | WWCS(norm1) |
> --> process WWCS(max1)
>     Winow state (norm) != target state (max)
>     Set window state to max and generate WWCS(max2)
>     queue : | WWCS(norm1) | WWCS(max2) |
> --> Process WWCS(norm1)
>     Winow state (max) != target state (norm)
>     Set window state to norm and generate WWCS(norm2)
>     queue : | WWCS(max2) | WWCS(norm2) |
> --> Process WWCS(max2)
>     Winow state (norm) != target state (max)
>     Set window state to max and generate WWCS(max3)
>     queue : | WWCS(norm2) | WWCS(max3) |
> --> Process WWCS(norm2)
>     Winow state (max) != target state (norm)
>     Set window state to norm and generate WWCS(norm3)
>     queue : | WWCS(max3) | WWCS(norm3) |
> :
> (loop forever)
>

I just did some more study on the code of multiwindow window manager and
X11 Window Properties as well as the questions you brought up.

The first question :

> it should check the message type as well, as e.g.
> a different type mesage could cause WM_WM_CHANGE_STYLE to be ignored.

Not sure what the "type" of a message means.
Do you mean window manager message WM_xxxxx that is processed by
winMultiWindowWMProc() where it is alreadychecked in HaveMessage(),
or do you mean X message XCB_xxxxxx that is processed by
winMultiWindowXMsgProc()?
Maybe you can provide an example of another message type?

The second question:

> Successive WM_WM_CHANGE_STATE messages cancel out each other
> isn't always true ex. _NET_WM_STATE_ADD _NET_WM_STATE_MAXIMIZED_VERT
> followed by _NET_WM_STATE_ADD _NET_WM_STATE_MAXIMIZED_HORZ.

Inspecting the code that deal with XCB_CLIENT_MESSAGE, it seems already
true before the patch: The window goes maximized only if X client do one
_WM_STATE_ADD action with _NET_WM_STATE_MAXIMIZED_VERT and
_NET_WM_STATE_MAXIMIZED_HORZ simultaneously specified. Otherwise,
the WM_WM_CHANGE would not be sent to the queue.
Further, in UpdateWindowState(), the state and related X properties are
either all-overwritten or all-skipped.
There seems to be no state property half-update case if we skip
UpdateWindowState().

In anyway, I just came out somewhat different patch attached.
Where it just skips the Win32 ShowWindow() call instead of
skipping processing WM_WM_CHANGE_STATE. It might be better because
the X properties are still always updated in each in WM_WM_CHANGE_STATE.
Maybe you could provide further comment.

SL
diff --git a/hw/xwin/winmultiwindowwm.c b/hw/xwin/winmultiwindowwm.c
index 7eee2fb..b2e5dc9 100644
--- a/hw/xwin/winmultiwindowwm.c
+++ b/hw/xwin/winmultiwindowwm.c
@@ -153,6 +153,9 @@ static void
 
 static WMMsgNodePtr PopMessage(WMMsgQueuePtr pQueue, WMInfoPtr pWMInfo);
 
+static Bool
+ HaveMessage(WMMsgQueuePtr pQueue, UINT msg, xcb_window_t iWindow);
+
 static Bool
  InitQueue(WMMsgQueuePtr pQueue);
 
@@ -319,7 +322,6 @@ PopMessage(WMMsgQueuePtr pQueue, WMInfoPtr pWMInfo)
     return pNode;
 }
 
-#if 0
 /*
  * HaveMessage -
  */
@@ -331,12 +333,11 @@ HaveMessage(WMMsgQueuePtr pQueue, UINT msg, xcb_window_t iWindow)
 
     for (pNode = pQueue->pHead; pNode != NULL; pNode = pNode->pNext) {
         if (pNode->msg.msg == msg && pNode->msg.iWindow == iWindow)
-            return True;
+            return TRUE;
     }
 
-    return False;
+    return FALSE;
 }
-#endif
 
 /*
  * InitQueue - Initialize the Window Manager message queue
@@ -748,12 +749,12 @@ UpdateStyle(WMInfoPtr pWMInfo, xcb_window_t iWindow, unsigned long *maxmin)
  */
 
 static void
-UpdateState(WMInfoPtr pWMInfo, xcb_window_t iWindow, int state)
+UpdateState(WMInfoPtr pWMInfo, xcb_window_t iWindow, int state, BOOL bShow)
 {
     HWND hWnd;
     int current_state = -1;
 
-    winDebug("UpdateState: iWindow 0x%08x %d\n", (int)iWindow, state);
+    winDebug("UpdateState: iWindow 0x%08x %d %d\n", (int)iWindow, state, (int) bShow);
 
     hWnd = getHwnd(pWMInfo, iWindow);
     if (hWnd)
@@ -764,29 +765,36 @@ UpdateState(WMInfoPtr pWMInfo, xcb_window_t iWindow, int state)
             if (current_state == state)
                 return;
 
+            // There would be small interval that variable 'state' does not
+            // match to Windows window state if bShow==0
+            // But meanwhile there should be a another WM_WM_CHANGE pending
+            // and soon the Windows state will be updated
             SetProp(hWnd, WIN_STATE_PROP, (HANDLE)(intptr_t)state);
 
-            switch (state)
-                {
-                case XCB_ICCCM_WM_STATE_ICONIC:
-                    ShowWindow(hWnd, SW_SHOWMINNOACTIVE);
-                    break;
-
-#define XCB_ICCCM_WM_STATE_ZOOM 2
-                case XCB_ICCCM_WM_STATE_ZOOM:
-                    // There doesn't seem to be a SW_SHOWMAXNOACTIVE.  Hopefully
-                    // always activating a maximized window isn't so bad...
-                    ShowWindow(hWnd, SW_SHOWMAXIMIZED);
-                    break;
-
-                case XCB_ICCCM_WM_STATE_NORMAL:
-                    ShowWindow(hWnd, SW_SHOWNOACTIVATE);
-                    break;
-
-                case XCB_ICCCM_WM_STATE_WITHDRAWN:
-                    ShowWindow(hWnd, SW_HIDE);
-                    break;
-                }
+            if(bShow) {
+
+                switch (state)
+                    {
+                    case XCB_ICCCM_WM_STATE_ICONIC:
+                        ShowWindow(hWnd, SW_SHOWMINNOACTIVE);
+                        break;
+
+    #define XCB_ICCCM_WM_STATE_ZOOM 2
+                    case XCB_ICCCM_WM_STATE_ZOOM:
+                        // There doesn't seem to be a SW_SHOWMAXNOACTIVE.  Hopefully
+                        // always activating a maximized window isn't so bad...
+                        ShowWindow(hWnd, SW_SHOWMAXIMIZED);
+                        break;
+
+                    case XCB_ICCCM_WM_STATE_NORMAL:
+                        ShowWindow(hWnd, SW_SHOWNOACTIVATE);
+                        break;
+
+                    case XCB_ICCCM_WM_STATE_WITHDRAWN:
+                        ShowWindow(hWnd, SW_HIDE);
+                        break;
+                    }
+            }
         }
 
     // Update WM_STATE property
@@ -1012,7 +1020,7 @@ winMultiWindowWMProc(void *pArg)
             UpdateWindow(pNode->msg.hwndWindow);
 
             /* Establish initial state */
-            UpdateState(pWMInfo, pNode->msg.iWindow, XCB_ICCCM_WM_STATE_NORMAL);
+            UpdateState(pWMInfo, pNode->msg.iWindow, XCB_ICCCM_WM_STATE_NORMAL, TRUE);
 
             /*
               It only makes sense to apply minimize/maximize override as the
@@ -1173,7 +1181,11 @@ winMultiWindowWMProc(void *pArg)
             break;
 
         case WM_WM_CHANGE_STATE:
-            UpdateState(pWMInfo, pNode->msg.iWindow, pNode->msg.dwID);
+            if(HaveMessage(&pWMInfo->wmMsgQueue, pNode->msg.msg, pNode->msg.iWindow))
+                UpdateState(pWMInfo, pNode->msg.iWindow, pNode->msg.dwID, FALSE);
+            else
+                UpdateState(pWMInfo, pNode->msg.iWindow, pNode->msg.dwID, TRUE);
+
             break;
 
         default:
-- 
Problem reports:      https://cygwin.com/problems.html
FAQ:                  https://cygwin.com/faq/
Documentation:        https://cygwin.com/docs.html
Unsubscribe info:     https://cygwin.com/ml/#unsubscribe-simple

Reply via email to