/* See LICENSE file for copyright and license details. */
#define IN_LIBGAMMA_W32_GDI
#include "common.h"
/* This file very sloppily translates Windows GDI calls to X RandR calls.
* It should by no means be used, without additional modification, as a
* part of a compatibility layer. The purpose of this file is only to make
* it possible to test for logical errors in Windows specific code on
* a Linux system under X.
*/
#ifndef HAVE_LIBGAMMA_METHOD_X_RANDR
/* Use dummy translation */
/**
* Get the device context for a window or the entire screen
*
* @param hWnd The windows, `NULL` for the entire screen
* @return The device context
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx
*/
HDC
GetDC(HWND hWnd)
{
/* Just a non-NULL value */
(void) hWnd;
return (HDC *)16;
}
/**
* Free a device context
*
* @param hWnd The window whose device context is `hDC`, `NULL` for the entire screen
* @param hDC The device context to free
* @return Whether the call was successful
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx
*/
int
ReleaseDC(HWND hWnd, HDC hDC)
{
/* Always successful */
(void) hWnd;
(void) hDC;
return 1;
}
/**
* Get information (capabilities) for a device context
*
* @param hDC The device context
* @param nIndex The information to retrieve, may be `COLORMGMTCAPS`
* @return The details of the queried information, can return `CM_GAMMA_RAMP`
* if `nIndex` is `COLORMGMTCAPS`
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx
*/
int
GetDeviceCaps(HDC hDC, int nIndex)
{
/* We have gamma ramps if the user asks for colour management capabilities */
(void) hDC;
return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS;
}
/**
* Set the gamma ramps for a device
*
* @param hDC The device context
* @param lpRamp The gamma ramps joined in the order: red, green, blue
* This is a `WORD*` casted to `void*`
* @return Whether the call was successful
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx
*/
BOOL
SetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp)
{
/* Always successful */
(void) hDC;
(void) lpRamp;
return TRUE;
}
/**
* Get the current gamma ramps for a device
*
* @param hDC The device context
* @param lpRamp The output for the gamma ramps joined in the order: red, green, blue
* This is a `WORD *` casted to `void *`
* @return Whether the call was successful
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx
*/
BOOL
GetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp)
{
/* Always successful */
(void) hDC;
(void) lpRamp;
return TRUE;
}
/**
* Get the context for a device
*
* @param lpszDriver The driver or a display device, use "DISPLAY" if you want a display
* @param lpszDevice The name of the device. If you want a display use can use the member
* name `DeviceName` in the third parameter, an output parameter, of
* `EnumDisplayDevices`
* @param lpszOutput We will always use `NULL` here
* @param lpInitData We will always use `NULL` here; this should actually by a `const DEVMODE *`
* @return The context for the device
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx
*/
HDC
CreateDC(LPCTSTR restrict lpszDriver, LPCTSTR restrict lpszDevice, LPCTSTR restrict lpszOutput, const void *restrict lpInitData)
{
/* `NULL` if not asking for a CRTC, otherwise a non-NULL value */
(void) lpszOutput;
(void) lpInitData;
(void) lpszDevice;
return !strcmp(lpszDriver, "DISPLAY") ? (HDC *)16 : NULL;
}
/**
* Get a display device by its name or index
*
* @param lpDevice The name of the device, use `NULL` to base the call on `iDevNum` instead
* @param iDevNum The index of the device
* @param lpDisplayDevice Output for the found device
* @param dwFlags Flags, we will always use zero
* @return Whether the call was successful; zero is also returned if `iDevNum`
* is greater than the largest device index on the system
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx
*/
BOOL
EnumDisplayDevices(LPCTSTR lpDevice, restrict DWORD iDevNum, PDISPLAY_DEVICE restrict lpDisplayDevice, DWORD dwFlags)
{
(void) dwFlags;
/* Check correctness of `lpDevice` */
if (lpDevice) {
fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n");
abort();
return FALSE;
}
/* Pretend that we have two CRTC:s */
if (iDevNum >= 2)
return FALSE;
/* Check correctness of `lpDisplayDevice` */
if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE)) {
fprintf(stderr, "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n");
abort();
return FALSE;
}
/* Store an arbitrary name for the monitor */
strcmp(lpDisplayDevice->DeviceName, "some monitor");
/* The connector is always enabled */
lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE;
return TRUE;
}
#else
/* Use translation to X RandR */
#include <xcb/xcb.h>
#include <xcb/randr.h>
/**
* The gamma ramp size that devices will
* always have in Windows GDI
*/
#define GAMMA_RAMP_SIZE 256
/**
* Connection to the X RandR display
*/
static xcb_connection_t *restrict connection = NULL;
/**
* Resouces for the screen
*
* We only have one screen, again this
* is a very sloppy compatibility layer
*/
static xcb_randr_get_screen_resources_current_reply_t *restrict res_reply = NULL;
/**
* The number of available CRTC:s, -1 if not known yet
*/
static ssize_t crtc_count = -1;
/**
* List of X RandR CRTC:s
*/
static xcb_randr_crtc_t *restrict crtcs = NULL;
/**
* The number of opened CRTC:s
*/
static size_t dc_count = 0;
/**
* Get the device context for a window or the entire screen
*
* @param hWnd The windows, `NULL` for the entire screen
* @return The device context
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd144871(v=vs.85).aspx
*/
HDC
GetDC(HWND hWnd)
{
/* Return the primary CRTC */
(void) hWnd;
return CreateDC(TEXT("DISPLAY"), "0", NULL, NULL);
}
/**
* Free a device context
*
* @param hWnd The window whose device context is `hDC`, `NULL` for the entire screen
* @param hDC The device context to free
* @return Whether the call was successful
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd162920(v=vs.85).aspx
*/
int
ReleaseDC(HWND hWnd, HDC hDC)
{
/* Disconnect from the RandR display when all monitors have been closed */
(void) hWnd;
(void) hDC;
if (!dc_count--) {
if (connection) {
xcb_disconnect(connection);
connection = NULL;
}
free(res_reply);
res_reply = NULL;
}
return 1;
}
/**
* Get information (capabilities) for a device context
*
* @param hDC The device context
* @param nIndex The information to retrieve, may be `COLORMGMTCAPS`
* @return The details of the queried information, can return `CM_GAMMA_RAMP`
* if `nIndex` is `COLORMGMTCAPS`
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd144877(v=vs.85).aspx
*/
int
GetDeviceCaps(HDC hDC, int nIndex)
{
/* We have gamma ramps if the user asks for colour management capabilities */
(void) hDC;
return CM_GAMMA_RAMP + nIndex - COLORMGMTCAPS;
}
/* xcb violates the rule to never return `struct`:s */
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Waggregate-return"
#endif
/**
* Set the gamma ramps for a device
*
* @param hDC The device context
* @param lpRamp The gamma ramps joined in the order: red, green, blue
* This is a `WORD *` casted to `void *`
* @return Whether the call was successful
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd372194(v=vs.85).aspx
*/
BOOL
SetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp)
{
/* We assume that our gamma ramps are of the same size
* as used by Windows GDI (we are so sloppy) */
uint16_t *ramp = lpRamp;
xcb_void_cookie_t gamma_cookie = xcb_randr_set_crtc_gamma_checked(connection, *(xcb_randr_crtc_t *)hDC, GAMMA_RAMP_SIZE,
&ramp[0 * GAMMA_RAMP_SIZE], &ramp[1 * GAMMA_RAMP_SIZE],
&ramp[2 * GAMMA_RAMP_SIZE]);
xcb_generic_error_t *error = xcb_request_check(connection, gamma_cookie);
return !error ? TRUE : FALSE;
}
/**
* Get the current gamma ramps for a device
*
* @param hDC The device context
* @param lpRamp The output for the gamma ramps joined in the order: red, green, blue
* This is a `WORD *` casted to `void *`
* @return Whether the call was successful
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd316946(v=vs.85).aspx
*/
BOOL
GetDeviceGammaRamp(HDC hDC, LPVOID restrict lpRamp)
{
xcb_randr_get_crtc_gamma_cookie_t gamma_cookie;
xcb_randr_get_crtc_gamma_reply_t *restrict gamma_reply;
xcb_generic_error_t *error;
uint16_t *restrict ramp = lpRamp;
/* Read current gamma ramps */
gamma_cookie = xcb_randr_get_crtc_gamma(connection, *(xcb_randr_crtc_t *)hDC);
gamma_reply = xcb_randr_get_crtc_gamma_reply(connection, gamma_cookie, &error);
if (error)
return FALSE;
/* Copy the ramps into the output parameter (coalesced) */
memcpy(&ramp[0 * GAMMA_RAMP_SIZE], xcb_randr_get_crtc_gamma_red(gamma_reply), GAMMA_RAMP_SIZE * sizeof(*ramp));
memcpy(&ramp[1 * GAMMA_RAMP_SIZE], xcb_randr_get_crtc_gamma_green(gamma_reply), GAMMA_RAMP_SIZE * sizeof(*ramp));
memcpy(&ramp[2 * GAMMA_RAMP_SIZE], xcb_randr_get_crtc_gamma_blue(gamma_reply), GAMMA_RAMP_SIZE * sizeof(*ramp));
free(gamma_reply);
return TRUE;
}
/**
* Get the context for a device
*
* @param lpszDriver The driver or a display device, use "DISPLAY" if you want a display
* @param lpszDevice The name of the device. If you want a display use can use the member
* name `DeviceName` in the third parameter, an output parameter, of
* `EnumDisplayDevices`
* @param lpszOutput We will always use `NULL` here
* @param lpInitData We will always use `NULL` here; this should actually by a `const DEVMODE *`
* @return The context for the device
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd183490(v=vs.85).aspx
*/
HDC
CreateDC(LPCTSTR restrict lpszDriver, LPCTSTR restrict lpszDevice, LPCTSTR restrict lpszOutput, const void *restrict lpInitData)
{
int crtc_index = atoi(lpszDevice);
xcb_generic_error_t *error;
xcb_screen_iterator_t iter;
xcb_randr_get_screen_resources_current_cookie_t res_cookie;
(void) lpszOutput;
(void) lpInitData;
/* Check correctness of input */
if (strcmp(lpszDriver, "DISPLAY"))
return NULL;
/* Connect to the display and get screen data if not already done so */
if (!dc_count) {
/* Connect to the display */
connection = xcb_connect(NULL, NULL);
/* Get the first screen */
iter = xcb_setup_roots_iterator(xcb_get_setup(connection));
/* Get the resources of the screen */
res_cookie = xcb_randr_get_screen_resources_current(connection, iter.data->root);
res_reply = xcb_randr_get_screen_resources_current_reply(connection, res_cookie, &error);
if (error) {
fprintf(stderr, "Failed to open X connection\n");
xcb_disconnect(connection);
crtc_count = -1;
return NULL;
}
/* Get the number of CRTC:s */
crtc_count = res_reply->num_crtcs;
/* Get the CRTC ID:s */
crtcs = xcb_randr_get_screen_resources_current_crtcs(res_reply);
}
/* Was the index too high */
if (crtc_index >= crtc_count) {
/* Disconnect and release resouces and mark that
* we do not know the number of available CRTC:s
* if we have not opened any monitors yet */
if (!dc_count) {
xcb_disconnect(connection);
free(res_reply);
res_reply = NULL;
crtc_count = -1;
}
return NULL;
}
/* We have opened a new CRTC */
dc_count++;
/* Return the ID of the CRTC */
return &crtcs[crtc_index];
}
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
/**
* Get a display device by its name or index
*
* @param lpDevice The name of the device, use `NULL` to base the call on `iDevNum` instead
* @param iDevNum The index of the device
* @param lpDisplayDevice Output for the found device
* @param dwFlags Flags, we will always use zero
* @return Whether the call was successful; zero is also returned if `iDevNum`
* is greater than the largest device index on the system
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/dd162609(v=vs.85).aspx
*/
BOOL
EnumDisplayDevices(LPCTSTR restrict lpDevice, DWORD iDevNum, PDISPLAY_DEVICE restrict lpDisplayDevice, DWORD dwFlags)
{
size_t count = (size_t)crtc_count;
(void) dwFlags;
/* Check the correctness of the input */
if (lpDevice) {
fprintf(stderr, "lpDevice (argument 1) for EnumDisplayDevices should be NULL\n");
abort();
return FALSE;
}
/* Do we know how many CRTC:s are available? */
if (crtc_count < 0) {
/* If not open the first CRTC so that will be figured out */
if (!GetDC(NULL))
return FALSE;
count = (size_t)crtc_count;
/* Close the primary monitor that we just closed */
ReleaseDC(NULL, NULL);
}
/* Check that the request CRTC exists */
if (iDevNum >= count)
return FALSE;
/* Check the correctness of the input */
if (lpDisplayDevice->cb != sizeof(DISPLAY_DEVICE)) {
fprintf(stderr, "lpDisplayDevice->cb for EnumDisplayDevices is not sizeof(DISPLAY_DEVICE)\n");
abort();
return FALSE;
}
/* Store name for the CRTC */
sprintf(lpDisplayDevice->DeviceName, "%lu", (unsigned long int)iDevNum);
/* The connector that the CRTC belongs to is enabled */
lpDisplayDevice->StateFlags = DISPLAY_DEVICE_ACTIVE;
return TRUE;
}
#endif