winex11.drv: Add DefaultRefreshRates option to specify refresh rate when application does not

Denver Gingerich denver at ossguy.com
Sun Mar 2 22:39:13 CST 2008


When an application calls ChangeDisplaySettingsEx without specifying a refresh rate, the first mode that matches the resolution and bits per pixel is used by default even if there is another mode that matches but has a different refresh rate.  This is problematic when the first mode in the list has a low refresh rate or a virtual refresh rate that specifies a behavior the user doesn't want.  For example, the default virtual refresh rate may cause clipping when a resolution's aspect ratio does not match the display while another virtual refresh rate scales the resolution to fit on the display without clipping.

This patch allows the user to specify a list of default refresh rates to use at particular resolutions to override the default behavior that chooses the refresh rate based on whichever mode is first in the list for a given resolution.  The list is specified by populating the X11 Driver/DefaultRefreshRates registry key with a semicolon-delimited list of resolution, refresh rate pairs.  For example, to specify that the default refresh rate for 800x600 is 75 Hz and the default refresh rate for 640x480 is 85 Hz, the value of the DefaultRefreshRates key should be

800x600,75;640x480,85

As with the other X11 Driver keys, DefaultRefreshRates can be specified on a global basis or per application.

Denver Gingerich


diff --git a/dlls/winex11.drv/settings.c b/dlls/winex11.drv/settings.c
index d68b674..a89d0f4 100644
--- a/dlls/winex11.drv/settings.c
+++ b/dlls/winex11.drv/settings.c
@@ -2,6 +2,7 @@
  * Wine X11drv display settings functions
  *
  * Copyright 2003 Alexander James Pasadyn
+ * Copyright 2008 Denver Gingerich
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -55,6 +56,14 @@ static int (*pGetCurrentMode)(void);
 static LONG (*pSetCurrentMode)(int mode);
 static const char *handler_name;
 
+/* in x11drv_main.c; stores value of DefaultRefreshRates */
+extern char *default_refresh_rates;
+
+/* return codes for is_valid_refresh_rate() */
+#define REFRESH_RATE_INVALID          0
+#define REFRESH_RATE_VALID_MATCHED    1
+#define REFRESH_RATE_VALID_UNMATCHED  2
+
 /*
  * Set the handlers for resolution changing functions
  * and initialize the master list of modes
@@ -344,6 +353,70 @@ static const char * _DM_fields(DWORD fields)
 }
 #undef _X_FIELD
 
+/* Check if wRefreshRate is a valid refresh rate for the resolution given by
+ * dmPelsWidth and dmPelsHeight according to the value of DefaultRefreshRates
+ * (default_refresh_rates).  If wRefreshRate matches the refresh rate specified
+ * in DefaultRefreshRates for the given resolution (wRefreshRate is the default
+ * refresh rate for the given resolution) or if the given resolution is not
+ * found in DefaultRefreshRates, then it is considered a valid refresh rate for
+ * the given resolution and REFRESH_RATE_VALID_MATCHED or
+ * REFRESH_RATE_VALID_UNMATCHED is returned, respectively.  Otherwise, if the
+ * refresh rate for the given resolution specified in DefaultRefreshRates is not
+ * wRefreshRate, REFRESH_RATE_INVALID is returned because the user wishes for a
+ * different refresh rate to be used for that particular resolution.
+ */
+static int is_valid_refresh_rate( WORD wRefreshRate, DWORD dmPelsWidth,
+                                  DWORD dmPelsHeight )
+{
+    WORD wCurrRefreshRate;
+    DWORD dmCurrPelsWidth;
+    DWORD dmCurrPelsHeight;
+    int num_fields;
+    char *res_start;
+
+    /* caller must verify default_refresh_rates is set */
+    assert(default_refresh_rates);
+
+    res_start = default_refresh_rates;
+
+    if (0 == strcmp(res_start, ""))
+        return REFRESH_RATE_VALID_UNMATCHED;
+
+    for (;;) {
+        num_fields = sscanf(res_start, "%dx%d,%hd", &dmCurrPelsWidth,
+               &dmCurrPelsHeight, &wCurrRefreshRate);
+
+        /* in the case of a poorly-formatted DefaultRefreshRate value, ignore
+         * the rest of the value, report a problem, and return
+         * REFRESH_RATE_VALID_UNMATCHED assuming the rest of the
+         * DefaultRefreshRate value does not contain further [resolution,
+         * refresh rate] pairs that need to be checked
+         */
+        if (num_fields < 3)
+        {
+            TRACE("Value of DefaultRefreshRates is poorly-formatted, please "
+                  "ensure it is of the form \"Y1xZ1,R1;Y2xZ2,R2;...\"; "
+                  "proceeding anyway\n");
+            return REFRESH_RATE_VALID_UNMATCHED;
+        }
+
+        if ((dmCurrPelsWidth == dmPelsWidth) &&
+            (dmCurrPelsHeight == dmPelsHeight))
+        {
+            if (wCurrRefreshRate == wRefreshRate)
+                return REFRESH_RATE_VALID_MATCHED;
+            else
+                return REFRESH_RATE_INVALID;
+        }
+
+        res_start = strchr(res_start, ';');
+        if (NULL == res_start)
+            return REFRESH_RATE_VALID_UNMATCHED;
+
+        res_start++;
+    }
+}
+
 /***********************************************************************
  *		ChangeDisplaySettingsEx  (X11DRV.@)
  *
@@ -354,6 +427,7 @@ LONG X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode,
     DWORD i, dwBpp = 0;
     DEVMODEW dm;
     BOOL def_mode = TRUE;
+    BOOL default_refresh_check_failed = FALSE;
 
     TRACE("(%s,%p,%p,0x%08x,%p)\n",debugstr_w(devname),devmode,hwnd,flags,lpvoid);
     TRACE("flags=%s\n",_CDS_flags(flags));
@@ -419,6 +493,61 @@ LONG X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode,
             if (devmode->dmDisplayFrequency != dd_modes[i].wRefreshRate)
                 continue;
         }
+        else /* DefaultRefreshRates only takes effect if the caller of
+              * ChangeDisplaySettingsEx does not specify a frequency (ie. a
+              * refresh rate)
+              */
+        {
+            if (default_refresh_rates)
+            {
+                switch (is_valid_refresh_rate(dd_modes[i].wRefreshRate,
+                                              devmode->dmPelsWidth,
+                                              devmode->dmPelsHeight))
+                {
+                case REFRESH_RATE_VALID_MATCHED:
+                    /* DefaultRefreshRates is defined and has an entry that
+                     * matches the current resolution and refresh rate so we
+                     * we will use those settings
+                     */
+                    TRACE("Found mode that matches refresh rate and resolution "
+                          "specified by DefaultRefreshRates\n");
+                case REFRESH_RATE_VALID_UNMATCHED:
+                    /* DefaultRefreshRates is defined, but does not have an
+                     * entry that matches the resolution passed in to
+                     * ChangeDisplaySettingsEx so it has no effect
+                     */
+
+                    /* since we are not continuing the loop, the following hold:
+                     * 1) we do not need to set default_refresh_check_failed
+                     *    back to FALSE even though it may be TRUE because it is
+                     *    not used again before this function exits
+                     * 2) we can print a trace message (see above) and be sure
+                     *    that it will not be printed again in another iteration
+                     *    of the loop
+                     */
+                    break;
+                case REFRESH_RATE_INVALID:
+                    /* DefaultRefreshRates is defined and has an entry for the
+                     * resolution passed in to ChangeDisplaySettingsEx but the
+                     * refresh rate specified for that resolution in
+                     * DefaultRefreshRates does not match the current refresh
+                     * rate
+                     */
+
+                    /* we can't print an error message here because we don't
+                     * know whether another refresh rate at this resolution will
+                     * be valid so we set the flag variable instead
+                     */
+                    default_refresh_check_failed = TRUE;
+                    continue;
+                }
+            }
+        }
+        /* NOTE: no additional "continue;"s can be put after the
+         * DefaultRefreshRates handling code above because it will break the
+         * logic; see inline comments
+         */
+
         /* we have a valid mode */
         TRACE("Requested display settings match mode %d (%s)\n", i, handler_name);
 
@@ -435,6 +564,18 @@ LONG X11DRV_ChangeDisplaySettingsEx( LPCWSTR devname, LPDEVMODEW devmode,
     ERR("No matching mode found %ux%ux%u @%u! (%s)\n",
         devmode->dmPelsWidth, devmode->dmPelsHeight,
         devmode->dmBitsPerPel, devmode->dmDisplayFrequency, handler_name);
+
+    /* Why do we need the default_refresh_check_failed flag variable?  We want
+     * to report whether the reason a match could not be found is related to the
+     * value of DefaultRefreshRates.  It is insufficient to check whether
+     * default_refresh_rates is NULL because it is possible that the match
+     * failed at an earlier check and never reached the DefaultRefreshRates
+     * handling code.  To verify that it was not an earlier check that prevented
+     * the match would require too much code here so the flag variable is used.
+     */
+    if (default_refresh_check_failed)
+        ERR("Value of DefaultRefreshRates registry entry prevented a match\n");
+
     return DISP_CHANGE_BADMODE;
 }
 
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c
index 8982716..5bf4e3a 100644
--- a/dlls/winex11.drv/x11drv_main.c
+++ b/dlls/winex11.drv/x11drv_main.c
@@ -3,6 +3,7 @@
  *
  * Copyright 1998 Patrik Stridvall
  * Copyright 2000 Alexandre Julliard
+ * Copyright 2008 Denver Gingerich
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -90,6 +91,7 @@ int client_side_antialias_with_core = 1;
 int client_side_antialias_with_render = 1;
 int copy_default_colors = 128;
 int alloc_system_colors = 256;
+char *default_refresh_rates = NULL;
 DWORD thread_data_tls_index = TLS_OUT_OF_INDEXES;
 int xrender_error_base = 0;
 
@@ -404,6 +406,12 @@ static void setup_options(void)
     if (!get_config_key( hkey, appkey, "AllocSystemColors", buffer, sizeof(buffer) ))
         alloc_system_colors = atoi(buffer);
 
+    if (!get_config_key( hkey, appkey, "DefaultRefreshRates", buffer, sizeof(buffer) ))
+    {
+        default_refresh_rates = HeapAlloc(GetProcessHeap(), 0, strlen(buffer)+1);
+        strcpy(default_refresh_rates, buffer);
+    }
+
     get_config_key( hkey, appkey, "InputStyle", input_style, sizeof(input_style) );
 
     if (appkey) RegCloseKey( appkey );
@@ -575,6 +583,7 @@ static void process_detach(void)
     /* cleanup GDI */
     X11DRV_GDI_Finalize();
 
+    HeapFree(GetProcessHeap(), 0, default_refresh_rates);
     DeleteCriticalSection( &X11DRV_CritSection );
     TlsFree( thread_data_tls_index );
 }





More information about the wine-patches mailing list