I have added XRandR support for resolution changing. This is much better than XVidMode as you can't scroll away from the full-screen applications. This even works for apps that change the resolution before they create any windows.
I reorganized the code to be a lot more flexible (and shorter!). Maintaining the list of modes, enumerating them, and finding an appropriate one given constraints now only happens in a single place.
As usual, I tested this with several apps, but I don't have them all so please let me know right away if anything got broken so I can fix it.
I'm sending this in two parts because I added some files and can't get cvs diff to have them show up. This part contains the new files, and the following one contains the changed files.
-ajp
New Files Added: - dlls/x11drv/settings.c - dlls/x11drv/xrandr.c - dlls/x11drv/xrandr.h
ChangeLog: - Added support for XRandR extension - Added new debugging channels for resolution changing - Streamlined resolution changing and removed duplicated code
Common subdirectories: old-wine/dlls/x11drv/CVS and wine/dlls/x11drv/CVS diff -Nu old-wine/dlls/x11drv/settings.c wine/dlls/x11drv/settings.c --- old-wine/dlls/x11drv/settings.c 1969-12-31 18:00:00.000000000 -0600 +++ wine/dlls/x11drv/settings.c 2003-10-11 12:34:41.000000000 -0500 @@ -0,0 +1,321 @@ +/* + * Wine X11drv display settings functions + * + * Copyright 2003 Alexander James Pasadyn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include <string.h> +#include <stdio.h> +#include "x11drv.h" +#include "x11ddraw.h" + +#include "windef.h" +#include "wingdi.h" +#include "ddrawi.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(x11settings); + +/* + * The DDHALMODEINFO type is used to hold all the mode information. + * This is done because the array of DDHALMODEINFO structures must be + * created for use by DirectDraw anyway. + */ +static LPDDHALMODEINFO dd_modes = NULL; +static unsigned int dd_mode_count = 0; +static unsigned int dd_max_modes = 0; +static int dd_mode_default = 0; +static const unsigned int depths[] = {8, 16, 32}; + +/* pointers to functions that actually do the hard stuff */ +static int (*pGetCurrentMode)(void); +static void (*pSetCurrentMode)(int mode); +static const char *handler_name; + +/* + * Set the handlers for resolution changing functions + * and initialize the master list of modes + */ +LPDDHALMODEINFO X11DRV_Settings_SetHandlers(const char *name, + int (*pNewGCM)(void), + void (*pNewSCM)(int), + unsigned int nmodes, + int reserve_depths) +{ + handler_name = name; + pGetCurrentMode = pNewGCM; + pSetCurrentMode = pNewSCM; + TRACE("Resolution settings now handled by: %s\n", name); + if (reserve_depths) + /* leave room for other depths */ + dd_max_modes = (3+1)*(nmodes); + else + dd_max_modes = nmodes; + + if (dd_modes) + { + TRACE("Destroying old display modes array\n"); + HeapFree(GetProcessHeap(), 0, dd_modes); + } + dd_modes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DDHALMODEINFO) * dd_max_modes); + dd_mode_count = 0; + TRACE("Initialized new display modes array\n"); + return dd_modes; +} + +/* Add one mode to the master list */ +void X11DRV_Settings_AddOneMode(unsigned int width, unsigned int height, unsigned int bpp, unsigned int freq) +{ + LPDDHALMODEINFO info = &(dd_modes[dd_mode_count]); + DWORD dwBpp = screen_depth; + if (dd_mode_count >= dd_max_modes) + { + ERR("Maximum modes (%d) exceeded\n", dd_max_modes); + return; + } + if (dwBpp == 24) dwBpp = 32; + if (bpp == 0) bpp = dwBpp; + info->dwWidth = width; + info->dwHeight = height; + info->wRefreshRate = freq; + info->lPitch = 0; + info->dwBPP = bpp; + info->wFlags = 0; + info->dwRBitMask = 0; + info->dwGBitMask = 0; + info->dwBBitMask = 0; + info->dwAlphaBitMask = 0; + TRACE("initialized mode %d: %dx%dx%d @%d Hz (%s)\n", + dd_mode_count, width, height, bpp, freq, handler_name); + dd_mode_count++; +} + +/* copy all the current modes using the other color depths */ +void X11DRV_Settings_AddDepthModes(void) +{ + int i, j; + int existing_modes = dd_mode_count; + DWORD dwBpp = screen_depth; + if (dwBpp == 24) dwBpp = 32; + for (j=0; j<3; j++) + { + if (depths[j] != dwBpp) + { + for (i=0; i < existing_modes; i++) + { + X11DRV_Settings_AddOneMode(dd_modes[i].dwWidth, dd_modes[i].dwHeight, + depths[j], dd_modes[i].wRefreshRate); + } + } + } +} + +/* set the default mode */ +void X11DRV_Settings_SetDefaultMode(int mode) +{ + dd_mode_default = mode; +} + +/* return the number of modes that are initialized */ +unsigned int X11DRV_Settings_GetModeCount(void) +{ + return dd_mode_count; +} + +/*********************************************************************** + * Default handlers if resolution switching is not enabled + * + */ +static int X11DRV_nores_GetCurrentMode(void) +{ + return 0; +} +static void X11DRV_nores_SetCurrentMode(int mode) +{ + TRACE("Ignoring mode change request\n"); +} +/* default handler only gets the current X desktop resolution */ +void X11DRV_Settings_Init(void) +{ + X11DRV_Settings_SetHandlers("NoRes", + X11DRV_nores_GetCurrentMode, + X11DRV_nores_SetCurrentMode, + 1, 0); + X11DRV_Settings_AddOneMode(screen_width, screen_height, 0, 0); +} + +/*********************************************************************** + * EnumDisplaySettingsExW (X11DRV.@) + * + */ +BOOL X11DRV_EnumDisplaySettingsExW( LPCWSTR name, DWORD n, LPDEVMODEW devmode, DWORD flags) +{ + DWORD dwBpp = screen_depth; + if (dwBpp == 24) dwBpp = 32; + devmode->dmDisplayFlags = 0; + devmode->dmDisplayFrequency = 0; + devmode->dmSize = sizeof(DEVMODEW); + if (n == (DWORD)-1) + { + TRACE("mode %ld (current) -- getting current mode (%s)\n", n, handler_name); + n = pGetCurrentMode(); + } + if (n == (DWORD)-2) + { + TRACE("mode %ld (registry) -- getting default mode (%s)\n", n, handler_name); + n = dd_mode_default; + } + if (n < dd_mode_count) + { + devmode->dmPelsWidth = dd_modes[n].dwWidth; + devmode->dmPelsHeight = dd_modes[n].dwHeight; + devmode->dmBitsPerPel = dd_modes[n].dwBPP; + devmode->dmDisplayFrequency = dd_modes[n].wRefreshRate; + devmode->dmFields = (DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL); + if (devmode->dmDisplayFrequency) + { + devmode->dmFields |= DM_DISPLAYFREQUENCY; + TRACE("mode %ld -- %ldx%ldx%ldbpp @%ld Hz (%s)\n", n, + devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel, + devmode->dmDisplayFrequency, handler_name); + } + else + { + TRACE("mode %ld -- %ldx%ldx%ldbpp (%s)\n", n, + devmode->dmPelsWidth, devmode->dmPelsHeight, devmode->dmBitsPerPel, + handler_name); + } + return TRUE; + } + TRACE("mode %ld -- not present (%s)\n", n, handler_name); + return FALSE; +} + +#define _X_FIELD(prefix, bits) if ((fields) & prefix##_##bits) {p+=sprintf(p, "%s%s", first ? "" : ",", #bits); first=FALSE;} +static const char * _CDS_flags(DWORD fields) +{ + BOOL first = TRUE; + char buf[128]; + char *p = buf; + _X_FIELD(CDS,UPDATEREGISTRY);_X_FIELD(CDS,TEST);_X_FIELD(CDS,FULLSCREEN); + _X_FIELD(CDS,GLOBAL);_X_FIELD(CDS,SET_PRIMARY);_X_FIELD(CDS,RESET); + _X_FIELD(CDS,SETRECT);_X_FIELD(CDS,NORESET); + *p = 0; + return wine_dbg_sprintf("%s", buf); +} +static const char * _DM_fields(DWORD fields) +{ + BOOL first = TRUE; + char buf[128]; + char *p = buf; + _X_FIELD(DM,BITSPERPEL);_X_FIELD(DM,PELSWIDTH);_X_FIELD(DM,PELSHEIGHT); + _X_FIELD(DM,DISPLAYFLAGS);_X_FIELD(DM,DISPLAYFREQUENCY);_X_FIELD(DM,POSITION); + *p = 0; + return wine_dbg_sprintf("%s", buf); +} +#undef _X_FIELD + +/*********************************************************************** + * ChangeDisplaySettingsExW (X11DRV.@) + * + */ +LONG X11DRV_ChangeDisplaySettingsExW( LPCWSTR devname, LPDEVMODEW devmode, + HWND hwnd, DWORD flags, LPVOID lpvoid ) +{ + DWORD i; + DEVMODEW dm; + + TRACE("(%s,%p,%p,0x%08lx,%p)\n",debugstr_w(devname),devmode,hwnd,flags,lpvoid); + TRACE("flags=%s\n",_CDS_flags(flags)); + if (devmode) + { + TRACE("DM_fields=%s\n",_DM_fields(devmode->dmFields)); + TRACE("width=%ld height=%ld bpp=%ld freq=%ld (%s)\n", + devmode->dmPelsWidth,devmode->dmPelsHeight, + devmode->dmBitsPerPel,devmode->dmDisplayFrequency, handler_name); + } + else + { + TRACE("Return to original display mode (%s)\n", handler_name); + if (!X11DRV_EnumDisplaySettingsExW(devname, dd_mode_default, &dm, 0)) + { + ERR("Default mode not found!\n"); + return DISP_CHANGE_BADMODE; + } + devmode = &dm; + } + + for (i = 0; i < dd_mode_count; i++) + { + if (devmode->dmFields & DM_BITSPERPEL) + { + if (devmode->dmBitsPerPel != dd_modes[i].dwBPP) + continue; + } + if (devmode->dmFields & DM_PELSWIDTH) + { + if (devmode->dmPelsWidth != dd_modes[i].dwWidth) + continue; + } + if (devmode->dmFields & DM_PELSHEIGHT) + { + if (devmode->dmPelsHeight != dd_modes[i].dwHeight) + continue; + } + if (devmode->dmFields & DM_DISPLAYFREQUENCY) + { + if (devmode->dmDisplayFrequency != dd_modes[i].wRefreshRate) + continue; + } + /* we have a valid mode */ + TRACE("Requested display settings match mode %ld (%s)\n", i, handler_name); + pSetCurrentMode(i); + return DISP_CHANGE_SUCCESSFUL; + } + + /* no valid modes found */ + ERR("No matching mode found! (%s)\n", handler_name); + return DISP_CHANGE_BADMODE; +} + + + + +/*********************************************************************** + * DirectDraw HAL interface + * + */ +static DWORD PASCAL X11DRV_Settings_SetMode(LPDDHAL_SETMODEDATA data) +{ + TRACE("Mode %ld requested by DDHAL (%s)\n", data->dwModeIndex, handler_name); + pSetCurrentMode(data->dwModeIndex); + X11DRV_DDHAL_SwitchMode(data->dwModeIndex, NULL, NULL); + data->ddRVal = DD_OK; + return DDHAL_DRIVER_HANDLED; +} +int X11DRV_Settings_CreateDriver(LPDDHALINFO info) +{ + if (!dd_mode_count) return 0; /* no settings defined */ + + TRACE("Setting up display settings for DDRAW (%s)\n", handler_name); + info->dwNumModes = dd_mode_count; + info->lpModeInfo = dd_modes; + X11DRV_DDHAL_SwitchMode(pGetCurrentMode(), NULL, NULL); + info->lpDDCallbacks->SetMode = X11DRV_Settings_SetMode; + return TRUE; +} diff -Nu old-wine/dlls/x11drv/xrandr.c wine/dlls/x11drv/xrandr.c --- old-wine/dlls/x11drv/xrandr.c 1969-12-31 18:00:00.000000000 -0600 +++ wine/dlls/x11drv/xrandr.c 2003-10-11 01:46:47.000000000 -0500 @@ -0,0 +1,260 @@ +/* + * Wine X11drv Xrandr interface + * + * Copyright 2003 Alexander James Pasadyn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include <string.h> +#include <stdio.h> + +#ifdef HAVE_LIBXRANDR + +#include "ts_xlib.h" + +#include <X11/extensions/Xrandr.h> +#include "x11drv.h" + +#include "x11ddraw.h" +#include "xrandr.h" + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "ddrawi.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(xrandr); + +extern int usexrandr; + +static int xrandr_event, xrandr_error, xrandr_major, xrandr_minor; + +static LPDDHALMODEINFO dd_modes; +static unsigned int dd_mode_count; +static XRRScreenSize *real_xrandr_sizes; +static short **real_xrandr_rates; +static unsigned int real_xrandr_sizes_count; +static int *real_xrandr_rates_count; +static unsigned int real_xrandr_modes_count; + +static int XRandRErrorHandler(Display *dpy, XErrorEvent *event, void *arg) +{ + return 1; +} + +static Bool in_desktop_mode; +static const unsigned int depths[] = {8, 16, 32}; + +/* create the mode structures */ +static void make_modes(void) +{ + int i, j; + for (i=0; i<real_xrandr_sizes_count; i++) + { + if (real_xrandr_rates_count[i]) + { + for (j=0; j < real_xrandr_rates_count[i]; j++) + { + X11DRV_Settings_AddOneMode(real_xrandr_sizes[i].width, + real_xrandr_sizes[i].height, + 0, real_xrandr_rates[i][j]); + } + } + else + { + X11DRV_Settings_AddOneMode(real_xrandr_sizes[i].width, + real_xrandr_sizes[i].height, + 0, 0); + } + } +} + +static int X11DRV_XRandR_GetCurrentMode(void) +{ + SizeID size; + Rotation rot; + Window root; + XRRScreenConfiguration *sc; + short rate; + int i; + int res = -1; + + wine_tsx11_lock(); + root = RootWindow (gdi_display, DefaultScreen(gdi_display)); + sc = XRRGetScreenInfo (gdi_display, root); + size = XRRConfigCurrentConfiguration (sc, &rot); + rate = XRRConfigCurrentRate (sc); + for (i = 0; i < real_xrandr_modes_count; i++) + { + if ( (dd_modes[i].dwWidth == real_xrandr_sizes[size].width ) && + (dd_modes[i].dwHeight == real_xrandr_sizes[size].height) && + (dd_modes[i].wRefreshRate == rate ) ) + { + res = i; + } + } + XRRFreeScreenConfigInfo(sc); + wine_tsx11_unlock(); + if (res == -1) + { + ERR("In unknown mode, returning default\n"); + res = 0; + } + return res; +} + +static void X11DRV_XRandR_SetCurrentMode(int mode) +{ + SizeID size; + Rotation rot; + Window root; + XRRScreenConfiguration *sc; + Status stat = RRSetConfigSuccess; + short rate; + int i, j; + DWORD dwBpp = screen_depth; + if (dwBpp == 24) dwBpp = 32; + + wine_tsx11_lock(); + root = RootWindow (gdi_display, DefaultScreen(gdi_display)); + sc = XRRGetScreenInfo (gdi_display, root); + size = XRRConfigCurrentConfiguration (sc, &rot); + if (dwBpp != dd_modes[mode].dwBPP) + { + FIXME("Cannot change screen BPP from %ld to %ld\n", dwBpp, dd_modes[mode].dwBPP); + } + mode = mode%real_xrandr_modes_count; + for (i = 0; i < real_xrandr_sizes_count; i++) + { + if ( (dd_modes[mode].dwWidth == real_xrandr_sizes[i].width ) && + (dd_modes[mode].dwHeight == real_xrandr_sizes[i].height) ) + { + size = i; + if (real_xrandr_rates_count[i]) + { + for (j=0; j < real_xrandr_rates_count[i]; j++) + { + if (dd_modes[mode].wRefreshRate == real_xrandr_rates[i][j]) + { + rate = real_xrandr_rates[i][j]; + TRACE("Resizing X display to %ldx%ld @%d Hz\n", + dd_modes[mode].dwWidth, dd_modes[mode].dwHeight, rate); + stat = XRRSetScreenConfigAndRate (gdi_display, sc, root, + size, rot, rate, CurrentTime); + FIXME("Need to update SYSMETRICS after resizing display (now %ldx%ld)\n", + dd_modes[mode].dwWidth, dd_modes[mode].dwHeight); + } + } + } + else + { + TRACE("Resizing X display to %ldx%ld\n", + dd_modes[mode].dwWidth, dd_modes[mode].dwHeight); + stat = XRRSetScreenConfig (gdi_display, sc, root, + size, rot, CurrentTime); + FIXME("Need to update SYSMETRICS after resizing display (now %ldx%ld)\n", + dd_modes[mode].dwWidth, dd_modes[mode].dwHeight); + } + } + } + if (stat != RRSetConfigSuccess) + { + ERR("Resolution change not successful -- perhaps display has chaned?"); + } + XRRFreeScreenConfigInfo(sc); + wine_tsx11_unlock(); +} + +void X11DRV_XRandR_Init(void) +{ + Bool ok; + int nmodes = 0; + int i; + in_desktop_mode = (root_window != DefaultRootWindow(gdi_display)); + + if (xrandr_major) return; /* already initialized? */ + if (!usexrandr) return; /* disabled in config */ + if (in_desktop_mode) return; /* not compatible with desktop mode */ + + /* see if Xrandr is available */ + wine_tsx11_lock(); + ok = XRRQueryExtension(gdi_display, &xrandr_event, &xrandr_error); + if (ok) + { + X11DRV_expect_error(gdi_display, XRandRErrorHandler, NULL); + ok = XRRQueryVersion(gdi_display, &xrandr_major, &xrandr_minor); + if (X11DRV_check_error()) ok = FALSE; + } + if (ok) + { + TRACE("Found XRandR - major: %d, minor: %d\n", xrandr_major, xrandr_minor); + /* retrieve modes */ + real_xrandr_sizes = XRRSizes(gdi_display, DefaultScreen(gdi_display), &real_xrandr_sizes_count); + ok = (real_xrandr_sizes_count>0); + } + if (ok) + { + real_xrandr_rates = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(short *) * real_xrandr_sizes_count); + real_xrandr_rates_count = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(int) * real_xrandr_sizes_count); + for (i=0; i < real_xrandr_sizes_count; i++) + { + real_xrandr_rates[i] = XRRRates (gdi_display, DefaultScreen(gdi_display), i, &(real_xrandr_rates_count[i])); + if (real_xrandr_rates_count[i]) + { + nmodes += real_xrandr_rates_count[i]; + } + else + { + nmodes++; + } + } + } + wine_tsx11_unlock(); + if (!ok) return; + + real_xrandr_modes_count = nmodes; + TRACE("XRandR modes: count=%d\n", nmodes); + + dd_modes = X11DRV_Settings_SetHandlers("XRandR", + X11DRV_XRandR_GetCurrentMode, + X11DRV_XRandR_SetCurrentMode, + nmodes, 1); + make_modes(); + X11DRV_Settings_AddDepthModes(); + dd_mode_count = X11DRV_Settings_GetModeCount(); + X11DRV_Settings_SetDefaultMode(0); + + TRACE("Available DD modes: count=%d\n", dd_mode_count); + TRACE("Enabling XRandR\n"); +} + +void X11DRV_XRandR_Cleanup(void) +{ + if (real_xrandr_rates) + { + HeapFree(GetProcessHeap(), 0, real_xrandr_rates); + real_xrandr_rates = NULL; + } + if (real_xrandr_rates_count) + { + HeapFree(GetProcessHeap(), 0, real_xrandr_rates_count); + real_xrandr_rates_count = NULL; + } +} + +#endif /* HAVE_LIBXRANDR */ diff -Nu old-wine/dlls/x11drv/xrandr.h wine/dlls/x11drv/xrandr.h --- old-wine/dlls/x11drv/xrandr.h 1969-12-31 18:00:00.000000000 -0600 +++ wine/dlls/x11drv/xrandr.h 2003-10-11 01:17:11.000000000 -0500 @@ -0,0 +1,33 @@ +/* + * Wine X11drv Xrandr interface + * + * Copyright 2003 Alexander James Pasadyn + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __WINE_XRANDR_H +#define __WINE_XRANDR_H + +#ifndef __WINE_CONFIG_H +# error You must include config.h to use this header +#endif + +#ifdef HAVE_LIBXRANDR + +void X11DRV_XRandR_Init(void); +void X11DRV_XRandR_Cleanup(void); + +#endif /* HAVE_LIBXRANDR */ +#endif /* __WINE_XRANDR_H */