Updating branch refs/heads/bluesabre/display-settings to 7ef545b96688fd5797a3d422e50fa7be7c676af5 (commit) from cb8ab60dc89ab61ef62fd85b4f11c11026e79794 (commit)
commit 7ef545b96688fd5797a3d422e50fa7be7c676af5 Author: Lionel Le Folgoc <lio...@lefolgoc.net> Date: Tue Nov 6 22:58:51 2012 +0100 Refactor XfceDisplaysHelper to listen to screen changes This will enable it to take some actions depending on the context, e.g. re-enable LVDS1 when VGA1 is disconnected, etc (to do for later). Signed-off-by: Simon Steinbeiss <simon.steinbe...@elfenbeinturm.at> xfsettingsd/displays.c | 1190 +++++++++++++++++++++++++++--------------------- 1 file changed, 675 insertions(+), 515 deletions(-) diff --git a/xfsettingsd/displays.c b/xfsettingsd/displays.c index af6e6cd..597e0d9 100644 --- a/xfsettingsd/displays.c +++ b/xfsettingsd/displays.c @@ -47,12 +47,44 @@ -static void xfce_displays_helper_channel_apply (XfceDisplaysHelper *helper, - const gchar *scheme); -static void xfce_displays_helper_channel_property_changed (XfconfChannel *channel, - const gchar *property_name, - const GValue *value, - XfceDisplaysHelper *helper); +/* wrappers to avoid querying too often */ +typedef struct _XfceRRCrtc XfceRRCrtc; + + + +static void xfce_displays_helper_dispose (GObject *object); +static void xfce_displays_helper_finalize (GObject *object); +static void xfce_displays_helper_reload (XfceDisplaysHelper *helper); +static GdkFilterReturn xfce_displays_helper_screen_on_event (GdkXEvent *xevent, + GdkEvent *event, + gpointer data); +static void xfce_displays_helper_set_screen_size (XfceDisplaysHelper *helper); +static gboolean xfce_displays_helper_load_from_xfconf (XfceDisplaysHelper *helper, + const gchar *scheme, + GHashTable *saved_outputs, + RROutput output); +static GPtrArray *xfce_displays_helper_list_crtcs (XfceDisplaysHelper *helper); +static XfceRRCrtc *xfce_displays_helper_find_crtc_by_id (XfceDisplaysHelper *helper, + RRCrtc id); +static void xfce_displays_helper_free_crtc (XfceRRCrtc *crtc); +static XfceRRCrtc *xfce_displays_helper_find_usable_crtc (XfceDisplaysHelper *helper, + RROutput output); +static void xfce_displays_helper_normalize_crtc (XfceRRCrtc *crtc, + XfceDisplaysHelper *helper); +static Status xfce_displays_helper_disable_crtc (XfceDisplaysHelper *helper, + RRCrtc crtc); +static void xfce_displays_helper_workaround_crtc_size (XfceRRCrtc *crtc, + XfceDisplaysHelper *helper); +static void xfce_displays_helper_apply_crtc (XfceRRCrtc *crtc, + XfceDisplaysHelper *helper); +static void xfce_displays_helper_set_outputs (XfceRRCrtc *crtc, + RROutput output); +static void xfce_displays_helper_channel_apply (XfceDisplaysHelper *helper, + const gchar *scheme); +static void xfce_displays_helper_channel_property_changed (XfconfChannel *channel, + const gchar *property_name, + const GValue *value, + XfceDisplaysHelper *helper); @@ -66,16 +98,33 @@ struct _XfceDisplaysHelper GObject __parent__; /* xfconf channel */ - XfconfChannel *channel; + XfconfChannel *channel; + guint handler; #ifdef HAS_RANDR_ONE_POINT_THREE - gint has_1_3; + gint has_1_3; + gint primary; #endif -}; -/* wrappers to avoid querying too often */ -typedef struct _XfceRRCrtc XfceRRCrtc; -typedef struct _XfceRROutput XfceRROutput; + GdkDisplay *display; + GdkWindow *root_window; + Display *xdisplay; + gint event_base; + + /* RandR cache */ + XRRScreenResources *resources; + GPtrArray *crtcs; + + /* screen size */ + gint width; + gint height; + gint mm_width; + gint mm_height; + + /* used to normalize positions */ + gint min_x; + gint min_y; +}; struct _XfceRRCrtc { @@ -94,12 +143,6 @@ struct _XfceRRCrtc gint changed; }; -struct _XfceRROutput -{ - RROutput id; - XRROutputInfo *info; -}; - G_DEFINE_TYPE (XfceDisplaysHelper, xfce_displays_helper, G_TYPE_OBJECT); @@ -109,7 +152,10 @@ G_DEFINE_TYPE (XfceDisplaysHelper, xfce_displays_helper, G_TYPE_OBJECT); static void xfce_displays_helper_class_init (XfceDisplaysHelperClass *klass) { + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->dispose = xfce_displays_helper_dispose; + gobject_class->finalize = xfce_displays_helper_finalize; } @@ -118,15 +164,51 @@ static void xfce_displays_helper_init (XfceDisplaysHelper *helper) { gint major = 0, minor = 0; - gint event_base, error_base; + gint error_base, err; + + helper->resources = NULL; + helper->crtcs = NULL; + helper->handler = 0; + + /* get the default display */ + helper->display = gdk_display_get_default (); + helper->xdisplay = gdk_x11_display_get_xdisplay (helper->display); + helper->root_window = gdk_get_default_root_window (); /* check if the randr extension is running */ - if (XRRQueryExtension (GDK_DISPLAY (), &event_base, &error_base)) + if (XRRQueryExtension (helper->xdisplay, &helper->event_base, &error_base)) { /* query the version */ - if (XRRQueryVersion (GDK_DISPLAY (), &major, &minor) + if (XRRQueryVersion (helper->xdisplay, &major, &minor) && (major > 1 || (major == 1 && minor >= 2))) { + gdk_error_trap_push (); + /* get the screen resource */ + helper->resources = XRRGetScreenResources (helper->xdisplay, + GDK_WINDOW_XID (helper->root_window)); + gdk_flush (); + err = gdk_error_trap_pop (); + if (err) + { + g_critical ("XRRGetScreenResources failed (err: %d). " + "Display settings won't be applied.", err); + return; + } + + /* get all existing CRTCs */ + helper->crtcs = xfce_displays_helper_list_crtcs (helper); + + /* Set up RandR notifications */ + XRRSelectInput (helper->xdisplay, + GDK_WINDOW_XID (helper->root_window), + RRScreenChangeNotifyMask); + gdk_x11_register_standard_event_type (helper->display, + helper->event_base, + RRNotify + 1); + gdk_window_add_filter (helper->root_window, + xfce_displays_helper_screen_on_event, + helper); + /* open the channel */ helper->channel = xfconf_channel_get ("displays"); @@ -134,8 +216,10 @@ xfce_displays_helper_init (XfceDisplaysHelper *helper) xfconf_channel_reset_property (helper->channel, "/Schemes/Apply", FALSE); /* monitor channel changes */ - g_signal_connect (G_OBJECT (helper->channel), "property-changed", - G_CALLBACK (xfce_displays_helper_channel_property_changed), helper); + helper->handler = g_signal_connect (G_OBJECT (helper->channel), + "property-changed", + G_CALLBACK (xfce_displays_helper_channel_property_changed), + helper); #ifdef HAS_RANDR_ONE_POINT_THREE helper->has_1_3 = (major > 1 || (major == 1 && minor >= 3)); @@ -153,209 +237,537 @@ xfce_displays_helper_init (XfceDisplaysHelper *helper) else { g_critical ("No RANDR extension found in display %s. Display settings won't be applied.", - gdk_display_get_name (gdk_display_get_default ())); + gdk_display_get_name (helper->display)); } } static void -xfce_displays_helper_process_screen_size (gint mode_width, - gint mode_height, - gint crtc_pos_x, - gint crtc_pos_y, - gint *width, - gint *height, - gint *mm_width, - gint *mm_height) +xfce_displays_helper_dispose (GObject *object) { - g_assert (width && height && mm_width && mm_height); + XfceDisplaysHelper *helper = XFCE_DISPLAYS_HELPER (object); - *width = MAX (*width, crtc_pos_x + mode_width); - *height = MAX (*height, crtc_pos_y + mode_height); + if (helper->handler > 0) + { + g_signal_handler_disconnect (G_OBJECT (helper->channel), + helper->handler); + helper->handler = 0; + } - /* The 'physical size' of an X screen is meaningless if that screen - * can consist of many monitors. So just pick a size that make the - * dpi 96. - * - * Firefox and Evince apparently believe what X tells them. - */ - *mm_width = (*width / 96.0) * 25.4 + 0.5; - *mm_height = (*height / 96.0) * 25.4 + 0.5; + gdk_window_remove_filter (helper->root_window, + xfce_displays_helper_screen_on_event, + helper); + + (*G_OBJECT_CLASS (xfce_displays_helper_parent_class)->dispose) (object); } -static XfceRRCrtc * -xfce_displays_helper_list_crtcs (Display *xdisplay, - XRRScreenResources *resources) +static void +xfce_displays_helper_finalize (GObject *object) { - XfceRRCrtc *crtcs; - XRRCrtcInfo *crtc_info; - gint n; + XfceDisplaysHelper *helper = XFCE_DISPLAYS_HELPER (object); - g_assert (xdisplay && resources); + /* Free the CRTC cache */ + if (helper->crtcs) + { + g_ptr_array_free (helper->crtcs, TRUE); + helper->crtcs = NULL; + } - /* get all existing CRTCs */ - crtcs = g_new0 (XfceRRCrtc, resources->ncrtc); - for (n = 0; n < resources->ncrtc; ++n) - { - xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Detected CRTC %lu.", resources->crtcs[n]); - - crtcs[n].id = resources->crtcs[n]; - crtc_info = XRRGetCrtcInfo (xdisplay, resources, resources->crtcs[n]); - crtcs[n].mode = crtc_info->mode; - crtcs[n].rotation = crtc_info->rotation; - crtcs[n].rotations = crtc_info->rotations; - crtcs[n].width = crtc_info->width; - crtcs[n].height = crtc_info->height; - crtcs[n].x = crtc_info->x; - crtcs[n].y = crtc_info->y; - - crtcs[n].noutput = crtc_info->noutput; - crtcs[n].outputs = NULL; - if (crtc_info->noutput > 0) - crtcs[n].outputs = g_memdup (crtc_info->outputs, - crtc_info->noutput * sizeof (RROutput)); + /* Free the screen resources */ + if (helper->resources) + { + gdk_error_trap_push (); + XRRFreeScreenResources (helper->resources); + gdk_flush (); + gdk_error_trap_pop (); + helper->resources = NULL; + } - crtcs[n].npossible = crtc_info->npossible; - crtcs[n].possible = NULL; - if (crtc_info->npossible > 0) - crtcs[n].possible = g_memdup (crtc_info->possible, - crtc_info->npossible * sizeof (RROutput)); + (*G_OBJECT_CLASS (xfce_displays_helper_parent_class)->finalize) (object); +} - crtcs[n].changed = FALSE; - XRRFreeCrtcInfo (crtc_info); - } - return crtcs; + +static void +xfce_displays_helper_reload (XfceDisplaysHelper *helper) +{ + gint err; + + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Refreshing RandR cache."); + + /* Free the CRTC cache */ + g_ptr_array_free (helper->crtcs, TRUE); + + gdk_error_trap_push (); + + /* Free the screen resources */ + XRRFreeScreenResources (helper->resources); + + /* get the screen resource */ +#ifdef HAS_RANDR_ONE_POINT_THREE + /* xfce_displays_helper_reload () is usually called after a xrandr notification, + which means that X is aware of the new hardware already. So, if possible, + do not reprobe the hardware again. */ + if (helper->has_1_3) + helper->resources = XRRGetScreenResourcesCurrent (helper->xdisplay, + GDK_WINDOW_XID (helper->root_window)); + else +#endif + helper->resources = XRRGetScreenResources (helper->xdisplay, + GDK_WINDOW_XID (helper->root_window)); + + gdk_flush (); + err = gdk_error_trap_pop (); + if (err) + g_critical ("Failed to reload the RandR cache (err: %d).", err); + + /* get all existing CRTCs */ + helper->crtcs = xfce_displays_helper_list_crtcs (helper); } -static XfceRRCrtc * -xfce_displays_helper_find_crtc_by_id (XRRScreenResources *resources, - XfceRRCrtc *crtcs, - RRCrtc id) +static GdkFilterReturn +xfce_displays_helper_screen_on_event (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) { - gint n; + XfceDisplaysHelper *helper = XFCE_DISPLAYS_HELPER (data); + XEvent *e = xevent; + gint event_num; + + if (!e) + return GDK_FILTER_CONTINUE; - g_assert (resources && crtcs); + event_num = e->type - helper->event_base; - for (n = 0; n < resources->ncrtc; ++n) + if (event_num == RRScreenChangeNotify) { - if (crtcs[n].id == id) - return &crtcs[n]; + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "RRScreenChangeNotify event received."); + + xfce_displays_helper_reload (helper); + + /*TODO: check that there is still one output enabled */ + /*TODO: e.g. reenable LVDS1 when VGA1 is diconnected. */ + /*TODO: also, disable LVDS1 when the lid is closed (needs upower though :/) */ } - return NULL; + /* Pass the event on to GTK+ */ + return GDK_FILTER_CONTINUE; } static void -xfce_displays_helper_cleanup_crtc (XfceRRCrtc *crtc) +xfce_displays_helper_set_screen_size (XfceDisplaysHelper *helper) { - if (crtc == NULL) + gint min_width, min_height, max_width, max_height; + + g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources); + + /* get the screen size extremums */ + if (!XRRGetScreenSizeRange (helper->xdisplay, GDK_WINDOW_XID (helper->root_window), + &min_width, &min_height, &max_width, &max_height)) + { + g_warning ("Unable to get the range of screen sizes. " + "Display settings may fail to apply."); return; + } - if (crtc->outputs != NULL) - g_free (crtc->outputs); - if (crtc->possible != NULL) - g_free (crtc->possible); + /* set the screen size only if it's really needed and valid */ + if (helper->width >= min_width && helper->width <= max_width + && helper->height >= min_height && helper->height <= max_height + && (helper->width != gdk_screen_width () + || helper->height != gdk_screen_height () + || helper->mm_width != gdk_screen_width_mm () + || helper->mm_height != gdk_screen_height_mm ())) + { + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Applying desktop dimensions: %dx%d (px), %dx%d (mm).", + helper->width, helper->height, helper->mm_width, helper->mm_height); + XRRSetScreenSize (helper->xdisplay, GDK_WINDOW_XID (helper->root_window), + helper->width, helper->height, helper->mm_width, helper->mm_height); + } } -static void -xfce_displays_helper_free_output (XfceRROutput *output) +static gboolean +xfce_displays_helper_load_from_xfconf (XfceDisplaysHelper *helper, + const gchar *scheme, + GHashTable *saved_outputs, + RROutput output) { - if (output == NULL) - return; + XfceRRCrtc *crtc = NULL; + XRROutputInfo *info; + GValue *value; + const gchar *str_value; + gchar property[512]; + gdouble output_rate, rate; + RRMode valid_mode; + Rotation rot; + gint x, y, n, m, int_value, err; + gboolean active = FALSE; - XRRFreeOutputInfo (output->info); - g_free (output); + gdk_error_trap_push (); + info = XRRGetOutputInfo (helper->xdisplay, helper->resources, output); + gdk_flush (); + err = gdk_error_trap_pop (); + if (err || !info) + { + g_warning ("Failed to load info for output %lu (err: %d). Skipping.", + output, err); + return FALSE; + } + + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Detected output %lu %s.", output, + info->name); + + /* ignore disconnected outputs */ + if (info->connection != RR_Connected) + goto next_output; + + /* Get the associated CRTC */ + if (info->crtc != None) + crtc = xfce_displays_helper_find_crtc_by_id (helper, info->crtc); + + /* track active outputs */ + if (crtc && crtc->mode != None) + { + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "%s is active.", info->name); + active = TRUE; + } + + /* does this output exist in xfconf? */ + g_snprintf (property, sizeof (property), "/%s/%s", scheme, info->name); + value = g_hash_table_lookup (saved_outputs, property); + + if (value == NULL || !G_VALUE_HOLDS_STRING (value)) + goto next_output; + +#ifdef HAS_RANDR_ONE_POINT_THREE + if (helper->has_1_3) + { + /* is it the primary output? */ + g_snprintf (property, sizeof (property), "/%s/%s/Primary", scheme, info->name); + value = g_hash_table_lookup (saved_outputs, property); + if (G_VALUE_HOLDS_BOOLEAN (value) && g_value_get_boolean (value)) + helper->primary = output; + } +#endif + + /* status */ + g_snprintf (property, sizeof (property), "/%s/%s/Active", scheme, info->name); + value = g_hash_table_lookup (saved_outputs, property); + + if (value == NULL || !G_VALUE_HOLDS_BOOLEAN (value)) + goto next_output; + + /* No existing CRTC, try to find a free one */ + if (info->crtc == None) + crtc = xfce_displays_helper_find_usable_crtc (helper, output); + + if (!crtc) + { + g_warning ("No available CRTC for %s, aborting.", info->name); + goto next_output; + } + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "CRTC %lu assigned to %s.", crtc->id, info->name); + + /* disable inactive outputs */ + if (!g_value_get_boolean (value)) + { + if (crtc->mode != None) + { + active = FALSE; + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "%s will be disabled by configuration.", info->name); + + crtc->mode = None; + crtc->noutput = 0; + crtc->changed = TRUE; + } + + goto next_output; + } + + /* rotation */ + g_snprintf (property, sizeof (property), "/%s/%s/Rotation", scheme, info->name); + value = g_hash_table_lookup (saved_outputs, property); + if (G_VALUE_HOLDS_INT (value)) + int_value = g_value_get_int (value); + else + int_value = 0; + + /* convert to a Rotation */ + switch (int_value) + { + case 90: rot = RR_Rotate_90; break; + case 180: rot = RR_Rotate_180; break; + case 270: rot = RR_Rotate_270; break; + default: rot = RR_Rotate_0; break; + } + + /* reflection */ + g_snprintf (property, sizeof (property), "/%s/%s/Reflection", scheme, info->name); + value = g_hash_table_lookup (saved_outputs, property); + if (G_VALUE_HOLDS_STRING (value)) + str_value = g_value_get_string (value); + else + str_value = "0"; + + /* convert to a Rotation */ + if (g_strcmp0 (str_value, "X") == 0) + rot |= RR_Reflect_X; + else if (g_strcmp0 (str_value, "Y") == 0) + rot |= RR_Reflect_Y; + else if (g_strcmp0 (str_value, "XY") == 0) + rot |= (RR_Reflect_X|RR_Reflect_Y); + + /* check rotation support */ + if ((crtc->rotations & rot) == 0) + { + g_warning ("Unsupported rotation for %s. Fallback to RR_Rotate_0.", info->name); + rot = RR_Rotate_0; + } + + /* update CRTC rotation */ + if (crtc->rotation != rot) + { + crtc->rotation = rot; + crtc->changed = TRUE; + } + + /* resolution */ + g_snprintf (property, sizeof (property), "/%s/%s/Resolution", scheme, info->name); + value = g_hash_table_lookup (saved_outputs, property); + if (value == NULL || !G_VALUE_HOLDS_STRING (value)) + str_value = ""; + else + str_value = g_value_get_string (value); + + /* refresh rate */ + g_snprintf (property, sizeof (property), "/%s/%s/RefreshRate", scheme, info->name); + value = g_hash_table_lookup (saved_outputs, property); + if (G_VALUE_HOLDS_DOUBLE (value)) + output_rate = g_value_get_double (value); + else + output_rate = 0.0; + + /* check mode validity */ + valid_mode = None; + for (n = 0; n < info->nmode; ++n) + { + /* walk all modes */ + for (m = 0; m < helper->resources->nmode; ++m) + { + /* does the mode info match the mode we seek? */ + if (helper->resources->modes[m].id != info->modes[n]) + continue; + + /* calculate the refresh rate */ + rate = (gdouble) helper->resources->modes[m].dotClock / + ((gdouble) helper->resources->modes[m].hTotal * (gdouble) helper->resources->modes[m].vTotal); + + /* find the mode corresponding to the saved values */ + if (rint (rate) == rint (output_rate) + && (g_strcmp0 (helper->resources->modes[m].name, str_value) == 0)) + { + valid_mode = helper->resources->modes[m].id; + break; + } + } + /* found it */ + if (valid_mode != None) + break; + } + + if (valid_mode == None) + { + /* unsupported mode, abort for this output */ + g_warning ("Unknown mode '%s @ %.1f' for output %s, aborting.", + str_value, output_rate, info->name); + goto next_output; + } + else if (crtc->mode != valid_mode) + { + if (crtc->mode == None) + active = TRUE; + + /* update CRTC mode */ + crtc->mode = valid_mode; + crtc->changed = TRUE; + } + + /* recompute dimensions according to the selected rotation */ + if ((crtc->rotation & (RR_Rotate_90|RR_Rotate_270)) != 0) + { + crtc->width = helper->resources->modes[m].height; + crtc->height = helper->resources->modes[m].width; + } + else + { + crtc->width = helper->resources->modes[m].width; + crtc->height = helper->resources->modes[m].height; + } + + /* position, x */ + g_snprintf (property, sizeof (property), "/%s/%s/Position/X", scheme, info->name); + value = g_hash_table_lookup (saved_outputs, property); + if (G_VALUE_HOLDS_INT (value)) + x = g_value_get_int (value); + else + x = 0; + + /* position, y */ + g_snprintf (property, sizeof (property), "/%s/%s/Position/Y", scheme, + info->name); + value = g_hash_table_lookup (saved_outputs, property); + if (G_VALUE_HOLDS_INT (value)) + y = g_value_get_int (value); + else + y = 0; + + /* update CRTC position */ + if (crtc->x != x || crtc->y != y) + { + crtc->x = x; + crtc->y = y; + crtc->changed = TRUE; + } + + /* used to normalize positions later */ + helper->min_x = MIN (helper->min_x, crtc->x); + helper->min_y = MIN (helper->min_y, crtc->y); + + xfce_displays_helper_set_outputs (crtc, output); + +next_output: + XRRFreeOutputInfo (info); + return active; } static GPtrArray * -xfce_displays_helper_list_outputs (Display *xdisplay, - XRRScreenResources *resources, - XfceRRCrtc *crtcs, - gint *nactive) +xfce_displays_helper_list_crtcs (XfceDisplaysHelper *helper) { - GPtrArray *outputs; - XRROutputInfo *info; - XfceRROutput *output; - XfceRRCrtc *crtc; - gint n; - - g_assert (xdisplay && resources && nactive); + GPtrArray *crtcs; + XRRCrtcInfo *crtc_info; + XfceRRCrtc *crtc; + gint n, err; - outputs = g_ptr_array_new (); + g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources); - /* get all connected outputs */ - *nactive = 0; - for (n = 0; n < resources->noutput; ++n) + /* get all existing CRTCs */ + crtcs = g_ptr_array_new_with_free_func ((GDestroyNotify) xfce_displays_helper_free_crtc); + for (n = 0; n < helper->resources->ncrtc; ++n) { - info = XRRGetOutputInfo (xdisplay, resources, resources->outputs[n]); + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Detected CRTC %lu.", helper->resources->crtcs[n]); - if (info->connection != RR_Connected) + gdk_error_trap_push (); + crtc_info = XRRGetCrtcInfo (helper->xdisplay, helper->resources, helper->resources->crtcs[n]); + gdk_flush (); + err = gdk_error_trap_pop (); + if (err || !crtc_info) { - XRRFreeOutputInfo (info); + g_warning ("Failed to load info for CRTC %lu (err: %d). Skipping.", + helper->resources->crtcs[n], err); continue; } - output = g_new0 (XfceRROutput, 1); - output->id = resources->outputs[n]; - output->info = info; + crtc = g_new0 (XfceRRCrtc, 1); + crtc->id = helper->resources->crtcs[n]; + crtc->mode = crtc_info->mode; + crtc->rotation = crtc_info->rotation; + crtc->rotations = crtc_info->rotations; + crtc->width = crtc_info->width; + crtc->height = crtc_info->height; + crtc->x = crtc_info->x; + crtc->y = crtc_info->y; + + crtc->noutput = crtc_info->noutput; + crtc->outputs = NULL; + if (crtc_info->noutput > 0) + crtc->outputs = g_memdup (crtc_info->outputs, + crtc_info->noutput * sizeof (RROutput)); - xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Detected output %lu %s.", output->id, - output->info->name); + crtc->npossible = crtc_info->npossible; + crtc->possible = NULL; + if (crtc_info->npossible > 0) + crtc->possible = g_memdup (crtc_info->possible, + crtc_info->npossible * sizeof (RROutput)); - /* track active outputs */ - crtc = xfce_displays_helper_find_crtc_by_id (resources, crtcs, - output->info->crtc); - if (crtc && crtc->mode != None) - { - xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "%s is active.", output->info->name); - ++(*nactive); - } + crtc->changed = FALSE; + XRRFreeCrtcInfo (crtc_info); /* cache it */ - g_ptr_array_add (outputs, output); + g_ptr_array_add (crtcs, crtc); } - return outputs; + return crtcs; } static XfceRRCrtc * -xfce_displays_helper_find_usable_crtc (XRRScreenResources *resources, - XfceRRCrtc *crtcs, - XfceRROutput *output) +xfce_displays_helper_find_crtc_by_id (XfceDisplaysHelper *helper, + RRCrtc id) +{ + XfceRRCrtc *crtc; + guint n; + + g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->crtcs); + + for (n = 0; n < helper->crtcs->len; ++n) + { + crtc = g_ptr_array_index (helper->crtcs, n); + if (crtc->id == id) + return crtc; + } + + return NULL; +} + + + +static void +xfce_displays_helper_free_crtc (XfceRRCrtc *crtc) { - gint m, n; + if (crtc == NULL) + return; + + if (crtc->outputs != NULL) + g_free (crtc->outputs); + if (crtc->possible != NULL) + g_free (crtc->possible); + g_free (crtc); +} - g_assert (resources && crtcs && output); - /* if there is one already assigned, return it */ - if (output->info->crtc != None) - return xfce_displays_helper_find_crtc_by_id (resources, crtcs, - output->info->crtc); + +static XfceRRCrtc * +xfce_displays_helper_find_usable_crtc (XfceDisplaysHelper *helper, + RROutput output) +{ + XfceRRCrtc *crtc; + guint n; + gint m; + + g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->crtcs); /* try to find one that is not already used by another output */ - for (n = 0; n < resources->ncrtc; ++n) + for (n = 0; n < helper->crtcs->len; ++n) { - if (crtcs[n].noutput > 0 || crtcs[n].changed) + crtc = g_ptr_array_index (helper->crtcs, n); + if (crtc->noutput > 0 || crtc->changed) continue; - for (m = 0; m < crtcs[n].npossible; ++m) + for (m = 0; m < crtc->npossible; ++m) { - if (crtcs[n].possible[m] == output->id) - return &crtcs[n]; + if (crtc->possible[m] == output) + return crtc; } } @@ -365,43 +777,110 @@ xfce_displays_helper_find_usable_crtc (XRRScreenResources *resources, +static void +xfce_displays_helper_normalize_crtc (XfceRRCrtc *crtc, + XfceDisplaysHelper *helper) +{ + g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && crtc); + + /* ignore disabled outputs for size computations */ + if (crtc->mode == None) + return; + + /* normalize positions to ensure the upper left corner is at (0,0) */ + if (helper->min_x || helper->min_y) + { + crtc->x -= helper->min_x; + crtc->y -= helper->min_y; + crtc->changed = TRUE; + } + + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Normalized CRTC %lu: size=%dx%d, pos=%dx%d.", + crtc->id, crtc->width, crtc->height, crtc->x, crtc->y); + + /* calculate the total screen size */ + helper->width = MAX (helper->width, crtc->x + crtc->width); + helper->height = MAX (helper->height, crtc->y + crtc->height); + + /* The 'physical size' of an X screen is meaningless if that screen + * can consist of many monitors. So just pick a size that make the + * dpi 96. + * + * Firefox and Evince apparently believe what X tells them. + */ + helper->mm_width = (helper->width / 96.0) * 25.4 + 0.5; + helper->mm_height = (helper->height / 96.0) * 25.4 + 0.5; +} + + + static Status -xfce_displays_helper_disable_crtc (Display *xdisplay, - XRRScreenResources *resources, +xfce_displays_helper_disable_crtc (XfceDisplaysHelper *helper, RRCrtc crtc) { - g_assert (xdisplay && resources); + g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources); xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Disabling CRTC %lu.", crtc); - return XRRSetCrtcConfig (xdisplay, resources, crtc, CurrentTime, - 0, 0, None, RR_Rotate_0, NULL, 0); + return XRRSetCrtcConfig (helper->xdisplay, helper->resources, crtc, + CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0); } -static Status -xfce_displays_helper_apply_crtc (Display *xdisplay, - XRRScreenResources *resources, - XfceRRCrtc *crtc) +static void +xfce_displays_helper_workaround_crtc_size (XfceRRCrtc *crtc, + XfceDisplaysHelper *helper) +{ + XRRCrtcInfo *crtc_info; + + g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources && crtc); + + /* The CRTC needs to be disabled if its previous mode won't fit in the new screen. + It will be reenabled with its new mode (known to fit) after the screen size is + changed, unless the user disabled it (no need to reenable it then). */ + crtc_info = XRRGetCrtcInfo (helper->xdisplay, helper->resources, crtc->id); + if ((crtc_info->x + crtc_info->width > (guint) helper->width) || + (crtc_info->y + crtc_info->height > (guint) helper->height)) + { + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "CRTC %lu must be temporarily disabled.", crtc->id); + if (xfce_displays_helper_disable_crtc (helper, crtc->id) == RRSetConfigSuccess) + crtc->changed = (crtc->mode != None); + else + g_warning ("Failed to temporarily disable CRTC %lu.", crtc->id); + } + XRRFreeCrtcInfo (crtc_info); +} + + + +static void +xfce_displays_helper_apply_crtc (XfceRRCrtc *crtc, + XfceDisplaysHelper *helper) { Status ret; - g_assert (xdisplay && resources && crtc); + g_assert (XFCE_IS_DISPLAYS_HELPER (helper) && helper->xdisplay && helper->resources && crtc); xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Configuring CRTC %lu.", crtc->id); - if (crtc->mode == None) - ret = xfce_displays_helper_disable_crtc (xdisplay, resources, crtc->id); - else - ret = XRRSetCrtcConfig (xdisplay, resources, crtc->id, CurrentTime, - crtc->x, crtc->y, crtc->mode, crtc->rotation, - crtc->outputs, crtc->noutput); + /* check if we really need to do something */ + if (crtc->changed) + { + xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Applying changes to CRTC %lu.", crtc->id); - if (ret == RRSetConfigSuccess) - crtc->changed = FALSE; + if (crtc->mode == None) + ret = xfce_displays_helper_disable_crtc (helper, crtc->id); + else + ret = XRRSetCrtcConfig (helper->xdisplay, helper->resources, crtc->id, + CurrentTime, crtc->x, crtc->y, crtc->mode, + crtc->rotation, crtc->outputs, crtc->noutput); - return ret; + if (ret == RRSetConfigSuccess) + crtc->changed = FALSE; + else + g_warning ("Failed to configure CRTC %lu.", crtc->id); + } } @@ -446,58 +925,16 @@ static void xfce_displays_helper_channel_apply (XfceDisplaysHelper *helper, const gchar *scheme) { - GdkDisplay *display; - Display *xdisplay; - GdkWindow *root_window; - XRRScreenResources *resources; - XRRCrtcInfo *crtc_info; - XfceRRCrtc *crtcs, *crtc; - gchar property[512]; - gint min_width, min_height, max_width, max_height; - gint mm_width, mm_height, width, height; - gint x, y, min_x, min_y; - gint l, m, int_value, nactive; - guint n; - GValue *value; - const gchar *str_value; - gdouble output_rate, rate; - RRMode valid_mode; - Rotation rot; - GPtrArray *connected_outputs; - GHashTable *saved_outputs; - XfceRROutput *output; -#ifdef HAS_RANDR_ONE_POINT_THREE - RROutput primary = None; -#endif - - gdk_error_trap_push (); + gchar property[512]; + gint n, nactive; + GHashTable *saved_outputs; saved_outputs = NULL; - - /* get the default display */ - display = gdk_display_get_default (); - xdisplay = gdk_x11_display_get_xdisplay (display); - root_window = gdk_get_default_root_window (); - - /* get the screen resource */ - resources = XRRGetScreenResources (xdisplay, GDK_WINDOW_XID (root_window)); - - /* get the range of screen sizes */ - mm_width = mm_height = width = height = 0; - min_x = min_y = 32768; - if (!XRRGetScreenSizeRange (xdisplay, GDK_WINDOW_XID (root_window), - &min_width, &min_height, &max_width, &max_height)) - { - g_critical ("Unable to get the range of screen sizes, aborting."); - goto err_abort; - } - - /* get all existing CRTCs */ - crtcs = xfce_displays_helper_list_crtcs (xdisplay, resources); - - /* then all connected outputs */ - connected_outputs = xfce_displays_helper_list_outputs (xdisplay, resources, - crtcs, &nactive); + helper->mm_width = helper->mm_height = helper->width = helper->height = 0; + helper->min_x = helper->min_y = 32768; +#ifdef HAS_RANDR_ONE_POINT_THREE + helper->primary = None; +#endif /* finally the list of saved outputs from xfconf */ g_snprintf (property, sizeof (property), "/%s", scheme); @@ -508,221 +945,12 @@ xfce_displays_helper_channel_apply (XfceDisplaysHelper *helper, goto err_cleanup; /* first loop, loads all the outputs, and gets the number of active ones */ - for (n = 0; n < connected_outputs->len; ++n) + nactive = 0; + for (n = 0; n < helper->resources->noutput; ++n) { - output = g_ptr_array_index (connected_outputs, n); - - /* does this output exist in xfconf? */ - g_snprintf (property, sizeof (property), "/%s/%s", scheme, - output->info->name); - value = g_hash_table_lookup (saved_outputs, property); - - if (value == NULL || !G_VALUE_HOLDS_STRING (value)) - continue; - -#ifdef HAS_RANDR_ONE_POINT_THREE - if (helper->has_1_3) - { - /* is it the primary output? */ - g_snprintf (property, sizeof (property), "/%s/%s/Primary", scheme, - output->info->name); - value = g_hash_table_lookup (saved_outputs, property); - if (G_VALUE_HOLDS_BOOLEAN (value) && g_value_get_boolean (value)) - primary = output->id; - } -#endif - - /* status */ - g_snprintf (property, sizeof (property), "/%s/%s/Active", scheme, - output->info->name); - value = g_hash_table_lookup (saved_outputs, property); - - if (value == NULL || !G_VALUE_HOLDS_BOOLEAN (value)) - continue; - - /* Pick a CRTC for this output */ - crtc = xfce_displays_helper_find_usable_crtc (resources, crtcs, output); - if (!crtc) - { - g_warning ("No available CRTC for %s, aborting.", output->info->name); - continue; - } - xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "CRTC %lu assigned to %s.", crtc->id, - output->info->name); - - /* disable inactive outputs */ - if (!g_value_get_boolean (value)) - { - if (crtc->mode != None) - { - --nactive; - xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "%s will be disabled by configuration.", - output->info->name); - - crtc->mode = None; - crtc->noutput = 0; - crtc->changed = TRUE; - } - - continue; - } - - /* rotation */ - g_snprintf (property, sizeof (property), "/%s/%s/Rotation", scheme, - output->info->name); - value = g_hash_table_lookup (saved_outputs, property); - if (G_VALUE_HOLDS_INT (value)) - int_value = g_value_get_int (value); - else - int_value = 0; - - /* convert to a Rotation */ - switch (int_value) - { - case 90: rot = RR_Rotate_90; break; - case 180: rot = RR_Rotate_180; break; - case 270: rot = RR_Rotate_270; break; - default: rot = RR_Rotate_0; break; - } - - /* reflection */ - g_snprintf (property, sizeof (property), "/%s/%s/Reflection", scheme, - output->info->name); - value = g_hash_table_lookup (saved_outputs, property); - if (G_VALUE_HOLDS_STRING (value)) - str_value = g_value_get_string (value); - else - str_value = "0"; - - /* convert to a Rotation */ - if (g_strcmp0 (str_value, "X") == 0) - rot |= RR_Reflect_X; - else if (g_strcmp0 (str_value, "Y") == 0) - rot |= RR_Reflect_Y; - else if (g_strcmp0 (str_value, "XY") == 0) - rot |= (RR_Reflect_X|RR_Reflect_Y); - - /* check rotation support */ - if ((crtc->rotations & rot) == 0) - { - g_warning ("Unsupported rotation for %s. Fallback to RR_Rotate_0.", - output->info->name); - rot = RR_Rotate_0; - } - - /* update CRTC rotation */ - if (crtc->rotation != rot) - { - crtc->rotation = rot; - crtc->changed = TRUE; - } - - /* resolution */ - g_snprintf (property, sizeof (property), "/%s/%s/Resolution", - scheme, output->info->name); - value = g_hash_table_lookup (saved_outputs, property); - if (value == NULL || !G_VALUE_HOLDS_STRING (value)) - str_value = ""; - else - str_value = g_value_get_string (value); - - /* refresh rate */ - g_snprintf (property, sizeof (property), "/%s/%s/RefreshRate", scheme, - output->info->name); - value = g_hash_table_lookup (saved_outputs, property); - if (G_VALUE_HOLDS_DOUBLE (value)) - output_rate = g_value_get_double (value); - else - output_rate = 0.0; - - /* check mode validity */ - valid_mode = None; - for (m = 0; m < output->info->nmode; ++m) - { - /* walk all modes */ - for (l = 0; l < resources->nmode; ++l) - { - /* does the mode info match the mode we seek? */ - if (resources->modes[l].id != output->info->modes[m]) - continue; - - /* calculate the refresh rate */ - rate = (gdouble) resources->modes[l].dotClock / - ((gdouble) resources->modes[l].hTotal * (gdouble) resources->modes[l].vTotal); - - /* find the mode corresponding to the saved values */ - if (rint (rate) == rint (output_rate) - && (g_strcmp0 (resources->modes[l].name, str_value) == 0)) - { - valid_mode = resources->modes[l].id; - break; - } - } - /* found it */ - if (valid_mode != None) - break; - } - - if (valid_mode == None) - { - /* unsupported mode, abort for this output */ - g_warning ("Unknown mode '%s @ %.1f' for output %s, aborting.", - str_value, output_rate, output->info->name); - continue; - } - else if (crtc->mode != valid_mode) - { - if (crtc->mode == None) - ++nactive; - - /* update CRTC mode */ - crtc->mode = valid_mode; - crtc->changed = TRUE; - } - - /* recompute dimensions according to the selected rotation */ - if ((crtc->rotation & (RR_Rotate_90|RR_Rotate_270)) != 0) - { - crtc->width = resources->modes[l].height; - crtc->height = resources->modes[l].width; - } - else - { - crtc->width = resources->modes[l].width; - crtc->height = resources->modes[l].height; - } - - /* position, x */ - g_snprintf (property, sizeof (property), "/%s/%s/Position/X", scheme, - output->info->name); - value = g_hash_table_lookup (saved_outputs, property); - if (G_VALUE_HOLDS_INT (value)) - x = g_value_get_int (value); - else - x = 0; - - /* position, y */ - g_snprintf (property, sizeof (property), "/%s/%s/Position/Y", scheme, - output->info->name); - value = g_hash_table_lookup (saved_outputs, property); - if (G_VALUE_HOLDS_INT (value)) - y = g_value_get_int (value); - else - y = 0; - - /* update CRTC position */ - if (crtc->x != x || crtc->y != y) - { - crtc->x = x; - crtc->y = y; - crtc->changed = TRUE; - } - - /* used to normalize positions later */ - min_x = MIN (min_x, crtc->x); - min_y = MIN (min_y, crtc->y); - - xfce_displays_helper_set_outputs (crtc, output->id); + if (xfce_displays_helper_load_from_xfconf (helper, scheme, saved_outputs, + helper->resources->outputs[n])) + ++nactive; } xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Total %d active output(s).", nactive); @@ -734,109 +962,41 @@ xfce_displays_helper_channel_apply (XfceDisplaysHelper *helper, goto err_cleanup; } - /* grab server to prevent clients from thinking no output is enabled */ - gdk_x11_display_grab (display); - /* second loop, normalization and screen size calculation */ - for (m = 0; m < resources->ncrtc; ++m) - { - /* ignore disabled outputs for size computations */ - if (crtcs[m].mode == None) - continue; + g_ptr_array_foreach (helper->crtcs, (GFunc) xfce_displays_helper_normalize_crtc, helper); - /* normalize positions to ensure the upper left corner is at (0,0) */ - if (min_x || min_y) - { - crtcs[m].x -= min_x; - crtcs[m].y -= min_y; - crtcs[m].changed = TRUE; - } - - xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Normalized CRTC %lu: size=%dx%d, pos=%dx%d.", - crtcs[m].id, crtcs[m].width, crtcs[m].height, crtcs[m].x, crtcs[m].y); + gdk_error_trap_push (); - /* calculate the total screen size */ - xfce_displays_helper_process_screen_size (crtcs[m].width, crtcs[m].height, - crtcs[m].x, crtcs[m].y, &width, - &height, &mm_width, &mm_height); - } + /* grab server to prevent clients from thinking no output is enabled */ + gdk_x11_display_grab (helper->display); /* disable CRTCs that won't fit in the new screen */ - for (m = 0; m < resources->ncrtc; ++m) - { - /* The CRTC needs to be disabled if its previous mode won't fit in the new screen. - It will be reenabled with its new mode (known to fit) after the screen size is - changed, unless the user disabled it (no need to reenable it then). */ - crtc_info = XRRGetCrtcInfo (xdisplay, resources, crtcs[m].id); - if ((crtc_info->x + crtc_info->width > (guint) width) || - (crtc_info->y + crtc_info->height > (guint) height)) - { - xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "CRTC %lu must be temporarily disabled.", crtcs[m].id); - if (xfce_displays_helper_disable_crtc (xdisplay, resources, crtcs[m].id) == RRSetConfigSuccess) - crtcs[m].changed = (crtcs[m].mode != None); - else - g_warning ("Failed to temporarily disable CRTC %lu.", crtc->id); - } - XRRFreeCrtcInfo (crtc_info); - } + g_ptr_array_foreach (helper->crtcs, (GFunc) xfce_displays_helper_workaround_crtc_size, helper); /* set the screen size only if it's really needed and valid */ - if (width >= min_width && width <= max_width - && height >= min_height && height <= max_height - && (width != gdk_screen_width () - || height != gdk_screen_height () - || mm_width != gdk_screen_width_mm () - || mm_height != gdk_screen_height_mm ())) - { - xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Applying desktop dimensions: %dx%d (px), %dx%d (mm).", - width, height, mm_width, mm_height); - XRRSetScreenSize (xdisplay, GDK_WINDOW_XID (root_window), - width, height, mm_width, mm_height); - } + xfce_displays_helper_set_screen_size (helper); /* final loop, apply crtc changes */ - for (m = 0; m < resources->ncrtc; ++m) - { - /* check if we really need to do something */ - if (crtcs[m].changed) - { - xfsettings_dbg (XFSD_DEBUG_DISPLAYS, "Applying changes to CRTC %lu.", crtcs[m].id); - - if (xfce_displays_helper_apply_crtc (xdisplay, resources, &crtcs[m]) != RRSetConfigSuccess) - g_warning ("Failed to configure CRTC %lu.", crtcs[m].id); - } - } + g_ptr_array_foreach (helper->crtcs, (GFunc) xfce_displays_helper_apply_crtc, helper); #ifdef HAS_RANDR_ONE_POINT_THREE if (helper->has_1_3) - XRRSetOutputPrimary (xdisplay, GDK_WINDOW_XID (root_window), primary); + XRRSetOutputPrimary (helper->xdisplay, GDK_WINDOW_XID (helper->root_window), + helper->primary); #endif /* release the grab, changes are done */ - gdk_x11_display_ungrab (display); + gdk_x11_display_ungrab (helper->display); + gdk_flush (); + gdk_error_trap_pop (); + + /* refresh the cache (better safe than sorry) */ + xfce_displays_helper_reload (helper); err_cleanup: /* Free the xfconf properties */ if (saved_outputs) g_hash_table_destroy (saved_outputs); - - /* Free our output cache */ - g_ptr_array_foreach (connected_outputs, (GFunc) xfce_displays_helper_free_output, NULL); - g_ptr_array_free (connected_outputs, TRUE); - - /* cleanup our CRTC cache */ - for (m = 0; m < resources->ncrtc; ++m) - { - xfce_displays_helper_cleanup_crtc (&crtcs[m]); - } - g_free (crtcs); - -err_abort: - /* free the screen resources */ - XRRFreeScreenResources (resources); - - gdk_flush (); - gdk_error_trap_pop (); } _______________________________________________ Xfce4-commits mailing list Xfce4-commits@xfce.org https://mail.xfce.org/mailman/listinfo/xfce4-commits