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 }