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 uiScaleX;
  52     jfloat uiScaleY;
  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 uiresx, uiresy;
 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         uiresx = resx;
 216         uiresy = resy;
 217         res = (*pGetDPIForMonitor)(hMonitor, MDT_Raw_DPI, &resx, &resy);
 218 #ifdef DEBUG_DPI
 219         fprintf(stderr, "raw DPI X,Y = [0x%08x] %d, %d\n", res, resx, resy);
 220 #endif /* DEBUG_DPI */
 221     } else {
 222         resx = ::GetDeviceCaps(hDC, LOGPIXELSX);
 223         resy = ::GetDeviceCaps(hDC, LOGPIXELSY);
 224 #ifdef DEBUG_DPI
 225         fprintf(stderr, "logpixelsX,Y = %d, %d\n", resx, resy);
 226 #endif /* DEBUG_DPI */
 227         uiresx = resx;
 228         uiresy = resy;
 229     }
 230     mis->dpiX = resx;
 231     mis->dpiY = resy;
 232     mis->uiScaleX = GlassApplication::GetUIScale(uiresx);
 233     mis->uiScaleY = GlassApplication::GetUIScale(uiresy);
 234 
 235     ::DeleteDC(hDC);
 236 }
 237 
 238 jclass GetScreenCls(JNIEnv *env)
 239 {
 240     static jclass screenCls = NULL;
 241     if (!screenCls) {
 242         jclass cls = GlassApplication::ClassForName(env, "com.sun.glass.ui.Screen");
 243         ASSERT(cls);
 244         screenCls = (jclass)env->NewGlobalRef(cls);
 245         env->DeleteLocalRef(cls);
 246     }
 247     return screenCls;
 248 }
 249 
 250 void anchor(MonitorInfoStruct *pMIS, int pass);
 251 
 252 jobject GlassScreen::GetJavaMonitor(JNIEnv *env, HMONITOR monitor)
 253 {
 254     for (int i = 0; i < g_MonitorInfos.numInfos; i++) {
 255         MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 256         if (pMIS->hMonitor == monitor) {
 257             return pMIS->gScreen;
 258         }
 259     }
 260 
 261 #ifdef DEBUG_DPI
 262     fprintf(stderr, "MONITOR NOT FOUND - making a new Java Screen object in isolation!\n");
 263 #endif /* DEBUG_DPI */
 264 
 265     MonitorInfoStruct mis;
 266     memset(&mis, 0, sizeof(MonitorInfoStruct));
 267     GetMonitorSettings(monitor, &mis);
 268     anchor(&mis, 0);
 269 
 270     jobject gScreen = CreateJavaMonitorFromMIS(env, &mis);
 271     // The return value (gScreen) is a local ref in addition to the global
 272     // ref stored in the mis.gScreen field.  We should not leave the global
 273     // ref laying around in this case because we are not saving the "mis"
 274     // structure.
 275     env->DeleteGlobalRef(mis.gScreen);
 276     return gScreen;
 277 }
 278 
 279 jobject CreateJavaMonitorFromMIS(JNIEnv *env, MonitorInfoStruct *pMIS)
 280 {
 281     jclass screenCls = GetScreenCls(env);
 282 
 283     if (javaIDs.Screen.init == NULL) {
 284         javaIDs.Screen.init = env->GetMethodID(screenCls, "<init>", "(JIIIIIIIIIIIIIIIFFFF)V");
 285         ASSERT(javaIDs.Screen.init);
 286         if (CheckAndClearException(env)) return NULL;
 287     }
 288 
 289     jobject gScn = env->NewObject(screenCls, javaIDs.Screen.init,
 290                           ptr_to_jlong(pMIS->hMonitor),
 291 
 292                           pMIS->colorDepth,
 293                           pMIS->fxMonitor.left,
 294                           pMIS->fxMonitor.top,
 295                           pMIS->fxMonitor.right  - pMIS->fxMonitor.left,
 296                           pMIS->fxMonitor.bottom - pMIS->fxMonitor.top,
 297 
 298                           pMIS->rcMonitor.left,
 299                           pMIS->rcMonitor.top,
 300                           pMIS->rcMonitor.right  - pMIS->rcMonitor.left,
 301                           pMIS->rcMonitor.bottom - pMIS->rcMonitor.top,
 302 
 303                           pMIS->fxWork.left,
 304                           pMIS->fxWork.top,
 305                           pMIS->fxWork.right  - pMIS->fxWork.left,
 306                           pMIS->fxWork.bottom - pMIS->fxWork.top,
 307 
 308                           pMIS->dpiX,
 309                           pMIS->dpiY,
 310 
 311                           pMIS->uiScaleX,
 312                           pMIS->uiScaleY,
 313                           pMIS->uiScaleX,
 314                           pMIS->uiScaleY);
 315     if (CheckAndClearException(env)) return NULL;
 316     pMIS->gScreen = env->NewGlobalRef(gScn);
 317     return gScn;
 318 }
 319 
 320 void GlassScreen::HandleDisplayChange()
 321 {
 322     JNIEnv *env = GetEnv();
 323 
 324     jclass screenCls = GetScreenCls(env);
 325 
 326     if (javaIDs.Screen.notifySettingsChanged == NULL) {
 327         javaIDs.Screen.notifySettingsChanged
 328              = env->GetStaticMethodID(screenCls, "notifySettingsChanged", "()V");
 329         ASSERT(javaIDs.Screen.notifySettingsChanged);
 330         if (CheckAndClearException(env)) return;
 331     }
 332 
 333     env->CallStaticVoidMethod(screenCls, javaIDs.Screen.notifySettingsChanged);
 334     CheckAndClearException(env);
 335 }
 336 
 337 jfloat distSqTo(RECT rect, jfloat x, jfloat y)
 338 {
 339     jfloat relx = x - (rect.left + rect.right) * 0.5f;
 340     jfloat rely = y - (rect.top + rect.bottom) * 0.5f;
 341     return relx * relx + rely * rely;
 342 }
 343 
 344 void convert(RECT from, RECT to, jfloat *pX, jfloat *pY)
 345 {
 346     jfloat t;
 347     t = (*pX - from.left) / (from.right - from.left);
 348     *pX = to.left + t * (to.right - to.left);
 349     t = (*pY - from.top) / (from.bottom - from.top);
 350     *pY = to.top + t * (to.bottom - to.top);
 351 }
 352 
 353 BOOL GlassScreen::FX2Win(jfloat* pX, jfloat* pY)
 354 {
 355     if (g_MonitorInfos.numInfos == 0) return FALSE;
 356     int monIndex = 0;
 357     jfloat distSq = distSqTo(g_MonitorInfos.pMonitorInfos[0].fxMonitor, *pX, *pY);
 358     for (int i = 1; i < g_MonitorInfos.numInfos; i++) {
 359         jfloat d = distSqTo(g_MonitorInfos.pMonitorInfos[i].fxMonitor, *pX, *pY);
 360         if (d < distSq) {
 361             distSq = d;
 362             monIndex = i;
 363         }
 364     }
 365     convert(g_MonitorInfos.pMonitorInfos[monIndex].fxMonitor,
 366             g_MonitorInfos.pMonitorInfos[monIndex].rcMonitor,
 367             pX, pY);
 368     return TRUE;
 369 }
 370 
 371 BOOL GlassScreen::Win2FX(jfloat* pX, jfloat* pY)
 372 {
 373     if (g_MonitorInfos.numInfos == 0) return FALSE;
 374     int monIndex = 0;
 375     jfloat distSq = distSqTo(g_MonitorInfos.pMonitorInfos[0].rcMonitor, *pX, *pY);
 376     for (int i = 1; i < g_MonitorInfos.numInfos; i++) {
 377         jfloat d = distSqTo(g_MonitorInfos.pMonitorInfos[i].rcMonitor, *pX, *pY);
 378         if (d < distSq) {
 379             distSq = d;
 380             monIndex = i;
 381         }
 382     }
 383     convert(g_MonitorInfos.pMonitorInfos[monIndex].rcMonitor,
 384             g_MonitorInfos.pMonitorInfos[monIndex].fxMonitor,
 385             pX, pY);
 386     return TRUE;
 387 }
 388 
 389 void anchorTo(MonitorInfoStruct *pMIS,
 390               jint fxX, jboolean xBefore,
 391               jint fxY, jboolean yBefore,
 392               jint pass)
 393 {
 394     jint monX = pMIS->rcMonitor.left;
 395     jint monY = pMIS->rcMonitor.top;
 396     jint monW = pMIS->rcMonitor.right  - monX;
 397     jint monH = pMIS->rcMonitor.bottom - monY;
 398     jint wrkL = pMIS->rcWork   .left   - monX;
 399     jint wrkT = pMIS->rcWork   .top    - monY;
 400     jint wrkR = pMIS->rcWork   .right  - monX;
 401     jint wrkB = pMIS->rcWork   .bottom - monY;
 402     jfloat scalex = pMIS->uiScaleX;
 403     jfloat scaley = pMIS->uiScaleY;
 404     if (scalex != 1.0f) {
 405         pMIS->dpiX = (jint) floorf((pMIS->dpiX / scalex) + 0.5f);
 406         monW = (jint) floorf((monW / scalex) + 0.5f);
 407         wrkL = (jint) floorf((wrkL / scalex) + 0.5f);
 408         wrkR = (jint) floorf((wrkR / scalex) + 0.5f);
 409     }
 410     if (scaley != 1.0f) {
 411         pMIS->dpiY = (jint) floorf((pMIS->dpiY / scaley) + 0.5f);
 412         monH = (jint) floorf((monH / scaley) + 0.5f);
 413         wrkT = (jint) floorf((wrkT / scaley) + 0.5f);
 414         wrkB = (jint) floorf((wrkB / scaley) + 0.5f);
 415     }
 416 
 417     if (xBefore) fxX -= monW;
 418     if (yBefore) fxY -= monH;
 419     pMIS->fxMonitor.left   = fxX;
 420     pMIS->fxMonitor.top    = fxY;
 421     pMIS->fxMonitor.right  = fxX + monW;
 422     pMIS->fxMonitor.bottom = fxY + monH;
 423     pMIS->fxWork   .left   = fxX + wrkL;
 424     pMIS->fxWork   .top    = fxY + wrkT;
 425     pMIS->fxWork   .right  = fxX + wrkR;
 426     pMIS->fxWork   .bottom = fxY + wrkB;
 427     pMIS->anchoredInPass = pass;
 428 }
 429 
 430 void anchor(MonitorInfoStruct *pMIS, int pass) {
 431     anchorTo(pMIS,
 432              pMIS->rcMonitor.left, JNI_FALSE,
 433              pMIS->rcMonitor.top, JNI_FALSE,
 434              pass);
 435 }
 436 
 437 jint originOffsetFromRanges(jint aV0, jint aV1,
 438                             jint mV0, jint mV1,
 439                             jfloat aScale, jfloat mScale)
 440 {
 441     jint v0 = (aV0 > mV0) ? aV0 : mV0;
 442     jint v1 = (aV1 < mV1) ? aV1 : mV1;
 443     jfloat mid = (v0 + v1) / 2.0f;
 444     jfloat rel = (mid - aV0) / aScale - (mid - mV0) / mScale;
 445     return (jint) floorf(rel + 0.5f);
 446 }
 447 
 448 void anchorH(MonitorInfoStruct *pAnchor, MonitorInfoStruct *pMon,
 449              jboolean before, jint pass)
 450 {
 451     int x = before ? pAnchor->fxMonitor.left : pAnchor->fxMonitor.right;
 452     int yoff = originOffsetFromRanges(pAnchor->rcMonitor.top, pAnchor->rcMonitor.bottom,
 453                                       pMon->rcMonitor.top, pMon->rcMonitor.bottom,
 454                                       pAnchor->uiScaleY, pMon->uiScaleY);
 455     int y = pAnchor->fxMonitor.top + yoff;
 456     anchorTo(pMon, x, before, y, false, pass);
 457 }
 458 
 459 void anchorV(MonitorInfoStruct *pAnchor, MonitorInfoStruct *pMon,
 460              jboolean before, jint pass)
 461 {
 462     int xoff = originOffsetFromRanges(pAnchor->rcMonitor.left, pAnchor->rcMonitor.right,
 463                                       pMon->rcMonitor.left, pMon->rcMonitor.right,
 464                                       pAnchor->uiScaleX, pMon->uiScaleX);
 465     int x = pAnchor->fxMonitor.left + xoff;
 466     int y = before ? pAnchor->fxMonitor.top : pAnchor->fxMonitor.bottom;
 467     anchorTo(pMon, x, false, y, before, pass);
 468 }
 469 
 470 BOOL touchesLeft(MonitorInfoStruct *pMISa, MonitorInfoStruct *pMISb) {
 471     return (pMISa->rcMonitor.left  == pMISb->rcMonitor.right &&
 472             pMISa->rcMonitor.top    < pMISb->rcMonitor.bottom &&
 473             pMISa->rcMonitor.bottom > pMISb->rcMonitor.top);
 474 }
 475 
 476 BOOL touchesAbove(MonitorInfoStruct *pMISa, MonitorInfoStruct *pMISb) {
 477     return (pMISa->rcMonitor.top  == pMISb->rcMonitor.bottom &&
 478             pMISa->rcMonitor.left  < pMISb->rcMonitor.right &&
 479             pMISa->rcMonitor.right > pMISb->rcMonitor.left);
 480 }
 481 
 482 void propagateAnchors(MonitorInfoStruct *pMIS, jint pass) {
 483     for (int i = 0; i < g_MonitorInfos.numInfos; i++) {
 484         MonitorInfoStruct *pMIS2 = &g_MonitorInfos.pMonitorInfos[i];
 485         if (pMIS2->anchoredInPass != 0) continue;
 486         if (touchesLeft(pMIS2, pMIS)) {
 487             anchorH(pMIS, pMIS2, JNI_FALSE, pass);
 488         } else if (touchesLeft(pMIS, pMIS2)) {
 489             anchorH(pMIS, pMIS2, JNI_TRUE, pass);
 490         } else if (touchesAbove(pMIS2, pMIS)) {
 491             anchorV(pMIS, pMIS2, JNI_FALSE, pass);
 492         } else if (touchesAbove(pMIS, pMIS2)) {
 493             anchorV(pMIS, pMIS2, JNI_TRUE, pass);
 494         }
 495     }
 496 }
 497 
 498 jobjectArray GlassScreen::CreateJavaScreens(JNIEnv *env)
 499 {
 500     if (g_MonitorInfos.maxInfos > 0) {
 501         int numMonitors = g_MonitorInfos.numInfos;
 502         for (int i = 0; i < numMonitors; i++) {
 503             MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 504             if (pMIS->gScreen != NULL) {
 505                 env->DeleteGlobalRef(pMIS->gScreen);
 506             }
 507         }
 508         free(g_MonitorInfos.pMonitorInfos);
 509         g_MonitorInfos.numInfos = g_MonitorInfos.maxInfos = 0;
 510         g_MonitorInfos.pMonitorInfos = NULL;
 511     }
 512 
 513     g_MonitorInfos.maxInfos = 0;
 514     ::EnumDisplayMonitors(NULL, NULL, CountMonitorsCallback, 0L);
 515     int numMonitors = g_MonitorInfos.maxInfos;
 516 
 517 #ifdef DEBUG_DPI
 518     fprintf(stderr, "numMonitors = %d\n", numMonitors);
 519 #endif /* DEBUG_DPI */
 520 
 521     g_MonitorInfos.numInfos = 0;
 522     g_MonitorInfos.pMonitorInfos =
 523             (MonitorInfoStruct *)malloc(numMonitors * sizeof(MonitorInfoStruct));
 524     memset(g_MonitorInfos.pMonitorInfos, 0, numMonitors * sizeof(MonitorInfoStruct));
 525     ::EnumDisplayMonitors(NULL, NULL, CollectMonitorsCallback, 0L);
 526     numMonitors = g_MonitorInfos.numInfos;
 527     if (numMonitors <= 0) {
 528         return NULL;
 529     }
 530 
 531     //The primary monitor should be set to the 0 index
 532     int primaryIndex = 0;
 533     for (int i = 0; i < numMonitors; i++) {
 534         MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 535         if (pMIS->rcMonitor.left  <= 0 &&
 536             pMIS->rcMonitor.top   <= 0 &&
 537             pMIS->rcMonitor.right  > 0 &&
 538             pMIS->rcMonitor.bottom > 0)
 539         {
 540             primaryIndex = i;
 541             break;
 542         } else if (pMIS->primaryScreen) {
 543             primaryIndex = i;
 544         }
 545     }
 546     // Swap the primary monitor to the 0 index
 547     if (primaryIndex > 0) {
 548         MonitorInfoStruct tmpMIS = g_MonitorInfos.pMonitorInfos[primaryIndex];
 549         g_MonitorInfos.pMonitorInfos[primaryIndex] = g_MonitorInfos.pMonitorInfos[0];
 550         g_MonitorInfos.pMonitorInfos[0] = tmpMIS;
 551     }
 552 
 553     // Anchor the primary screen.
 554     // Then loop, propagating the geometry of the primary screen to its
 555     // neighbors first and then each screen in preference to how closely
 556     // it was anchored to the primary screen.
 557     // If all propagations are done and we still have unanchored screens,
 558     // choose the lowest such screen in the list and anchor it, repeating
 559     // the propagation process until all screens are anchored, preferably
 560     // to each other, but in isolated groups as well if necessary.
 561     int pass = 1;
 562     anchor(&g_MonitorInfos.pMonitorInfos[0], pass);
 563     do {
 564         jboolean foundUnpropagated = JNI_FALSE;
 565         for (int i = 0; i < numMonitors; i++) {
 566             MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 567             if (pMIS->anchoredInPass == pass) {
 568                 foundUnpropagated = JNI_TRUE;
 569                 propagateAnchors(pMIS, pass+1);
 570             }
 571         }
 572         if (foundUnpropagated) {
 573             pass++;
 574         } else {
 575             jboolean foundUnanchored = JNI_FALSE;
 576             for (int i = 0; i < numMonitors; i++) {
 577                 MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 578                 if (pMIS->anchoredInPass == 0) {
 579                     foundUnanchored = JNI_TRUE;
 580                     anchor(pMIS, pass);
 581                     break;
 582                 }
 583             }
 584             if (!foundUnanchored) break;
 585             // Loop back without incrementing "pass" so that we propagate
 586             // the screen that we just anchored above.
 587         }
 588     } while (JNI_TRUE);
 589 
 590     jclass screenCls = GetScreenCls(env);
 591 
 592     jobjectArray jScreens = env->NewObjectArray(numMonitors, screenCls, NULL);
 593     if (CheckAndClearException(env)) {
 594         free(g_MonitorInfos.pMonitorInfos);
 595         g_MonitorInfos.numInfos = g_MonitorInfos.maxInfos = 0;
 596         g_MonitorInfos.pMonitorInfos = NULL;
 597         return NULL;
 598     }
 599 
 600     int arrayIndex = 1;
 601     for (int i = 0; i < numMonitors; i++) {
 602         MonitorInfoStruct *pMIS = &g_MonitorInfos.pMonitorInfos[i];
 603         HMONITOR hMonitor = pMIS->hMonitor;
 604         jobject jScreen = CreateJavaMonitorFromMIS(env, pMIS);
 605         env->SetObjectArrayElement(jScreens, i, jScreen);
 606         CheckAndClearException(env);
 607         env->DeleteLocalRef(jScreen);
 608     }
 609 
 610     return jScreens;
 611 }
 612 
 613 ////////////////////////////////////////////////////////////////////////////////////
 614 //                               native callbacks
 615 ////////////////////////////////////////////////////////////////////////////////////
 616 
 617 BOOL CALLBACK CountMonitorsCallback(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
 618 {
 619     g_MonitorInfos.maxInfos++;
 620     return TRUE;
 621 }
 622 
 623 BOOL CALLBACK CollectMonitorsCallback(HMONITOR hMonitor, HDC hDC, LPRECT rRect, LPARAM lP)
 624 {
 625     if ((hMonitor != NULL) &&
 626         (g_MonitorInfos.numInfos < g_MonitorInfos.maxInfos) &&
 627         (g_MonitorInfos.pMonitorInfos != NULL))
 628     {
 629         GetMonitorSettings(hMonitor, &(g_MonitorInfos.pMonitorInfos[g_MonitorInfos.numInfos]));
 630         g_MonitorInfos.numInfos++;
 631     }
 632     return TRUE;
 633 }