blob: 27299d34a0cd1540e9e7f41156c94a85d4bebcf9 [file] [log] [blame]
/*
* Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* This class encapsulates the array of Win32GraphicsDevices,
* allowing it to be accessed and recreated from multiple
* threads in a thread-safe manner.
*
* The MT-safeness of the array is assured in the following ways:
* - hide the actual array being used so that access to
* it can only be made from this class
* - Do not delete the array until all references to the
* array have released it. That way, anyone that happens
* to have a pointer to an element of the array can still
* safely refer to that item, even if the situation has
* changed and the array is out of date.
* - ensure that the user of the array always gets a non-disposed
* instance (before the user is handed over a reference to the
* instance, a ref counter of the instance is increased atomically)
* - The act of replacing an old encapsulated array
* of devices with the new one is protected via common lock
*
* Expected usage patterns:
* 1. The array element will not be used outside of this code block.
* {
* // first, get the reference to the Devices instance through InstanceAccess
* // subclass (this automatically increases ref count of this instance)
* Devices::InstanceAccess devices; // increases the ref count of current instance
* // Then the object can be used, for example, to retrieve the awt device.
* // (note: ref count is not increased with GetDevice())
* AwtWin32GraphicsDevice *dev = devices->GetDevice(idx);
* dev->DoStuff();
* Data data = dev->GetData();
* return data;
* // don't need to release the reference, it's done automatically in
* // InstanceAccess destructor
* }
*
* 2. The array element will be used outside of this code block (i.e.
* saved for later use).
* {
* Devices::InstanceAccess devices; // increases the ref count
* // next call increases the ref count of the instance again
* AwtWin32GraphicsDevice *dev = devices->GetDeviceReference(idx);
* wsdo->device = dev;
* // we saved the ref to the device element, the first reference
* // will be released automatically in the InstanceAccess destructor
* }
*
* {
* wsdo->device->DoStuff(); // safe because we hold a reference
* // then, sometime later (different thread, method, whatever)
* // release the reference to the array element, which in
* // turn will decrease the ref count of the instance of Devices class
* // this element belongs to
* wsdo->device->Release();
* wsdo->device = NULL; // this reference can no longer be used
* }
*/
#include "Devices.h"
#include "Trace.h"
#include "D3DPipelineManager.h"
/* Some helper functions (from awt_MMStub.h/cpp) */
int g_nMonitorCounter;
int g_nMonitorLimit;
HMONITOR* g_hmpMonitors;
// Callback for CountMonitors below
BOOL WINAPI clb_fCountMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
{
g_nMonitorCounter ++;
return TRUE;
}
int WINAPI CountMonitors(void)
{
g_nMonitorCounter = 0;
::EnumDisplayMonitors(NULL, NULL, clb_fCountMonitors, 0L);
return g_nMonitorCounter;
}
// Callback for CollectMonitors below
BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
{
if ((g_nMonitorCounter < g_nMonitorLimit) && (NULL != g_hmpMonitors)) {
g_hmpMonitors[g_nMonitorCounter] = hMon;
g_nMonitorCounter ++;
}
return TRUE;
}
int WINAPI CollectMonitors(HMONITOR* hmpMonitors, int nNum)
{
int retCode = 0;
if (NULL != hmpMonitors) {
g_nMonitorCounter = 0;
g_nMonitorLimit = nNum;
g_hmpMonitors = hmpMonitors;
::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, 0L);
retCode = g_nMonitorCounter;
g_nMonitorCounter = 0;
g_nMonitorLimit = 0;
g_hmpMonitors = NULL;
}
return retCode;
}
BOOL WINAPI MonitorBounds(HMONITOR hmMonitor, RECT* rpBounds)
{
BOOL retCode = FALSE;
if ((NULL != hmMonitor) && (NULL != rpBounds)) {
MONITORINFOEX miInfo;
memset((void*)(&miInfo), 0, sizeof(MONITORINFOEX));
miInfo.cbSize = sizeof(MONITORINFOEX);
if (TRUE == (retCode = ::GetMonitorInfo(hmMonitor, &miInfo))) {
(*rpBounds) = miInfo.rcMonitor;
}
}
return retCode;
}
/* End of helper functions */
Devices* Devices::theInstance = NULL;
CriticalSection Devices::arrayLock;
/**
* Create a new Devices object with numDevices elements.
*/
Devices::Devices(int numDevices)
{
J2dTraceLn1(J2D_TRACE_INFO, "Devices::Devices numDevices=%d", numDevices);
this->numDevices = numDevices;
this->refCount = 0;
devices = (AwtWin32GraphicsDevice**)safe_Malloc
(numDevices * sizeof(AwtWin32GraphicsDevice *));
}
/**
* Static method which updates the array of the devices
* while holding global lock.
*
* If the update was successful, method returns TRUE,
* otherwise it returns FALSE.
*/
// static
BOOL Devices::UpdateInstance(JNIEnv *env)
{
J2dTraceLn(J2D_TRACE_INFO, "Devices::UpdateInstance");
int numScreens = CountMonitors();
HMONITOR *monHds = (HMONITOR *)safe_Malloc(numScreens * sizeof(HMONITOR));
if (numScreens != CollectMonitors(monHds, numScreens)) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"Devices::UpdateInstance: Failed to get all "\
"monitor handles.");
free(monHds);
return FALSE;
}
Devices *newDevices = new Devices(numScreens);
// This way we know that the array will not be disposed of
// at least until we replaced it with a new one.
newDevices->AddReference();
// Create all devices first, then initialize them. This allows
// correct configuration of devices after contruction of the
// primary device (which may not be device 0).
AwtWin32GraphicsDevice** rawDevices = newDevices->GetRawArray();
int i;
for (i = 0; i < numScreens; ++i) {
J2dTraceLn2(J2D_TRACE_VERBOSE, " hmon[%d]=0x%x", i, monHds[i]);
rawDevices[i] = new AwtWin32GraphicsDevice(i, monHds[i], newDevices);
}
for (i = 0; i < numScreens; ++i) {
rawDevices[i]->Initialize();
}
{
CriticalSection::Lock l(arrayLock);
// install the new devices array
Devices *oldDevices = theInstance;
theInstance = newDevices;
if (oldDevices) {
// Invalidate the devices with indexes out of the new set of
// devices. This doesn't cover all cases when the device
// might should be invalidated (like if it's not the last device
// that was removed), but it will have to do for now.
int oldNumScreens = oldDevices->GetNumDevices();
int newNumScreens = theInstance->GetNumDevices();
J2dTraceLn(J2D_TRACE_VERBOSE, " Invalidating removed devices");
for (int i = newNumScreens; i < oldNumScreens; i++) {
// removed device, needs to be invalidated
J2dTraceLn1(J2D_TRACE_WARNING,
"Devices::UpdateInstance: device removed: %d", i);
oldDevices->GetDevice(i)->Invalidate(env);
}
// Now that we have a new array in place, remove this (possibly the
// last) reference to the old instance.
oldDevices->Release();
}
D3DPipelineManager::HandleAdaptersChange((HMONITOR*)monHds,
theInstance->GetNumDevices());
}
free(monHds);
return TRUE;
}
/**
* Add a reference to the array. This could be someone that wants
* to register interest in the array, versus someone that actually
* holds a reference to an array item (in which case they would
* call GetDeviceReference() instead). This mechanism can keep
* the array from being deleted when it has no elements being
* referenced but is still a valid array to use for new elements
* or references.
*/
void Devices::AddReference()
{
J2dTraceLn(J2D_TRACE_INFO, "Devices::AddReference");
CriticalSection::Lock l(arrayLock);
refCount++;
J2dTraceLn1(J2D_TRACE_VERBOSE, " refCount=%d", refCount);
}
/**
* Static method for getting a reference
* to the instance of the current devices array.
* The instance will automatically have reference count increased.
*
* The caller thus must call Release() when done dealing with
* the array.
*/
// static
Devices* Devices::GetInstance()
{
J2dTraceLn(J2D_TRACE_INFO, "Devices::GetInstance");
CriticalSection::Lock l(arrayLock);
if (theInstance != NULL) {
theInstance->AddReference();
} else {
J2dTraceLn(J2D_TRACE_ERROR,
"Devices::GetInstance NULL instance");
}
return theInstance;
}
/**
* Retrieve a pointer to an item in the array and register a
* reference to the array. This increases the refCount of the
* instance, used to track when the array can be deleted.
*
* This method must be called while holding a reference to the instance.
*
* If adjust parameter is true (default), adjust the index into the
* devices array so that it falls within the current devices array.
* This is needed because the devices array can be changed at any
* time, and the index may be from the old array. But in some
* cases we prefer to know that the index is incorrect.
*
*/
AwtWin32GraphicsDevice *Devices::GetDeviceReference(int index,
BOOL adjust)
{
J2dTraceLn2(J2D_TRACE_INFO,
"Devices::GetDeviceReference index=%d adjust?=%d",
index, adjust);
AwtWin32GraphicsDevice * ret = GetDevice(index, adjust);
if (ret != NULL) {
AddReference();
}
return ret;
}
/**
* Returns a reference to a device with the passed index.
*
* This method does not increase the ref count of the Devices instance.
*
* This method must be called while holding a reference to the instance.
*/
AwtWin32GraphicsDevice *Devices::GetDevice(int index, BOOL adjust)
{
J2dTraceLn2(J2D_TRACE_INFO,
"Devices::GetDevice index=%d adjust?=%d",
index, adjust);
if (index < 0 || index >= numDevices) {
if (!adjust) {
J2dTraceLn1(J2D_TRACE_WARNING,
"Devices::GetDevice: "\
"incorrect index %d, returning NULL.", index);
return NULL;
}
J2dTraceLn1(J2D_TRACE_WARNING,
"Devices::GetDevice: "\
"adjusted index %d to 0.", index);
index = 0;
}
return devices[index];
}
/**
* Returns a raw reference to the incapsulated array.
*
* This method does not increase the ref count of the Devices instance.
*
* This method must be called while holding a reference to the instance.
*/
AwtWin32GraphicsDevice **Devices::GetRawArray()
{
J2dTraceLn(J2D_TRACE_INFO, "Devices::GetRawArray");
return devices;
}
/**
* Decreases the reference count of the array. If the refCount goes to 0,
* then there are no more references to the array and all of the
* array elements, the array itself, and this object can be destroyed.
*
* Returns the number of references left after it was decremented.
*/
int Devices::Release()
{
J2dTraceLn(J2D_TRACE_INFO, "Devices::Release");
CriticalSection::Lock l(arrayLock);
int refs = --refCount;
J2dTraceLn1(J2D_TRACE_VERBOSE, " refCount=%d", refs);
if (refs == 0) {
J2dTraceLn(J2D_TRACE_VERBOSE, " disposing the array");
if (devices != NULL) {
for (int i = 0; i < numDevices; ++i) {
if (devices[i] != NULL) {
delete devices[i];
devices[i] = NULL;
}
}
free(devices);
// null out data, can help with debugging
devices = NULL;
}
// it's safe to delete the instance and only
// then release the static lock
delete this;
// for safety return immediately after committing suicide
// (note: can not reference refCount here!)
return refs;
} else if (refs < 0) {
J2dTraceLn1(J2D_TRACE_ERROR,
"Devices::Release: Negative ref count! refCount=%d",
refs);
}
return refs;
}