forked from enlightenment/enlightenment
334 lines
12 KiB
C
334 lines
12 KiB
C
#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;
|
|
|
|
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);
|
|
}
|
|
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;
|
|
|
|
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]);
|
|
crtc_info->possible_outputs = eina_list_append(crtc_info->possible_outputs, output_info);
|
|
}
|
|
|
|
_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);
|
|
|
|
EINA_SAFETY_ON_TRUE_RETURN_VAL((crtc == Ecore_X_Randr_None), crtc_info);
|
|
|
|
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.
|
|
*/
|
|
const Eina_Bool
|
|
_crtc_move_policy(E_Randr_Crtc_Info *new_crtc)
|
|
{
|
|
const E_Randr_Crtc_Info *crtc_rel;
|
|
E_Randr_Output_Info *first_output = NULL;
|
|
int dx = Ecore_X_Randr_None, dy = Ecore_X_Randr_None;
|
|
Eina_Bool ret = EINA_TRUE;
|
|
|
|
//use the policy of the new crtc's first output
|
|
first_output = (E_Randr_Output_Info *)eina_list_data_get(new_crtc->outputs);
|
|
if (!first_output)
|
|
return 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, first_output->policy)))
|
|
return EINA_TRUE;
|
|
|
|
//following is policy dependend.
|
|
switch (first_output->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);
|
|
}
|
|
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);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
ret &= ecore_x_randr_crtc_pos_relative_set(e_randr_screen_info.root, new_crtc->xid, crtc_rel->xid, first_output->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;
|
|
}
|