#include "e_randr_private.h" #include "e_randr.h" #define MODE_STR_LENGTH_MAX 100 static const char *_POLICIES_STRINGS[] = {"ABOVE", "RIGHT", "BELOW", "LEFT", "CLONE", "NONE"}; static E_Randr_Serialized_Crtc *_serialized_crtc_new(E_Randr_Crtc_Info *crtc_info); static inline int _sort_by_number_of_edids(const void *d1, const void *d2); static inline Eina_List *_find_matching_outputs(Eina_List *sois); static inline E_Randr_Crtc_Info *_find_matching_crtc(E_Randr_Serialized_Crtc *sc); static inline Ecore_X_Randr_Mode_Info *_find_matching_mode_info(Ecore_X_Randr_Mode_Info *mode); /********************************************************************** * * Storage/Restorage of setups * ********************************************************************** */ /* * Layout: * * Serialized_Setup_12 { * - timestamp * - List * - List * } * Serialized_Crtc { * - index * - List * - pos * - orientation * - mode * } * Serialized_Output { * - name * - name_length * - serialized_edid * - backlight_level * } * Serialized_EDID { * - edid_hash * } * */ //"Free" helper functions void _serialized_output_free(E_Randr_Serialized_Output *so) { EINA_SAFETY_ON_NULL_RETURN(so); eina_stringshare_del(so->name); free(so); } void _serialized_output_policy_free(E_Randr_Serialized_Output_Policy *sop) { EINA_SAFETY_ON_NULL_RETURN(sop); eina_stringshare_del(sop->name); free(sop); } EINTERN void e_randr_12_serialized_output_policy_free(E_Randr_Serialized_Output_Policy *policy) { _serialized_output_policy_free(policy); } void _mode_info_free(Ecore_X_Randr_Mode_Info *mode_info) { EINA_SAFETY_ON_NULL_RETURN(mode_info); eina_stringshare_del(mode_info->name); free(mode_info); } void _serialized_crtc_free(E_Randr_Serialized_Crtc *sc) { E_Randr_Serialized_Output *so; EINA_SAFETY_ON_NULL_RETURN(sc); EINA_LIST_FREE (sc->outputs, so) _serialized_output_free(so); _mode_info_free(sc->mode_info); free(sc); } //"New" helper functions /** * @brief Seeks given data in the list and returns the index * of the first element equaling @data * @param list The list to be examined * @param data The data to be found * @return if found, the index of the list node. Else -1. */ int _eina_list_data_index_get(const Eina_List *list, const void *data) { Eina_List *iter; void *ndata; int i = 0; EINA_LIST_REVERSE_FOREACH(list, iter, ndata) { if (ndata == data) return i; else i++; } return -1; } Ecore_X_Randr_Mode_Info *_mode_info_clone(const Ecore_X_Randr_Mode_Info *src) { Ecore_X_Randr_Mode_Info *mi = NULL; EINA_SAFETY_ON_NULL_RETURN_VAL(src, NULL); mi = E_NEW(Ecore_X_Randr_Mode_Info, 1); mi->xid = src->xid; mi->width = src->width; mi->height = src->height; mi->dotClock = src->dotClock; mi->hSyncStart = src->hSyncStart; mi->hSyncEnd = src->hSyncEnd; mi->hTotal = src->hTotal; mi->hSkew = src->hSkew; mi->vSyncStart = src->vSyncStart; mi->vSyncEnd = src->vSyncEnd; mi->vTotal = src->vTotal; if (src->nameLength > 0) { mi->name = (char*)eina_stringshare_add(src->name); } mi->nameLength = src->nameLength; mi->modeFlags = src->modeFlags; return mi; } E_Randr_Edid_Hash *_monitor_edid_hash_clone(E_Randr_Monitor_Info *mi) { E_Randr_Edid_Hash *edid_hash; EINA_SAFETY_ON_NULL_RETURN_VAL(mi, NULL); EINA_SAFETY_ON_TRUE_RETURN_VAL((mi->edid_hash.hash == 0), NULL); edid_hash = malloc(sizeof(E_Randr_Edid_Hash)); edid_hash->hash = mi->edid_hash.hash; return edid_hash; } Eina_List * _outputs_policies_list_new(Eina_List *outputs) { E_Randr_Serialized_Output_Policy *sop; Eina_List *iter, *list = NULL; E_Randr_Output_Info *oi; EINA_SAFETY_ON_NULL_RETURN_VAL(outputs, NULL); EINA_LIST_FOREACH(outputs, iter, oi) { if (!oi->name) continue; sop = E_NEW(E_Randr_Serialized_Output_Policy, 1); sop->name = eina_stringshare_add(oi->name); sop->policy = oi->policy; list = eina_list_append(list, sop); } return list; } E_Randr_Serialized_Output * _serialized_output_new(E_Randr_Output_Info *output_info) { E_Randr_Serialized_Output *so; EINA_SAFETY_ON_NULL_RETURN_VAL(output_info, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(output_info->name, NULL); so = E_NEW(E_Randr_Serialized_Output, 1); so->name = eina_stringshare_add(output_info->name); if (output_info->monitor) { so->backlight_level = output_info->monitor->backlight_level; } else { so->backlight_level = Ecore_X_Randr_Unset; } return so; } E_Randr_Serialized_Crtc * _serialized_crtc_new(E_Randr_Crtc_Info *crtc_info) { E_Randr_Serialized_Crtc *sc = NULL; E_Randr_Serialized_Output *so = NULL; E_Randr_Output_Info *output_info = NULL; Eina_List *iter; EINA_SAFETY_ON_NULL_RETURN_VAL(crtc_info, NULL); sc = E_NEW(E_Randr_Serialized_Crtc, 1); //Get relative index of CRTC sc->index = _eina_list_data_index_get(e_randr_screen_info.rrvd_info.randr_info_12->crtcs, crtc_info); //Create list of serialized outputs EINA_LIST_FOREACH(crtc_info->outputs, iter, output_info) { if (!(so = _serialized_output_new(output_info))) continue; sc->outputs = eina_list_append(sc->outputs, so); INF("E_RANDR:\t Serialized output %s.", so->name); } sc->pos.x = crtc_info->geometry.x; sc->pos.y = crtc_info->geometry.y; sc->orientation = crtc_info->current_orientation; //Clone mode sc->mode_info = _mode_info_clone(crtc_info->current_mode); return sc; } E_Randr_Serialized_Setup_12 * _12_serialized_setup_new(void) { E_Randr_Serialized_Setup_12 *ss = NULL; Eina_List *iter; E_Randr_Crtc_Info *ci; E_Randr_Output_Info *oi; E_Randr_Serialized_Crtc *sc; E_Randr_Edid_Hash *edid_hash; EINA_SAFETY_ON_TRUE_RETURN_VAL(E_RANDR_12_NO, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(e_randr_screen_info.rrvd_info.randr_info_12->outputs, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(e_randr_screen_info.rrvd_info.randr_info_12->crtcs, NULL); ss = E_NEW(E_Randr_Serialized_Setup_12, 1); ss->timestamp = ecore_time_get(); //Add CRTCs and their configuration EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->crtcs, iter, ci) { sc = _serialized_crtc_new(ci); if (!sc) continue; ss->crtcs = eina_list_append(ss->crtcs, sc); INF("E_RANDR: Serialized CRTC %d (index %d) in mode %s.", ci->xid, sc->index, (sc->mode_info ? sc->mode_info->name : "(disabled)")); } /* * Add EDID hashes of connected outputs * for easier comparison during * setup restoration */ EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->outputs, iter, oi) { if (oi->connection_status != ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED) continue; edid_hash = _monitor_edid_hash_clone(oi->monitor); ss->edid_hashes = eina_list_append(ss->edid_hashes, edid_hash); } return ss; } //Update (also retrieval) helper functions E_Randr_Serialized_Setup_12 * _matching_serialized_setup_get(Eina_List *setups_12) { E_Randr_Serialized_Setup_12 *ss_12; Eina_List *setups_iter, *edid_iter; E_Randr_Edid_Hash *edid_hash; EINA_SAFETY_ON_TRUE_RETURN_VAL(E_RANDR_12_NO, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(setups_12, NULL); //Sort list of setups by the number of monitors involved setups_12 = eina_list_sort(setups_12, 0, _sort_by_number_of_edids); EINA_LIST_FOREACH(setups_12, setups_iter, ss_12) { //1. Make sure: #outputs >= #serialized EDIDs if (eina_list_count(e_randr_screen_info.rrvd_info.randr_info_12->outputs) < eina_list_count(ss_12->edid_hashes)) continue; //2. Compare #CRTCs if (eina_list_count(e_randr_screen_info.rrvd_info.randr_info_12->crtcs) != eina_list_count(ss_12->crtcs)) continue; //3. Find all serialized EDIDs EINA_LIST_FOREACH(ss_12->edid_hashes, edid_iter, edid_hash) { if (!_12_screen_info_edid_is_available(edid_hash)) goto _setup_12_skip; } //4. All EDIDs found? Great, let's go! return ss_12; _setup_12_skip: continue; } // None found! return NULL; } Eina_List * _outputs_policies_update(Eina_List *sops) { E_Randr_Serialized_Output_Policy *sop; EINA_LIST_FREE (sops, sop) { _serialized_output_policy_free(sop); } return _outputs_policies_list_new(e_randr_screen_info.rrvd_info.randr_info_12->outputs); } Eina_List * _12_serialized_setup_update(Eina_List *setups_12) { E_Randr_Serialized_Setup_12 *ss_12; if (setups_12) { /* * try to find the setup with the same monitors * connected in order to replace it */ if ((ss_12 = _matching_serialized_setup_get(setups_12))) { INF("E_RANDR: Found stored configuration that matches current setup. It was created at %f. Freeing it...", ss_12->timestamp); _12_serialized_setup_free(ss_12); setups_12 = eina_list_remove(setups_12, ss_12); } } ss_12 = _12_serialized_setup_new(); setups_12 = eina_list_append(setups_12, ss_12); return setups_12; } void _12_policies_restore(void) { E_Randr_Output_Info *output; E_Randr_Serialized_Output_Policy *sop; Eina_List *iter, *iter2; EINA_SAFETY_ON_TRUE_RETURN(E_RANDR_12_NO); EINA_SAFETY_ON_NULL_RETURN(e_config->randr_serialized_setup); EINA_SAFETY_ON_NULL_RETURN(e_config->randr_serialized_setup->outputs_policies); // Restore policies EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->outputs, iter, output) { EINA_LIST_FOREACH(e_config->randr_serialized_setup->outputs_policies, iter2, sop) { if (!strncmp(sop->name, output->name, output->name_length)) { output->policy = sop->policy; INF("E_RANDR: Policy \"%s\" for output \"%s\" restored.", _POLICIES_STRINGS[sop->policy - 1], output->name); } } } } Eina_Bool _12_try_restore_configuration(void) { E_Randr_Serialized_Setup_12 *ss_12; E_Randr_Serialized_Crtc *sc; E_Randr_Crtc_Info *ci; Ecore_X_Randr_Output *outputs_array; E_Randr_Output_Info *output_info; Ecore_X_Randr_Mode_Info *mi = NULL; Ecore_X_Randr_Mode mode = 0; Eina_List *iter, *outputs_list, *outputs_iter; Eina_Bool ret = EINA_TRUE; EINA_SAFETY_ON_NULL_RETURN_VAL(e_config, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(e_config->randr_serialized_setup, EINA_FALSE); if (!(ss_12 = _matching_serialized_setup_get(e_config->randr_serialized_setup->serialized_setups_12))) return EINA_FALSE; INF("E_RANDR: Found matching serialized setup."); EINA_LIST_FOREACH(ss_12->crtcs, iter, sc) { ci = _find_matching_crtc(sc); if (!ci) { ERR("E_RANDR: Cannot find a matching CRTC for serialized CRTC index %d.", sc->index); return EINA_FALSE; } outputs_list = _find_matching_outputs(sc->outputs); outputs_array = _outputs_to_array(outputs_list); if (!sc->mode_info) { INF("E_RANDR: \tSerialized mode was disabled."); mode = Ecore_X_Randr_None; } else if ((mi = _find_matching_mode_info(sc->mode_info))) { INF("E_RANDR: \tSerialized mode is now known under the name %s.", mi->name); mode = mi->xid; } else if (mi) /* FIXME: this is impossible, so whoever wrote it probably meant something else */ { // The serialized mode is no longer available mi->name = malloc(MODE_STR_LENGTH_MAX); //IMPROVABLE: Use random string, like mktemp for files snprintf(mi->name, (MODE_STR_LENGTH_MAX - 1), "%ux%u,%lu,%lu", sc->mode_info->width, sc->mode_info->height, sc->mode_info->dotClock, sc->mode_info->modeFlags); mi = sc->mode_info; mode = ecore_x_randr_mode_info_add(e_randr_screen_info.root, mi); if (mode == Ecore_X_Randr_None) { eina_list_free(outputs_list); free(outputs_array); continue; } EINA_LIST_FOREACH(outputs_list, outputs_iter, output_info) ecore_x_randr_output_mode_add(output_info->xid, mode); INF("E_RANDR: \tSerialized mode was added to the server manually using the name %s.", mi->name); } // DEBUG if (mi) DBG("E_RANDR: \tRestoring CRTC %d (index %d) in mode %s.", ci->xid, sc->index, (mode == Ecore_X_Randr_None) ? "(disabled)" : mi->name); DBG("E_RANDR: \t\tUsed outputs:"); EINA_LIST_FOREACH(outputs_list, outputs_iter, output_info) DBG("\t\t%s", output_info->name); // DEBUG END ret &= ecore_x_randr_crtc_settings_set(e_randr_screen_info.root, ci->xid, outputs_array, eina_list_count(outputs_list), sc->pos.x, sc->pos.y, mode, sc->orientation); eina_list_free(outputs_list); free(outputs_array); } return ret; } void _12_serialized_setup_free(E_Randr_Serialized_Setup_12 *ss_12) { E_Randr_Serialized_Crtc *sc; E_Randr_Edid_Hash *edid_hash; if (!ss_12) return; EINA_LIST_FREE (ss_12->crtcs, sc) { _serialized_crtc_free(sc); } EINA_LIST_FREE (ss_12->edid_hashes, edid_hash) free(edid_hash); free(ss_12); } EINTERN void e_randr_12_serialized_setup_free(E_Randr_Serialized_Setup_12 *ss_12) { _12_serialized_setup_free(ss_12); } void _12_store_configuration(E_Randr_Configuration_Store_Modifier modifier) { if (modifier & (E_RANDR_CONFIGURATION_STORE_RESOLUTIONS | E_RANDR_CONFIGURATION_STORE_ARRANGEMENT | E_RANDR_CONFIGURATION_STORE_ORIENTATIONS)) { e_config->randr_serialized_setup->serialized_setups_12 = _12_serialized_setup_update(e_config->randr_serialized_setup->serialized_setups_12); } if (modifier & E_RANDR_CONFIGURATION_STORE_POLICIES) { //update output policies e_config->randr_serialized_setup->outputs_policies = _outputs_policies_update(e_config->randr_serialized_setup->outputs_policies); } } //Retrievel functions for the current e_randr_screen_info context // Find entities for restoration in current e_randr_screen_info context static E_Randr_Crtc_Info * _find_matching_crtc(E_Randr_Serialized_Crtc *sc) { EINA_SAFETY_ON_NULL_RETURN_VAL(sc, NULL); EINA_SAFETY_ON_TRUE_RETURN_VAL(E_RANDR_12_NO, NULL); INF("E_RANDR: Setup restore.. Runtime system knows about %d CRTCs. Requested CRTC has index %d", eina_list_count(e_randr_screen_info.rrvd_info.randr_info_12->crtcs), sc->index); return eina_list_nth(e_randr_screen_info.rrvd_info.randr_info_12->crtcs, sc->index); } /** * @brief Creates list of E_Randr_Output_Info* elements for list of * E_Randr_Serialized_Output* objects. * @param sois list of E_Randr_Serialized_Output* elements * @return List of E_Randr_Output* elements or NULL, if not all outputs could be * found or monitors are connected to different outputs */ static Eina_List * _find_matching_outputs(Eina_List *sois) { Eina_List *r_output_iter, *s_output_iter, *list = NULL; E_Randr_Output_Info *oi; E_Randr_Serialized_Output *so; EINA_LIST_FOREACH(sois, s_output_iter, so) { INF("E_RANDR: \tLooking for serialized output \"%s\"", so->name); EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->outputs, r_output_iter, oi) { INF("E_RANDR: \t\tComparing to output \"%s\"", oi->name); if (!strncmp(so->name, oi->name, oi->name_length)) { list = eina_list_append(list, oi); break; } } } if (list && (eina_list_count(sois) != eina_list_count(list))) { eina_list_free(list); list = NULL; } return list; } static Ecore_X_Randr_Mode_Info * _find_matching_mode_info(Ecore_X_Randr_Mode_Info *mode) { Eina_List *iter; Ecore_X_Randr_Mode_Info *mi = NULL; EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->modes, iter, mi) { if (!strncmp(mode->name, mi->name, mode->nameLength)) return mi; } EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->modes, iter, mi) { #define EQL(arg) (mi->arg == mode->arg) if (EQL(width) && EQL(height) && EQL(dotClock) && EQL(hSyncStart) && EQL(hSyncEnd) && EQL(hTotal) && EQL(hSkew) && EQL(vSyncStart) && EQL(vSyncEnd) && EQL(vTotal) && EQL(modeFlags)) return mi; #undef EQL } return NULL; } static int _sort_by_number_of_edids(const void *d1, const void *d2) { const E_Randr_Serialized_Setup_12 *ss1 = (const E_Randr_Serialized_Setup_12*)d1; const E_Randr_Serialized_Setup_12 *ss2 = (const E_Randr_Serialized_Setup_12*)d2; EINA_SAFETY_ON_NULL_RETURN_VAL(ss1, 1); EINA_SAFETY_ON_NULL_RETURN_VAL(ss2, -1); if (eina_list_count(ss2->edid_hashes) > eina_list_count(ss1->edid_hashes)) return 1; else return -1; }