Author Topic: Better PlaceWindow implementation on Linux  (Read 3366 times)

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Better PlaceWindow implementation on Linux
« on: August 04, 2015, 09:40:34 pm »
I'm using a dual monitor setup on linux and the behaviour without this patch is pretty bad -> all new windows/dialogs are placed close to the middle of the whole two-monitor setup. With this patch they are placed in the middle of the screen where the cb main window/parent windows is. The same as on windows.

I'm running with the patch for the day and it seems that it works fine.
I'm posting the patch, so windows people can test it and they can verify that I've not broken something, because the implementation is now shared between windows and unix, and it uses wxDisplay APIs instead of the native APIs.

Code: diff
Index: src/sdk/globals.cpp
===================================================================
--- src/sdk/globals.cpp    (revision 10379)
+++ src/sdk/globals.cpp    (working copy)
@@ -32,6 +32,7 @@
 #include "tinyxml/tinyxml.h"
 
 #include <wx/dirdlg.h>
+#include <wx/display.h>
 #include <wx/filefn.h>
 #include <wx/fontmap.h>
 #include <wx/msgdlg.h>
@@ -1065,18 +1066,8 @@ SettingsIconsStyle GetSettingsIconsStyle()
     return SettingsIconsStyle(Manager::Get()->GetConfigManager(_T("app"))->ReadInt(_T("/environment/settings_size"), 0));
 }
 
-#ifdef __WXMSW__
-
-typedef APIENTRY HMONITOR (*MonitorFromWindow_t)(HWND, DWORD);
-typedef APIENTRY BOOL (*GetMonitorInfo_t)(HMONITOR, LPMONITORINFO);
-
 void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode, bool enforce)
 {
-    HMONITOR hMonitor;
-    MONITORINFO mi;
-    RECT        r;
-
-    int the_mode;
 
     if (!w)
         cbThrow(_T("Passed NULL pointer to PlaceWindow."));
@@ -1089,51 +1080,49 @@ void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode, bool enforce)
     if (!referenceWindow)    // no application window available, so this is as good as we can get
         referenceWindow = w;
 
-    wxRect windowRect = w->GetRect();
-
+    int the_mode;
     if (mode == pdlBest)
         the_mode = cfg->ReadInt(_T("/dialog_placement/dialog_position"), (int) pdlCentre);
     else
         the_mode = (int) mode;
 
+    wxRect monitorRect;
 
-    static MonitorFromWindow_t MonitorFromWindowProc = (MonitorFromWindow_t) GetProcAddress(GetModuleHandle(_T("user32.dll")), "MonitorFromWindow");
-    static GetMonitorInfo_t    GetMonitorInfoProc    = (GetMonitorInfo_t)    GetProcAddress(GetModuleHandle(_T("user32.dll")), "GetMonitorInfoA");
-    int monitorWidth;
-    int monitorHeight;
-
-    if (GetMonitorInfoProc)
+    if (wxDisplay::GetCount() > 0)
     {
-        hMonitor = MonitorFromWindowProc((HWND) referenceWindow->GetHandle(), MONITOR_DEFAULTTONEAREST);
-
-        mi.cbSize = sizeof(mi);
-        GetMonitorInfoProc(hMonitor, &mi);
-        r = mi.rcWork;
-
-        monitorWidth  = r.right - r.left;
-        monitorHeight = r.bottom - r. top;
+        int displayIdx = wxDisplay::GetFromWindow(referenceWindow);
+        if (displayIdx == wxNOT_FOUND)
+            displayIdx = 0;
+        wxDisplay display(displayIdx);
+        monitorRect = display.GetClientArea();
+        // This is needed because on Linux the client area returned for the first monitor in a twin
+        // monitor setup with nVidia card is spanning the two monitors.
+        // The intersection function will return just the client for the specified monitor.
+        monitorRect = display.GetGeometry().Intersect(monitorRect);
     }
-    else // Win95, NT4: support only single monitor
+    else
     {
-        wxDisplaySize(&monitorWidth, &monitorHeight);
-        r.left = r.top = 0;
+        int width, height;
+        wxDisplaySize(&width, &height);
+        monitorRect = wxRect(0, 0, width, height);
     }
 
+    wxRect windowRect = w->GetRect();
 
     switch(the_mode)
     {
         case pdlCentre:
         {
-            windowRect.x = r.left + (monitorWidth  - windowRect.width)/2;
-            windowRect.y = r.top  + (monitorHeight - windowRect.height)/2;
+            windowRect.x = monitorRect.x + (monitorRect.width  - windowRect.width)/2;
+            windowRect.y = monitorRect.y  + (monitorRect.height - windowRect.height)/2;
         }
         break;
 
 
         case pdlHead:
         {
-            windowRect.x = r.left + (monitorWidth  - windowRect.width)/2;
-            windowRect.y = r.top  + (monitorHeight - windowRect.height)/3;
+            windowRect.x = monitorRect.x + (monitorRect.width  - windowRect.width)/2;
+            windowRect.y = monitorRect.y  + (monitorRect.height - windowRect.height)/3;
         }
         break;
 
@@ -1145,26 +1134,26 @@ void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode, bool enforce)
             int y1 = windowRect.y;
             int y2 = windowRect.y + windowRect.height;
 
-            if (windowRect.width > monitorWidth) // cannot place without clipping, so centre it
+            if (windowRect.width > monitorRect.width) // cannot place without clipping, so centre it
             {
-                x1 = r.left + (monitorWidth  - windowRect.width)/2;
+                x1 = monitorRect.x + (monitorRect.width  - windowRect.width)/2;
                 x2 = x1 + windowRect.width;
             }
             else
             {
-                x2 = std::min((int) r.right, windowRect.GetRight());
-                x1 = std::max(x2 - windowRect.width, (int) r.left);
+                x2 = std::min(monitorRect.GetRight(), windowRect.GetRight());
+                x1 = std::max(x2 - windowRect.width, monitorRect.x);
                 x2 = x1 + windowRect.width;
             }
-            if (windowRect.height > monitorHeight) // cannot place without clipping, so centre it
+            if (windowRect.height > monitorRect.height) // cannot place without clipping, so centre it
             {
-                y1 = r.top + (monitorHeight  - windowRect.height)/2;
+                y1 = monitorRect.y + (monitorRect.height  - windowRect.height)/2;
                 y2 = y1 + windowRect.height;
             }
             else
             {
-                y2 = std::min((int) r.bottom, windowRect.GetBottom());
-                y1 = std::max(y2 - windowRect.height, (int) r.top);
+                y2 = std::min(monitorRect.GetBottom(), windowRect.GetBottom());
+                y1 = std::max(y2 - windowRect.height, monitorRect.y);
                 y2 = y1 + windowRect.height;
             }
             windowRect = wxRect(x1, y1, x2-x1, y2-y1);
@@ -1179,10 +1168,10 @@ void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode, bool enforce)
             int y1 = windowRect.y;
             int y2 = windowRect.y + windowRect.height;
 
-            x1 = std::max(x1, (int) r.left);
-            x2 = std::min(x2, (int) r.right);
-            y1 = std::max(y1, (int) r.top);
-            y2 = std::min(y2, (int) r.bottom);
+            x1 = std::max(x1, monitorRect.x);
+            x2 = std::min(x2, monitorRect.GetRight());
+            y1 = std::max(y1, monitorRect.y);
+            y2 = std::min(y2, monitorRect.GetBottom());
 
             windowRect = wxRect(x1, y1, x2-x1, y2-y1);
         }
@@ -1192,61 +1181,6 @@ void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode, bool enforce)
     w->SetSize(windowRect.x,  windowRect.y, windowRect.width, windowRect.height, wxSIZE_ALLOW_MINUS_ONE);
 }
 
-
-#else // ----- non-Windows ----------------------------------------------
-
-
-void PlaceWindow(wxTopLevelWindow *w, cbPlaceDialogMode mode, bool enforce)
-// TODO (thomas#1#): The non-Windows implementation is *pathetic*.
-// However, I don't know how to do it well under GTK / X / Xinerama / whatever.
-// Anyone?
-{
-    int the_mode;
-
-    wxWindow* referenceWindow = Manager::Get()->GetAppWindow();
-    if (!referenceWindow) // let's not crash on shutdown
-        return;
-
-    if (!w)
-        cbThrow(_T("Passed NULL pointer to PlaceWindow."));
-
-
-    ConfigManager *cfg = Manager::Get()->GetConfigManager(_T("app"));
-    if (!enforce && cfg->ReadBool(_T("/dialog_placement/do_place")) == false)
-        return;
-
-    if (mode == pdlBest)
-        the_mode = cfg->ReadInt(_T("/dialog_placement/dialog_position"), (int) pdlCentre);
-    else
-        the_mode = (int) mode;
-
-
-    if (the_mode == pdlCentre || the_mode == pdlHead)
-    {
-        w->CentreOnScreen();
-        return;
-    }
-    else
-    {
-        wxRect windowRect = w->GetRect();
-        wxRect parentRect = referenceWindow->GetRect();   // poo again!
-
-        int x1 = windowRect.x;
-        int x2 = windowRect.x + windowRect.width;
-        int y1 = windowRect.y;
-        int y2 = windowRect.y + windowRect.height;
-
-        x1 = std::max(x1, parentRect.x);
-        x2 = std::min(x2, parentRect.GetRight());
-        y1 = std::max(y1, parentRect.y);
-        y2 = std::min(y2, parentRect.GetBottom());
-
-        w->SetSize(x1, y1, x2-x1, y2-y1, wxSIZE_ALLOW_MINUS_ONE);
-    }
-}
-
-#endif //platform-specific placement code
-
 DirAccessCheck cbDirAccessCheck(const wxString& dir)
 {
     wxString actualDir = dir;

If there are no complaints I'll commit in some time, week or two.
« Last Edit: August 04, 2015, 10:31:07 pm by oBFusCATed »
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]

Offline oBFusCATed

  • Developer
  • Lives here!
  • *****
  • Posts: 13413
    • Travis build status
Re: Better PlaceWindow implementation on Linux
« Reply #1 on: August 15, 2015, 05:02:04 pm »
In svn.
(most of the time I ignore long posts)
[strangers don't send me private messages, I'll ignore them; post a topic in the forum, but first read the rules!]