PDA

View Full Version : OS/X how to get physical display handle



hvengel
22nd June 2007, 20:06
I need to be able to manipulate the video card gamma tables in my cross platform application. This code needs to be able to do this for each display on multi-display systems based on the location of the dialog that the user is interacting with. I have this working on X11 and Windows and on OS/X the code is far enough along that it will manipulate the gamma table of CGMainDisplayID but I have been unable to find any documentation or any other source on how to get from something I can get from Qt such as QDesktopWidget::screenNumber() or this -> winID() to a handle that can be used to manipulate the video card gamma for a specific display.

The code in my dialog looks like this:


#if defined (Q_WS_WIN)
screen = (int) this -> winId ();
#elif defined(Q_WS_MAC) // FIXME
screen = (int) this -> winId ();
#elif defined(Q_WS_X11) // this is an X11 system
screen = desktop -> screenNumber(pixmapColor -> mapToGlobal(pixmapColor -> pos()));
#else
#error "Your system is not supported yet. The video gamma setting code will need to be updated to support your system."
#endif[

gamma_ramp gammaRamp = new gamma_ramp(screen);
gammaRamp->setGammaRamp();
...


For X11 this is very simple to use as the above boils down to a call to XF86VidModeGetGammaRamp(...) where the screenNumber value is passed in as the second parameter. On Windows it is a little more involved since winID needs to be translated into a handle and the called code looks looks like this:


void gamma_ramp::getGammaRamp()
{
HDC hDisplayDC;
HRESULT hr;

hDisplayDC = GetDCEx((HWND) screen, 0, DCX_CLIPSIBLINGS|DCX_CACHE);
hr = GetDeviceGammaRamp(hDisplayDC, (LPVOID) ramp_buffer);
ReleaseDC((HWND) screen, hDisplayDC);

this -> convertPrivShort2ramp_type();
}

This is much closer to what needs to happen on OS/X where the code currently looks like this:


void gamma_ramp::getGammaRamp()
{
CGTableCount count;
CGDisplayErr error_code;

// FIXME only does the main display
CGDirectDisplayID dspID = CGMainDisplayID();

error_code = CGGetDisplayTransferByTable (dspID,
(CGTableCount) size,
&ramp_buffer[0],
&ramp_buffer[size],
&ramp_buffer[size*2],
&count);

for (int i=0; i < size; ++i)
{
red[i] = interpol(ramp_buffer, i, size, count);
green[i] = interpol(ramp_buffer + size, i, size, count);
blue[i] = interpol(ramp_buffer + size*2, i, size, count);
}
}

As you can see currently the OS/X code only uses the main display:



CGDirectDisplayID dspID = CGMainDisplayID();

What I need is a way to get from winID(), screenNumber() or something that is Qt supplied to a CGDirectDisplayID and I have been desperately looking for anything about how to do this. Anything about this would be helpful such as a possible source of documentation or perhaps a link to a forum that specializes in Qt OS/X. Even the programmer who worked on the OS/X code didn't have any idea how to handle this and he works on a large Qt open source project. I should add that he did get this code to the point where it builds and will work on the main display gamma table. So I am very grateful for his efforts.

marcel
22nd June 2007, 20:18
I'm not sure if you can do this solely with qt, but have you looked into what CVDisplayLinkCreateWithActiveCGDisplays&friends can do?
It returns a display link associated with all the active display. Therefore if you alter this link( gamma, or whatever), you alter all the displays.

Here are the links too:
http://developer.apple.com/documentation/GraphicsImaging/Reference/CoreVideoRef/Reference/reference.html#//apple_ref/c/func/CVDisplayLinkCreateWithActiveCGDisplays
Some samples:
http://developer.apple.com/documentation/GraphicsImaging/Conceptual/CoreVideo/CVProg_Tasks/chapter_3_section_2.html

Regards

marcel
22nd June 2007, 20:21
Oh, and of course, this is how you get the active display list and their respective IDs:
http://developer.apple.com/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/Reference/reference.html#//apple_ref/doc/uid/TP30001070-CH202-251664

Each physical display has an unique ID associated with it( by Quartz I think ). So if you get these, then you can get thei CGDirectDisplayIDs.

Actually, I think it is pretty clear:


CGGetActiveDisplayList
Provides a list of displays that are active (or drawable).
CGDisplayErr (http://developer.apple.com/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/Reference/reference.html#//apple_ref/doc/c_ref/CGDisplayErr) CGGetActiveDisplayList (
CGDisplayCount (http://developer.apple.com/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/Reference/reference.html#//apple_ref/doc/c_ref/CGDisplayCount) maxDisplays,
CGDirectDisplayID (http://developer.apple.com/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/Reference/reference.html#//apple_ref/doc/c_ref/CGDirectDisplayID) * displays,
CGDisplayCount (http://developer.apple.com/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/Reference/reference.html#//apple_ref/doc/c_ref/CGDisplayCount) * displayCount
);
Parameters
maxDisplays
The size of the displays array. This value determines the maximum number of displays that can be returned.
displays
A pointer to storage provided by the caller for an array of display IDs. On return, this storage contains a list of active displays.
displayCount
A pointer to a CGDisplayCount variable provided by the caller. On return, the variable contains the actual number of displays returned in the displays array. If displays is NULL, this variable contains the total number of active displays.
Return Value
A result code. See “Quartz Display Services Result Codes” (http://developer.apple.com/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/Reference/reference.html#//apple_ref/doc/uid/TP30001070-CH4g-187190).
Discussion
The first entry in the list of active displays is the main display. In case of mirroring, the first entry is the largest drawable display or, if all are the same size, the display with the greatest pixel depth.
Availability

Available in Mac OS X version 10.0 and later. Declared In
CGDirectDisplay.h



Regards

hvengel
22nd June 2007, 22:18
I found one source that says the following about OS/X:

"In Qt 4, every widget is an HIView, so you calling winId() will
return you the HIViewRef, you can then use Carbon functions like HIViewGetWindow() to get a WindowPtr.

In Qt 3, you can get a WindowPtr by calling handle() on the top-level widget."

But looking at the Qt3 docs I can't find handle() anywhere. So I don't trust this source. If it were correct once I had a WindowPtr I could call GetWindowBounds to get the coordinates for the window and then a call to either CGGetDisplaysWithPoint or CGGetDisplaysWithRect using the window coordinates should get the correct CGDirectDisplayID.

This is frustrating since I found it fairly easy to find OK docs about this, meaning how to map either winId() or screenNumber() to get the native display handle, for both Windows and X11 and the complete absence of any information on how this should be programmed on OS/X is frustrating. In addition, I don't have a machine I can test any of this on so I have to work with other programmers who have access to an OS/X machine. If I had an OS/X machine I could play around with these things and figure out how to make it work. I would like to be able to say to the OS/X programmer "Look here and here ... for some information about how this can be handled." But what I have is not enough that I am willing to bother them with this at this point.

I have a few more clues now about how this might be made to work. Your information was helpful. Maybe someone else here will see this and either confirm that the above is on the right track or that it is off base.

hvengel
3rd January 2009, 19:51
For those interested in how this issue is solved I have this working. So I will post some code snippets.

The header file looks like this:


typedef double ramp_type;

#ifdef __WIN32__
#include <windows.h>
typedef unsigned short ramp_type_private;
typedef HDC displayHandle;
#elif defined (__APPLE__)
#ifndef DEBUG
#define DEBUG 0
#endif
#include <Carbon/Carbon.h>
#include <CoreServices/CoreServices.h>
#include <IOKit/Graphics/IOGraphicsLib.h>
typedef CGGammaValue ramp_type_private;
typedef CGDirectDisplayID displayHandle;
#elif defined (UNIX)
typedef unsigned short ramp_type_private;
typedef int displayHandle;
#else
#error "no typedef for ramp_type_private and displayHandle are available for your platform"
#endif

class gamma_ramp
{
public:
int size;
ramp_type *red, *green, *blue;

gamma_ramp(int screen, int leftIn, int topIn); // screen is ignored on OS/X and Windows
// leftIn and topIn are ignored on X11

~gamma_ramp();
void getGammaRamp(); // returns size
void setGammaRamp();

void setLinearGammaRamp();

private:
int getGammaRampSize();
void convertPrivShort2ramp_type();
void convert_ramp_type2short();

ramp_type_private* ramp_buffer;
int screen, left, top;
displayHandle display;
};

The main thing of interest is how the code gets the handle to the display device so that it manipulates the correct video card gamma table.

For OS/X it looks like this:


CGPoint point;

CGDirectDisplayID getDisplay()
{
const int maxDisplays = 64; // 64 should be enough for any system
CGDisplayErr displayStatus;
CGDisplayCount displayCount; // Total number of display IDs
CGDirectDisplayID displayIDs[maxDisplays]; // Array of display IDs

displayStatus = CGGetDisplaysWithPoint ( point, maxDisplays, displayIDs, &displayCount);
if (displayStatus != kCGErrorSuccess || displayCount!= 1)
{
printf("CGGetDisplaysWithPoint returned error or the wrong number of displays\n");
return NULL;
}
else
return displayIDs[0];
}

gamma_ramp::gamma_ramp(int Screen, int leftIn, int topIn)
{
// screen = Screen;
left = leftIn;
top = topIn;
point.y = float(top);
point.x = float(left);

display = getDisplay();
screen = 0;
if (display == NULL)
{
ramp_buffer = 0;
red = 0;
green = 0;
blue = 0;
printf("gamma_ramp::gamma_ramp - error failed to get display handle");
}
else // do what needs to be done to set up the gamma tables
}

On Windows it looks like this:


static int numMatchingDisplays;
static RECT clipRegion;

static BOOL CALLBACK MonitorEnumProc(
HMONITOR hMonitor, // handle to display monitor
HDC hdcMonitor, // NULL, because EnumDisplayMonitors hdc is NULL
LPRECT displayCoordintes, // Virtual screen coordinates of this monitor
LPARAM name // Context data used to pass back display name in our case
)
{
MONITORINFOEX pmi;

if (displayCoordintes -> left <= clipRegion.left &&
displayCoordintes -> bottom >= clipRegion.top &&
displayCoordintes -> right >= clipRegion.left &&
displayCoordintes -> top <= clipRegion.top)
{
// display contains the widget
pmi.cbSize = sizeof(MONITORINFOEX);
if (GetMonitorInfo(hMonitor, (MONITORINFO *)&pmi) == 0)
{
printf("MonitorEnumProc - get_displays failed GetMonitorInfo\n");
return FALSE;
}
else // it worked we have the device we want
{
numMatchingDisplays++;
return TRUE;
}
}
else // this is OK as the device is not the one we want
{
return TRUE;
}
}

HDC getDisplay()
{

BOOL (WINAPI* pEnumDisplayDevices)(PVOID,DWORD,PVOID,DWORD);
char name[256];
HDC dispHand;

pEnumDisplayDevices = (BOOL (WINAPI*)(PVOID,DWORD,PVOID,DWORD)) GetProcAddress(LoadLibrary("USER32"), "EnumDisplayDevicesA");

numMatchingDisplays = 0;
if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&name) == 0)
{
printf("getDisplay - EnumDisplayMonitors failed\n");
return NULL;
}

if (numMatchingDisplays != 1 ) // there is a problem we should only have one
{
printf("getDisplay - wrong number of displays %i", numMatchingDisplays);
return NULL;
}
else
{
if ((dispHand = CreateDC(name, name, NULL, NULL)) == NULL)
{
printf("getDisplay - CreateDC failed");
return NULL;
}
else
{
return dispHand;
}
}
}


gamma_ramp::gamma_ramp(int Screen, int leftIn, int topIn)
{
screen = Screen;
left = leftIn;
top = topIn;
clipRegion.left = left;
clipRegion.top = top;

display = getDisplay();
if (display == NULL)
{
ramp_buffer = 0;
red = 0;
green = 0;
blue = 0;
printf("gamma_ramp::gamma_ramp - error failed to get display handle");
}
else // set up the gamma tables
}

On X11 I just pass in the desktop -> screenNumber since X11 uses this to allow access to the video card gamma table. For for OS/X and Windows I needed to pass in a set of coordinates that were located on the display I wanted to manipulate the gamma table for and then parse through a list of devices to find the one that contained these coordinates and then get it's handle to use internal in the gamma_ramp class.