#include "e_randr_private.h" #include "e_randr.h" // E_Randr_Crtc_Info helper functions static Eina_Bool _crtc_mode_intersects_crtcs(E_Randr_Crtc_Info *crtc_info, Ecore_X_Randr_Mode_Info *mode); static Eina_Bool _crtc_outputs_mode_max_set(E_Randr_Crtc_Info *crtc_info); void _crtc_outputs_refs_set(E_Randr_Crtc_Info *crtc_info) { E_Randr_Output_Info *output_info = NULL; Ecore_X_Randr_Output *outputs = NULL; int noutputs = 0; EINA_SAFETY_ON_NULL_RETURN(crtc_info); outputs = ecore_x_randr_crtc_outputs_get(e_randr_screen_info.root, crtc_info->xid, &noutputs); while (--noutputs >= 0) { output_info = _12_screen_info_output_info_get(outputs[noutputs]); if (!output_info) fprintf(stderr, "E_RANDR: Could not find output struct for output %d.\n", outputs[noutputs]); crtc_info->outputs = eina_list_append(crtc_info->outputs, output_info); } free(outputs); crtc_info->outputs_common_modes = _outputs_common_modes_get(crtc_info->outputs, NULL); } void _crtc_refs_set(E_Randr_Crtc_Info *crtc_info) { Ecore_X_Randr_Mode mode = Ecore_X_Randr_None; Ecore_X_Randr_Mode_Info *mode_info = NULL; Ecore_X_Randr_Output *poutputs = NULL; E_Randr_Output_Info *output_info = NULL; int npoutputs = 0; EINA_SAFETY_ON_NULL_RETURN(crtc_info); mode = ecore_x_randr_crtc_mode_get(e_randr_screen_info.root, crtc_info->xid); if (!(mode_info = _12_screen_info_mode_info_get(mode))) { //Mode unknown to the global structure, so add it mode_info = ecore_x_randr_mode_info_get(e_randr_screen_info.root, mode); e_randr_screen_info.rrvd_info.randr_info_12->modes = eina_list_append(e_randr_screen_info.rrvd_info.randr_info_12->modes, mode_info); } crtc_info->current_mode = mode_info; poutputs = ecore_x_randr_crtc_possible_outputs_get(e_randr_screen_info.root, crtc_info->xid, &npoutputs); while (--npoutputs >= 0) { output_info = _12_screen_info_output_info_get(poutputs[npoutputs]); if (!output_info) { fprintf(stderr, "E_RANDR: Could not find output struct for output %d.\n", poutputs[npoutputs]); continue; } crtc_info->possible_outputs = eina_list_append(crtc_info->possible_outputs, output_info); } free(poutputs); _crtc_outputs_refs_set(crtc_info); } /** * @brief Allocate and init with values. * @param crtc the crtc the display is queried for. If Ecore_X_Randr_None is * given, a struct with only the xid will be set * @return E_Randr_Crtc_Info element */ E_Randr_Crtc_Info * _crtc_info_new(Ecore_X_Randr_Crtc crtc) { E_Randr_Crtc_Info *crtc_info = NULL; EINA_SAFETY_ON_TRUE_RETURN_VAL(E_RANDR_12_NO, NULL); crtc_info = E_NEW(E_Randr_Crtc_Info, 1); crtc_info->xid = crtc; crtc_info->panning.x = Ecore_X_Randr_Unset; crtc_info->panning.y = Ecore_X_Randr_Unset; crtc_info->panning.w = Ecore_X_Randr_Unset; crtc_info->panning.h = Ecore_X_Randr_Unset; crtc_info->tracking.x = Ecore_X_Randr_Unset; crtc_info->tracking.y = Ecore_X_Randr_Unset; crtc_info->tracking.w = Ecore_X_Randr_Unset; crtc_info->tracking.h = Ecore_X_Randr_Unset; crtc_info->border.x = Ecore_X_Randr_Unset; crtc_info->border.y = Ecore_X_Randr_Unset; crtc_info->border.w = Ecore_X_Randr_Unset; crtc_info->border.h = Ecore_X_Randr_Unset; crtc_info->gamma_ramps = NULL; crtc_info->gamma_ramp_size = Ecore_X_Randr_Unset; crtc_info->outputs = NULL; crtc_info->possible_outputs = NULL; crtc_info->outputs_common_modes = NULL; crtc_info->current_mode = NULL; ecore_x_randr_crtc_geometry_get(e_randr_screen_info.root, crtc_info->xid, &crtc_info->geometry.x, &crtc_info->geometry.y, &crtc_info->geometry.w, &crtc_info->geometry.h); crtc_info->current_orientation = ecore_x_randr_crtc_orientation_get(e_randr_screen_info.root, crtc_info->xid); crtc_info->orientations = ecore_x_randr_crtc_orientations_get(e_randr_screen_info.root, crtc_info->xid); return crtc_info; } /** * @param crtc_info the crtc info to be freed. */ void _crtc_info_free(E_Randr_Crtc_Info *crtc_info) { EINA_SAFETY_ON_NULL_RETURN(crtc_info); if (crtc_info->gamma_ramps) free(crtc_info->gamma_ramps); if (crtc_info->outputs) { eina_list_free(crtc_info->outputs); crtc_info->outputs = NULL; } if (crtc_info->possible_outputs) { eina_list_free(crtc_info->possible_outputs); crtc_info->possible_outputs = NULL; } } /* * returns EINA_TRUE if given CRTC would intersect with other CRTCs if set to * given mode */ static Eina_Bool _crtc_mode_intersects_crtcs(E_Randr_Crtc_Info *crtc_info, Ecore_X_Randr_Mode_Info *mode) { Eina_List *iter; E_Randr_Crtc_Info *tmp; EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->crtcs, iter, tmp) { if ((tmp == crtc_info) || ((tmp->geometry.w <= 0) || (tmp->geometry.h <= 0))) continue; if (E_INTERSECTS(crtc_info->geometry.x, crtc_info->geometry.y, mode->width, mode->height, tmp->geometry.x, tmp->geometry.y, tmp->geometry.w, tmp->geometry.h) && ((crtc_info->geometry.x != tmp->geometry.x) && (crtc_info->geometry.y != tmp->geometry.y))) return EINA_TRUE; } return EINA_FALSE; } /* * reconfigures a CRTC enabling the highest resolution amongst its outputs, * without touching any other CRTC currently activated */ static Eina_Bool _crtc_outputs_mode_max_set(E_Randr_Crtc_Info *crtc_info) { Ecore_X_Randr_Mode_Info *mode_info; Eina_List *iter; Eina_Bool ret = EINA_TRUE; Ecore_X_Randr_Output *outputs; if (!crtc_info || !crtc_info->outputs || !crtc_info->outputs_common_modes) return EINA_FALSE; EINA_LIST_REVERSE_FOREACH(crtc_info->outputs_common_modes, iter, mode_info) { if (!_crtc_mode_intersects_crtcs(crtc_info, mode_info)) break; } if (!mode_info) { //eina_list_free(crtc_info->outputs_common_modes); return EINA_FALSE; } if ((outputs = _outputs_to_array(crtc_info->outputs))) { ret = ecore_x_randr_crtc_mode_set(e_randr_screen_info.root, crtc_info->xid, outputs, eina_list_count(crtc_info->outputs), mode_info->xid); free(outputs); } //eina_list_free(crtc_info->outputs_common_modes); //crtc_info->outputs_common_modes = NULL; ecore_x_randr_screen_reset(e_randr_screen_info.root); return ret; } /* * reconfigure screen setup according to policy. This is only required if all * CRTCs' positions might be affected by the another screens' movement. This includes 'virtual' moves, * which means that e.g. when a crtc should be placed at a position < 0, all * other crtcs are accordingly moved instead, so the result is the same. */ Eina_Bool _crtc_move_policy(E_Randr_Crtc_Info *new_crtc, Ecore_X_Randr_Output_Policy policy) { const E_Randr_Crtc_Info *crtc_rel; int dx = Ecore_X_Randr_None, dy = Ecore_X_Randr_None; Eina_Bool ret = EINA_TRUE; EINA_SAFETY_ON_NULL_RETURN_VAL(new_crtc, EINA_FALSE); //get the crtc we will place our's relative to. If it's NULL, this is the //only output attached, work done. if (!(crtc_rel = _crtc_according_to_policy_get(new_crtc, policy))) { fprintf(stderr, "E_RANDR: Moving CRTC %d, there is no other CRTC to move this one relative to.\n", new_crtc->xid); return EINA_TRUE; } fprintf(stderr, "E_RANDR: Moved CRTC %d has geometry (x,y,wxh): %d, %d, %dx%d.\n", new_crtc->xid, new_crtc->geometry.x, new_crtc->geometry.y, new_crtc->geometry.w, new_crtc->geometry.h); //following is policy dependend. switch (policy) { case ECORE_X_RANDR_OUTPUT_POLICY_ABOVE: dy = (crtc_rel->geometry.y - new_crtc->geometry.h); if (dy < 0) { //virtual move (move other CRTCs as nessesary) dy = -dy; ret = ecore_x_randr_move_all_crtcs_but(e_randr_screen_info.root, &new_crtc->xid, 1, dx, dy); fprintf(stderr, "E_RANDR: Moving all CRTCs but %d, by %dx%d delta.\n", new_crtc->xid, dx, dy); } break; case ECORE_X_RANDR_OUTPUT_POLICY_LEFT: dx = (crtc_rel->geometry.x - new_crtc->geometry.w); if (dx < 0) { //virtual move (move other CRTCs as nessesary) dx = -dx; ret = ecore_x_randr_move_all_crtcs_but(e_randr_screen_info.root, &new_crtc->xid, 1, dx, dy); fprintf(stderr, "E_RANDR: Moving all CRTCs but %d, by %dx%d delta.\n", new_crtc->xid, dx, dy); } break; default: break; } ret &= ecore_x_randr_crtc_pos_relative_set(e_randr_screen_info.root, new_crtc->xid, crtc_rel->xid, policy, e_randr_screen_info.rrvd_info.randr_info_12->alignment); return ret; } /* * this retrieves a CRTC depending on a policy. * Note that this is enlightenment specific! Enlightenment doesn't 'allow' zones * to overlap. Thus we always use the output with the most extreme position * instead of trying to fill gaps like tetris. Though this could be done by * simply implementing another policy. * * Simply put: get the * -right * -left * -top * -bottom * most CRTC and return it. */ const E_Randr_Crtc_Info * _crtc_according_to_policy_get(E_Randr_Crtc_Info *but, Ecore_X_Randr_Output_Policy policy) { Eina_List *iter, *possible_crtcs = NULL; E_Randr_Crtc_Info *crtc_info, *ret = NULL; EINA_SAFETY_ON_TRUE_RETURN_VAL(E_RANDR_12_NO_CRTCS, NULL); //get any crtc that besides 'but' to start with possible_crtcs = eina_list_clone(e_randr_screen_info.rrvd_info.randr_info_12->crtcs); possible_crtcs = eina_list_remove(possible_crtcs, but); if ((eina_list_count(possible_crtcs) == 0) && (policy != ECORE_X_RANDR_OUTPUT_POLICY_CLONE)) { eina_list_free(possible_crtcs); return NULL; } // get an initial value for ret ret = (E_Randr_Crtc_Info*)eina_list_data_get(eina_list_last(possible_crtcs)); switch (policy) { case ECORE_X_RANDR_OUTPUT_POLICY_ABOVE: EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->crtcs, iter, crtc_info) { if (crtc_info->geometry.y < ret->geometry.y) ret = crtc_info; } break; case ECORE_X_RANDR_OUTPUT_POLICY_RIGHT: EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->crtcs, iter, crtc_info) { if ((crtc_info->geometry.x + crtc_info->geometry.w) > (ret->geometry.x + ret->geometry.w)) ret = crtc_info; } break; case ECORE_X_RANDR_OUTPUT_POLICY_BELOW: EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->crtcs, iter, crtc_info) { if ((crtc_info->geometry.y + crtc_info->geometry.h) > (ret->geometry.y + ret->geometry.h)) ret = crtc_info; } break; case ECORE_X_RANDR_OUTPUT_POLICY_LEFT: EINA_LIST_FOREACH(e_randr_screen_info.rrvd_info.randr_info_12->crtcs, iter, crtc_info) { if (crtc_info->geometry.x < ret->geometry.x) ret = crtc_info; } break; case ECORE_X_RANDR_OUTPUT_POLICY_CLONE: ret = (e_randr_screen_info.rrvd_info.randr_info_12->primary_output) ? e_randr_screen_info.rrvd_info.randr_info_12->primary_output->crtc : NULL; break; default: break; } eina_list_free(possible_crtcs); return ret; }