1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "common.h"
  27 
  28 #include "GlassScreen.h"
  29 #include "GlassApplication.h"
  30 
  31 #include "com_sun_glass_ui_Screen.h"
  32 
  33 extern BOOL CALLBACK CountMonitorsCallback(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP);
  34 extern BOOL CALLBACK CollectMonitorsCallback(HMONITOR hMonitor, HDC hDC, LPRECT rRect, LPARAM lP);
  35 
  36 enum AnchorType {
  37     ANCHOR_TO_LEFT,
  38     ANCHOR_TO_TOP,
  39     ANCHOR_TO_RIGHT,
  40     ANCHOR_TO_BOTTOM
  41 };
  42 
  43 struct MonitorInfoStruct {
  44     HMONITOR hMonitor;
  45     RECT rcMonitor;
  46     RECT rcWork;
  47     RECT fxMonitor;
  48     RECT fxWork;
  49     jboolean primaryScreen;
  50     jint colorDepth;
  51     jfloat uiScale;
  52     jfloat renderScale;
  53     jint dpiX;
  54     jint dpiY;
  55     jint anchoredInPass;
  56     jobject gScreen;
  57 };
  58 extern jobject CreateJavaMonitorFromMIS(JNIEnv *env, MonitorInfoStruct *pMIS);
  59 
  60 struct {
  61     int numInfos;
  62     int maxInfos;
  63     MonitorInfoStruct *pMonitorInfos;
  64 } g_MonitorInfos;
  65 
  66 typedef enum _Monitor_DPI_Type {
  67     MDT_Effective_DPI  = 0,
  68     MDT_Angular_DPI    = 1,
  69     MDT_Raw_DPI        = 2,
  70     MDT_Default        = MDT_Effective_DPI
  71 } Monitor_DPI_Type;
  72 
  73 typedef enum _Process_DPI_Awareness {
  74     Process_DPI_Unaware            = 0,
  75     Process_System_DPI_Aware       = 1,
  76     Process_Per_Monitor_DPI_Aware  = 2
  77 } Process_DPI_Awareness;
  78 
  79 #undef DEBUG_DPI
  80 
  81 BOOL triedToFindDPIFuncs = FALSE;
  82 typedef HRESULT WINAPI FnGetDPIForMonitor(HMONITOR hmonitor, Monitor_DPI_Type dpiType, UINT *dpiX, UINT *dpiY);
  83 typedef HRESULT WINAPI FnGetProcessDPIAwareness(HANDLE hprocess, Process_DPI_Awareness *value);
  84 typedef HRESULT WINAPI FnSetProcessDPIAwareness(Process_DPI_Awareness value);
  85 FnGetDPIForMonitor * pGetDPIForMonitor = 0;
  86 FnGetProcessDPIAwareness * pGetProcessDPIAwareness = 0;
  87 FnSetProcessDPIAwareness * pSetProcessDPIAwareness = 0;
  88 
  89 void GlassScreen::LoadDPIFuncs(jint awareRequested)
  90 {
  91     if (triedToFindDPIFuncs) {
  92         return;
  93     }
  94     triedToFindDPIFuncs = TRUE;
  95     wchar_t path[MAX_PATH];
  96     HMODULE hLibSHCore = 0;
  97     if (::GetSystemDirectory(path, sizeof(path) / sizeof(wchar_t)) != 0) {
  98         wcscat_s(path, MAX_PATH-1, L"\\SHCore.dll");
  99         hLibSHCore = ::LoadLibrary(path);
 100     }
 101     if (hLibSHCore) {
 102         pGetProcessDPIAwareness = (FnGetProcessDPIAwareness*)GetProcAddress(hLibSHCore, "GetProcessDpiAwareness");
 103         pSetProcessDPIAwareness = (FnSetProcessDPIAwareness*)GetProcAddress(hLibSHCore, "SetProcessDpiAwareness");
 104         pGetDPIForMonitor = (FnGetDPIForMonitor*)GetProcAddress(hLibSHCore, "GetDpiForMonitor");
 105         if (!pGetProcessDPIAwareness || !pSetProcessDPIAwareness || !pGetDPIForMonitor) {
 106             pGetProcessDPIAwareness = 0;
 107             pSetProcessDPIAwareness = 0;
 108             pGetDPIForMonitor = 0;
 109         }
 110     } else {
 111 #ifdef DEBUG_DPI
 112         fprintf(stderr, "Could not find libSHCore.dll\n");
 113 #endif /* DEBUG_DPI */
 114     }
 115     if (pSetProcessDPIAwareness) {
 116         HRESULT res = (*pSetProcessDPIAwareness)((Process_DPI_Awareness) awareRequested);
 117 #ifdef DEBUG_DPI
 118         if (res != S_OK) {
 119             if (res == E_ACCESSDENIED) {
 120                 fprintf(stderr, "Process DPI awareness already set! (by application manifest or prior call)\n");
 121             } else {
 122                 fprintf(stderr, "SetProcessDpiAwareness(%d) returned (0x%08x)\n", awareRequested, res);
 123             }
 124         }
 125 #endif /* DEBUG_DPI */
 126     } else {
 127         BOOL ok = ::SetProcessDPIAware();
 128 #ifdef DEBUG_DPI
 129         fprintf(stderr, "Could not find SetProcessDpiAwareness function, SetProcessDPIAware returned %d\n", ok);
 130 #endif /* DEBUG_DPI */
 131     }
 132     if (pGetProcessDPIAwareness) {
 133         Process_DPI_Awareness awareness;
 134         HRESULT res = (*pGetProcessDPIAwareness)(NULL, &awareness);
 135 #ifdef DEBUG_DPI
 136         if (res != S_OK) {
 137             fprintf(stderr, "Unable to query process DPI Awareness (0x%08X)\n", res);
 138         } else {
 139             char *awareDescription;
 140             if (awareness == Process_DPI_Unaware) {
 141                 awareDescription = "DPI Unaware";
 142             } else if (awareness == Process_System_DPI_Aware) {
 143                 awareDescription = "System DPI aware (legacy)";
 144             } else if (awareness == Process_Per_Monitor_DPI_Aware) {
 145                 awareDescription = "Per Monitor (dynamic) DPI aware (best)";
 146             } else {
 147                 awareDescription = "Unknown awareness value";
 148             }
 149             fprintf(stderr, "ProcessDPIAwareness = %d [%s]\n", awareness, awareDescription);
 150         }
 151 #endif /* DEBUG_DPI */
 152     } else {
 153 #ifdef DEBUG_DPI
 154         fprintf(stderr, "Could not find GetProcessDpiAwareness function\n");
 155 #endif /* DEBUG_DPI */
 156     }
 157 }
 158 
 159 void GetMonitorSettings(HMONITOR hMonitor, MonitorInfoStruct *mis)
 160 {
 161     if (!triedToFindDPIFuncs) {
 162 #ifdef DEBUG_DPI
 163         fprintf(stderr, "Monitor settings queried before DPI functions initialized!\n");
 164 #endif /* DEBUG_DPI */
 165         GlassScreen::LoadDPIFuncs(Process_Per_Monitor_DPI_Aware);
 166     }
 167 
 168     MONITORINFOEX mix;
 169     memset(&mix, 0, sizeof(MONITORINFOEX));
 170     mix.cbSize = sizeof(MONITORINFOEX);
 171 
 172     mis->hMonitor = hMonitor;
 173 
 174     ::GetMonitorInfo(hMonitor, &mix);
 175 
 176     ::CopyRect(&mis->rcMonitor, &mix.rcMonitor);
 177     ::CopyRect(&mis->rcWork, &mix.rcWork);
 178 #ifdef DEBUG_DPI
 179     fprintf(stderr, "raw monitor bounds = (%d, %d, %d, %d)\n",
 180             mis->rcMonitor.left, mis->rcMonitor.top,
 181             mis->rcMonitor.right, mis->rcMonitor.bottom);
 182     fprintf(stderr, "raw monitor working bounds = (%d, %d, %d, %d)\n",
 183             mis->rcWork.left, mis->rcWork.top,
 184             mis->rcWork.right, mis->rcWork.bottom);
 185 #endif /* DEBUG_DPI */
 186 
 187     HDC hDC = ::CreateDC(TEXT("DISPLAY"), mix.szDevice, NULL, NULL);
 188     ASSERT(hDC);
 189 
 190     mis->primaryScreen = ((mix.dwFlags & MONITORINFOF_PRIMARY) != 0) ? JNI_TRUE : JNI_FALSE;
 191     mis->colorDepth = ::GetDeviceCaps(hDC, BITSPIXEL) * ::GetDeviceCaps(hDC, PLANES);
 192     UINT resx, resy;
 193     UINT uires;
 194     if (pGetDPIForMonitor) {
 195         // If we can use the GetDPIForMonitor function, then its Effective
 196         // value will tell us how much we should scale ourselves based on
 197         // all system settings, and its Raw value will tell us exactly how
 198         // many pixels per inch there are.  The Effective value can be
 199         // affected by user preference, accessibility settings, monitor
 200         // size, and resolution all computed by the system into a single
 201         // value that all applications should scale themselves by.
 202 #ifdef DEBUG_DPI
 203         fprintf(stderr, "logpixelsX,Y = %d, %d\n",
 204                 ::GetDeviceCaps(hDC, LOGPIXELSX),
 205                 ::GetDeviceCaps(hDC, LOGPIXELSY));
 206 #endif /* DEBUG_DPI */
 207         HRESULT res = (*pGetDPIForMonitor)(hMonitor, MDT_Effective_DPI, &resx, &resy);
 208 #ifdef DEBUG_DPI
 209         fprintf(stderr, "effective DPI X,Y = [0x%08x] %d, %d\n", res, resx, resy);
 210 #endif /* DEBUG_DPI */
 211         if (res != S_OK) {
 212             resx = ::GetDeviceCaps(hDC, LOGPIXELSX);
 213             resy = ::GetDeviceCaps(hDC, LOGPIXELSY);
 214         }
 215         uires = resx;
 216         res = (*pGetDPIForMonitor)(hMonitor, MDT_Raw_DPI, &resx, &resy);
 217 #ifdef DEBUG_DPI
 218         fprintf(stderr, "raw DPI X,Y = [0x%08x] %d, %d\n", res, resx, resy);
 219 #endif /* DEBUG_DPI */
 220     } else {
 221         resx = ::GetDeviceCaps(hDC, LOGPIXELSX);
 222         resy = ::GetDeviceCaps(hDC, LOGPIXELSY);
 223 #ifdef DEBUG_DPI
 224         fprintf(stderr, "logpixelsX,Y = %d, %d\n", resx, resy);
 225 #endif /* DEBUG_DPI */
 226         uires = resx;
 227     }
 228     mis->dpiX = resx;
 229     mis->dpiY = resy;
 230     mis->uiScale = GlassApplication::GetUIScale(uires);
 231     mis->renderScale = GlassApplication::getRenderScale(mis->uiScale);
 232 
 233     ::DeleteDC(hDC);
 234 }
 235 
 236 jclass GetScreenCls(JNIEnv *env)
 237 {
 238     static jclass screenCls = NULL;
 239     if (!screenCls) {
 240         jclass cls = GlassApplication::ClassForName(env, "com.sun.glass.ui.Screen");
 241         ASSERT(cls);
 242         screenCls = (jclass)env->NewGlobalRef(cls);
 243         env->DeleteLocalRef(cls);
 244     }
 245     return screenCls;
 246 }
 247 
 248 void anchor(MonitorInfoStruct *pMIS, int pass);
 249 
 250 jobject GlassScreen::GetJavaMonitor(JNIEnv *env, HMONITOR monitor)
 251 {
 252     for (int i = 0; i < g_MonitorInfos.numInfos; i++) {
 253         MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 254         if (pMIS->hMonitor == monitor) {
 255             return pMIS->gScreen;
 256         }
 257     }
 258 
 259 #ifdef DEBUG_DPI
 260     fprintf(stderr, "MONITOR NOT FOUND - making a new Java Screen object in isolation!\n");
 261 #endif /* DEBUG_DPI */
 262 
 263     MonitorInfoStruct mis;
 264     memset(&mis, 0, sizeof(MonitorInfoStruct));
 265     GetMonitorSettings(monitor, &mis);
 266     anchor(&mis, 0);
 267 
 268     jobject gScreen = CreateJavaMonitorFromMIS(env, &mis);
 269     // The return value (gScreen) is a local ref in addition to the global
 270     // ref stored in the mis.gScreen field.  We should not leave the global
 271     // ref laying around in this case because we are not saving the "mis"
 272     // structure.
 273     env->DeleteGlobalRef(mis.gScreen);
 274     return gScreen;
 275 }
 276 
 277 jobject CreateJavaMonitorFromMIS(JNIEnv *env, MonitorInfoStruct *pMIS)
 278 {
 279     jclass screenCls = GetScreenCls(env);
 280 
 281     if (javaIDs.Screen.init == NULL) {
 282         javaIDs.Screen.init = env->GetMethodID(screenCls, "<init>", "(JIIIIIIIIIIIFF)V");
 283         ASSERT(javaIDs.Screen.init);
 284         if (CheckAndClearException(env)) return NULL;
 285     }
 286 
 287     jobject gScn = env->NewObject(screenCls, javaIDs.Screen.init,
 288                           ptr_to_jlong(pMIS->hMonitor),
 289 
 290                           pMIS->colorDepth,
 291                           pMIS->fxMonitor.left,
 292                           pMIS->fxMonitor.top,
 293                           pMIS->fxMonitor.right  - pMIS->fxMonitor.left,
 294                           pMIS->fxMonitor.bottom - pMIS->fxMonitor.top,
 295 
 296                           pMIS->fxWork.left,
 297                           pMIS->fxWork.top,
 298                           pMIS->fxWork.right  - pMIS->fxWork.left,
 299                           pMIS->fxWork.bottom - pMIS->fxWork.top,
 300 
 301                           pMIS->dpiX,
 302                           pMIS->dpiY,
 303 
 304                           pMIS->uiScale,
 305                           pMIS->renderScale);
 306     if (CheckAndClearException(env)) return NULL;
 307     pMIS->gScreen = env->NewGlobalRef(gScn);
 308     return gScn;
 309 }
 310 
 311 void GlassScreen::HandleDisplayChange()
 312 {
 313     JNIEnv *env = GetEnv();
 314 
 315     jclass screenCls = GetScreenCls(env);
 316 
 317     if (javaIDs.Screen.notifySettingsChanged == NULL) {
 318         javaIDs.Screen.notifySettingsChanged
 319              = env->GetStaticMethodID(screenCls, "notifySettingsChanged", "()V");
 320         ASSERT(javaIDs.Screen.notifySettingsChanged);
 321         if (CheckAndClearException(env)) return;
 322     }
 323 
 324     env->CallStaticVoidMethod(screenCls, javaIDs.Screen.notifySettingsChanged);
 325     CheckAndClearException(env);
 326 }
 327 
 328 jfloat distSqTo(RECT rect, jfloat x, jfloat y)
 329 {
 330     jfloat relx = x - (rect.left + rect.right) * 0.5f;
 331     jfloat rely = y - (rect.top + rect.bottom) * 0.5f;
 332     return relx * relx + rely * rely;
 333 }
 334 
 335 void convert(RECT from, RECT to, jfloat *pX, jfloat *pY)
 336 {
 337     jfloat t;
 338     t = (*pX - from.left) / (from.right - from.left);
 339     *pX = to.left + t * (to.right - to.left);
 340     t = (*pY - from.top) / (from.bottom - from.top);
 341     *pY = to.top + t * (to.bottom - to.top);
 342 }
 343 
 344 BOOL GlassScreen::FX2Win(jfloat* pX, jfloat* pY)
 345 {
 346     if (g_MonitorInfos.numInfos == 0) return FALSE;
 347     int monIndex = 0;
 348     jfloat distSq = distSqTo(g_MonitorInfos.pMonitorInfos[0].fxMonitor, *pX, *pY);
 349     for (int i = 1; i < g_MonitorInfos.numInfos; i++) {
 350         jfloat d = distSqTo(g_MonitorInfos.pMonitorInfos[i].fxMonitor, *pX, *pY);
 351         if (d < distSq) {
 352             distSq = d;
 353             monIndex = i;
 354         }
 355     }
 356     convert(g_MonitorInfos.pMonitorInfos[monIndex].fxMonitor,
 357             g_MonitorInfos.pMonitorInfos[monIndex].rcMonitor,
 358             pX, pY);
 359     return TRUE;
 360 }
 361 
 362 BOOL GlassScreen::Win2FX(jfloat* pX, jfloat* pY)
 363 {
 364     if (g_MonitorInfos.numInfos == 0) return FALSE;
 365     int monIndex = 0;
 366     jfloat distSq = distSqTo(g_MonitorInfos.pMonitorInfos[0].rcMonitor, *pX, *pY);
 367     for (int i = 1; i < g_MonitorInfos.numInfos; i++) {
 368         jfloat d = distSqTo(g_MonitorInfos.pMonitorInfos[i].rcMonitor, *pX, *pY);
 369         if (d < distSq) {
 370             distSq = d;
 371             monIndex = i;
 372         }
 373     }
 374     convert(g_MonitorInfos.pMonitorInfos[monIndex].rcMonitor,
 375             g_MonitorInfos.pMonitorInfos[monIndex].fxMonitor,
 376             pX, pY);
 377     return TRUE;
 378 }
 379 
 380 void anchorTo(MonitorInfoStruct *pMIS,
 381               jint fxX, jboolean xBefore,
 382               jint fxY, jboolean yBefore,
 383               jint pass)
 384 {
 385     jint monX = pMIS->rcMonitor.left;
 386     jint monY = pMIS->rcMonitor.top;
 387     jint monW = pMIS->rcMonitor.right  - monX;
 388     jint monH = pMIS->rcMonitor.bottom - monY;
 389     jint wrkL = pMIS->rcWork   .left   - monX;
 390     jint wrkT = pMIS->rcWork   .top    - monY;
 391     jint wrkR = pMIS->rcWork   .right  - monX;
 392     jint wrkB = pMIS->rcWork   .bottom - monY;
 393     jfloat scale = pMIS->uiScale;
 394     if (scale > 1.0f) {
 395         pMIS->dpiX = (jint) floorf((pMIS->dpiX / scale) + 0.5f);
 396         pMIS->dpiY = (jint) floorf((pMIS->dpiY / scale) + 0.5f);
 397         monW = (jint) floorf((monW / scale) + 0.5f);
 398         monH = (jint) floorf((monH / scale) + 0.5f);
 399         wrkL = (jint) floorf((wrkL / scale) + 0.5f);
 400         wrkT = (jint) floorf((wrkT / scale) + 0.5f);
 401         wrkR = (jint) floorf((wrkR / scale) + 0.5f);
 402         wrkB = (jint) floorf((wrkB / scale) + 0.5f);
 403     }
 404 
 405     if (xBefore) fxX -= monW;
 406     if (yBefore) fxY -= monH;
 407     pMIS->fxMonitor.left   = fxX;
 408     pMIS->fxMonitor.top    = fxY;
 409     pMIS->fxMonitor.right  = fxX + monW;
 410     pMIS->fxMonitor.bottom = fxY + monH;
 411     pMIS->fxWork   .left   = fxX + wrkL;
 412     pMIS->fxWork   .top    = fxY + wrkT;
 413     pMIS->fxWork   .right  = fxX + wrkR;
 414     pMIS->fxWork   .bottom = fxY + wrkB;
 415     pMIS->anchoredInPass = pass;
 416 }
 417 
 418 void anchor(MonitorInfoStruct *pMIS, int pass) {
 419     anchorTo(pMIS,
 420              pMIS->rcMonitor.left, JNI_FALSE,
 421              pMIS->rcMonitor.top, JNI_FALSE,
 422              pass);
 423 }
 424 
 425 jint originOffsetFromRanges(jint aV0, jint aV1,
 426                             jint mV0, jint mV1,
 427                             jfloat aScale, jfloat mScale)
 428 {
 429     jint v0 = (aV0 > mV0) ? aV0 : mV0;
 430     jint v1 = (aV1 < mV1) ? aV1 : mV1;
 431     jfloat mid = (v0 + v1) / 2.0f;
 432     jfloat rel = (mid - aV0) / aScale - (mid - mV0) / mScale;
 433     return (jint) floorf(rel + 0.5f);
 434 }
 435 
 436 void anchorH(MonitorInfoStruct *pAnchor, MonitorInfoStruct *pMon,
 437              jboolean before, jint pass)
 438 {
 439     int x = before ? pAnchor->fxMonitor.left : pAnchor->fxMonitor.right;
 440     int yoff = originOffsetFromRanges(pAnchor->rcMonitor.top, pAnchor->rcMonitor.bottom,
 441                                       pMon->rcMonitor.top, pMon->rcMonitor.bottom,
 442                                       pAnchor->uiScale, pMon->uiScale);
 443     int y = pAnchor->fxMonitor.top + yoff;
 444     anchorTo(pMon, x, before, y, false, pass);
 445 }
 446 
 447 void anchorV(MonitorInfoStruct *pAnchor, MonitorInfoStruct *pMon,
 448              jboolean before, jint pass)
 449 {
 450     int xoff = originOffsetFromRanges(pAnchor->rcMonitor.left, pAnchor->rcMonitor.right,
 451                                       pMon->rcMonitor.left, pMon->rcMonitor.right,
 452                                       pAnchor->uiScale, pMon->uiScale);
 453     int x = pAnchor->fxMonitor.left + xoff;
 454     int y = before ? pAnchor->fxMonitor.top : pAnchor->fxMonitor.bottom;
 455     anchorTo(pMon, x, false, y, before, pass);
 456 }
 457 
 458 BOOL touchesLeft(MonitorInfoStruct *pMISa, MonitorInfoStruct *pMISb) {
 459     return (pMISa->rcMonitor.left  == pMISb->rcMonitor.right &&
 460             pMISa->rcMonitor.top    < pMISb->rcMonitor.bottom &&
 461             pMISa->rcMonitor.bottom > pMISb->rcMonitor.top);
 462 }
 463 
 464 BOOL touchesAbove(MonitorInfoStruct *pMISa, MonitorInfoStruct *pMISb) {
 465     return (pMISa->rcMonitor.top  == pMISb->rcMonitor.bottom &&
 466             pMISa->rcMonitor.left  < pMISb->rcMonitor.right &&
 467             pMISa->rcMonitor.right > pMISb->rcMonitor.left);
 468 }
 469 
 470 void propagateAnchors(MonitorInfoStruct *pMIS, jint pass) {
 471     for (int i = 0; i < g_MonitorInfos.numInfos; i++) {
 472         MonitorInfoStruct *pMIS2 = &g_MonitorInfos.pMonitorInfos[i];
 473         if (pMIS2->anchoredInPass != 0) continue;
 474         if (touchesLeft(pMIS2, pMIS)) {
 475             anchorH(pMIS, pMIS2, JNI_FALSE, pass);
 476         } else if (touchesLeft(pMIS, pMIS2)) {
 477             anchorH(pMIS, pMIS2, JNI_TRUE, pass);
 478         } else if (touchesAbove(pMIS2, pMIS)) {
 479             anchorV(pMIS, pMIS2, JNI_FALSE, pass);
 480         } else if (touchesAbove(pMIS, pMIS2)) {
 481             anchorV(pMIS, pMIS2, JNI_TRUE, pass);
 482         }
 483     }
 484 }
 485 
 486 jobjectArray GlassScreen::CreateJavaScreens(JNIEnv *env)
 487 {
 488     if (g_MonitorInfos.maxInfos > 0) {
 489         int numMonitors = g_MonitorInfos.numInfos;
 490         for (int i = 0; i < numMonitors; i++) {
 491             MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 492             if (pMIS->gScreen != NULL) {
 493                 env->DeleteGlobalRef(pMIS->gScreen);
 494             }
 495         }
 496         free(g_MonitorInfos.pMonitorInfos);
 497         g_MonitorInfos.numInfos = g_MonitorInfos.maxInfos = 0;
 498         g_MonitorInfos.pMonitorInfos = NULL;
 499     }
 500 
 501     g_MonitorInfos.maxInfos = 0;
 502     ::EnumDisplayMonitors(NULL, NULL, CountMonitorsCallback, 0L);
 503     int numMonitors = g_MonitorInfos.maxInfos;
 504 
 505 #ifdef DEBUG_DPI
 506     fprintf(stderr, "numMonitors = %d\n", numMonitors);
 507 #endif /* DEBUG_DPI */
 508 
 509     g_MonitorInfos.numInfos = 0;
 510     g_MonitorInfos.pMonitorInfos =
 511             (MonitorInfoStruct *)malloc(numMonitors * sizeof(MonitorInfoStruct));
 512     memset(g_MonitorInfos.pMonitorInfos, 0, numMonitors * sizeof(MonitorInfoStruct));
 513     ::EnumDisplayMonitors(NULL, NULL, CollectMonitorsCallback, 0L);
 514     numMonitors = g_MonitorInfos.numInfos;
 515     if (numMonitors <= 0) {
 516         return NULL;
 517     }
 518 
 519     //The primary monitor should be set to the 0 index
 520     int primaryIndex = 0;
 521     for (int i = 0; i < numMonitors; i++) {
 522         MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 523         if (pMIS->rcMonitor.left  <= 0 &&
 524             pMIS->rcMonitor.top   <= 0 &&
 525             pMIS->rcMonitor.right  > 0 &&
 526             pMIS->rcMonitor.bottom > 0)
 527         {
 528             primaryIndex = i;
 529             break;
 530         } else if (pMIS->primaryScreen) {
 531             primaryIndex = i;
 532         }
 533     }
 534     // Swap the primary monitor to the 0 index
 535     if (primaryIndex > 0) {
 536         MonitorInfoStruct tmpMIS = g_MonitorInfos.pMonitorInfos[primaryIndex];
 537         g_MonitorInfos.pMonitorInfos[primaryIndex] = g_MonitorInfos.pMonitorInfos[0];
 538         g_MonitorInfos.pMonitorInfos[0] = tmpMIS;
 539     }
 540 
 541     // Anchor the primary screen.
 542     // Then loop, propagating the geometry of the primary screen to its
 543     // neighbors first and then each screen in preference to how closely
 544     // it was anchored to the primary screen.
 545     // If all propagations are done and we still have unanchored screens,
 546     // choose the lowest such screen in the list and anchor it, repeating
 547     // the propagation process until all screens are anchored, preferably
 548     // to each other, but in isolated groups as well if necessary.
 549     int pass = 1;
 550     anchor(&g_MonitorInfos.pMonitorInfos[0], pass);
 551     do {
 552         jboolean foundUnpropagated = JNI_FALSE;
 553         for (int i = 0; i < numMonitors; i++) {
 554             MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 555             if (pMIS->anchoredInPass == pass) {
 556                 foundUnpropagated = JNI_TRUE;
 557                 propagateAnchors(pMIS, pass+1);
 558             }
 559         }
 560         if (foundUnpropagated) {
 561             pass++;
 562         } else {
 563             jboolean foundUnanchored = JNI_FALSE;
 564             for (int i = 0; i < numMonitors; i++) {
 565                 MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 566                 if (pMIS->anchoredInPass == 0) {
 567                     foundUnanchored = JNI_TRUE;
 568                     anchor(pMIS, pass);
 569                     break;
 570                 }
 571             }
 572             if (!foundUnanchored) break;
 573             // Loop back without incrementing "pass" so that we propagate
 574             // the screen that we just anchored above.
 575         }
 576     } while (JNI_TRUE);
 577 
 578     jclass screenCls = GetScreenCls(env);
 579 
 580     jobjectArray jScreens = env->NewObjectArray(numMonitors, screenCls, NULL);
 581     if (CheckAndClearException(env)) {
 582         free(g_MonitorInfos.pMonitorInfos);
 583         g_MonitorInfos.numInfos = g_MonitorInfos.maxInfos = 0;
 584         g_MonitorInfos.pMonitorInfos = NULL;
 585         return NULL;
 586     }
 587 
 588     int arrayIndex = 1;
 589     for (int i = 0; i < numMonitors; i++) {
 590         MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 591         HMONITOR hMonitor = pMIS->hMonitor;
 592         jobject jScreen = CreateJavaMonitorFromMIS(env, pMIS);
 593         env->SetObjectArrayElement(jScreens, i, jScreen);
 594         CheckAndClearException(env);
 595         env->DeleteLocalRef(jScreen);
 596     }
 597 
 598     return jScreens;
 599 }
 600 
 601 ////////////////////////////////////////////////////////////////////////////////////
 602 //                               native callbacks
 603 ////////////////////////////////////////////////////////////////////////////////////
 604 
 605 BOOL CALLBACK CountMonitorsCallback(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
 606 {
 607     g_MonitorInfos.maxInfos++;
 608     return TRUE;
 609 }
 610 
 611 BOOL CALLBACK CollectMonitorsCallback(HMONITOR hMonitor, HDC hDC, LPRECT rRect, LPARAM lP)
 612 {
 613     if ((hMonitor != NULL) &&
 614         (g_MonitorInfos.numInfos < g_MonitorInfos.maxInfos) &&
 615         (g_MonitorInfos.pMonitorInfos != NULL))
 616     {
 617         GetMonitorSettings(hMonitor, &(g_MonitorInfos.pMonitorInfos[g_MonitorInfos.numInfos]));
 618         g_MonitorInfos.numInfos++;
 619     }
 620     return TRUE;
 621 }