At 12:50 PM 9/15/00 -0400, you wrote: <snip> I have actually looked further at your patch and my first patch is not correct. In fact, while a 'true' child window can not be made ws_ex_topmost, a 'child of the desktop' can (useless as it is) And I failed to notice that the call setting the combolbox topmost was actually ... Wine internal code :-)) [I don't use +relay in this trace because it change the program execution in this case; there is no problem if I use relay tracing :-((((] I start from there : hwnd wndPtr queue Class Name Style WndProc Text 008c 40345b18 0257 #32769 16000000 40ca02bc <null> 02fc 40345d88 0257 TSwatForm 16ca0000 40ca07a8 L"Swat!" 0540 40345fcc 0257 TOptionsDlg 86c80000 40ca0898 L"Options" 043c 40345ec8 0257 TAboutBox 86c80000 40ca0820 L"About" 0270 40345cfc 0257 TPUtilWindow 84000000 40ca0730 L"" 0130 40345bbc 0257 TApplication 94ca0000 40ca06b8 L"Swat!" Then I open the menu for TSwatForm: trace:win:WINPOS_WindowFromPoint scope 02fc 293,101 trace:win:WIN_CreateWindowEx #0000 "#32768" 00000008 80000000 260,103 97x59 02fc 0000 00400000 0x234 trace:win:WINPOS_SendNCCalcSize 260,103-357,162 trace:win:WIN_SetWindowLong 644=0x403460d0 0 234 2 trace:win:WIN_CreateWindowEx created window 0644 trace:win:SetWindowPos >>>hwnd 0644, ins 0000 swp (0,0)-(0,0) flags 00000053 trace:win:SetWindowPos current (260,103)-(357,162), style 84000000 warn:win:SWP_DoOwnedPopups (0644) hInsertAfter = 0000 Then I select the menu item for opening TOptionsDlg, the following code is run (I have added a trace) : /* find the first non-topmost window */ for (wndTemp = WIN_LockWndPtr(WIN_GetDesktop()->child); wndTemp; WIN_UpdateWndPtr(&wndTemp,wndTemp->next)) { TRACE("TOP: wnd=%x visible=%s top=%s\n", wndTemp->hwndSelf, (wndTemp->dwStyle & WS_VISIBLE)? "Yes":"No", (wndTemp->dwExStyle & WS_EX_TOPMOST) ? "Yes":"No"); if (!(wndTemp->dwExStyle & WS_EX_TOPMOST)) break; else { /* this is a topmost window, place the window after this one */ hwndInsertAfter = wndTemp->hwndSelf; TRACE("the top window is a topmost window(hwnd=%x, hwndInsertAfter=%x)\n", wndPtr->hwndSelf, hwndInsertAfter); } } trace:win:ShowWindow hwnd=0540, cmd=1 trace:win:SetWindowPos >>>hwnd 0540, ins 0000 swp (0,0)-(0,0) flags 00000063 trace:win:SetWindowPos current (208,183)-(592,416), style 86c80000 warn:win:SWP_DoOwnedPopups (0540) hInsertAfter = 0000 trace:win:WINPOS_SendNCCalcSize 208,183-592,416 trace:win:SetWindowPos A non topmost window(hwnd=540) is placed at the top trace:win:SetWindowPos TOP: wnd=644 visible=No top=Yes trace:win:SetWindowPos the top window is a topmost window(hwnd=540, hwndInsertAfter=644) trace:win:SetWindowPos TOP: wnd=2fc visible=Yes top=No trace:event:EVENT_ProcessAllEvents called (thread 80c3330). trace:win:X11DRV_WND_SetZOrder restack 540 over 644 trace:event:EVENT_ProcessAllEvents called (thread 80c3330). trace:event:EVENT_ProcessAllEvents called (thread 8064b80). trace:win:WINPOS_SetActiveWindow (0540, 0, 1) trace:win:SetWindowPos status flags = 1863 trace:event:EVENT_ProcessAllEvents called (thread 80c3330). trace:event:EVENT_ProcessAllEvents called (thread 8064b80). trace:win:SetWindowPos <<<<hwnd 0540, swp (0,0)-(0,0) flags 00000063 trace:win:SetWindowPos >>>hwnd 0540, ins 0000 swp (0,0)-(0,0) flags 00000003 trace:win:SetWindowPos current (208,183)-(592,416), style 96c80000 warn:win:SWP_DoOwnedPopups (0540) hInsertAfter = 0000 trace:win:SetWindowPos A non topmost window(hwnd=540) is placed at the top trace:win:SetWindowPos TOP: wnd=644 visible=No top=Yes trace:win:SetWindowPos the top window is a topmost window(hwnd=540, hwndInsertAfter=644) trace:win:SetWindowPos TOP: wnd=540 visible=Yes top=No trace:event:EVENT_ProcessAllEvents called (thread 80c3330). trace:win:X11DRV_WND_SetZOrder restack 540 over 644 trace:event:EVENT_ProcessAllEvents called (thread 80c3330) As you can see (I have also added trace in XSetWindowPos), your last patch is doing restacking with unmapped windows :-) While it does not crash *my* X server, this kind of call seems to not produce any effect for me. In my other failing app, the problem was similar but the app was trying to set a window before a combolbox (hidden, of course). The following patch fixes both my failing apps, but is it correct ? Probably it should be done at the X SetWindowPos level instead, but I'm not sure, these 'Z-order + visible' problems are complex :-( @@ -2737,7 +2737,7 @@ { if (!(wndTemp->dwExStyle & WS_EX_TOPMOST)) break; - else + else if (wndTemp->dwStyle & WS_VISIBLE) { /* this is a topmost window, place the window after this one */ hwndInsertAfter = wndTemp->hwndSelf; OTOH, I have tried to reimplement your rejected patch on zero-size windows; the attached patch tries to tackle the same problem in the X-driver I have changed the presentation of your code because I found difficult to wrap my small brain around it and added a feature to reset the z-order when a window is reordered while not being mapped. This patch fixes my 2 not-yet-broken apps, fixes Lotus Notes, fixes also 2 other problems of managed mode (pseudotooltips of ForteFreeAgent, pseudo combos of the open dialog box of WordViewer 16 bits). OTOH it probably breaks lots of other applications :-) Gerard
Index: include/x11drv.h =================================================================== RCS file: /home/wine/wine/include/x11drv.h,v retrieving revision 1.59 diff -u -r1.59 x11drv.h --- include/x11drv.h 2000/08/19 21:38:56 1.59 +++ include/x11drv.h 2000/09/17 15:47:16 @@ -391,7 +391,11 @@ HBITMAP hWMIconBitmap; HBITMAP hWMIconMask; int bit_gravity; + int flags; } X11DRV_WND_DATA; + +#define MappedWindow 1 +#define ZOrderNeeded 2 extern Window X11DRV_WND_GetXWindow(struct tagWND *wndPtr); extern Window X11DRV_WND_FindXWindow(struct tagWND *wndPtr); Index: windows/x11drv/wnd.c =================================================================== RCS file: /home/wine/wine/windows/x11drv/wnd.c,v retrieving revision 1.58 diff -u -r1.58 wnd.c --- windows/x11drv/wnd.c 2000/08/19 21:38:56 1.58 +++ windows/x11drv/wnd.c 2000/09/17 15:47:36 @@ -132,6 +132,8 @@ return FALSE; } +#define ISZEROSIZE(wnd) X11DRV_WND_IsZeroSizeWnd(wnd) + /********************************************************************** * X11DRV_WND_Initialize */ @@ -143,6 +145,7 @@ wndPtr->pDriverData = (void *) pWndDriverData; pWndDriverData->window = 0; + pWndDriverData->flags = 0; } /********************************************************************** @@ -549,7 +554,7 @@ XWindowChanges winChanges; WND *wndPrev,*pDesktop = WIN_GetDesktop(); - if (X11DRV_WND_IsZeroSizeWnd(wndPtr)) + if (ISZEROSIZE( wndPtr )) { WIN_ReleaseDesktop(); return; @@ -568,7 +573,7 @@ winChanges.stack_mode = Above; while (wndPtr) { - if ( !X11DRV_WND_IsZeroSizeWnd(wndPtr) && X11DRV_WND_GetXWindow(wndPtr) ) + if ( !ISZEROSIZE(wndPtr) && X11DRV_WND_GetXWindow(wndPtr) ) TSXReconfigureWMWindow( display, X11DRV_WND_GetXWindow(wndPtr), 0, CWStackMode, &winChanges ); @@ -608,8 +613,181 @@ } } +/*********************************************************************** + * X11DRV_WND_SetSize + * + * Set size for X native windows - Internal + */ +void X11DRV_WND_SetSize(WND *winposPtr, const WINDOWPOS *winpos, int *changeMask, + XWindowChanges *winChanges) +{ + winChanges->width = (winpos->cx > 0 ) ? winpos->cx : 1; + winChanges->height = (winpos->cy > 0 ) ? winpos->cy : 1; + *changeMask |= CWWidth | CWHeight; + + /* Tweak dialog window size hints */ + + if ((winposPtr->flags & WIN_MANAGED) && + HAS_DLGFRAME(winposPtr->dwStyle,winposPtr->dwExStyle)) + { + XSizeHints *size_hints = TSXAllocSizeHints(); + + if (size_hints) + { + long supplied_return; + + TSXGetWMSizeHints( display, X11DRV_WND_GetXWindow(winposPtr), size_hints, + &supplied_return, XA_WM_NORMAL_HINTS); + size_hints->min_width = size_hints->max_width = winpos->cx; + size_hints->min_height = size_hints->max_height = winpos->cy; + TSXSetWMSizeHints( display, X11DRV_WND_GetXWindow(winposPtr), size_hints, + XA_WM_NORMAL_HINTS ); + TSXFree(size_hints); + } + } +} + +/*********************************************************************** + * X11DRV_WND_SetZOrder + * + * Set z order for X native windows - Internal + */ +void X11DRV_WND_SetZOrder(WND *winposPtr, const WINDOWPOS *winpos, int *changeMask, + XWindowChanges *winChanges) +{ + winChanges->stack_mode = Below; + *changeMask |= CWStackMode; + + if (winpos->hwndInsertAfter == HWND_TOP) + { + winChanges->stack_mode = Above; + TRACE("restack %x above\n", winposPtr->hwndSelf); + } + else if (winpos->hwndInsertAfter != HWND_BOTTOM) + { + WND* insertPtr = WIN_FindWndPtr( winpos->hwndInsertAfter ); + Window stack[2]; + + /* If the window where we should do the insert is zero-sized (not mapped) + don't use this window since it will possibly crash the X server, + use the "non zero-sized" window above */ + if (!(insertPtr->dwStyle & WS_VISIBLE) || (ISZEROSIZE(insertPtr))) + { + /* find the window on top of the zero sized window */ + WND *pDesktop = WIN_GetDesktop(); + WND *wndPrev = pDesktop->child; + WND *wndZeroSized = insertPtr; + + ((X11DRV_WND_DATA*)insertPtr->pDriverData)->flags |= ZOrderNeeded; + while (1) + { + if (wndPrev == wndZeroSized) + break; /* zero-sized window is on top */ + + while (wndPrev && (wndPrev->next != wndZeroSized)) + wndPrev = wndPrev->next; + + /* check if the window found is not zero-sized */ + if (ISZEROSIZE(wndPrev)) + { + wndZeroSized = wndPrev; /* restart the search */ + wndPrev = pDesktop->child; + } + else + break; /* "above" window is found */ + } + WIN_ReleaseDesktop(); + + if (wndPrev == wndZeroSized) + { + /* the zero-sized window is on top */ + /* so set the window on top */ + TRACE("restack %x above\n", winposPtr->hwndSelf); + winChanges->stack_mode = Above; + } + else + { + stack[0] = X11DRV_WND_FindDesktopXWindow( wndPrev ); + stack[1] = X11DRV_WND_FindDesktopXWindow( winposPtr ); + TRACE("restack %x over %x\n", winposPtr->hwndSelf, wndPrev->hwndSelf); + TSXRestackWindows(display, stack, 2); + *changeMask &= ~CWStackMode; + } + } + else /* Normal behavior, windows are not zero-sized */ + { + stack[0] = X11DRV_WND_FindDesktopXWindow( insertPtr ); + stack[1] = X11DRV_WND_FindDesktopXWindow( winposPtr ); + + TRACE("restack %x over %x\n", winposPtr->hwndSelf, insertPtr->hwndSelf); + TSXRestackWindows(display, stack, 2); + *changeMask &= ~CWStackMode; + } + WIN_ReleaseWndPtr(insertPtr); + } +} + + +/*********************************************************************** + * X11DRV_WND_MapWindow + * + * map a window - Internal + */ +void X11DRV_WND_MapWindow(WND *wndPtr) +{ + Window w; + X11DRV_WND_DATA *data; + if (!(w = X11DRV_WND_GetXWindow(wndPtr))) return; + TSXMapWindow( display, w ); + data = ((X11DRV_WND_DATA*)wndPtr->pDriverData); + data->flags |= MappedWindow; + TRACE("map %x\n", wndPtr->hwndSelf); + + if (data->flags & ZOrderNeeded) + { /* need to reset the zorder after showing the window */ + WND *p = wndPtr->parent? wndPtr->parent->child:NULL; + + data->flags &= ~ZOrderNeeded; + /* search previous window */ + while (p && (p != wndPtr) && (p->next != wndPtr)) + p = p->next; + if (p) + { + int changeMask = 0; + XWindowChanges winChanges; + WINDOWPOS winpos; + + if (p == wndPtr) + winpos.hwndInsertAfter = HWND_TOP; + else + winpos.hwndInsertAfter = p->next->hwndSelf; + winpos.flags = SWP_NOSIZE | SWP_NOMOVE; + + X11DRV_WND_SetZOrder(wndPtr, &winpos, &changeMask, &winChanges); + if (changeMask && w) + TSXReconfigureWMWindow( display, w, 0, changeMask, &winChanges ); + + TRACE("reset zorder %x\n", wndPtr->hwndSelf); + } + } +} + +/*********************************************************************** + * X11DRV_WND_UnMapWindow + * + * Unmap a window - Internal + */ +void X11DRV_WND_UnmapWindow(WND *wndPtr) +{ + Window w; + if (!(w = X11DRV_WND_GetXWindow(wndPtr))) return; + TSXUnmapWindow( display, w ); + ((X11DRV_WND_DATA*)wndPtr->pDriverData)->flags &= ~MappedWindow; + TRACE("unmap %x\n", wndPtr->hwndSelf); +} + /*********************************************************************** - * WINPOS_SetXWindowPos + * X11DRV_WND_SetWindowPos * * SetWindowPos() for an X window. Used by the real SetWindowPos(). */ @@ -623,147 +801,54 @@ if ( !winposPtr ) return; /* find out if after this function we will end out with a zero-size window */ - if (X11DRV_WND_IsZeroSizeWnd(winposPtr)) + if (ISZEROSIZE(winposPtr)) + isZeroSizeWnd = TRUE; + else if ( !(winpos->flags & SWP_NOSIZE) && + (winposPtr->dwStyle & WS_VISIBLE) && + !(winpos->flags & SWP_HIDEWINDOW) && + !(winpos->flags & SWP_SHOWWINDOW) ) { - /* if current size is 0, and no resizing */ - if (winpos->flags & SWP_NOSIZE) - isZeroSizeWnd = TRUE; - else if ((winpos->cx > 0) && (winpos->cy > 0)) - { - /* if this function is setting a new size > 0 for the window, we - should map the window if WS_VISIBLE is set */ - if ((winposPtr->dwStyle & WS_VISIBLE) && !(winpos->flags & SWP_HIDEWINDOW)) - forceMapWindow = TRUE; - } + /* check if the window is mapped + if not it means, it was a zero-sized window and + it needs to be forced to be mapped */ + if (!(((X11DRV_WND_DATA *)winposPtr->pDriverData)->flags & MappedWindow)) + forceMapWindow = TRUE; } /* if resizing to 0 */ if ( !(winpos->flags & SWP_NOSIZE) && ((winpos->cx <= 0) || (winpos->cy <= 0)) ) isZeroSizeWnd = TRUE; if(!wndPtr->hwndSelf) wndPtr = NULL; /* FIXME: WND destroyed, shouldn't happen!!! */ - + + if (isZeroSizeWnd) + ((X11DRV_WND_DATA *)winposPtr->pDriverData)->flags |= ZOrderNeeded; + if (!(winpos->flags & SWP_SHOWWINDOW) && (winpos->flags & SWP_HIDEWINDOW)) - { - if(X11DRV_WND_GetXWindow(wndPtr)) - TSXUnmapWindow( display, X11DRV_WND_GetXWindow(wndPtr) ); - } + X11DRV_WND_UnmapWindow( wndPtr ); if(bChangePos) { - if ( !(winpos->flags & SWP_NOSIZE)) - { - winChanges.width = (winpos->cx > 0 ) ? winpos->cx : 1; - winChanges.height = (winpos->cy > 0 ) ? winpos->cy : 1; - changeMask |= CWWidth | CWHeight; - - /* Tweak dialog window size hints */ - - if ((winposPtr->flags & WIN_MANAGED) && - HAS_DLGFRAME(winposPtr->dwStyle,winposPtr->dwExStyle)) - { - XSizeHints *size_hints = TSXAllocSizeHints(); - - if (size_hints) - { - long supplied_return; - - TSXGetWMSizeHints( display, X11DRV_WND_GetXWindow(winposPtr), size_hints, - &supplied_return, XA_WM_NORMAL_HINTS); - size_hints->min_width = size_hints->max_width = winpos->cx; - size_hints->min_height = size_hints->max_height = winpos->cy; - TSXSetWMSizeHints( display, X11DRV_WND_GetXWindow(winposPtr), size_hints, - XA_WM_NORMAL_HINTS ); - TSXFree(size_hints); - } - } - } - if (!(winpos->flags & SWP_NOMOVE)) - { - winChanges.x = winpos->x; - winChanges.y = winpos->y; - changeMask |= CWX | CWY; - } - if (!(winpos->flags & SWP_NOZORDER) && !isZeroSizeWnd) - { - winChanges.stack_mode = Below; - changeMask |= CWStackMode; - - if (winpos->hwndInsertAfter == HWND_TOP) winChanges.stack_mode = Above; - else if (winpos->hwndInsertAfter != HWND_BOTTOM) - { - WND* insertPtr = WIN_FindWndPtr( winpos->hwndInsertAfter ); - Window stack[2]; - - /* If the window where we should do the insert is zero-sized (not mapped) - don't used this window since it will possibly crash the X server, - use the "non zero-sized" window above */ - if (X11DRV_WND_IsZeroSizeWnd(insertPtr)) - { - /* find the window on top of the zero sized window */ - WND *pDesktop = WIN_GetDesktop(); - WND *wndPrev = pDesktop->child; - WND *wndZeroSized = insertPtr; - - while (1) - { - if (wndPrev == wndZeroSized) - break; /* zero-sized window is on top */ - - while (wndPrev && (wndPrev->next != wndZeroSized)) - wndPrev = wndPrev->next; - - /* check if the window found is not zero-sized */ - if (X11DRV_WND_IsZeroSizeWnd(wndPrev)) - { - wndZeroSized = wndPrev; /* restart the search */ - wndPrev = pDesktop->child; - } - else - break; /* "above" window is found */ - } - WIN_ReleaseDesktop(); - - if (wndPrev == wndZeroSized) - { - /* the zero-sized window is on top */ - /* so set the window on top */ - winChanges.stack_mode = Above; - } - else - { - stack[0] = X11DRV_WND_FindDesktopXWindow( wndPrev ); - stack[1] = X11DRV_WND_FindDesktopXWindow( winposPtr ); - - TSXRestackWindows(display, stack, 2); - changeMask &= ~CWStackMode; - } - } - else /* Normal behavior, windows are not zero-sized */ - { - stack[0] = X11DRV_WND_FindDesktopXWindow( insertPtr ); - stack[1] = X11DRV_WND_FindDesktopXWindow( winposPtr ); - - TSXRestackWindows(display, stack, 2); - changeMask &= ~CWStackMode; - } - - WIN_ReleaseWndPtr(insertPtr); - } - } - if (changeMask && X11DRV_WND_GetXWindow(winposPtr)) - { - TSXReconfigureWMWindow( display, X11DRV_WND_GetXWindow(winposPtr), 0, changeMask, &winChanges ); - if( winposPtr->class->style & (CS_VREDRAW | CS_HREDRAW) ) - X11DRV_WND_SetHostAttr( winposPtr, HAK_BITGRAVITY, BGForget ); - } + if ( !(winpos->flags & SWP_NOSIZE)) + X11DRV_WND_SetSize(winposPtr, winpos, &changeMask, &winChanges); + if (!(winpos->flags & SWP_NOMOVE)) + { + winChanges.x = winpos->x; + winChanges.y = winpos->y; + changeMask |= CWX | CWY; + } + if (!(winpos->flags & SWP_NOZORDER) && !isZeroSizeWnd) + X11DRV_WND_SetZOrder(winposPtr, winpos, &changeMask, &winChanges); + if (changeMask && X11DRV_WND_GetXWindow(winposPtr)) + { + TSXReconfigureWMWindow( display, X11DRV_WND_GetXWindow(winposPtr), 0, +changeMask, &winChanges ); + if( winposPtr->class->style & (CS_VREDRAW | CS_HREDRAW) ) + X11DRV_WND_SetHostAttr( winposPtr, HAK_BITGRAVITY, BGForget ); + } } - /* don't map the window if it's a zero size window */ + /* map the window unless it's a zero size window */ if ( ((winpos->flags & SWP_SHOWWINDOW) && !isZeroSizeWnd) || forceMapWindow ) - { - if(X11DRV_WND_GetXWindow(wndPtr)) - TSXMapWindow( display, X11DRV_WND_GetXWindow(wndPtr) ); - } + X11DRV_WND_MapWindow(wndPtr); WIN_ReleaseWndPtr(winposPtr); } @@ -812,7 +897,7 @@ Window win; WND *w = wndPtr; - if (X11DRV_WND_IsZeroSizeWnd(wndPtr)) + if (ISZEROSIZE(wndPtr)) return; /* Only mess with the X focus if there's */ @@ -1035,7 +1120,7 @@ case HAK_ICONICSTATE: /* called when a window is minimized/restored */ /* don't do anything if it'a zero size window */ - if (X11DRV_WND_IsZeroSizeWnd(wnd)) + if (ISZEROSIZE(wnd)) return TRUE; if( (wnd->flags & WIN_MANAGED) ) @@ -1076,7 +1161,7 @@ else { XEvent xe; - TSXMapWindow(display, w ); + X11DRV_WND_MapWindow( wnd ); while( !TSXCheckTypedWindowEvent( display, w, MapNotify, &xe) ); } }