2417 lines
90 KiB
C
2417 lines
90 KiB
C
/*
|
|
* vim:ts=8:sw=3:sts=8:expandtab:cino=>5n-3f0^-2{2
|
|
*/
|
|
#include "e.h"
|
|
|
|
#define ECORE_X_RANDR_1_1 ((1 << 16) | 1)
|
|
#define ECORE_X_RANDR_1_2 ((1 << 16) | 2)
|
|
#define ECORE_X_RANDR_1_3 ((1 << 16) | 3)
|
|
|
|
#define Ecore_X_Randr_None 0
|
|
#define Ecore_X_Randr_Unset -1
|
|
|
|
#define POLLINTERVAL 128
|
|
|
|
/*
|
|
* Save mechanism:
|
|
* Single monitor:
|
|
* - Save monitor using the resolution
|
|
*
|
|
* Multiple monitors:
|
|
* - Use the EDID information to make sure we restore the right monitor.
|
|
*
|
|
* TODO:
|
|
* -fix output policies above and left
|
|
*/
|
|
|
|
//following macro namescheme follows cardinal relation
|
|
//1 : M
|
|
#define E_RANDR_NO_SCREEN_RET(ret) if (!e_randr_screen_info) \
|
|
return ret
|
|
#define E_RANDR_NO_11_RET(ret) if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_1) || !e_randr_screen_info->rrvd_info.randr_info_11) \
|
|
return ret
|
|
#define E_RANDR_NO_12_RET(ret) if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12) \
|
|
return ret
|
|
#define E_RANDR_NO_CRTCS_RET(ret) if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->crtcs) \
|
|
return ret
|
|
#define E_RANDR_NO_OUTPUTS_RET(ret) if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->outputs) \
|
|
return ret
|
|
#define E_RANDR_NO_CRTC_RET(crtc, ret) if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->crtcs || !crtc) \
|
|
return ret
|
|
#define E_RANDR_NO_OUTPUT_RET(output, ret) if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->outputs || !output) \
|
|
return ret
|
|
#define E_RANDR_NO_MODE_RET(mode, ret) if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->modes || !mode) \
|
|
return ret
|
|
#define E_RANDR_NO_CRTC_OUTPUT_RET(crtc, output, ret) if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->crtcs || !crtc || !e_randr_screen_info->rrvd_info.randr_info_12->outputs || !output) \
|
|
return ret
|
|
#define E_RANDR_NO_CRTC_OUTPUT_MODE_RET(crtc, output, mode, ret) if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->crtcs || !crtc || !e_randr_screen_info->rrvd_info.randr_info_12->outputs || !output || !e_randr_screen_info->rrvd_info.randr_info_12->modes || !mode) \
|
|
return ret
|
|
|
|
#define E_RANDR_NO_11 (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_1) || !e_randr_screen_info->rrvd_info.randr_info_11)
|
|
#define E_RANDR_NO_12 (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12)
|
|
#define E_RANDR_NO_CRTCS (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->crtcs)
|
|
#define E_RANDR_NO_OUTPUTS (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->outputs)
|
|
#define E_RANDR_NO_MODES (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->modes)
|
|
#define E_RANDR_NO_CRTC(crtc) (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->crtcs || !crtc)
|
|
#define E_RANDR_NO_OUTPUT(output) (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->outputs || !output)
|
|
#define E_RANDR_NO_CRTC_OUTPUT(crtc, output) (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->crtcs || !crtc || !e_randr_screen_info->rrvd_info.randr_info_12->outputs || !output)
|
|
#define E_RANDR_NO_CRTC_OUTPUT_MODE(crtc, output, mode) (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2) || !e_randr_screen_info->rrvd_info.randr_info_12 || !e_randr_screen_info->rrvd_info.randr_info_12->crtcs || !crtc || !e_randr_screen_info->rrvd_info.randr_info_12->outputs || !output || !e_randr_screen_info->rrvd_info.randr_info_12->modes || !mode)
|
|
|
|
static Eina_Bool _e_randr_init(void);
|
|
static void _e_randr_shutdown(void);
|
|
static Eina_Bool _e_randr_screen_info_refresh(void);
|
|
static void _e_randr_event_listeners_add(void);
|
|
static void _e_randr_event_listeners_remove(void);
|
|
static Eina_Bool _e_randr_event_cb(void *data, int type, void *e);
|
|
static Eina_Bool _e_event_config_loaded_cb(void *data, int type, void *e);
|
|
static Eina_Bool _e_randr_x_poll_cb(void *data __UNUSED__);
|
|
static E_Randr_Screen_Info *_e_randr_screen_info_new(void);
|
|
static void _e_randr_screen_info_free(E_Randr_Screen_Info *screen_info);
|
|
static E_Randr_Screen_Info_11 *_e_randr_screen_info_11_new(void);
|
|
static Eina_Bool _e_randr_screen_info_11_set(void);
|
|
static void _e_randr_screen_info_11_free(E_Randr_Screen_Info_11 *screen_info_11);
|
|
static E_Randr_Screen_Info_12 *_e_randr_screen_info_12_new(void);
|
|
static Eina_Bool _e_randr_screen_info_12_set(E_Randr_Screen_Info_12 *screen_info_12);
|
|
static void _e_randr_screen_info_12_free(E_Randr_Screen_Info_12 *screen_info_12);
|
|
static E_Randr_Output_Info *_e_randr_output_info_new(int nrequested);
|
|
static void _e_randr_output_info_free(E_Randr_Output_Info *output_info);
|
|
static E_Randr_Crtc_Info *_e_randr_crtc_info_new(int nrequested);
|
|
static void _e_randr_crtc_info_free(E_Randr_Crtc_Info *crtc_info);
|
|
static Eina_Bool _e_randr_screen_outputs_init(void);
|
|
static Eina_Bool _e_randr_screen_crtcs_init(void);
|
|
|
|
static Eina_Bool _e_randr_try_restore_11(E_Randr_Screen_Info_11 *si_11);
|
|
static Eina_Bool _e_randr_try_restore_12(E_Randr_Screen_Info_12 *si_12);
|
|
void _e_randr_restore_12_policies(E_Randr_Screen_Info_12 *si_12);
|
|
static Eina_Bool _e_randr_output_modes_add(E_Randr_Output_Info *output_info);
|
|
static void _e_randr_notify_crtc_mode_change(E_Randr_Crtc_Info *crtc_info);
|
|
static void _e_randr_notify_output_change(E_Randr_Output_Info *output_info);
|
|
static Ecore_X_Randr_Mode_Info *_e_randr_mode_info_get(Ecore_X_Randr_Mode mode);
|
|
static E_Randr_Crtc_Info *_e_randr_crtc_info_get(Ecore_X_Randr_Crtc crtc);
|
|
static E_Randr_Output_Info *_e_randr_output_info_get(Ecore_X_Randr_Output output);
|
|
static void _e_randr_output_info_set(E_Randr_Output_Info *output_info);
|
|
static void _e_randr_crtc_info_set(E_Randr_Crtc_Info *crtc_info);
|
|
static const E_Randr_Crtc_Info *_e_randr_policy_crtc_get(E_Randr_Crtc_Info *but, E_Randr_Crtc_Info *hint, Ecore_X_Randr_Output_Policy policy);
|
|
static Ecore_X_Randr_Output *_e_randr_outputs_to_array(Eina_List *outputs_info);
|
|
static Eina_Bool _e_randr_try_enable_output(E_Randr_Output_Info *output_info, Eina_Bool force);
|
|
static void _e_randr_crtcs_possible_output_update(E_Randr_Output_Info *output_info);
|
|
static void _e_randr_crtc_outputs_refs_update(E_Randr_Crtc_Info *crtc_info);
|
|
static Eina_Bool _e_randr_crtc_move_policy(E_Randr_Crtc_Info *new_crtc);
|
|
//static int _crtcs_size_sort_cb(const void *d1, const void *d2);
|
|
static int _outputs_size_sort_cb(const void *d1, const void *d2);
|
|
static int _modes_size_sort_cb(const void *d1, const void *d2);
|
|
static Eina_List *_e_randr_outputs_common_modes_get(Eina_List *outputs, Ecore_X_Randr_Mode_Info *max_size_mode);
|
|
static Ecore_X_Randr_Mode_Info *_e_randr_outputs_common_mode_max_get(Eina_List *outputs, Ecore_X_Randr_Mode_Info *max_size_mode);
|
|
static Ecore_X_Randr_Mode_Info *_e_randr_mode_geo_identical_find(Eina_List *modes, Ecore_X_Randr_Mode_Info *mode);
|
|
static Eina_Bool _e_randr_crtc_mode_intersects_crtcs(E_Randr_Crtc_Info *crtc_info, Ecore_X_Randr_Mode_Info *mode);
|
|
static Eina_Bool _e_randr_crtc_outputs_mode_max_set(E_Randr_Crtc_Info *crtc_info);
|
|
static Eina_Bool _e_randr_crtcs_clone_crtc_removed(E_Randr_Crtc_Info *former_clone);
|
|
static void _e_randr_screen_primary_output_assign(E_Randr_Output_Info *removed);
|
|
static void _e_randr_output_info_hw_info_set(E_Randr_Output_Info *output_info);
|
|
static void _e_randr_output_hw_info_free(E_Randr_Output_Info *output_info);
|
|
static Eina_Bool _e_randr_outputs_are_clones(E_Randr_Output_Info *output_info, Eina_List *outputs);
|
|
|
|
EAPI E_Randr_Screen_Info * e_randr_screen_info = NULL;
|
|
static Eina_List *_e_randr_event_handlers = NULL;
|
|
static Ecore_Poller *poller = NULL;
|
|
|
|
EINTERN Eina_Bool
|
|
e_randr_init(void)
|
|
{
|
|
return _e_randr_init();
|
|
}
|
|
|
|
EINTERN int
|
|
e_randr_shutdown(void)
|
|
{
|
|
_e_randr_shutdown();
|
|
return 1;
|
|
}
|
|
|
|
EAPI Eina_Bool
|
|
e_randr_screen_info_refresh(void)
|
|
{
|
|
return _e_randr_screen_info_refresh();
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_randr_init(void)
|
|
{
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL(e_randr_screen_info_refresh(), EINA_FALSE);
|
|
_e_randr_event_listeners_add();
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
_e_randr_shutdown(void)
|
|
{
|
|
_e_randr_screen_info_free(e_randr_screen_info);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_randr_screen_info_refresh(void)
|
|
{
|
|
int n;
|
|
Ecore_X_Window *roots;
|
|
Ecore_X_Window root;
|
|
|
|
if (e_randr_screen_info)
|
|
_e_randr_screen_info_free(e_randr_screen_info);
|
|
|
|
if (!(roots = ecore_x_window_root_list(&n))) return EINA_FALSE;
|
|
/* first (and only) root window */
|
|
root = roots[0];
|
|
free(roots);
|
|
|
|
if (!ecore_x_randr_query())
|
|
goto e_randr_screen_info_refresh_fail;
|
|
if (!(e_randr_screen_info = _e_randr_screen_info_new()))
|
|
goto e_randr_screen_info_refresh_fail_free_screen;
|
|
|
|
e_randr_screen_info->randr_version = ecore_x_randr_version_get();
|
|
e_randr_screen_info->root = root;
|
|
|
|
if (e_randr_screen_info->randr_version == ECORE_X_RANDR_1_1)
|
|
{
|
|
if (!(e_randr_screen_info->rrvd_info.randr_info_11 = _e_randr_screen_info_11_new()))
|
|
goto e_randr_screen_info_refresh_fail_free_screen;
|
|
_e_randr_screen_info_11_set();
|
|
}
|
|
else if (e_randr_screen_info->randr_version >= ECORE_X_RANDR_1_2)
|
|
{
|
|
if (!(e_randr_screen_info->rrvd_info.randr_info_12 = _e_randr_screen_info_12_new()))
|
|
goto e_randr_screen_info_refresh_fail_free_screen;
|
|
_e_randr_screen_info_12_set(e_randr_screen_info->rrvd_info.randr_info_12);
|
|
if (!_e_randr_screen_outputs_init()
|
|
|| !_e_randr_screen_crtcs_init())
|
|
goto e_randr_screen_info_refresh_fail_free_screen;
|
|
_e_randr_screen_primary_output_assign(NULL);
|
|
//also restore stored policies
|
|
_e_randr_restore_12_policies(e_randr_screen_info->rrvd_info.randr_info_12);
|
|
}
|
|
else
|
|
goto e_randr_screen_info_refresh_fail_free_screen;
|
|
|
|
return EINA_TRUE;
|
|
|
|
e_randr_screen_info_refresh_fail_free_screen:
|
|
if (e_randr_screen_info)
|
|
_e_randr_screen_info_free(e_randr_screen_info);
|
|
e_randr_screen_info_refresh_fail:
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
/**
|
|
* @param nrequeste
|
|
* @return Instance of E_Randr_Screen_Info or if memory couldn't be
|
|
* allocated NULL.
|
|
*/
|
|
static E_Randr_Screen_Info *
|
|
_e_randr_screen_info_new(void)
|
|
{
|
|
E_Randr_Screen_Info *ret = NULL;
|
|
E_Randr_Screen_Info default_info =
|
|
{
|
|
.root = Ecore_X_Randr_Unset,
|
|
.randr_version = Ecore_X_Randr_None,
|
|
.rrvd_info.randr_info_11 = NULL
|
|
};
|
|
|
|
if (!(ret = malloc(sizeof(E_Randr_Screen_Info)))) return NULL;
|
|
|
|
memcpy(ret, &default_info, sizeof(default_info));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @param screen_info the screen info to free.
|
|
*/
|
|
static void
|
|
_e_randr_screen_info_free(E_Randr_Screen_Info *screen_info)
|
|
{
|
|
if ((!screen_info) || !(screen_info->rrvd_info.randr_info_11)) return;
|
|
switch (e_randr_screen_info->randr_version)
|
|
{
|
|
case ECORE_X_RANDR_1_1:
|
|
_e_randr_screen_info_11_free(screen_info->rrvd_info.randr_info_11);
|
|
break;
|
|
|
|
case ECORE_X_RANDR_1_2:
|
|
case ECORE_X_RANDR_1_3:
|
|
_e_randr_screen_info_12_free(screen_info->rrvd_info.randr_info_12);
|
|
break;
|
|
}
|
|
free(screen_info);
|
|
screen_info = NULL;
|
|
}
|
|
|
|
/**
|
|
* @return array of E_Randr_Screen_Info_11 elements, or in case not all could
|
|
* be created or parameter 'nrequested'==0, NULL
|
|
*/
|
|
static E_Randr_Screen_Info_11 *
|
|
_e_randr_screen_info_11_new(void)
|
|
{
|
|
E_Randr_Screen_Info_11 *ret = NULL;
|
|
static const E_Randr_Screen_Info_11 default_info =
|
|
{
|
|
.sizes = NULL,
|
|
.csize_index = Ecore_X_Randr_Unset,
|
|
.corientation = Ecore_X_Randr_Unset,
|
|
.orientations = Ecore_X_Randr_Unset,
|
|
.rates = NULL,
|
|
.current_rate = Ecore_X_Randr_Unset
|
|
};
|
|
|
|
if (!(ret = malloc(sizeof(E_Randr_Screen_Info_11)))) return NULL;
|
|
|
|
ret = memcpy(ret, &default_info, sizeof(default_info));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @param screen_info the screen info to be freed.
|
|
*/
|
|
static void
|
|
_e_randr_screen_info_11_free(E_Randr_Screen_Info_11 *screen_info)
|
|
{
|
|
if (!screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_1))
|
|
return;
|
|
|
|
if (screen_info->sizes)
|
|
{
|
|
free(eina_list_nth(screen_info->sizes, 0));
|
|
eina_list_free(screen_info->sizes);
|
|
screen_info->sizes = NULL;
|
|
}
|
|
if (screen_info->rates)
|
|
{
|
|
/* this may be leaking, but at least it will be valid */
|
|
eina_list_free(eina_list_nth(screen_info->rates, 0));
|
|
eina_list_free(screen_info->rates);
|
|
screen_info->rates = NULL;
|
|
}
|
|
free(screen_info);
|
|
screen_info = NULL;
|
|
}
|
|
|
|
/**
|
|
* @return array of E_Randr_Screen_Info_12 elements, or in case not all could
|
|
* be created or parameter 'nrequested'==0, NULL
|
|
*/
|
|
static E_Randr_Screen_Info_12 *
|
|
_e_randr_screen_info_12_new(void)
|
|
{
|
|
E_Randr_Screen_Info_12 *ret = NULL;
|
|
static const E_Randr_Screen_Info_12 default_info =
|
|
{
|
|
.min_size = {Ecore_X_Randr_Unset, Ecore_X_Randr_Unset},
|
|
.max_size = {Ecore_X_Randr_Unset, Ecore_X_Randr_Unset},
|
|
.current_size = {Ecore_X_Randr_Unset, Ecore_X_Randr_Unset},
|
|
.crtcs = NULL,
|
|
.outputs = NULL,
|
|
.primary_output = NULL,
|
|
.alignment = ECORE_X_RANDR_RELATIVE_ALIGNMENT_NONE
|
|
};
|
|
|
|
if (!(ret = malloc(sizeof(E_Randr_Screen_Info_12)))) return NULL;
|
|
ret = memcpy(ret, &default_info, sizeof(default_info));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_randr_screen_info_12_set(E_Randr_Screen_Info_12 *screen_info)
|
|
{
|
|
E_RANDR_NO_12_RET(EINA_FALSE);
|
|
|
|
ecore_x_randr_screen_size_range_get(e_randr_screen_info->root,
|
|
&screen_info->min_size.width,
|
|
&screen_info->min_size.height,
|
|
&screen_info->max_size.width,
|
|
&screen_info->max_size.height);
|
|
ecore_x_randr_screen_current_size_get(e_randr_screen_info->root,
|
|
&screen_info->current_size.width,
|
|
&screen_info->current_size.height,
|
|
NULL, NULL);
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_randr_screen_info_11_set(void)
|
|
{
|
|
E_RANDR_NO_11_RET(EINA_FALSE);
|
|
|
|
E_Randr_Screen_Info_11 *screen_info_11 = e_randr_screen_info->rrvd_info.randr_info_11;
|
|
Ecore_X_Randr_Screen_Size_MM *sizes = NULL;
|
|
Ecore_X_Randr_Refresh_Rate *rates = NULL;
|
|
Eina_List *rates_list;
|
|
int i, j, nsizes, nrates;
|
|
|
|
if (!(sizes = ecore_x_randr_screen_primary_output_sizes_get(e_randr_screen_info->root, &nsizes)))
|
|
return EINA_FALSE;
|
|
for (i = 0; i < nsizes; i++)
|
|
if (!(screen_info_11->sizes = eina_list_append(screen_info_11->sizes, &sizes[i])))
|
|
goto _e_randr_screen_info_11_fill_fail_sizes;
|
|
ecore_x_randr_screen_primary_output_current_size_get(e_randr_screen_info->root, NULL, NULL, NULL, NULL, &(screen_info_11->csize_index));
|
|
screen_info_11->corientation = ecore_x_randr_screen_primary_output_orientation_get(e_randr_screen_info->root);
|
|
screen_info_11->orientations = ecore_x_randr_screen_primary_output_orientations_get(e_randr_screen_info->root);
|
|
for (i = 0; i < nsizes; i++)
|
|
{
|
|
rates_list = NULL;
|
|
if (!(rates = ecore_x_randr_screen_primary_output_refresh_rates_get(e_randr_screen_info->root, i, &nrates)))
|
|
return EINA_FALSE;
|
|
for (j = 0; j < nrates; j++)
|
|
if (!(rates_list = eina_list_append(rates_list, &rates[j])))
|
|
goto _e_randr_screen_info_11_fill_fail_rates_list;
|
|
if (!(screen_info_11->rates = eina_list_append(screen_info_11->rates, rates_list)))
|
|
goto _e_randr_screen_info_11_fill_fail_rates;
|
|
}
|
|
screen_info_11->current_rate = ecore_x_randr_screen_primary_output_current_refresh_rate_get(e_randr_screen_info->root);
|
|
|
|
return EINA_TRUE;
|
|
|
|
_e_randr_screen_info_11_fill_fail_rates_list:
|
|
eina_list_free(rates_list);
|
|
_e_randr_screen_info_11_fill_fail_rates:
|
|
free(rates);
|
|
_e_randr_screen_info_11_fill_fail_sizes:
|
|
free(sizes);
|
|
free(screen_info_11);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
/**
|
|
* @param screen_info the screen info to be freed.
|
|
*/
|
|
static void
|
|
_e_randr_screen_info_12_free(E_Randr_Screen_Info_12 *screen_info)
|
|
{
|
|
Ecore_X_Randr_Mode_Info *mode_info;
|
|
E_Randr_Crtc_Info *crtc_info;
|
|
E_Randr_Output_Info *output_info;
|
|
|
|
if (!screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2)) return;
|
|
|
|
if (e_randr_screen_info->randr_version >= ECORE_X_RANDR_1_2 && screen_info->crtcs)
|
|
{
|
|
EINA_LIST_FREE (screen_info->crtcs, crtc_info)
|
|
_e_randr_crtc_info_free(crtc_info);
|
|
free(eina_list_nth(screen_info->crtcs, 0));
|
|
}
|
|
|
|
if (e_randr_screen_info->randr_version >= ECORE_X_RANDR_1_2 && screen_info->outputs)
|
|
{
|
|
EINA_LIST_FREE (screen_info->outputs, output_info)
|
|
_e_randr_output_info_free(output_info);
|
|
free(eina_list_nth(screen_info->outputs, 0));
|
|
}
|
|
|
|
if (e_randr_screen_info->randr_version >= ECORE_X_RANDR_1_2 && screen_info->modes)
|
|
{
|
|
EINA_LIST_FREE (screen_info->modes, mode_info)
|
|
ecore_x_randr_mode_info_free(mode_info);
|
|
}
|
|
|
|
_e_randr_event_listeners_remove();
|
|
|
|
free (screen_info);
|
|
screen_info = NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief allocates structs with and fills them with default values. The
|
|
* returned pointer is allocated as one chunk of data since it won't change.
|
|
* @param nrequested number of E_Randr_Crtc_Info to be created
|
|
* @return array of E_Randr_Crtc_Info elements, or in case not all could
|
|
* be created or parameter 'nrequested'==0, NULL
|
|
*/
|
|
static E_Randr_Crtc_Info *
|
|
_e_randr_crtc_info_new(int nrequested)
|
|
{
|
|
E_Randr_Crtc_Info *ret = NULL;
|
|
static E_Randr_Crtc_Info default_info =
|
|
{
|
|
.xid = Ecore_X_Randr_Unset,
|
|
.geometry = {Ecore_X_Randr_Unset, Ecore_X_Randr_Unset, Ecore_X_Randr_Unset, Ecore_X_Randr_Unset},
|
|
.panning = {Ecore_X_Randr_Unset, Ecore_X_Randr_Unset, Ecore_X_Randr_Unset, Ecore_X_Randr_Unset},
|
|
.tracking = {Ecore_X_Randr_Unset, Ecore_X_Randr_Unset, Ecore_X_Randr_Unset, Ecore_X_Randr_Unset},
|
|
.border = {Ecore_X_Randr_Unset, Ecore_X_Randr_Unset, Ecore_X_Randr_Unset, Ecore_X_Randr_Unset},
|
|
.current_orientation = ECORE_X_RANDR_ORIENTATION_ROT_0,
|
|
.orientations = Ecore_X_Randr_Unset,
|
|
.gamma_ramps = NULL,
|
|
.gamma_ramp_size = Ecore_X_Randr_Unset,
|
|
.outputs = NULL,
|
|
.possible_outputs = NULL
|
|
};
|
|
|
|
if (!(ret = malloc(sizeof(E_Randr_Crtc_Info) * nrequested))) return NULL;
|
|
|
|
while (nrequested > 0)
|
|
{
|
|
memcpy(&ret[--nrequested], &default_info, sizeof(default_info));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @param crtc_info the crtc info to be freed.
|
|
*/
|
|
static void
|
|
_e_randr_crtc_info_free(E_Randr_Crtc_Info *crtc_info)
|
|
{
|
|
if (!crtc_info) return;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief allocates structs with and fills them with default values. The
|
|
* returned pointer is allocated as one chunk of data since it won't change.
|
|
* @param nrequested number of E_Randr_Output_Info to be created
|
|
* @return E_Randr_Output_Info element, or it could not be
|
|
* created, NULL
|
|
*/
|
|
static E_Randr_Output_Info *
|
|
_e_randr_output_info_new(int nrequested)
|
|
{
|
|
E_Randr_Output_Info *ret = NULL;
|
|
static E_Randr_Output_Info default_info =
|
|
{
|
|
.xid = Ecore_X_Randr_Unset,
|
|
.name = NULL,
|
|
.crtc = NULL,
|
|
.possible_crtcs = NULL,
|
|
.preferred_modes = NULL,
|
|
.max_backlight = Ecore_X_Randr_Unset,
|
|
.backlight_level = 0.0,
|
|
.edid = NULL,
|
|
.edid_length = 0,
|
|
.size_mm = {Ecore_X_Randr_Unset, Ecore_X_Randr_Unset},
|
|
.wired_clones = NULL,
|
|
.signalformats = Ecore_X_Randr_Unset,
|
|
.signalformat = Ecore_X_Randr_Unset,
|
|
.connector_number = Ecore_X_Randr_Unset,
|
|
.connector_type = Ecore_X_Randr_Unset,
|
|
.connection_status = ECORE_X_RANDR_CONNECTION_STATUS_DISCONNECTED,
|
|
.subpixel_order = Ecore_X_Randr_Unset,
|
|
.compatible_outputs = NULL,
|
|
.policy = ECORE_X_RANDR_OUTPUT_POLICY_NONE
|
|
};
|
|
|
|
if (!(ret = malloc(sizeof(E_Randr_Output_Info) * nrequested))) return NULL;
|
|
|
|
while (nrequested > 0)
|
|
{
|
|
memcpy(&ret[--nrequested], &default_info, sizeof(default_info));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* removes all traces of an output within the data.
|
|
* @param output_info the output info to be freed.
|
|
*/
|
|
static void
|
|
_e_randr_output_info_free(E_Randr_Output_Info *output_info)
|
|
{
|
|
Eina_List *iter;
|
|
E_Randr_Crtc_Info *crtc_info;
|
|
|
|
if (!output_info) return;
|
|
if (output_info->name)
|
|
{
|
|
free(output_info->name);
|
|
output_info->name = NULL;
|
|
}
|
|
_e_randr_output_hw_info_free(output_info);
|
|
|
|
EINA_LIST_FOREACH (e_randr_screen_info->rrvd_info.randr_info_12->crtcs, iter, crtc_info)
|
|
{
|
|
crtc_info->outputs = eina_list_remove(crtc_info->outputs, output_info);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_e_randr_output_info_set(E_Randr_Output_Info *output_info)
|
|
{
|
|
if (E_RANDR_NO_12 || !output_info) return;
|
|
|
|
output_info->name = ecore_x_randr_output_name_get(e_randr_screen_info->root, output_info->xid, &output_info->name_length);
|
|
output_info->connection_status = ecore_x_randr_output_connection_status_get(e_randr_screen_info->root, output_info->xid);
|
|
}
|
|
|
|
/*
|
|
* fills a given crtc_info using its xid with
|
|
* - geometry data (x,y,w,h)
|
|
* - used outputs structs
|
|
* - possible outputs structs
|
|
* - mode
|
|
* - connection status
|
|
* - orientation
|
|
*/
|
|
static void
|
|
_e_randr_crtc_info_set(E_Randr_Crtc_Info *crtc_info)
|
|
{
|
|
Ecore_X_Randr_Mode mode = 0;
|
|
fprintf(stderr, "E_RANDR: Filling CRTC %d (%p)\n", crtc_info->xid, crtc_info);
|
|
if (E_RANDR_NO_12 || !crtc_info) return;
|
|
|
|
//get references to used and possible E_Randr_Output_Info structs
|
|
_e_randr_crtc_outputs_refs_update(crtc_info);
|
|
|
|
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);
|
|
mode = ecore_x_randr_crtc_mode_get(e_randr_screen_info->root, crtc_info->xid);
|
|
crtc_info->current_mode = _e_randr_mode_info_get(mode);
|
|
fprintf(stderr, "E_RANDR:\t CRTC %x apparently is in mode %x, trying to find it in the list of modes..\n", crtc_info->xid, mode);
|
|
if (crtc_info->current_mode)
|
|
fprintf(stderr, "E_RANDR:\t\t found CRTC %d in mode %d\n", crtc_info->xid, crtc_info->current_mode->xid);
|
|
crtc_info->current_orientation = ecore_x_randr_crtc_orientation_get(e_randr_screen_info->root, crtc_info->xid);
|
|
if (crtc_info->outputs_common_modes)
|
|
{
|
|
eina_list_free(crtc_info->outputs_common_modes);
|
|
crtc_info->outputs_common_modes = NULL;
|
|
}
|
|
crtc_info->outputs_common_modes = _e_randr_outputs_common_modes_get(crtc_info->outputs, NULL);
|
|
}
|
|
|
|
/*
|
|
* looks up modes supported by an output and adds them - if they are not already
|
|
* known by - to the screen's information struct ant the output_info itself
|
|
*/
|
|
static Eina_Bool
|
|
_e_randr_output_modes_add(E_Randr_Output_Info *output_info)
|
|
{
|
|
Ecore_X_Randr_Mode *modes;
|
|
Ecore_X_Randr_Mode_Info *mode_info;
|
|
int nmodes, npreferred;
|
|
Eina_List *iter;
|
|
Eina_Bool added_yet = EINA_FALSE;
|
|
|
|
if (E_RANDR_NO_12 || !(modes = ecore_x_randr_output_modes_get(e_randr_screen_info->root, output_info->xid, &nmodes, &npreferred))) return EINA_FALSE;
|
|
|
|
//In case the monitor does not have any preferred mode at all
|
|
if (nmodes > 0 && npreferred == 0) npreferred = 1;
|
|
|
|
while (--nmodes >= 0)
|
|
{
|
|
added_yet = EINA_FALSE;
|
|
EINA_LIST_FOREACH (e_randr_screen_info->rrvd_info.randr_info_12->modes, iter, mode_info)
|
|
{
|
|
if (mode_info && mode_info->xid == modes[nmodes])
|
|
{
|
|
added_yet = EINA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!added_yet)
|
|
{
|
|
mode_info = ecore_x_randr_mode_info_get(e_randr_screen_info->root, modes[nmodes]);
|
|
e_randr_screen_info->rrvd_info.randr_info_12->modes = eina_list_prepend(e_randr_screen_info->rrvd_info.randr_info_12->modes, mode_info);
|
|
}
|
|
output_info->modes = eina_list_prepend(output_info->modes, mode_info);
|
|
if (nmodes < npreferred) output_info->preferred_modes = eina_list_prepend(output_info->preferred_modes, mode_info);
|
|
}
|
|
|
|
free(modes);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_randr_screen_crtcs_init(void)
|
|
{
|
|
Ecore_X_Randr_Crtc *crtcs = NULL;
|
|
E_Randr_Crtc_Info *crtcs_info = NULL, *crtc = NULL;
|
|
int i, ncrtcs;
|
|
|
|
if (E_RANDR_NO_12 || !(crtcs = ecore_x_randr_crtcs_get(e_randr_screen_info->root, &ncrtcs))) return EINA_FALSE;
|
|
|
|
if (!(crtcs_info = _e_randr_crtc_info_new(ncrtcs))) goto ecore_x_randr_screen_crtcs_init_fail_free_crtcs;
|
|
for (i = 0; i < ncrtcs; i++)
|
|
{
|
|
fprintf (stderr, "E_RANDR: filling %d/%d (%d)\n", (i + 1), ncrtcs, crtcs[i]);
|
|
crtcs_info[i].xid = crtcs[i];
|
|
_e_randr_crtc_info_set(&crtcs_info[i]);
|
|
if (!(e_randr_screen_info->rrvd_info.randr_info_12->crtcs = eina_list_append(e_randr_screen_info->rrvd_info.randr_info_12->crtcs, &crtcs_info[i]))) break;
|
|
}
|
|
if (i == ncrtcs)
|
|
{
|
|
//successfully initialized crtcs!
|
|
free (crtcs);
|
|
return EINA_TRUE;
|
|
}
|
|
EINA_LIST_FREE (e_randr_screen_info->rrvd_info.randr_info_12->crtcs, crtc)
|
|
_e_randr_crtc_info_free(crtc);
|
|
if (e_randr_screen_info->rrvd_info.randr_info_12->crtcs)
|
|
{
|
|
free(eina_list_nth(e_randr_screen_info->rrvd_info.randr_info_12->crtcs, 0));
|
|
}
|
|
|
|
ecore_x_randr_screen_crtcs_init_fail_free_crtcs:
|
|
free(crtcs);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_randr_screen_outputs_init(void)
|
|
{
|
|
Ecore_X_Randr_Output *outputs;
|
|
E_Randr_Output_Info *outputs_info = NULL, *output_info = NULL;
|
|
int noutputs = 0;
|
|
if (E_RANDR_NO_12 || !(outputs = ecore_x_randr_outputs_get(e_randr_screen_info->root, &noutputs))) return EINA_FALSE;
|
|
|
|
if (!(outputs_info = _e_randr_output_info_new(noutputs))) goto _e_randr_screen_outputs_init_fail_free_outputs;
|
|
while (--noutputs >= 0)
|
|
{
|
|
outputs_info[noutputs].xid = outputs[noutputs];
|
|
_e_randr_output_info_set(&outputs_info[noutputs]);
|
|
if (outputs_info[noutputs].connection_status == ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED)
|
|
_e_randr_output_info_hw_info_set(&outputs_info[noutputs]);
|
|
|
|
if (!(e_randr_screen_info->rrvd_info.randr_info_12->outputs = eina_list_append(e_randr_screen_info->rrvd_info.randr_info_12->outputs, &outputs_info[noutputs]))) goto _e_randr_screen_outputs_init_fail_free_outputs_list;
|
|
}
|
|
|
|
free(outputs);
|
|
return EINA_TRUE;
|
|
|
|
_e_randr_screen_outputs_init_fail_free_outputs_list:
|
|
if (e_randr_screen_info->rrvd_info.randr_info_12->outputs)
|
|
{
|
|
EINA_LIST_FREE (e_randr_screen_info->rrvd_info.randr_info_12->outputs, output_info)
|
|
free(output_info);
|
|
}
|
|
_e_randr_screen_outputs_init_fail_free_outputs:
|
|
free(outputs);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
static Ecore_X_Randr_Mode_Info *
|
|
_e_randr_mode_info_get(Ecore_X_Randr_Mode mode)
|
|
{
|
|
Eina_List *iter;
|
|
Ecore_X_Randr_Mode_Info *mode_info;
|
|
|
|
E_RANDR_NO_MODE_RET(mode, NULL);
|
|
EINA_LIST_FOREACH (e_randr_screen_info->rrvd_info.randr_info_12->modes, iter, mode_info)
|
|
{
|
|
if (mode_info && mode_info->xid == mode) return mode_info;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static E_Randr_Output_Info *
|
|
_e_randr_output_info_get(Ecore_X_Randr_Output output)
|
|
{
|
|
Eina_List *iter;
|
|
E_Randr_Output_Info *output_info;
|
|
|
|
E_RANDR_NO_OUTPUTS_RET(NULL);
|
|
EINA_LIST_FOREACH (e_randr_screen_info->rrvd_info.randr_info_12->outputs, iter, output_info)
|
|
{
|
|
if (output_info && output_info->xid == output) return output_info;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static E_Randr_Crtc_Info *
|
|
_e_randr_crtc_info_get(Ecore_X_Randr_Crtc crtc)
|
|
{
|
|
Eina_List *iter;
|
|
E_Randr_Crtc_Info *crtc_info;
|
|
|
|
E_RANDR_NO_CRTCS_RET(NULL);
|
|
EINA_LIST_FOREACH (e_randr_screen_info->rrvd_info.randr_info_12->crtcs, iter, crtc_info)
|
|
{
|
|
if (crtc_info && crtc_info->xid == crtc) return crtc_info;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_randr_event_cb(void *data __UNUSED__, int type, void *ev)
|
|
{
|
|
E_Randr_Crtc_Info *crtc_info;
|
|
Ecore_X_Randr_Mode_Info *mode_info;
|
|
Eina_Bool enabled = EINA_FALSE;
|
|
|
|
if (!e_randr_screen_info) return ECORE_CALLBACK_RENEW;
|
|
|
|
if (type == ECORE_X_EVENT_RANDR_CRTC_CHANGE)
|
|
{
|
|
Ecore_X_Event_Randr_Crtc_Change *event = (Ecore_X_Event_Randr_Crtc_Change *)ev;
|
|
/* available information:
|
|
struct _Ecore_X_Event_Randr_Crtc_Change
|
|
{
|
|
Ecore_X_Window win;
|
|
Ecore_X_Randr_Crtc crtc;
|
|
Ecore_X_Randr_Mode mode;
|
|
Ecore_X_Randr_Orientation orientation;
|
|
int x;
|
|
int y;
|
|
int width;
|
|
int height;
|
|
};
|
|
*/
|
|
crtc_info = _e_randr_crtc_info_get(event->crtc);
|
|
if (!crtc_info) return ECORE_CALLBACK_RENEW;
|
|
|
|
// Update CRTC's information (Output(s), (Common) Mode(s), etc.)
|
|
_e_randr_crtc_info_set(crtc_info);
|
|
|
|
// Get mode info of set mode
|
|
mode_info = _e_randr_mode_info_get(event->mode);
|
|
|
|
fprintf(stderr, "E_RANDR: CRTC %x was set to mode %x.\n", event->crtc, event->mode);
|
|
|
|
if (event->mode != Ecore_X_Randr_None)
|
|
{
|
|
//switched (on)
|
|
if(!crtc_info->outputs_common_modes)
|
|
fprintf(stderr, "E_RANDR: Though this monitor was switched on, no outputs are known to be connected to CRTC %x. Therefore no common modes available.\n", crtc_info->xid);
|
|
if ((crtc_info->current_mode != mode_info))
|
|
{
|
|
crtc_info->current_mode = mode_info;
|
|
_e_randr_notify_crtc_mode_change(crtc_info);
|
|
}
|
|
else
|
|
crtc_info->current_mode = _e_randr_mode_info_get(event->mode);
|
|
crtc_info->current_orientation = event->orientation;
|
|
crtc_info->geometry.x = event->geo.x;
|
|
crtc_info->geometry.y = event->geo.y;
|
|
crtc_info->geometry.w = event->geo.w;
|
|
crtc_info->geometry.h = event->geo.h;
|
|
//update screensize if necessary
|
|
e_randr_screen_info->rrvd_info.randr_info_12->current_size.width = MAX((event->geo.x + event->geo.w), e_randr_screen_info->rrvd_info.randr_info_12->current_size.width);
|
|
e_randr_screen_info->rrvd_info.randr_info_12->current_size.height = MAX((event->geo.y + event->geo.h), e_randr_screen_info->rrvd_info.randr_info_12->current_size.height);
|
|
}
|
|
else
|
|
{
|
|
//set the max mode common amongst outputs
|
|
_e_randr_crtcs_clone_crtc_removed(crtc_info);
|
|
|
|
//disabled
|
|
crtc_info->current_orientation = event->orientation;
|
|
crtc_info->geometry.x = 0;
|
|
crtc_info->geometry.y = 0;
|
|
crtc_info->geometry.w = 0;
|
|
crtc_info->geometry.h = 0;
|
|
crtc_info->current_mode = NULL;
|
|
if (crtc_info->outputs)
|
|
{
|
|
eina_list_free(crtc_info->outputs);
|
|
crtc_info->outputs = NULL;
|
|
}
|
|
|
|
//update screensize of necessary
|
|
ecore_x_randr_screen_reset(e_randr_screen_info->root);
|
|
ecore_x_randr_screen_current_size_get(e_randr_screen_info->root, &e_randr_screen_info->rrvd_info.randr_info_12->current_size.width, &e_randr_screen_info->rrvd_info.randr_info_12->current_size.height, NULL, NULL);
|
|
}
|
|
}
|
|
else if (type == ECORE_X_EVENT_RANDR_OUTPUT_CHANGE)
|
|
{
|
|
Ecore_X_Event_Randr_Output_Change *event = ev;
|
|
E_Randr_Output_Info *output_info = NULL;
|
|
/* available information:
|
|
struct _Ecore_X_Event_Randr_Output_Change
|
|
{
|
|
Ecore_X_Window win;
|
|
Ecore_X_Randr_Output output;
|
|
Ecore_X_Randr_Crtc crtc;
|
|
Ecore_X_Randr_Mode mode;
|
|
Ecore_X_Randr_Orientation orientation;
|
|
Ecore_X_Randr_Connection_Status connection;
|
|
Ecore_X_Render_Subpixel_Order subpixel_order;
|
|
};
|
|
*/
|
|
fprintf(stderr, "E_RANDR: Output connected!: \n"
|
|
"E_RANDR: relative to win: %x\n"
|
|
"E_RANDR: output (xid): %x\n"
|
|
"E_RANDR: used by crtc (xid): %x\n"
|
|
"E_RANDR: mode: %x\n"
|
|
"E_RANDR: orientation: %d\n"
|
|
"E_RANDR: connection state: %d (connected = %d, disconnected = %d, unknown = %d)\n"
|
|
"E_RANDR: subpixel_order: %d\n",
|
|
event->win, event->output, event->crtc, event->mode, event->orientation, event->connection, ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED, ECORE_X_RANDR_CONNECTION_STATUS_DISCONNECTED, ECORE_X_RANDR_CONNECTION_STATUS_UNKNOWN, event->subpixel_order);
|
|
|
|
output_info = _e_randr_output_info_get(event->output);
|
|
if (!output_info) return ECORE_CALLBACK_RENEW;
|
|
|
|
if ((output_info->crtc = _e_randr_crtc_info_get(event->crtc)))
|
|
{
|
|
if (!eina_list_data_find(output_info->crtc->outputs, output_info))
|
|
output_info->crtc->outputs = eina_list_append(output_info->crtc->outputs, output_info);
|
|
//update the list of common modes for the crtc's connected outputs
|
|
if (output_info->crtc->outputs_common_modes)
|
|
{
|
|
eina_list_free(output_info->crtc->outputs_common_modes);
|
|
output_info->crtc->outputs_common_modes = NULL;
|
|
}
|
|
output_info->crtc->outputs_common_modes = _e_randr_outputs_common_modes_get(output_info->crtc->outputs, NULL);
|
|
}
|
|
|
|
output_info->connection_status = event->connection;
|
|
output_info->subpixel_order = event->subpixel_order;
|
|
|
|
if (event->connection == ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED)
|
|
{
|
|
if (event->crtc)
|
|
output_info->crtc = _e_randr_crtc_info_get(event->crtc);
|
|
|
|
if (output_info && !output_info->crtc && (event->crtc == Ecore_X_Randr_None) && (event->mode == Ecore_X_Randr_None))
|
|
{
|
|
//Monitor was attached!
|
|
_e_randr_output_info_hw_info_set(output_info);
|
|
//make the crtcs aware of their possibly new output
|
|
_e_randr_crtcs_possible_output_update(output_info);
|
|
|
|
if (output_info->policy != ECORE_X_RANDR_OUTPUT_POLICY_NONE)
|
|
enabled = _e_randr_try_enable_output(output_info, EINA_FALSE); //maybe give a success message?
|
|
fprintf(stderr, "E_RANDR: Output %d %s.", output_info->xid, (enabled ? "was enabled sucessfully" : "could not be enabled"));
|
|
}
|
|
_e_randr_notify_output_change(output_info);
|
|
}
|
|
else if (event->connection == ECORE_X_RANDR_CONNECTION_STATUS_DISCONNECTED)
|
|
{
|
|
if (output_info->crtc)
|
|
{
|
|
//remove output from CRTC
|
|
output_info->crtc->outputs = eina_list_remove(output_info->crtc->outputs, output_info);
|
|
if (eina_list_count(output_info->crtc->outputs) == 0)
|
|
output_info->crtc->outputs = NULL;
|
|
|
|
if (output_info->crtc->current_mode)
|
|
{
|
|
//in case this output was enabled on some CRTC
|
|
if (eina_list_count(output_info->crtc->outputs) == 0)
|
|
{
|
|
//in case it was the only output running on this CRTC, disable
|
|
//it.
|
|
ecore_x_randr_crtc_mode_set(e_randr_screen_info->root, output_info->crtc->xid, NULL, Ecore_X_Randr_None, Ecore_X_Randr_None);
|
|
//crop the screen of course.
|
|
ecore_x_randr_screen_reset(e_randr_screen_info->root);
|
|
}
|
|
else
|
|
_e_randr_crtc_outputs_mode_max_set(output_info->crtc);
|
|
}
|
|
|
|
//reset assigned CRTC
|
|
output_info->crtc = NULL;
|
|
if (output_info == e_randr_screen_info->rrvd_info.randr_info_12->primary_output)
|
|
_e_randr_screen_primary_output_assign(output_info);
|
|
//let's try to get a proper config for the new setup and crop the
|
|
//screen afterwards.
|
|
/*
|
|
if ((restore_info = _e_randr_config_find_suiting_config_12()))
|
|
{
|
|
//in case we didn't have, init it anyway...
|
|
//_e_randr_config_enable_12(restore_info);
|
|
}
|
|
*/
|
|
}
|
|
_e_randr_notify_output_change(output_info);
|
|
_e_randr_output_hw_info_free(output_info);
|
|
}
|
|
}
|
|
else if (type == ECORE_X_EVENT_RANDR_OUTPUT_PROPERTY_NOTIFY)
|
|
{
|
|
//Ecore_X_Event_Randr_Output_Property_Notify *event = (Ecore_X_Event_Randr_Output_Property_Notify*) ev;
|
|
/* available information:
|
|
struct _Ecore_X_Event_Randr_Output_Property_Notify
|
|
{
|
|
Ecore_X_Window win;
|
|
Ecore_X_Randr_Output output;
|
|
Ecore_X_Atom property;
|
|
Ecore_X_Time time;
|
|
Ecore_X_Randr_Property_Change state;
|
|
};
|
|
*/
|
|
}
|
|
e_randr_try_restore_configuration(e_randr_screen_info);
|
|
return ECORE_CALLBACK_RENEW;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_randr_x_poll_cb(void *data __UNUSED__)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(e_randr_screen_info, ECORE_CALLBACK_CANCEL);
|
|
|
|
ecore_x_randr_screen_primary_output_orientations_get(e_randr_screen_info->root);
|
|
|
|
return ECORE_CALLBACK_RENEW;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_e_event_config_loaded_cb(void *data __UNUSED__, int type, void *ev __UNUSED__)
|
|
{
|
|
if (type != E_EVENT_CONFIG_LOADED) return EINA_TRUE;
|
|
|
|
e_randr_try_restore_configuration(e_randr_screen_info);
|
|
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
static void
|
|
_e_randr_event_listeners_add(void)
|
|
{
|
|
if (E_RANDR_NO_12) return;
|
|
ecore_x_randr_events_select(e_randr_screen_info->root, EINA_TRUE);
|
|
_e_randr_event_handlers = eina_list_append(_e_randr_event_handlers, ecore_event_handler_add(ECORE_X_EVENT_RANDR_CRTC_CHANGE, _e_randr_event_cb, NULL));
|
|
_e_randr_event_handlers = eina_list_append(_e_randr_event_handlers, ecore_event_handler_add(ECORE_X_EVENT_RANDR_OUTPUT_CHANGE, _e_randr_event_cb, NULL));
|
|
_e_randr_event_handlers = eina_list_append(_e_randr_event_handlers, ecore_event_handler_add(ECORE_X_EVENT_RANDR_OUTPUT_PROPERTY_NOTIFY, _e_randr_event_cb, NULL));
|
|
_e_randr_event_handlers = eina_list_append(_e_randr_event_handlers, ecore_event_handler_add(E_EVENT_CONFIG_LOADED, _e_event_config_loaded_cb, NULL));
|
|
// WORKAROUND problem of X not sending events
|
|
poller = ecore_poller_add(ECORE_POLLER_CORE, POLLINTERVAL, _e_randr_x_poll_cb, NULL);
|
|
}
|
|
|
|
static void
|
|
_e_randr_event_listeners_remove(void)
|
|
{
|
|
Ecore_Event_Handler *_event_handler = NULL;
|
|
EINA_LIST_FREE (_e_randr_event_handlers, _event_handler)
|
|
ecore_event_handler_del(_event_handler);
|
|
ecore_poller_del(poller);
|
|
poller = NULL;
|
|
}
|
|
|
|
static void
|
|
_e_randr_notify_crtc_mode_change(E_Randr_Crtc_Info *crtc_info)
|
|
{
|
|
// E_Notification *n;
|
|
// char buff[200];
|
|
//
|
|
if (crtc_info->current_mode)
|
|
{
|
|
// snprintf(buff, 200, "New resolution is %dx%d. Click here for further information.", crtc_info->current_mode->width, crtc_info->current_mode->height);
|
|
// n = e_notification_full_new("RandRR", crtc_info->xid, NULL, "Resolution changed", buff, -1);
|
|
// //n = e_notification_full_new("RandRR", id, icon, function, body, timeout);
|
|
// e_notification_send(n, NULL, NULL);
|
|
// e_notification_unref(n);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_e_randr_notify_output_change(E_Randr_Output_Info *output_info)
|
|
{
|
|
// E_Notification *n;
|
|
// char buff[100];
|
|
if (output_info->connection_status == ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED)
|
|
{
|
|
// snprintf(buff, 100, "Output %s connected", output_info->name);
|
|
// n = e_notification_full_new("RandRR", output_info->xid, NULL, buff, "Click here for further information.", -1);
|
|
}
|
|
else
|
|
{
|
|
// snprintf(buff, 100, "Output %s disconnected", output_info->name);
|
|
// n = e_notification_full_new("RandRR", output_info->xid, NULL, buff, "Click here to adjust screen setup.", -1);
|
|
}
|
|
|
|
// //n = e_notification_full_new("RandRR", id, icon, function, body, timeout);
|
|
// e_notification_send(n, NULL, NULL);
|
|
// e_notification_unref(n);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static const E_Randr_Crtc_Info *
|
|
_e_randr_policy_crtc_get(E_Randr_Crtc_Info *but, E_Randr_Crtc_Info *hint __UNUSED__, Ecore_X_Randr_Output_Policy policy)
|
|
{
|
|
Eina_List *iter, *possible_crtcs = NULL;
|
|
E_Randr_Crtc_Info *crtc_info, *ret = NULL;
|
|
|
|
E_RANDR_NO_CRTCS_RET(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;
|
|
}
|
|
|
|
// Setup store functions
|
|
Eina_Bool
|
|
_e_randr_copy_mode_info(Ecore_X_Randr_Mode_Info *dest, Ecore_X_Randr_Mode_Info *src)
|
|
{
|
|
if (!dest || !src) return EINA_FALSE;
|
|
|
|
dest->width = src->width;
|
|
dest->height = src->height;
|
|
dest->dotClock = src->dotClock;
|
|
dest->hSyncStart = src->hSyncStart;
|
|
dest->hSyncEnd = src->hSyncEnd;
|
|
dest->hTotal = src->hTotal;
|
|
dest->hSkew = src->hSkew;
|
|
dest->vSyncStart = src->vSyncStart;
|
|
dest->vSyncEnd = src->vSyncEnd;
|
|
dest->vTotal = src->vTotal;
|
|
if (src->nameLength > 0)
|
|
{
|
|
if (!(dest->name = malloc(src->nameLength + 1))) return EINA_FALSE;
|
|
if (!strncpy(dest->name, src->name, src->nameLength)) goto _e_randr_copy_mode_info_fail_free_name;
|
|
}
|
|
dest->nameLength = src->nameLength;
|
|
dest->modeFlags = src->modeFlags;
|
|
|
|
return EINA_TRUE;
|
|
|
|
_e_randr_copy_mode_info_fail_free_name:
|
|
free(dest->name);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
void
|
|
_e_randr_free_serialized_mode_info(Ecore_X_Randr_Mode_Info *mode_info)
|
|
{
|
|
if (mode_info->name) free(mode_info->name);
|
|
}
|
|
|
|
Eina_List *
|
|
_e_randr_create_outputs_policies_list(Eina_List *outputs)
|
|
{
|
|
Eina_List *iter, *list = NULL;
|
|
E_Randr_Output_Info *oi;
|
|
E_Randr_Serialized_Output_Policy *sop;
|
|
|
|
EINA_LIST_FOREACH (outputs, iter, oi)
|
|
{
|
|
if (!oi->name || (oi->name_length <= 0)) continue;
|
|
if (!(sop = E_NEW(E_Randr_Serialized_Output_Policy, 1))
|
|
|| !(sop->name = malloc(oi->name_length + 1))
|
|
|| !(strncpy(sop->name, oi->name, oi->name_length)))
|
|
goto _e_randr_create_outputs_policies_list_fail_free_list;
|
|
sop->name_length = oi->name_length;
|
|
sop->policy = oi->policy;
|
|
if (!(list = eina_list_append(list, sop)))
|
|
goto _e_randr_create_outputs_policies_list_fail_free_list;
|
|
}
|
|
|
|
return list;
|
|
_e_randr_create_outputs_policies_list_fail_free_list:
|
|
EINA_LIST_FREE (list, sop)
|
|
{
|
|
if (sop->name) free(sop->name);
|
|
free(sop);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
_e_randr_free_serialized_output_policy(E_Randr_Serialized_Output_Policy *sop)
|
|
{
|
|
if (!sop) return;
|
|
|
|
free(sop->name);
|
|
free(sop);
|
|
}
|
|
|
|
Eina_List *
|
|
_e_randr_update_serialized_outputs_policies(E_Randr_Screen_Info_12 *si_12, Eina_List *sops)
|
|
{
|
|
/*
|
|
* Do not free this mem, since it might be in e_config's teritory
|
|
*
|
|
E_Randr_Serialized_Output_Policy *sop;
|
|
|
|
EINA_LIST_FREE (sops, sop)
|
|
{
|
|
_e_randr_free_serialized_output_policy(sop);
|
|
}
|
|
*/
|
|
|
|
eina_list_free(sops);
|
|
|
|
return _e_randr_create_outputs_policies_list(si_12->outputs);
|
|
}
|
|
|
|
Eina_List *
|
|
_e_randr_create_possible_outputs_names_list(Eina_List *outputs)
|
|
{
|
|
Eina_List *iter, *list = NULL;
|
|
E_Randr_Output_Info *oi;
|
|
char *name;
|
|
|
|
if (!outputs) return NULL;
|
|
|
|
EINA_LIST_FOREACH (outputs, iter, oi)
|
|
{
|
|
if (!oi->name || (oi->name_length <= 0)) continue;
|
|
if (!(name = malloc(oi->name_length))
|
|
|| !strncpy(name, oi->name, oi->name_length)
|
|
|| !(list = eina_list_append(list, name))) goto _e_randr_create_possible_outputs_names_list_fail_free_list;
|
|
}
|
|
return list;
|
|
|
|
_e_randr_create_possible_outputs_names_list_fail_free_list:
|
|
EINA_LIST_FREE (list, name)
|
|
{
|
|
if (name) free(name);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
E_Randr_Edid_Hash *
|
|
_e_randr_create_edid_hash(E_Randr_Output_Info *output_info)
|
|
{
|
|
E_Randr_Edid_Hash *edid_hash;
|
|
|
|
if (!output_info || (output_info->edid_hash.hash == 0) || !(edid_hash = malloc(sizeof(E_Randr_Edid_Hash)))) return NULL;
|
|
|
|
edid_hash->hash = output_info->edid_hash.hash;
|
|
|
|
return edid_hash;
|
|
}
|
|
|
|
E_Randr_Serialized_Output *
|
|
_e_randr_create_serialized_output(E_Randr_Output_Info *output_info)
|
|
{
|
|
E_Randr_Serialized_Output *so;
|
|
char *name;
|
|
|
|
if (!output_info || !output_info->name || (output_info->name_length <= 0) || !(so = malloc(sizeof(E_Randr_Serialized_Output)))) return NULL;
|
|
|
|
if (!(name = malloc(output_info->name_length))
|
|
|| !strncpy(so->name, output_info->name, output_info->name_length))
|
|
goto _e_randr_create_serialized_outputs_fail_free_so;
|
|
so->name_length = output_info->name_length;
|
|
so->edid_hash.hash = output_info->edid_hash.hash;
|
|
so->backlight_level = output_info->backlight_level;
|
|
|
|
return so;
|
|
|
|
_e_randr_create_serialized_outputs_fail_free_so:
|
|
free(so);
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
_e_randr_free_serialized_output(E_Randr_Serialized_Output *so)
|
|
{
|
|
if (so->name) free(so->name);
|
|
free(so);
|
|
}
|
|
|
|
E_Randr_Serialized_Crtc *
|
|
_e_randr_create_serialized_crtc(E_Randr_Crtc_Info *crtc_info)
|
|
{
|
|
E_Randr_Serialized_Crtc *sc;
|
|
E_Randr_Serialized_Output *so;
|
|
E_Randr_Output_Info *output_info;
|
|
Eina_List *iter;
|
|
char *output_name;
|
|
|
|
if (!(sc = E_NEW(E_Randr_Serialized_Crtc, 1))) return NULL;
|
|
if (!_e_randr_copy_mode_info(&sc->mode_info, crtc_info->current_mode)) goto _e_randr_create_serialized_crtc_free_sc;
|
|
if (!(sc->possible_outputs_names = _e_randr_create_possible_outputs_names_list(crtc_info->possible_outputs))) goto _e_randr_create_serialized_crtc_free_mode_sc;
|
|
//Create list of serialized outputs
|
|
EINA_LIST_FOREACH (crtc_info->outputs, iter, output_info)
|
|
{
|
|
if (!(so = _e_randr_create_serialized_output(output_info))
|
|
|| !(sc->serialized_outputs = eina_list_append(sc->serialized_outputs, so))) goto _e_randr_create_serialized_crtc_free_outputs_list_sc;
|
|
}
|
|
sc->pos.x = crtc_info->geometry.x;
|
|
sc->pos.y = crtc_info->geometry.y;
|
|
sc->orientation = crtc_info->current_orientation;
|
|
|
|
return sc;
|
|
|
|
_e_randr_create_serialized_crtc_free_outputs_list_sc:
|
|
EINA_LIST_FREE (sc->possible_outputs_names, output_name)
|
|
{
|
|
if (output_name) free(output_name);
|
|
}
|
|
EINA_LIST_FREE (sc->serialized_outputs, so)
|
|
{
|
|
if (so) _e_randr_free_serialized_output(so);
|
|
}
|
|
_e_randr_create_serialized_crtc_free_mode_sc:
|
|
_e_randr_free_serialized_mode_info(&sc->mode_info);
|
|
_e_randr_create_serialized_crtc_free_sc:
|
|
E_FREE(sc);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
_e_randr_free_serialized_crtc(E_Randr_Serialized_Crtc *sc)
|
|
{
|
|
E_Randr_Serialized_Output *so;
|
|
char *name;
|
|
|
|
EINA_LIST_FREE (sc->serialized_outputs, so)
|
|
_e_randr_free_serialized_output(so);
|
|
_e_randr_free_serialized_mode_info(&sc->mode_info);
|
|
EINA_LIST_FREE (sc->possible_outputs_names, name)
|
|
free(name);
|
|
free(sc);
|
|
}
|
|
|
|
E_Randr_Serialized_Setup_11 *
|
|
_e_randr_create_serialized_setup_11(E_Randr_Screen_Info_11 *screen_info_11)
|
|
{
|
|
E_Randr_Serialized_Setup_11 *ss;
|
|
Ecore_X_Randr_Screen_Size_MM *size;
|
|
|
|
if (!(ss = malloc(sizeof(*ss)))) return NULL;
|
|
if (!(size = (Ecore_X_Randr_Screen_Size_MM *)eina_list_data_get(eina_list_nth(screen_info_11->sizes, screen_info_11->csize_index)))) goto _e_randr_create_serialized_setup_11_failed_free_ss;
|
|
ss->size.width = size->width;
|
|
ss->size.height = size->height;
|
|
ss->refresh_rate = screen_info_11->current_rate;
|
|
ss->orientation = screen_info_11->corientation;
|
|
|
|
return ss;
|
|
|
|
_e_randr_create_serialized_setup_11_failed_free_ss:
|
|
free(ss);
|
|
return NULL;
|
|
}
|
|
|
|
E_Randr_Serialized_Setup_11 *
|
|
_e_randr_update_serialized_setup_11(E_Randr_Serialized_Setup_11 *ss_11, E_Randr_Screen_Info_11 *si_11)
|
|
{
|
|
Ecore_X_Randr_Screen_Size_MM *size;
|
|
|
|
if (ss_11)
|
|
{
|
|
if (!(size = (Ecore_X_Randr_Screen_Size_MM *)eina_list_data_get(eina_list_nth(si_11->sizes, si_11->csize_index)))) return NULL;
|
|
if (!memcpy(&ss_11->size, size, sizeof(Ecore_X_Randr_Screen_Size_MM)))
|
|
goto _e_randr_update_serialized_setup_11_failed_free_ss;
|
|
ss_11->refresh_rate = si_11->current_rate;
|
|
ss_11->orientation = si_11->corientation;
|
|
}
|
|
else
|
|
ss_11 = _e_randr_create_serialized_setup_11(si_11);
|
|
|
|
return ss_11;
|
|
|
|
_e_randr_update_serialized_setup_11_failed_free_ss:
|
|
free(ss_11);
|
|
return NULL;
|
|
}
|
|
|
|
E_Randr_Serialized_Setup_12 *
|
|
_e_randr_create_serialized_setup_12(E_Randr_Screen_Info_12 *screen_info_12)
|
|
{
|
|
E_Randr_Serialized_Setup_12 *ss;
|
|
Eina_List *iter;
|
|
E_Randr_Crtc_Info *ci;
|
|
E_Randr_Output_Info *oi;
|
|
E_Randr_Serialized_Crtc *sc;
|
|
E_Randr_Edid_Hash *edid_hash;
|
|
|
|
if (!(ss = E_NEW(E_Randr_Serialized_Setup_12, 1))) return NULL;
|
|
|
|
ss->timestamp = ecore_time_get();
|
|
|
|
//Add CRTCs and their configuration
|
|
EINA_LIST_FOREACH (screen_info_12->crtcs, iter, ci)
|
|
{
|
|
//ignore disabled crtcs for now
|
|
if (!ci->current_mode) continue;
|
|
|
|
if (!(sc = _e_randr_create_serialized_crtc(ci))
|
|
|| !(ss->serialized_crtcs = eina_list_append(ss->serialized_crtcs, sc)))
|
|
goto _e_randr_create_serialized_setup_12_failed_free_list_ss;
|
|
}
|
|
|
|
/*
|
|
* Add EDID hashes of connected and enabled
|
|
* outputs for easier comparison during
|
|
* setup restoration
|
|
*/
|
|
EINA_LIST_FOREACH (screen_info_12->outputs, iter, oi)
|
|
{
|
|
if ((oi->connection_status != ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED) || !oi->crtc || !oi->crtc->current_mode)
|
|
continue;
|
|
if (!(edid_hash = _e_randr_create_edid_hash(oi)) || !(ss->serialized_edid_hashes = eina_list_append(ss->serialized_edid_hashes, edid_hash)))
|
|
goto _e_randr_create_serialized_setup_12_failed_free_output_list_crtc_list_ss;
|
|
}
|
|
|
|
return ss;
|
|
|
|
_e_randr_create_serialized_setup_12_failed_free_output_list_crtc_list_ss:
|
|
EINA_LIST_FREE (ss->serialized_edid_hashes, edid_hash)
|
|
{
|
|
if (edid_hash) free(edid_hash);
|
|
}
|
|
_e_randr_create_serialized_setup_12_failed_free_list_ss:
|
|
EINA_LIST_FREE (ss->serialized_crtcs, sc)
|
|
{
|
|
_e_randr_free_serialized_crtc(sc);
|
|
}
|
|
free(ss);
|
|
return NULL;
|
|
}
|
|
|
|
E_Randr_Serialized_Setup_12 *
|
|
_e_randr_find_matching_serialized_setup(Eina_List *setups_12, E_Randr_Screen_Info_12 *si_12)
|
|
{
|
|
E_Randr_Serialized_Setup_12 *ss_12;
|
|
Eina_List *setups_iter, *r_iter, *s_iter;
|
|
Eina_Bool found = EINA_FALSE;
|
|
E_Randr_Edid_Hash *edid_hash;
|
|
E_Randr_Output_Info *oi;
|
|
|
|
if (!setups_12 || !si_12) return NULL;
|
|
|
|
EINA_LIST_FOREACH (setups_12, setups_iter, ss_12)
|
|
{
|
|
EINA_LIST_FOREACH (si_12->outputs, r_iter, oi)
|
|
{
|
|
//skip disconnected/-abled monitors
|
|
if ((oi->connection_status != ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED) || !oi->crtc || !oi->crtc->current_mode)
|
|
continue;
|
|
found = EINA_FALSE;
|
|
EINA_LIST_FOREACH (ss_12->serialized_edid_hashes, s_iter, edid_hash)
|
|
{
|
|
if (oi->edid_hash.hash == edid_hash->hash)
|
|
{
|
|
found = EINA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
break;
|
|
}
|
|
if (found)
|
|
break;
|
|
}
|
|
if (found)
|
|
return ss_12;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
_e_randr_free_serialized_setup_12(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->serialized_crtcs, sc)
|
|
{
|
|
if (!sc) continue;
|
|
_e_randr_free_serialized_crtc(sc);
|
|
}
|
|
EINA_LIST_FREE (ss_12->serialized_edid_hashes, edid_hash)
|
|
if (edid_hash) free(edid_hash);
|
|
|
|
free(ss_12);
|
|
}
|
|
|
|
Eina_List *
|
|
_e_randr_update_serialized_setup_12(Eina_List *setups_12, E_Randr_Screen_Info_12 *si_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 = _e_randr_find_matching_serialized_setup(setups_12, si_12)))
|
|
{
|
|
//_e_randr_free_serialized_setup_12(ss_12);
|
|
setups_12 = eina_list_remove(setups_12, ss_12);
|
|
}
|
|
}
|
|
ss_12 = _e_randr_create_serialized_setup_12(si_12);
|
|
setups_12 = eina_list_append(setups_12, ss_12);
|
|
|
|
return setups_12;
|
|
}
|
|
|
|
E_Randr_Serialized_Setup *
|
|
_e_randr_create_serialized_setup(E_Randr_Screen_Info *screen_info __UNUSED__)
|
|
{
|
|
return E_NEW(E_Randr_Serialized_Setup, 1);
|
|
}
|
|
|
|
EAPI void
|
|
e_randr_store_configuration(E_Randr_Screen_Info *screen_info, E_Randr_Configuration_Store_Modifier modifier)
|
|
{
|
|
if (E_RANDR_NO_11) return;
|
|
|
|
if (!e_config->randr_serialized_setup)
|
|
e_config->randr_serialized_setup = _e_randr_create_serialized_setup(screen_info);
|
|
|
|
fprintf(stderr, "E_RANDR: Configuration shall be stored using the following modifier: %d.\n", modifier);
|
|
|
|
if (screen_info->randr_version == ECORE_X_RANDR_1_1)
|
|
{
|
|
if (e_config->randr_serialized_setup->serialized_setup_11)
|
|
e_config->randr_serialized_setup->serialized_setup_11 = _e_randr_update_serialized_setup_11(e_config->randr_serialized_setup->serialized_setup_11, screen_info->rrvd_info.randr_info_11);
|
|
else
|
|
e_config->randr_serialized_setup->serialized_setup_11 = _e_randr_create_serialized_setup_11(screen_info->rrvd_info.randr_info_11);
|
|
}
|
|
else if (screen_info->randr_version >= ECORE_X_RANDR_1_2)
|
|
{
|
|
/*
|
|
if (modifier & (E_RANDR_CONFIGURATION_STORE_RESOLUTIONS | E_RANDR_CONFIGURATION_STORE_ARRANGEMENT | E_RANDR_CONFIGURATION_STORE_ORIENTATIONS))
|
|
{
|
|
if (e_config->randr_serialized_setup->serialized_setups_12)
|
|
e_config->randr_serialized_setup->serialized_setups_12 = _e_randr_update_serialized_setup_12(e_config->randr_serialized_setup->serialized_setups_12, screen_info->rrvd_info.randr_info_12);
|
|
else
|
|
e_config->randr_serialized_setup->serialized_setups_12 = eina_list_append(e_config->randr_serialized_setup->serialized_setups_12, _e_randr_create_serialized_setup_12(screen_info->rrvd_info.randr_info_12));
|
|
}
|
|
*/
|
|
|
|
if (modifier & E_RANDR_CONFIGURATION_STORE_POLICIES)
|
|
{
|
|
//update output policies
|
|
e_config->randr_serialized_setup->serialized_outputs_policies = _e_randr_update_serialized_outputs_policies(screen_info->rrvd_info.randr_info_12, e_config->randr_serialized_setup->serialized_outputs_policies);
|
|
}
|
|
}
|
|
e_config_save_queue();
|
|
}
|
|
|
|
//setup restore functions
|
|
EAPI Eina_Bool
|
|
e_randr_try_restore_configuration(E_Randr_Screen_Info *si)
|
|
{
|
|
if (!e_config || !e_config->randr_serialized_setup) return EINA_FALSE;
|
|
|
|
if (si->randr_version == ECORE_X_RANDR_1_1)
|
|
return _e_randr_try_restore_11(si->rrvd_info.randr_info_11);
|
|
else if (si->randr_version >= ECORE_X_RANDR_1_2)
|
|
return _e_randr_try_restore_12(si->rrvd_info.randr_info_12);
|
|
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
Eina_Bool
|
|
_e_randr_try_restore_11(E_Randr_Screen_Info_11 *si_11)
|
|
{
|
|
Eina_List *iter;
|
|
Ecore_X_Randr_Screen_Size_MM *stored_size, *size;
|
|
int i = 0;
|
|
|
|
if (!e_config->randr_serialized_setup->serialized_setup_11) return EINA_FALSE;
|
|
stored_size = &e_config->randr_serialized_setup->serialized_setup_11->size;
|
|
EINA_LIST_FOREACH (si_11->sizes, iter, size)
|
|
{
|
|
if ((stored_size->width == size->width)
|
|
&& (stored_size->height == size->height)
|
|
&& (stored_size->width_mm == size->width_mm)
|
|
&& (stored_size->height_mm == size->height_mm))
|
|
{
|
|
return ecore_x_randr_screen_primary_output_size_set(e_randr_screen_info->root, i);
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
E_Randr_Crtc_Info *
|
|
_e_randr_find_matching_crtc(Eina_List *crtcs, E_Randr_Serialized_Crtc *sc)
|
|
{
|
|
Eina_List *iter, *s_name_iter, *p_output_iter;
|
|
E_Randr_Crtc_Info *ci;
|
|
E_Randr_Output_Info *oi;
|
|
char *s_output_name;
|
|
|
|
EINA_LIST_FOREACH (crtcs, iter, ci)
|
|
{
|
|
if (eina_list_count(ci->possible_outputs) != eina_list_count(sc->possible_outputs_names))
|
|
continue;
|
|
EINA_LIST_FOREACH (sc->possible_outputs_names, s_name_iter, s_output_name)
|
|
{
|
|
EINA_LIST_FOREACH (ci->possible_outputs, p_output_iter, oi)
|
|
{
|
|
if (!strncmp(s_output_name, oi->name, oi->name_length))
|
|
return ci;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Eina_List *
|
|
_e_randr_find_matching_outputs(Eina_List *sois, Eina_List *ois)
|
|
{
|
|
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)
|
|
{
|
|
fprintf(stderr, "E_RANDR: Looking for serialized output %d(hash)\n", so->edid_hash.hash);
|
|
EINA_LIST_FOREACH (ois, r_output_iter, oi)
|
|
{
|
|
fprintf(stderr, "E_RANDR: \tComparing to output %d(hash)\n", oi->edid_hash.hash);
|
|
if (so->edid_hash.hash == oi->edid_hash.hash)
|
|
{
|
|
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;
|
|
}
|
|
|
|
Ecore_X_Randr_Mode_Info *
|
|
_e_randr_find_matching_mode_info(Eina_List *modes, Ecore_X_Randr_Mode_Info *mode)
|
|
{
|
|
Eina_List *iter;
|
|
Ecore_X_Randr_Mode_Info *mi = NULL;
|
|
|
|
EINA_LIST_FOREACH (modes, iter, mi)
|
|
{
|
|
if ((mode->width == mi->width)
|
|
&& (mode->height == mi->height)
|
|
&& (mode->dotClock == mi->dotClock)
|
|
&& (mode->hSyncStart == mi->hSyncStart)
|
|
&& (mode->hSyncEnd == mi->hSyncEnd)
|
|
&& (mode->hTotal == mi->hTotal)
|
|
&& (mode->hSkew == mi->hSkew)
|
|
&& (mode->vSyncStart == mi->vSyncStart)
|
|
&& (mode->vSyncEnd == mi->vSyncEnd)
|
|
&& (mode->vTotal == mi->vTotal)
|
|
&& (mode->nameLength == mi->nameLength)
|
|
&& !strncpy(mode->name, mi->name, mode->nameLength)
|
|
&& (mode->modeFlags == mi->modeFlags))
|
|
return mi;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
_e_randr_restore_12_policies(E_Randr_Screen_Info_12 *si_12)
|
|
{
|
|
E_Randr_Output_Info *output;
|
|
E_Randr_Serialized_Output_Policy *sop;
|
|
Eina_List *iter, *iter2;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(si_12);
|
|
EINA_SAFETY_ON_NULL_RETURN(e_config->randr_serialized_setup);
|
|
EINA_SAFETY_ON_NULL_RETURN(e_config->randr_serialized_setup->serialized_outputs_policies);
|
|
|
|
// Restore policies
|
|
EINA_LIST_FOREACH(si_12->outputs, iter, output)
|
|
{
|
|
EINA_LIST_FOREACH(e_config->randr_serialized_setup->serialized_outputs_policies, iter2, sop)
|
|
{
|
|
if (strncmp(sop->name, output->name, sop->name_length) == 0)
|
|
{
|
|
output->policy = sop->policy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Eina_Bool
|
|
_e_randr_try_restore_12(E_Randr_Screen_Info_12 *si_12)
|
|
{
|
|
/*
|
|
E_Randr_Serialized_Setup_12 *ss_12;
|
|
E_Randr_Serialized_Crtc *sc;
|
|
E_Randr_Crtc_Info *ci;
|
|
Ecore_X_Randr_Output *outputs_array;
|
|
Ecore_X_Randr_Mode_Info *mi;
|
|
Eina_List *iter;
|
|
*/
|
|
Eina_Bool ret = EINA_TRUE;
|
|
|
|
//Restore policies
|
|
_e_randr_restore_12_policies(si_12);
|
|
|
|
/*
|
|
* step by step. disable entire setup matching for now
|
|
if (!(ss_12 = _e_randr_find_matching_serialized_setup(e_config->randr_serialized_setup->serialized_setups_12, si_12))) return EINA_FALSE;
|
|
|
|
EINA_LIST_FOREACH (ss_12->serialized_crtcs, iter, sc)
|
|
{
|
|
ci = _e_randr_find_matching_crtc(si_12->crtcs, sc);
|
|
outputs_list = _e_randr_find_matching_outputs(si_12->outputs, sc->serialized_outputs);
|
|
outputs_array = _e_randr_outputs_to_array(outputs_list);
|
|
if (!(mi = _e_randr_find_matching_mode_info(si_12->modes, &sc->mode_info)))
|
|
{
|
|
// The serialized mode is no longer available
|
|
eina_list_free(outputs_list);
|
|
free(outputs_array);
|
|
continue;
|
|
}
|
|
ret &= ecore_x_randr_crtc_mode_set(e_randr_screen_info->root, ci->xid, outputs_array, eina_list_count(outputs_list), mi->xid);
|
|
ret &= ecore_x_randr_crtc_pos_set(e_randr_screen_info->root, ci->xid, sc->pos.x, sc->pos.y);
|
|
}
|
|
*/
|
|
return ret;
|
|
}
|
|
|
|
//Utility functions
|
|
static Ecore_X_Randr_Output *
|
|
_e_randr_outputs_to_array(Eina_List *outputs_info)
|
|
{
|
|
Ecore_X_Randr_Output *ret = NULL;
|
|
E_Randr_Output_Info *output_info;
|
|
Eina_List *output_iter;
|
|
int i = 0;
|
|
|
|
if (!outputs_info || !(ret = malloc(sizeof(Ecore_X_Randr_Output) * eina_list_count(outputs_info)))) return NULL;
|
|
EINA_LIST_FOREACH (outputs_info, output_iter, output_info)
|
|
/* output_info == NULL should _not_ be possible! */
|
|
ret[i++] = output_info ? output_info->xid : Ecore_X_Randr_None;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Try to enable this output on an unoccupied CRTC. 'Force' in this context
|
|
* means, that if there are only occupied CRTCs, we disable another output to
|
|
* enable this one. If not forced we will - if we don't find an unoccupied CRTC
|
|
* - try to share the output of a CRTC with other outputs already using it
|
|
* (clone).
|
|
*/
|
|
static Eina_Bool
|
|
_e_randr_try_enable_output(E_Randr_Output_Info *output_info, Eina_Bool force)
|
|
{
|
|
if (!output_info) return EINA_FALSE;
|
|
else if (output_info->crtc && output_info->crtc->current_mode)
|
|
return EINA_TRUE;
|
|
|
|
Eina_List *iter, *outputs_list = NULL;
|
|
E_Randr_Crtc_Info *crtc_info, *usable_crtc = NULL;
|
|
E_Randr_Output_Info *primary_output;
|
|
Ecore_X_Randr_Output *outputs;
|
|
Ecore_X_Randr_Mode_Info *mode_info;
|
|
Eina_Bool ret = EINA_FALSE;
|
|
|
|
/*
|
|
* Try to find a usable crtc for this output. Either unused or forced.
|
|
*/
|
|
EINA_LIST_FOREACH (output_info->possible_crtcs, iter, crtc_info)
|
|
{
|
|
if (crtc_info && (!crtc_info->current_mode || force))
|
|
{
|
|
usable_crtc = crtc_info;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* apparently we don't have a CRTC to make use of the device
|
|
*/
|
|
if (!usable_crtc) return EINA_FALSE;
|
|
|
|
fprintf(stderr, "E_RANDR: Try to enable output %x using policy %d.\n", output_info->xid, output_info->policy);
|
|
|
|
//get the CRTC we will refer to, dependend on policy
|
|
switch (output_info->policy)
|
|
{
|
|
case ECORE_X_RANDR_OUTPUT_POLICY_NONE:
|
|
return EINA_TRUE;
|
|
|
|
case ECORE_X_RANDR_OUTPUT_POLICY_CLONE:
|
|
/*
|
|
* Order of approaches to enable a clone (of the primary output):
|
|
*
|
|
* 0. Get Primary output from Server
|
|
* 1. Try to add new Output to primary output's CRTC, using the mode used
|
|
* by the primary output
|
|
* 2. Try to enable clone in the same
|
|
* 2a. exact mode or a
|
|
* 2b. geometrically identical mode
|
|
* 3. Find a most high resolution mode in common to enable on primary output's CRTC and the new
|
|
* output's CRTC
|
|
* 4. fail.
|
|
*/
|
|
if ((primary_output = e_randr_screen_info->rrvd_info.randr_info_12->primary_output))
|
|
{
|
|
if (primary_output->crtc && primary_output->crtc->current_mode && eina_list_data_find(output_info->modes, primary_output->crtc->current_mode))
|
|
{
|
|
/*
|
|
* mode currently used by primary output's CRTC is also supported by the new output
|
|
*/
|
|
if (_e_randr_outputs_are_clones(output_info, primary_output->crtc->outputs))
|
|
{
|
|
/*
|
|
* 1. Try to add new Output to primary output's CRTC, using the mode used
|
|
* by the primary output
|
|
* TODO: check with compatibility list in RandRR >= 1.3
|
|
* if available
|
|
*
|
|
* The new output is also usable by the primary output's
|
|
* CRTC. Try to enable this output together with the already
|
|
* enabled outputs on the CRTC in already used mode.
|
|
*/
|
|
outputs_list = eina_list_clone(primary_output->crtc->outputs);
|
|
outputs_list = eina_list_append(outputs_list, output_info);
|
|
outputs = _e_randr_outputs_to_array(outputs_list);
|
|
ret = ecore_x_randr_crtc_mode_set(e_randr_screen_info->root, primary_output->crtc->xid, outputs, eina_list_count(outputs_list), primary_output->crtc->current_mode->xid);
|
|
free(outputs);
|
|
eina_list_free(outputs_list);
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* 2. Try to enable clone in the same
|
|
*/
|
|
|
|
/*
|
|
* 2a. exact mode.
|
|
*/
|
|
ret = ecore_x_randr_crtc_mode_set(e_randr_screen_info->root, usable_crtc->xid, &output_info->xid, 1, primary_output->crtc->current_mode->xid);
|
|
return ret && ecore_x_randr_crtc_pos_relative_set(e_randr_screen_info->root, usable_crtc->xid, primary_output->crtc->xid, ECORE_X_RANDR_OUTPUT_POLICY_CLONE, e_randr_screen_info->rrvd_info.randr_info_12->alignment);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* 2b. geometrically identical mode
|
|
*/
|
|
if (primary_output->crtc && (mode_info = _e_randr_mode_geo_identical_find(output_info->modes, primary_output->crtc->current_mode)))
|
|
{
|
|
ret = ecore_x_randr_crtc_mode_set(e_randr_screen_info->root, usable_crtc->xid, &output_info->xid, 1, mode_info->xid);
|
|
return ret && ecore_x_randr_crtc_pos_relative_set(e_randr_screen_info->root, usable_crtc->xid, primary_output->crtc->xid, ECORE_X_RANDR_OUTPUT_POLICY_CLONE, e_randr_screen_info->rrvd_info.randr_info_12->alignment);
|
|
}
|
|
/*
|
|
* 3. Find the highest resolution mode common to enable on primary output's CRTC and the new one.
|
|
*/
|
|
if (((outputs_list = eina_list_append(outputs_list, primary_output)) && (outputs_list = eina_list_append(outputs_list, output_info))))
|
|
{
|
|
if (primary_output->crtc)
|
|
{
|
|
if ((mode_info = _e_randr_outputs_common_mode_max_get(outputs_list, primary_output->crtc->current_mode)))
|
|
{
|
|
fprintf(stderr, "Will try to set mode: %dx%d for primary and clone.\n", mode_info->width, mode_info->height);
|
|
ret = ecore_x_randr_crtc_mode_set(e_randr_screen_info->root, primary_output->crtc->xid, ((Ecore_X_Randr_Output *)Ecore_X_Randr_Unset), Ecore_X_Randr_Unset, mode_info->xid);
|
|
ret = (ret && ecore_x_randr_crtc_mode_set(e_randr_screen_info->root, usable_crtc->xid, &output_info->xid, 1, mode_info->xid));
|
|
ret = (ret && ecore_x_randr_crtc_pos_relative_set(e_randr_screen_info->root, usable_crtc->xid, primary_output->crtc->xid, ECORE_X_RANDR_OUTPUT_POLICY_CLONE, e_randr_screen_info->rrvd_info.randr_info_12->alignment));
|
|
}
|
|
}
|
|
eina_list_free(outputs_list);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
fprintf(stderr, "Couldn't get primary output!\n");
|
|
/*
|
|
* 4. FAIL
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
if (usable_crtc)
|
|
{
|
|
//enable and position according to used policies
|
|
if (!(mode_info = ((Ecore_X_Randr_Mode_Info *)eina_list_nth(output_info->preferred_modes, 0))))
|
|
{
|
|
fprintf(stderr, "E_RANDR: Could not enable output(%x), as it has no preferred (and there for none at all) modes.!\n", output_info->xid);
|
|
ret = EINA_FALSE;
|
|
break;
|
|
}
|
|
if ((ret = ecore_x_randr_crtc_mode_set(e_randr_screen_info->root, usable_crtc->xid, &output_info->xid, 1, mode_info->xid)))
|
|
{
|
|
usable_crtc->geometry.w = mode_info->width;
|
|
usable_crtc->geometry.h = mode_info->height;
|
|
usable_crtc->geometry.x = 0;
|
|
usable_crtc->geometry.y = 0;
|
|
usable_crtc->outputs = eina_list_append(usable_crtc->outputs, output_info);
|
|
|
|
ret &= _e_randr_crtc_move_policy(usable_crtc);
|
|
}
|
|
}
|
|
}
|
|
ecore_x_randr_screen_reset(e_randr_screen_info->root);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* updates all crtcs information regarding a new output
|
|
*/
|
|
static void
|
|
_e_randr_crtcs_possible_output_update(E_Randr_Output_Info *output_info)
|
|
{
|
|
Eina_List *iter;
|
|
E_Randr_Crtc_Info *crtc_info;
|
|
Ecore_X_Randr_Output *outputs = NULL;
|
|
int noutputs = 0;
|
|
|
|
EINA_LIST_FOREACH (e_randr_screen_info->rrvd_info.randr_info_12->crtcs, iter, crtc_info)
|
|
{
|
|
if (!eina_list_data_find(crtc_info->possible_outputs, output_info))
|
|
{
|
|
if ((outputs = ecore_x_randr_crtc_possible_outputs_get(e_randr_screen_info->root, crtc_info->xid, &noutputs)))
|
|
{
|
|
while (--noutputs >= 0)
|
|
{
|
|
if (outputs[noutputs] == output_info->xid)
|
|
{
|
|
crtc_info->possible_outputs = eina_list_append(crtc_info->possible_outputs, output_info);
|
|
break;
|
|
}
|
|
}
|
|
free(outputs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* setup a crtc's current (possible) outputs references
|
|
*/
|
|
static void
|
|
_e_randr_crtc_outputs_refs_update(E_Randr_Crtc_Info *crtc_info)
|
|
{
|
|
Ecore_X_Randr_Output *outputs;
|
|
E_Randr_Output_Info *output_info;
|
|
int i, noutputs;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(e_randr_screen_info->rrvd_info.randr_info_12->outputs);
|
|
|
|
//get references to output_info structs which are related to this CRTC
|
|
if ((outputs = ecore_x_randr_crtc_outputs_get(e_randr_screen_info->root, crtc_info->xid, &noutputs)))
|
|
{
|
|
eina_list_free(crtc_info->outputs);
|
|
crtc_info->outputs = NULL;
|
|
for (i = 0; i < noutputs; i++)
|
|
{
|
|
if (!(output_info = _e_randr_output_info_get(outputs[i])))
|
|
{
|
|
output_info = _e_randr_output_info_new(1);
|
|
output_info->xid = outputs[i];
|
|
_e_randr_output_info_set(output_info);
|
|
e_randr_screen_info->rrvd_info.randr_info_12->outputs = eina_list_append(e_randr_screen_info->rrvd_info.randr_info_12->outputs, output_info);
|
|
}
|
|
crtc_info->outputs = eina_list_append(crtc_info->outputs, output_info);
|
|
output_info->crtc = crtc_info;
|
|
}
|
|
free(outputs);
|
|
}
|
|
//get references to possible output_info structs which are related to this CRTC
|
|
if ((outputs = ecore_x_randr_crtc_possible_outputs_get(e_randr_screen_info->root, crtc_info->xid, &noutputs)))
|
|
{
|
|
eina_list_free(crtc_info->possible_outputs);
|
|
crtc_info->possible_outputs = NULL;
|
|
for (i = 0; i < noutputs; i++)
|
|
{
|
|
output_info = _e_randr_output_info_get(outputs[i]);
|
|
crtc_info->possible_outputs = eina_list_append(crtc_info->possible_outputs, output_info);
|
|
}
|
|
free(outputs);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
static Eina_Bool
|
|
_e_randr_crtc_move_policy(E_Randr_Crtc_Info *new_crtc)
|
|
{
|
|
const E_Randr_Crtc_Info *crtc_rel;
|
|
E_Randr_Output_Info *last_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 last output
|
|
last_output = (E_Randr_Output_Info *)eina_list_data_get(eina_list_last(new_crtc->outputs));
|
|
if (!last_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 = _e_randr_policy_crtc_get(new_crtc, NULL, last_output->policy))) return EINA_TRUE;
|
|
|
|
//following is policy dependend.
|
|
switch (last_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, last_output->policy, e_randr_screen_info->rrvd_info.randr_info_12->alignment);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* returns the highest resolution mode common ammongst the given outputs,
|
|
* optionally limited by max_size_mode. If none is found, NULL is returned.
|
|
*/
|
|
static Ecore_X_Randr_Mode_Info *
|
|
_e_randr_outputs_common_mode_max_get(Eina_List *outputs, Ecore_X_Randr_Mode_Info *max_size_mode)
|
|
{
|
|
Eina_List *all_modes = NULL, *iter, *output_iter, *right;
|
|
E_Randr_Output_Info *output_info;
|
|
Ecore_X_Randr_Mode_Info *mode_info;
|
|
int outputs_mode_found;
|
|
|
|
//create a list of all available modes
|
|
EINA_LIST_FOREACH (outputs, iter, output_info)
|
|
{
|
|
right = eina_list_clone(output_info->modes);
|
|
all_modes = eina_list_merge(all_modes, right);
|
|
}
|
|
|
|
if (max_size_mode)
|
|
{
|
|
//remove all modes that are larger than max_size_mode
|
|
EINA_LIST_FOREACH (all_modes, iter, mode_info)
|
|
{
|
|
if (_modes_size_sort_cb((void *)max_size_mode, (void *)mode_info) < 0)
|
|
all_modes = eina_list_remove(all_modes, mode_info);
|
|
}
|
|
}
|
|
|
|
//sort modes by their sizes
|
|
all_modes = eina_list_sort(all_modes, eina_list_count(all_modes), _modes_size_sort_cb);
|
|
EINA_LIST_REVERSE_FOREACH (all_modes, iter, mode_info)
|
|
{
|
|
outputs_mode_found = 0;
|
|
EINA_LIST_FOREACH (outputs, output_iter, output_info)
|
|
{
|
|
if (eina_list_data_find(output_info->modes, mode_info))
|
|
outputs_mode_found++;
|
|
}
|
|
if (outputs_mode_found == (int)eina_list_count(outputs))
|
|
break;
|
|
mode_info = NULL;
|
|
}
|
|
return mode_info;
|
|
}
|
|
|
|
/*
|
|
static int
|
|
_crtcs_size_sort_cb(const void *d1, const void *d2)
|
|
{
|
|
E_Randr_Crtc_Info *crtc1 = ((E_Randr_Crtc_Info*)d1), *crtc2 = ((E_Randr_Crtc_Info*)d2);
|
|
|
|
return ((crtc1->geometry.w * crtc1->geometry.h) - (crtc2->geometry.w * crtc2->geometry.h));
|
|
}
|
|
*/
|
|
|
|
static int
|
|
_outputs_size_sort_cb(const void *d1, const void *d2)
|
|
{
|
|
E_Randr_Output_Info *output1 = ((E_Randr_Output_Info *)d1), *output2 = ((E_Randr_Output_Info *)d2);
|
|
|
|
return (output1 && output1->crtc && output1->crtc->current_mode && output2 && output2->crtc && output2->crtc->current_mode) ? ((output1->crtc->current_mode->width * output1->crtc->current_mode->height) - (output2->crtc->current_mode->width * output2->crtc->current_mode->height)) : 0;
|
|
}
|
|
|
|
static int
|
|
_modes_size_sort_cb(const void *d1, const void *d2)
|
|
{
|
|
Ecore_X_Randr_Mode_Info *mode1 = ((Ecore_X_Randr_Mode_Info *)d1), *mode2 = ((Ecore_X_Randr_Mode_Info *)d2);
|
|
|
|
return (mode1->width * mode1->height) - (mode2->width * mode2->height);
|
|
}
|
|
|
|
/*
|
|
* returns a mode within a given list of modes that is gemetrically identical.
|
|
* If none is found, NULL is returned.
|
|
*/
|
|
static Ecore_X_Randr_Mode_Info *
|
|
_e_randr_mode_geo_identical_find(Eina_List *modes, Ecore_X_Randr_Mode_Info *mode)
|
|
{
|
|
Eina_List *iter;
|
|
Ecore_X_Randr_Mode_Info *mode_info;
|
|
|
|
EINA_LIST_FOREACH (modes, iter, mode_info)
|
|
{
|
|
if ((mode_info->width == mode->width) && (mode_info->height == mode->height))
|
|
return mode_info;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* reconfigures a CRTC enabling the highest resolution amongst its outputs,
|
|
* without touching any other CRTC currently activated
|
|
*/
|
|
static Eina_Bool
|
|
_e_randr_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 (!_e_randr_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 = _e_randr_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;
|
|
}
|
|
|
|
/*
|
|
* returns EINA_TRUE if given CRTC would intersect with other CRTCs if set to
|
|
* given mode
|
|
*/
|
|
static Eina_Bool
|
|
_e_randr_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;
|
|
}
|
|
|
|
/*
|
|
* returns a list of modes common ammongst the given outputs,
|
|
* optionally limited by max_size_mode. If none are found, NULL is returned.
|
|
*/
|
|
static Eina_List *
|
|
_e_randr_outputs_common_modes_get(Eina_List *outputs, Ecore_X_Randr_Mode_Info *max_size_mode)
|
|
{
|
|
Eina_List *common_modes = NULL, *iter, *output_iter, *right;
|
|
E_Randr_Output_Info *output_info;
|
|
Ecore_X_Randr_Mode_Info *mode_info;
|
|
int outputs_mode_found;
|
|
|
|
if (!outputs) return NULL;
|
|
|
|
//create a list of all available modes
|
|
EINA_LIST_FOREACH (outputs, iter, output_info)
|
|
{
|
|
right = eina_list_clone(output_info->modes);
|
|
common_modes = eina_list_merge(common_modes, right);
|
|
}
|
|
|
|
if (max_size_mode)
|
|
{
|
|
//remove all modes that are larger than max_size_mode
|
|
EINA_LIST_FOREACH (common_modes, iter, mode_info)
|
|
{
|
|
if (_modes_size_sort_cb((void *)max_size_mode, (void *)mode_info) < 0)
|
|
common_modes = eina_list_remove(common_modes, mode_info);
|
|
}
|
|
}
|
|
|
|
//sort modes desc. by their sizes
|
|
for (iter = eina_list_last(common_modes); iter; )
|
|
{
|
|
mode_info = iter->data;
|
|
iter = iter->prev;
|
|
outputs_mode_found = 0;
|
|
EINA_LIST_FOREACH (outputs, output_iter, output_info)
|
|
{
|
|
if (eina_list_data_find(output_info->modes, mode_info))
|
|
outputs_mode_found++;
|
|
}
|
|
if (outputs_mode_found != (int)eina_list_count(outputs))
|
|
common_modes = eina_list_remove(common_modes, mode_info);
|
|
}
|
|
return common_modes;
|
|
}
|
|
|
|
/*
|
|
* reconfigure all CRTCs that had a given CRTC as a clone
|
|
*/
|
|
static Eina_Bool
|
|
_e_randr_crtcs_clone_crtc_removed(E_Randr_Crtc_Info *former_clone)
|
|
{
|
|
Eina_List *iter;
|
|
E_Randr_Crtc_Info *crtc_info;
|
|
|
|
if (!former_clone) return EINA_FALSE;
|
|
|
|
EINA_LIST_FOREACH (e_randr_screen_info->rrvd_info.randr_info_12->crtcs, iter, crtc_info)
|
|
{
|
|
if ((crtc_info == former_clone) || ((crtc_info->geometry.w <= 0) || (crtc_info->geometry.h <= 0))) continue;
|
|
if ((former_clone->geometry.x == crtc_info->geometry.x) && (former_clone->geometry.y == crtc_info->geometry.y) && (former_clone->geometry.w == crtc_info->geometry.w) && (former_clone->geometry.h == crtc_info->geometry.h))
|
|
{
|
|
if (!_e_randr_crtc_outputs_mode_max_set(crtc_info)) return EINA_FALSE;
|
|
}
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
_e_randr_screen_primary_output_assign(E_Randr_Output_Info *removed)
|
|
{
|
|
Eina_List *iter;
|
|
E_Randr_Output_Info *primary_output = NULL, *output_info;
|
|
|
|
E_RANDR_NO_OUTPUTS_RET();
|
|
|
|
if (e_randr_screen_info->rrvd_info.randr_info_12->primary_output && (removed != e_randr_screen_info->rrvd_info.randr_info_12->primary_output)) return;
|
|
if (!(primary_output = _e_randr_output_info_get(ecore_x_randr_primary_output_get(e_randr_screen_info->root))))
|
|
{
|
|
primary_output = eina_list_data_get(eina_list_last(e_randr_screen_info->rrvd_info.randr_info_12->outputs));
|
|
|
|
EINA_LIST_FOREACH (e_randr_screen_info->rrvd_info.randr_info_12->outputs, iter, output_info)
|
|
{
|
|
if (output_info->connection_status == ECORE_X_RANDR_CONNECTION_STATUS_DISCONNECTED || !output_info->crtc || !output_info->crtc->current_mode) continue;
|
|
if ((!primary_output->crtc || !primary_output->crtc->current_mode) || _outputs_size_sort_cb(output_info, primary_output) > 0) primary_output = output_info;
|
|
}
|
|
if (!primary_output->crtc || !primary_output->crtc->current_mode) primary_output = NULL;
|
|
}
|
|
e_randr_screen_info->rrvd_info.randr_info_12->primary_output = primary_output;
|
|
}
|
|
|
|
static void
|
|
_e_randr_output_info_hw_info_set(E_Randr_Output_Info *output_info)
|
|
{
|
|
Ecore_X_Randr_Output *outputs;
|
|
Ecore_X_Randr_Crtc *crtcs;
|
|
E_Randr_Output_Info *output;
|
|
E_Randr_Crtc_Info *crtc;
|
|
int i, num;
|
|
|
|
_e_randr_output_modes_add(output_info);
|
|
output_info->edid = ecore_x_randr_output_edid_get(e_randr_screen_info->root, output_info->xid, &output_info->edid_length);
|
|
if (output_info->edid_length > 0)
|
|
output_info->edid_hash.hash = eina_hash_superfast((char *)output_info->edid, output_info->edid_length);
|
|
|
|
//get the outputs we can use on the same CRTC alongside this one.
|
|
if (output_info->clones)
|
|
{
|
|
eina_list_free(output_info->clones);
|
|
output_info->clones = NULL;
|
|
}
|
|
if ((outputs = ecore_x_randr_output_clones_get(e_randr_screen_info->root, output_info->xid, &num)))
|
|
{
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
if ((output = _e_randr_output_info_get(outputs[i])))
|
|
output_info->clones = eina_list_append(output_info->clones, output);
|
|
}
|
|
free(outputs);
|
|
}
|
|
|
|
//get the CRTCs which are usable with this output.
|
|
if (output_info->possible_crtcs)
|
|
{
|
|
eina_list_free(output_info->possible_crtcs);
|
|
output_info->possible_crtcs = NULL;
|
|
}
|
|
if ((crtcs = ecore_x_randr_output_possible_crtcs_get(e_randr_screen_info->root, output_info->xid, &num)))
|
|
{
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
fprintf(stderr, "E_RANDR: possible CRTC: %d\n", crtcs[i]);
|
|
if ((crtc = _e_randr_crtc_info_get(crtcs[i])))
|
|
{
|
|
fprintf(stderr, "E_RANDR: \tfound the suiting struct at %p\n", crtc);
|
|
output_info->possible_crtcs = eina_list_append(output_info->possible_crtcs, crtc);
|
|
}
|
|
}
|
|
free(crtcs);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "E_RANDR: Output %x does not have a single possible CRTC.\n", output_info->xid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* free the hardware specifig parts of the information
|
|
* removes all traces of an output within the data.
|
|
* @param output_info the output info to be freed.
|
|
*/
|
|
static void
|
|
_e_randr_output_hw_info_free(E_Randr_Output_Info *output_info)
|
|
{
|
|
E_Randr_Crtc_Info *crtc_info;
|
|
Eina_List *iter;
|
|
if (!output_info) return;
|
|
|
|
if (output_info->modes)
|
|
{
|
|
eina_list_free(output_info->modes);
|
|
output_info->modes = NULL;
|
|
}
|
|
if (output_info->preferred_modes)
|
|
{
|
|
eina_list_free(output_info->preferred_modes);
|
|
output_info->preferred_modes = NULL;
|
|
}
|
|
if (output_info->edid)
|
|
{
|
|
free(output_info->edid);
|
|
output_info->edid = NULL;
|
|
}
|
|
if (output_info->wired_clones)
|
|
{
|
|
eina_list_free(output_info->wired_clones);
|
|
output_info->wired_clones = NULL;
|
|
}
|
|
if (output_info->compatible_outputs)
|
|
{
|
|
eina_list_free(output_info->compatible_outputs);
|
|
output_info->compatible_outputs = NULL;
|
|
}
|
|
if (output_info->possible_crtcs)
|
|
{
|
|
eina_list_free(output_info->possible_crtcs);
|
|
output_info->possible_crtcs = NULL;
|
|
}
|
|
if (output_info->clones)
|
|
{
|
|
eina_list_free(output_info->clones);
|
|
output_info->clones = NULL;
|
|
}
|
|
|
|
EINA_LIST_FOREACH (e_randr_screen_info->rrvd_info.randr_info_12->crtcs, iter, crtc_info)
|
|
{
|
|
crtc_info->possible_outputs = eina_list_remove(crtc_info->possible_outputs, output_info);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* checks whether a given output is a common clone of the given list's outputs
|
|
*/
|
|
static Eina_Bool
|
|
_e_randr_outputs_are_clones(E_Randr_Output_Info *output_info, Eina_List *outputs)
|
|
{
|
|
E_Randr_Output_Info *output;
|
|
Eina_List *iter;
|
|
|
|
if (!outputs || !output_info) return EINA_FALSE;
|
|
|
|
EINA_LIST_FOREACH (output_info->clones, iter, output)
|
|
{
|
|
if (!eina_list_data_find(output_info->clones, output))
|
|
return EINA_FALSE;
|
|
}
|
|
return EINA_TRUE;
|
|
}
|
|
|