On Sat, May 06, 2006 at 10:23:50PM +0100, James Harvey wrote: > On Sat, May 06, 2006 at 10:13:36PM +0200, Bram Moolenaar wrote: > > James Harvey wrote: > > > [snip description of multi-monitor resize window bug] > > > > Your code fails when using one monitor and the taskbar is at the left or > > the top of the screen. In this situation the window position is > > relative to the workarea. > > Oh, drat, it's even more confusing than I thought. :( Apologies. > > > Example: Suppose the taskbar is at the bottom of the screen and > > auto-hidden, and the Vim window is at 200 from the left and zero from > > the top. Then the numbers are: > > workarea left:0 right:1024 top:0 bot:768 > > Window: left:200 top:0 > > > > If I now move the taskbar to the left of the screen and disable > > auto-hide, without moving the Vim window, then the numbers are: > > workarea left:94 right:1024 top:0 bot:768 > > Window: left:106 top:0 > > > > You can see the size of the taskbar is subtracted from the window > > position. Thus to compute the position of the right edge of the Vim > > window the workarea left offset must be added. > > > > It is very strange that GetWindowPlacement() returns a value of 1172 for > > the window position. This is not consistent with using one monitor. > > Gaaah. I have a feeling that GetWindowPlacement() is using a value for > the offset from the entire visible area across all monitors (but > relative to any taskbar). Which doesn't play nice with our get_work_area > function that carefully fetches the work area of only the current > monitor.
A search of the MSDN library came up with this: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/monitor_9t0w.asp which is, basically, that they recommend using GetWindowRect/SetWindowPos alongside GetMonitorInfo, instead of Get/SetWindowPlacement. This _almost_ works; I attach a patch that uses this method. I've moved my taskbar around my monitors, and it seems to be correctly taking this into account, and if my secondary monitor is to the right of my primary (so its coordinates are large positive) it DTRT. However, if my secondary monitor is left of primary (and thus has negative coordinates) it still yanks itself onto the primary screen occasionally; on a vertical-split, and a set-columns, but not if you change a vertical-split window to remove the split. Adding debug to the code tells me that the SetWindowPos is doing the right thing, because a GetWindowRect immediately afterwards is returning the -ve coordinates passed in. So my hunch is that there's something else (either in vim, or else a bug in SetWindowPos) that's moving it to have X coordinate 0. I don't know vim's UI code all that well, so I'll keep digging, but does anyone have any other ideas of any code that might assume all window coordinates are +ve? But, this patch certainly fixes the problem for me in the usual case, and also seems to work with toolbars. -James PS Oh, and vim 7 is looking great, the tabs are just what I've been looking for! Thank you all...
Index: gui_w32.c =================================================================== --- gui_w32.c (revision 20) +++ gui_w32.c (working copy) @@ -1595,7 +1595,7 @@ int min_width, int min_height, int base_width, int base_height, int direction) { - RECT workarea_rect; + RECT workarea_rect, window_rect; int win_width, win_height; int win_xpos, win_ypos; WINDOWPLACEMENT wndpl; @@ -1605,24 +1605,23 @@ * used by the taskbar or appbars. */ get_work_area(&workarea_rect); - /* Get current posision of our window. Note that the .left and .top are - * relative to the work area. */ - wndpl.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(s_hwnd, &wndpl); - /* Resizing a maximized window looks very strange, unzoom it first. * But don't do it when still starting up, it may have been requested in * the shortcut. */ + wndpl.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(s_hwnd, &wndpl); if (wndpl.showCmd == SW_SHOWMAXIMIZED && starting == 0) { ShowWindow(s_hwnd, SW_SHOWNORMAL); - /* Need to get the settings of the normal window. */ - GetWindowPlacement(s_hwnd, &wndpl); } - win_xpos = wndpl.rcNormalPosition.left; - win_ypos = wndpl.rcNormalPosition.top; + /* Get position of our window's work area. These are absolute coords + * relative to the (0,0) point of the virtual screen, over all monitors. */ + GetWindowRect(s_hwnd, &window_rect); + win_xpos = window_rect.left; + win_ypos = window_rect.top; + /* compute the size of the outside of the window */ win_width = width + GetSystemMetrics(SM_CXFRAME) * 2; win_height = height + GetSystemMetrics(SM_CYFRAME) * 2 @@ -1632,33 +1631,26 @@ #endif ; - /* If the window is going off the screen, move it on to the screen. - * win_xpos and win_ypos are relative to the workarea. */ + /* If the window is going off the screen, move it on to the screen. */ if ((direction & RESIZE_HOR) - && workarea_rect.left + win_xpos + win_width > workarea_rect.right) - win_xpos = workarea_rect.right - win_width - workarea_rect.left; + && win_xpos + win_width > workarea_rect.right) + win_xpos = workarea_rect.right - win_width; - if ((direction & RESIZE_HOR) && win_xpos < 0) - win_xpos = 0; + if ((direction & RESIZE_HOR) && win_xpos < workarea_rect.left) + win_xpos = workarea_rect.left; if ((direction & RESIZE_VERT) - && workarea_rect.top + win_ypos + win_height > workarea_rect.bottom) - win_ypos = workarea_rect.bottom - win_height - workarea_rect.top; + && win_ypos + win_height > workarea_rect.bottom) + win_ypos = workarea_rect.bottom - win_height; - if ((direction & RESIZE_VERT) && win_ypos < 0) - win_ypos = 0; + if ((direction & RESIZE_VERT) && win_ypos < workarea_rect.top) + win_ypos = workarea_rect.top; - wndpl.rcNormalPosition.left = win_xpos; - wndpl.rcNormalPosition.right = win_xpos + win_width; - wndpl.rcNormalPosition.top = win_ypos; - wndpl.rcNormalPosition.bottom = win_ypos + win_height; - - /* set window position - we should use SetWindowPlacement rather than - * SetWindowPos as the MSDN docs say the coord systems returned by - * these two are not compatible. */ - SetWindowPlacement(s_hwnd, &wndpl); - - SetActiveWindow(s_hwnd); + /* Set the new window position; this will also activate the + * window. */ + SetWindowPos(s_hwnd, NULL, + win_xpos, win_ypos, win_width, win_height, + SWP_NOZORDER); SetFocus(s_hwnd); #ifdef FEAT_MENU