winex11.drv: Migrate XVidMode display settings handler to a new interface.

Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zhiyi Zhang 2020-07-27 16:01:38 +08:00 committed by Alexandre Julliard
parent 5491e939bc
commit 2116b49717
1 changed files with 173 additions and 94 deletions

View File

@ -2,6 +2,7 @@
* DirectDraw XVidMode interface
*
* Copyright 2001 TransGaming Technologies, Inc.
* Copyright 2020 Zhiyi Zhang for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -21,10 +22,14 @@
#include "config.h"
#include "wine/port.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#define NONAMELESSSTRUCT
#define NONAMELESSUNION
#include "x11drv.h"
#ifdef HAVE_X11_EXTENSIONS_XF86VMODE_H
@ -38,6 +43,7 @@
#include "wingdi.h"
#include "wine/debug.h"
#include "wine/heap.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(xvidmode);
@ -52,11 +58,6 @@ static int xf86vm_gammaramp_size;
static BOOL xf86vm_use_gammaramp;
#endif /* X_XF86VidModeSetGammaRamp */
static struct x11drv_mode_info *dd_modes;
static unsigned int dd_mode_count;
static XF86VidModeModeInfo** real_xf86vm_modes;
static unsigned int real_xf86vm_mode_count;
#define MAKE_FUNCPTR(f) static typeof(f) * p##f;
MAKE_FUNCPTR(XF86VidModeGetAllModeLines)
MAKE_FUNCPTR(XF86VidModeGetModeLine)
@ -76,86 +77,182 @@ MAKE_FUNCPTR(XF86VidModeSetGammaRamp)
#endif
#undef MAKE_FUNCPTR
static void convert_modeinfo( const XF86VidModeModeInfo *mode)
{
int rate;
if (mode->htotal!=0 && mode->vtotal!=0)
rate = mode->dotclock * 1000 / (mode->htotal * mode->vtotal);
else
rate = 0;
X11DRV_Settings_AddOneMode(mode->hdisplay, mode->vdisplay, 0, rate);
}
static void convert_modeline(int dotclock, const XF86VidModeModeLine *mode,
struct x11drv_mode_info *info, unsigned int bpp)
{
info->width = mode->hdisplay;
info->height = mode->vdisplay;
if (mode->htotal!=0 && mode->vtotal!=0)
info->refresh_rate = dotclock * 1000 / (mode->htotal * mode->vtotal);
else
info->refresh_rate = 0;
TRACE(" width=%d, height=%d, refresh=%d\n",
info->width, info->height, info->refresh_rate);
info->bpp = bpp;
}
static int XVidModeErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
{
return 1;
}
static int X11DRV_XF86VM_GetCurrentMode(void)
/* XF86VidMode display settings handler */
static BOOL xf86vm_get_id(const WCHAR *device_name, ULONG_PTR *id)
{
XF86VidModeModeLine line;
int dotclock;
unsigned int i;
struct x11drv_mode_info cmode;
DWORD dwBpp = screen_bpp;
WCHAR primary_adapter[CCHDEVICENAME];
TRACE("Querying XVidMode current mode\n");
pXF86VidModeGetModeLine(gdi_display, DefaultScreen(gdi_display), &dotclock, &line);
convert_modeline(dotclock, &line, &cmode, dwBpp);
for (i=0; i<dd_mode_count; i++)
if (memcmp(&dd_modes[i], &cmode, sizeof(cmode)) == 0) {
TRACE("mode=%d\n", i);
return i;
}
ERR("In unknown mode, returning default\n");
return 0;
if (!get_primary_adapter( primary_adapter ))
return FALSE;
/* XVidMode only supports changing the primary adapter settings.
* For non-primary adapters, an id is still provided but getting
* and changing non-primary adapters' settings will be ignored. */
*id = !lstrcmpiW( device_name, primary_adapter ) ? 1 : 0;
return TRUE;
}
static LONG X11DRV_XF86VM_SetCurrentMode(int mode)
static void add_xf86vm_mode(DEVMODEW *mode, DWORD depth, const XF86VidModeModeInfo *mode_info)
{
DWORD dwBpp = screen_bpp;
/* only set modes from the original color depth */
if (dwBpp != dd_modes[mode].bpp)
mode->dmSize = sizeof(*mode);
mode->dmDriverExtra = sizeof(mode_info);
mode->dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFLAGS;
if (mode_info->htotal && mode_info->vtotal)
{
FIXME("Cannot change screen BPP from %d to %d\n", dwBpp, dd_modes[mode].bpp);
mode->dmFields |= DM_DISPLAYFREQUENCY;
mode->dmDisplayFrequency = mode_info->dotclock * 1000 / (mode_info->htotal * mode_info->vtotal);
}
mode->u1.s2.dmDisplayOrientation = DMDO_DEFAULT;
mode->dmBitsPerPel = depth;
mode->dmPelsWidth = mode_info->hdisplay;
mode->dmPelsHeight = mode_info->vdisplay;
mode->u2.dmDisplayFlags = 0;
memcpy((BYTE *)mode + sizeof(*mode), &mode_info, sizeof(mode_info));
}
mode = mode % real_xf86vm_mode_count;
TRACE("Resizing X display to %dx%d\n",
real_xf86vm_modes[mode]->hdisplay, real_xf86vm_modes[mode]->vdisplay);
pXF86VidModeSwitchToMode(gdi_display, DefaultScreen(gdi_display), real_xf86vm_modes[mode]);
static BOOL xf86vm_get_modes(ULONG_PTR id, DWORD flags, DEVMODEW **new_modes, UINT *mode_count)
{
INT xf86vm_mode_idx, xf86vm_mode_count;
XF86VidModeModeInfo **xf86vm_modes;
UINT depth_idx, mode_idx = 0;
DEVMODEW *modes, *mode;
SIZE_T size;
BYTE *ptr;
Bool ret;
X11DRV_expect_error(gdi_display, XVidModeErrorHandler, NULL);
ret = pXF86VidModeGetAllModeLines(gdi_display, DefaultScreen(gdi_display), &xf86vm_mode_count, &xf86vm_modes);
if (X11DRV_check_error() || !ret || !xf86vm_mode_count)
return FALSE;
/* Put a XF86VidModeModeInfo ** at the start to store the XF86VidMode modes pointer */
size = sizeof(XF86VidModeModeInfo **);
/* Display modes in different color depth, with a XF86VidModeModeInfo * at the end of each
* DEVMODEW as driver private data */
size += (xf86vm_mode_count * DEPTH_COUNT) * (sizeof(DEVMODEW) + sizeof(XF86VidModeModeInfo *));
ptr = heap_alloc_zero(size);
if (!ptr)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
memcpy(ptr, &xf86vm_modes, sizeof(xf86vm_modes));
modes = (DEVMODEW *)(ptr + sizeof(xf86vm_modes));
for (depth_idx = 0; depth_idx < DEPTH_COUNT; ++depth_idx)
{
for (xf86vm_mode_idx = 0; xf86vm_mode_idx < xf86vm_mode_count; ++xf86vm_mode_idx)
{
mode = (DEVMODEW *)((BYTE *)modes + (sizeof(DEVMODEW) + sizeof(XF86VidModeModeInfo *)) * mode_idx++);
add_xf86vm_mode(mode, depths[depth_idx], xf86vm_modes[xf86vm_mode_idx]);
}
}
*new_modes = modes;
*mode_count = mode_idx;
return TRUE;
}
static void xf86vm_free_modes(DEVMODEW *modes)
{
XF86VidModeModeInfo **xf86vm_modes;
if (modes)
{
assert(modes[0].dmDriverExtra == sizeof(XF86VidModeModeInfo *));
memcpy(&xf86vm_modes, (BYTE *)modes - sizeof(xf86vm_modes), sizeof(xf86vm_modes));
XFree(xf86vm_modes);
}
heap_free(modes);
}
static BOOL xf86vm_get_current_mode(ULONG_PTR id, DEVMODEW *mode)
{
XF86VidModeModeLine xf86vm_mode;
INT dotclock;
Bool ret;
mode->dmFields = DM_DISPLAYORIENTATION | DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT |
DM_DISPLAYFLAGS | DM_DISPLAYFREQUENCY | DM_POSITION;
mode->u1.s2.dmDisplayOrientation = DMDO_DEFAULT;
mode->u2.dmDisplayFlags = 0;
mode->u1.s2.dmPosition.x = 0;
mode->u1.s2.dmPosition.y = 0;
if (id != 1)
{
FIXME("Non-primary adapters are unsupported.\n");
mode->dmBitsPerPel = 0;
mode->dmPelsWidth = 0;
mode->dmPelsHeight = 0;
mode->dmDisplayFrequency = 0;
return TRUE;
}
X11DRV_expect_error(gdi_display, XVidModeErrorHandler, NULL);
ret = pXF86VidModeGetModeLine(gdi_display, DefaultScreen(gdi_display), &dotclock, &xf86vm_mode);
if (X11DRV_check_error() || !ret)
return FALSE;
mode->dmBitsPerPel = screen_bpp;
mode->dmPelsWidth = xf86vm_mode.hdisplay;
mode->dmPelsHeight = xf86vm_mode.vdisplay;
if (xf86vm_mode.htotal && xf86vm_mode.vtotal)
mode->dmDisplayFrequency = dotclock * 1000 / (xf86vm_mode.htotal * xf86vm_mode.vtotal);
else
mode->dmDisplayFrequency = 0;
if (xf86vm_mode.privsize)
XFree(xf86vm_mode.private);
return TRUE;
}
static LONG xf86vm_set_current_mode(ULONG_PTR id, DEVMODEW *mode)
{
XF86VidModeModeInfo *xf86vm_mode;
Bool ret;
if (id != 1)
{
FIXME("Non-primary adapters are unsupported.\n");
return DISP_CHANGE_SUCCESSFUL;
}
if (is_detached_mode(mode))
{
FIXME("Detaching adapters is unsupported.\n");
return DISP_CHANGE_SUCCESSFUL;
}
if (mode->dmFields & DM_BITSPERPEL && mode->dmBitsPerPel != screen_bpp)
WARN("Cannot change screen bit depth from %dbits to %dbits!\n", screen_bpp, mode->dmBitsPerPel);
assert(mode->dmDriverExtra == sizeof(XF86VidModeModeInfo *));
memcpy(&xf86vm_mode, (BYTE *)mode + sizeof(*mode), sizeof(xf86vm_mode));
X11DRV_expect_error(gdi_display, XVidModeErrorHandler, NULL);
ret = pXF86VidModeSwitchToMode(gdi_display, DefaultScreen(gdi_display), xf86vm_mode);
if (X11DRV_check_error() || !ret)
return DISP_CHANGE_FAILED;
#if 0 /* it is said that SetViewPort causes problems with some X servers */
pXF86VidModeSetViewPort(gdi_display, DefaultScreen(gdi_display), 0, 0);
#else
XWarpPointer(gdi_display, None, DefaultRootWindow(gdi_display), 0, 0, 0, 0, 0, 0);
#endif
XSync(gdi_display, False);
X11DRV_DisplayDevices_Update( TRUE );
XFlush(gdi_display);
return DISP_CHANGE_SUCCESSFUL;
}
void X11DRV_XF86VM_Init(void)
{
struct x11drv_settings_handler xf86vm_handler;
void *xvidmode_handle;
Bool ok;
int nmodes;
unsigned int i;
if (xf86vm_major) return; /* already initialized? */
@ -207,35 +304,17 @@ void X11DRV_XF86VM_Init(void)
}
#endif /* X_XF86VidModeSetGammaRamp */
/* retrieve modes */
if (usexvidmode && !is_virtual_desktop())
{
X11DRV_expect_error(gdi_display, XVidModeErrorHandler, NULL);
ok = pXF86VidModeGetAllModeLines(gdi_display, DefaultScreen(gdi_display), &nmodes, &real_xf86vm_modes);
if (X11DRV_check_error() || !ok) return;
}
else return; /* In desktop mode, do not switch resolution... But still use the Gamma ramp stuff */
if (!usexvidmode)
return;
TRACE("XVidMode modes: count=%d\n", nmodes);
real_xf86vm_mode_count = nmodes;
dd_modes = X11DRV_Settings_SetHandlers("XF86VidMode",
X11DRV_XF86VM_GetCurrentMode,
X11DRV_XF86VM_SetCurrentMode,
nmodes, 1);
/* convert modes to x11drv_mode_info format */
for (i=0; i<real_xf86vm_mode_count; i++)
{
convert_modeinfo(real_xf86vm_modes[i]);
}
/* add modes for different color depths */
X11DRV_Settings_AddDepthModes();
dd_mode_count = X11DRV_Settings_GetModeCount();
TRACE("Available DD modes: count=%d\n", dd_mode_count);
TRACE("Enabling XVidMode\n");
xf86vm_handler.name = "XF86VidMode";
xf86vm_handler.priority = 100;
xf86vm_handler.get_id = xf86vm_get_id;
xf86vm_handler.get_modes = xf86vm_get_modes;
xf86vm_handler.free_modes = xf86vm_free_modes;
xf86vm_handler.get_current_mode = xf86vm_get_current_mode;
xf86vm_handler.set_current_mode = xf86vm_set_current_mode;
X11DRV_Settings_SetHandler(&xf86vm_handler);
return;
sym_not_found: