diff --git a/src/core/monitor-config.c b/src/core/monitor-config.c index e18880497..b62b4e297 100644 --- a/src/core/monitor-config.c +++ b/src/core/monitor-config.c @@ -740,16 +740,21 @@ init_key_from_output (MetaOutputKey *key, static void make_config_key (MetaConfiguration *key, MetaOutput *outputs, - unsigned n_outputs) + unsigned n_outputs, + unsigned skip) { - unsigned int i; + unsigned int o, i; - key->n_outputs = n_outputs; key->outputs = NULL; key->keys = g_new0 (MetaOutputKey, n_outputs); - for (i = 0; i < key->n_outputs; i++) - init_key_from_output (&key->keys[i], &outputs[i]); + for (o = 0, i = 0; i < n_outputs; o++, i++) + if (i == skip) + o--; + else + init_key_from_output (&key->keys[o], &outputs[i]); + + key->n_outputs = o; } gboolean @@ -766,7 +771,7 @@ meta_monitor_config_match_current (MetaMonitorConfig *self, outputs = meta_monitor_manager_get_outputs (manager, &n_outputs); - make_config_key (&key, outputs, n_outputs); + make_config_key (&key, outputs, n_outputs, -1); ok = config_equal (&key, self->current); config_clear (&key); @@ -781,7 +786,7 @@ meta_monitor_config_get_stored (MetaMonitorConfig *self, MetaConfiguration key; MetaConfiguration *stored; - make_config_key (&key, outputs, n_outputs); + make_config_key (&key, outputs, n_outputs, -1); stored = g_hash_table_lookup (self->configs, &key); config_clear (&key); @@ -840,12 +845,220 @@ meta_monitor_config_apply_stored (MetaMonitorConfig *self, return FALSE; } -static MetaConfiguration * -make_default_config (MetaOutput *outputs, - unsigned n_outputs) +/* + * Tries to find the primary output according to the current layout, + * or failing that, an output that is good to be a primary (LVDS or eDP, + * which are internal monitors), or failing that, the one with the + * best resolution + * + * Input assertions: there is at least one output + */ +static MetaOutput * +find_primary_output (MetaOutput *outputs, + unsigned n_outputs) { - /* FIXME */ - return NULL; + unsigned i; + MetaOutput *best; + int best_width, best_height; + + for (i = 0; i < n_outputs; i++) + { + if (outputs[i].is_primary) + return &outputs[i]; + } + + for (i = 0; i < n_outputs; i++) + { + if (g_str_has_prefix (outputs[i].name, "LVDS") || + g_str_has_prefix (outputs[i].name, "eDP")) + return &outputs[i]; + } + + best = NULL; + best_width = 0; best_height = 0; + for (i = 0; i < n_outputs; i++) + { + if (outputs[i].preferred_mode->width * outputs[i].preferred_mode->height > + best_width * best_height) + { + best = &outputs[i]; + best_width = outputs[i].preferred_mode->width; + best_height = outputs[i].preferred_mode->height; + } + } + + return best; +} + +static MetaConfiguration * +make_default_config (MetaMonitorConfig *self, + MetaOutput *outputs, + unsigned n_outputs, + int max_width, + int max_height) +{ + unsigned i, j; + int x, y; + MetaConfiguration *ret; + MetaOutput *primary; + + ret = g_slice_new (MetaConfiguration); + make_config_key (ret, outputs, n_outputs, -1); + ret->outputs = g_new0 (MetaOutputConfig, n_outputs); + + /* Special case the simple case: one output, primary at preferred mode, + nothing else to do */ + if (n_outputs == 1) + { + ret->outputs[0].enabled = TRUE; + ret->outputs[0].rect.x = 0; + ret->outputs[0].rect.y = 0; + ret->outputs[0].rect.width = outputs[0].preferred_mode->width; + ret->outputs[0].rect.height = outputs[0].preferred_mode->height; + ret->outputs[0].refresh_rate = outputs[0].preferred_mode->refresh_rate; + ret->outputs[0].transform = WL_OUTPUT_TRANSFORM_NORMAL; + ret->outputs[0].is_primary = TRUE; + + return ret; + } + + /* If we reach this point, this is either the first time mutter runs + on this system ever, or we just hotplugged a new screen. + In the latter case, search for a configuration that includes one + less screen, then add the new one as a presentation screen + in preferred mode. + */ + x = 0; + y = 0; + for (i = 0; i < n_outputs; i++) + { + MetaConfiguration key; + MetaConfiguration *ref; + + make_config_key (&key, outputs, n_outputs, i); + ref = g_hash_table_lookup (self->configs, &key); + config_clear (&key); + + if (ref) + { + for (j = 0; j < n_outputs; j++) + { + if (j < i) + { + g_assert (output_key_equal (&ret->keys[j], &ref->keys[j])); + ret->outputs[j] = ref->outputs[j]; + x = MAX (x, ref->outputs[j].rect.x + ref->outputs[j].rect.width); + y = MAX (y, ref->outputs[j].rect.y + ref->outputs[j].rect.height); + } + else if (j > i) + { + g_assert (output_key_equal (&ret->keys[i], &ref->keys[j - 1])); + ret->outputs[j] = ref->outputs[j - 1]; + x = MAX (x, ref->outputs[j - 1].rect.x + ref->outputs[j - 1].rect.width); + y = MAX (y, ref->outputs[j - 1].rect.y + ref->outputs[j - 1].rect.height); + } + else + { + ret->outputs[j].enabled = TRUE; + ret->outputs[j].rect.x = 0; + ret->outputs[j].rect.y = 0; + ret->outputs[j].rect.width = outputs[0].preferred_mode->width; + ret->outputs[j].rect.height = outputs[0].preferred_mode->height; + ret->outputs[j].refresh_rate = outputs[0].preferred_mode->refresh_rate; + ret->outputs[j].transform = WL_OUTPUT_TRANSFORM_NORMAL; + ret->outputs[j].is_primary = FALSE; + ret->outputs[j].is_presentation = TRUE; + } + } + + /* Place the new output at the right end of the screen, if it fits, + otherwise below it, otherwise disable it (or apply_configuration will fail) */ + if (x + ret->outputs[i].rect.width <= max_width) + ret->outputs[i].rect.x = x; + else if (y + ret->outputs[i].rect.height <= max_height) + ret->outputs[i].rect.y = y; + else + ret->outputs[i].enabled = FALSE; + + return ret; + } + } + + /* No previous configuration found, try with a really default one, which + is one primary that goes first and the rest to the right of it, extended. + */ + primary = find_primary_output (outputs, n_outputs); + + x = primary->preferred_mode->width; + for (i = 0; i < n_outputs; i++) + { + MetaOutput *output = &outputs[i]; + + ret->outputs[i].enabled = TRUE; + ret->outputs[i].rect.x = (output == primary) ? 0 : x; + ret->outputs[i].rect.y = 0; + ret->outputs[i].rect.width = output->preferred_mode->width; + ret->outputs[i].rect.height = output->preferred_mode->height; + ret->outputs[i].refresh_rate = output->preferred_mode->refresh_rate; + ret->outputs[i].transform = WL_OUTPUT_TRANSFORM_NORMAL; + ret->outputs[i].is_primary = (output == primary); + + /* Disable outputs that would go beyond framebuffer limits */ + if (ret->outputs[i].rect.x + ret->outputs[i].rect.width > max_width) + ret->outputs[i].enabled = FALSE; + else if (output != primary) + x += output->preferred_mode->width; + } + + return ret; +} + +static gboolean +ensure_at_least_one_output (MetaMonitorConfig *self, + MetaMonitorManager *manager, + MetaOutput *outputs, + unsigned n_outputs) +{ + MetaConfiguration *ret; + MetaOutput *primary; + unsigned i; + + /* Check that we have at least one active output */ + for (i = 0; i < n_outputs; i++) + if (outputs[i].crtc != NULL) + return TRUE; + + /* Oh no, we don't! Activate the primary one and disable everything else */ + + ret = g_slice_new (MetaConfiguration); + make_config_key (ret, outputs, n_outputs, -1); + ret->outputs = g_new0 (MetaOutputConfig, n_outputs); + + primary = find_primary_output (outputs, n_outputs); + + for (i = 0; i < n_outputs; i++) + { + MetaOutput *output = &outputs[i]; + + if (output == primary) + { + ret->outputs[i].enabled = TRUE; + ret->outputs[i].rect.x = 0; + ret->outputs[i].rect.y = 0; + ret->outputs[i].rect.width = output->preferred_mode->width; + ret->outputs[i].rect.height = output->preferred_mode->height; + ret->outputs[i].refresh_rate = output->preferred_mode->refresh_rate; + ret->outputs[i].transform = WL_OUTPUT_TRANSFORM_NORMAL; + ret->outputs[i].is_primary = TRUE; + } + else + { + ret->outputs[i].enabled = FALSE; + } + } + + apply_configuration (self, ret, manager, FALSE); + return FALSE; } void @@ -855,16 +1068,24 @@ meta_monitor_config_make_default (MetaMonitorConfig *self, MetaOutput *outputs; MetaConfiguration *default_config; unsigned n_outputs; + gboolean ok; + int max_width, max_height; outputs = meta_monitor_manager_get_outputs (manager, &n_outputs); - default_config = make_default_config (outputs, n_outputs); + meta_monitor_manager_get_screen_limits (manager, &max_width, &max_height); + + default_config = make_default_config (self, outputs, n_outputs, max_width, max_height); if (default_config != NULL) - apply_configuration (self, default_config, manager, FALSE); + ok = apply_configuration (self, default_config, manager, FALSE); else + ok = FALSE; + + if (!ok) { meta_warning ("Could not make default configuration for current output layout, leaving unconfigured\n"); - meta_monitor_config_update_current (self, manager); + if (ensure_at_least_one_output (self, manager, outputs, n_outputs)) + meta_monitor_config_update_current (self, manager); } } @@ -1297,9 +1518,6 @@ real_assign_crtcs (CrtcAssignment *assignment, } out: - if (!success) - meta_warning ("Could not assign CRTC to outputs, ignoring configuration\n"); - return success; } @@ -1323,6 +1541,8 @@ meta_monitor_config_assign_crtcs (MetaConfiguration *config, if (!real_assign_crtcs (&assignment, 0)) { + meta_warning ("Could not assign CRTC to outputs, ignoring configuration\n"); + g_hash_table_destroy (assignment.info); return FALSE; } @@ -1341,10 +1561,10 @@ meta_monitor_config_assign_crtcs (MetaConfiguration *config, for (i = 0; i < n_outputs; i++) { MetaOutputInfo *output_info = g_slice_new (MetaOutputInfo); - MetaOutputConfig *output_config = &config->outputs[0]; + MetaOutputConfig *output_config = &config->outputs[i]; output_info->output = find_output_by_key (all_outputs, n_outputs, - &config->keys[0]); + &config->keys[i]); output_info->is_primary = output_config->is_primary; output_info->is_presentation = output_config->is_presentation; diff --git a/src/core/monitor-private.h b/src/core/monitor-private.h index af37c7e53..14237bd5d 100644 --- a/src/core/monitor-private.h +++ b/src/core/monitor-private.h @@ -229,6 +229,10 @@ void meta_monitor_manager_get_screen_size (MetaMonitorManager * int *width, int *height); +void meta_monitor_manager_get_screen_limits (MetaMonitorManager *manager, + int *width, + int *height); + void meta_monitor_manager_apply_configuration (MetaMonitorManager *manager, MetaCRTCInfo **crtcs, unsigned int n_crtcs, diff --git a/src/core/monitor.c b/src/core/monitor.c index 7749e9f91..54112bd7f 100644 --- a/src/core/monitor.c +++ b/src/core/monitor.c @@ -64,6 +64,7 @@ struct _MetaMonitorManager packed, but I like the logical organization of fields */ + gboolean in_init; unsigned int serial; MetaPowerSave power_save_mode; @@ -501,6 +502,9 @@ read_monitor_infos_from_xrandr (MetaMonitorManager *manager) } meta_output->preferred_mode = meta_output->modes[0]; + if (meta_output->preferred_mode == NULL) + meta_output->preferred_mode = meta_output->modes[0]; + meta_output->n_possible_crtcs = output->ncrtc; meta_output->possible_crtcs = g_new0 (MetaCRTC *, meta_output->n_possible_crtcs); for (j = 0; j < (unsigned)output->ncrtc; j++) @@ -739,6 +743,7 @@ meta_monitor_manager_new (Display *display) manager = g_object_new (META_TYPE_MONITOR_MANAGER, NULL); + manager->in_init = TRUE; manager->xdisplay = display; manager->backend = make_debug_config (manager); @@ -806,8 +811,10 @@ meta_monitor_manager_new (Display *display) g_free (old_modes); g_free (old_crtcs); } - + make_logical_config (manager); + + manager->in_init = FALSE; return manager; } @@ -1529,6 +1536,8 @@ meta_monitor_manager_handle_apply_configuration (MetaDBusDisplayConfig *skeleto "Mode specified without outputs?"); return TRUE; } + + g_ptr_array_add (crtc_infos, crtc_info); } g_variant_iter_init (&output_iter, outputs); @@ -1553,6 +1562,8 @@ meta_monitor_manager_handle_apply_configuration (MetaDBusDisplayConfig *skeleto if (g_variant_lookup (properties, "presentation", "b", &presentation)) output_info->is_presentation = presentation; + + g_ptr_array_add (output_infos, output_info); } /* If we were in progress of making a persistent change and we see a @@ -1703,6 +1714,15 @@ meta_monitor_manager_get_screen_size (MetaMonitorManager *manager, *height = manager->screen_height; } +void +meta_monitor_manager_get_screen_limits (MetaMonitorManager *manager, + int *width, + int *height) +{ + *width = manager->max_screen_width; + *height = manager->max_screen_height; +} + static void invalidate_logical_config (MetaMonitorManager *manager) { @@ -1710,6 +1730,9 @@ invalidate_logical_config (MetaMonitorManager *manager) old_monitor_infos = manager->monitor_infos; + if (manager->in_init) + return; + make_logical_config (manager); g_signal_emit (manager, signals[MONITORS_CHANGED], 0);