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 }