From 20e8feca2cd9cb00106f82590cace0e0a1c67d16 Mon Sep 17 00:00:00 2001 From: Carsten Haitzler Date: Sun, 6 Nov 2011 06:41:39 +0000 Subject: [PATCH] i'll put in leif's randr code - even though it's buggy, it's the best way for it to be worked on by people for release. SVN revision: 64801 --- configure.ac | 3 + src/bin/e_config.c | 309 +++--- src/bin/e_config.h | 4 +- src/bin/e_randr.c | 751 +++++++++++--- src/bin/e_randr.h | 103 +- src/modules/Makefile.am | 4 + .../conf_display/e_int_config_display.c | 50 +- src/modules/conf_randr/Makefile.am | 51 + .../conf_randr/e-module-conf_randr.edc | 945 ++++++++++++++++++ src/modules/conf_randr/e_int_config_randr.c | 424 ++++++++ src/modules/conf_randr/e_int_config_randr.h | 73 ++ .../e_int_config_randr_arrangement.c | 806 +++++++++++++++ .../e_int_config_randr_orientation.c | 336 +++++++ .../conf_randr/e_int_config_randr_policies.c | 302 ++++++ .../e_int_config_randr_resolutions.c | 265 +++++ src/modules/conf_randr/e_mod_main.c | 42 + src/modules/conf_randr/e_mod_main.h | 19 + .../conf_randr/images/display-glass-shine.png | Bin 0 -> 3719 bytes src/modules/conf_randr/images/display.png | Bin 0 -> 105928 bytes src/modules/conf_randr/images/icon.png | Bin 0 -> 3507 bytes .../conf_randr/images/video-display.svg | 741 ++++++++++++++ src/modules/conf_randr/module.desktop.in | 32 + 22 files changed, 4863 insertions(+), 397 deletions(-) create mode 100644 src/modules/conf_randr/Makefile.am create mode 100644 src/modules/conf_randr/e-module-conf_randr.edc create mode 100644 src/modules/conf_randr/e_int_config_randr.c create mode 100644 src/modules/conf_randr/e_int_config_randr.h create mode 100644 src/modules/conf_randr/e_int_config_randr_arrangement.c create mode 100644 src/modules/conf_randr/e_int_config_randr_orientation.c create mode 100644 src/modules/conf_randr/e_int_config_randr_policies.c create mode 100644 src/modules/conf_randr/e_int_config_randr_resolutions.c create mode 100644 src/modules/conf_randr/e_mod_main.c create mode 100644 src/modules/conf_randr/e_mod_main.h create mode 100644 src/modules/conf_randr/images/display-glass-shine.png create mode 100644 src/modules/conf_randr/images/display.png create mode 100644 src/modules/conf_randr/images/icon.png create mode 100644 src/modules/conf_randr/images/video-display.svg create mode 100644 src/modules/conf_randr/module.desktop.in diff --git a/configure.ac b/configure.ac index b93bda440..e9865bd1d 100644 --- a/configure.ac +++ b/configure.ac @@ -745,6 +745,7 @@ AC_E_OPTIONAL_MODULE([comp], true) AC_E_OPTIONAL_MODULE([shot], true) AC_E_OPTIONAL_MODULE([backlight], true) AC_E_OPTIONAL_MODULE([tasks], true) +AC_E_OPTIONAL_MODULE([conf_randr], true) SUSPEND="" HIBERNATE="" @@ -843,6 +844,8 @@ src/modules/conf_interaction/Makefile src/modules/conf_interaction/module.desktop src/modules/msgbus/Makefile src/modules/msgbus/module.desktop +src/modules/conf_randr/Makefile +src/modules/conf_randr/module.desktop src/modules/gadman/Makefile src/modules/gadman/module.desktop src/modules/mixer/Makefile diff --git a/src/bin/e_config.c b/src/bin/e_config.c index c08a01830..edf43c9c5 100644 --- a/src/bin/e_config.c +++ b/src/bin/e_config.c @@ -6,23 +6,9 @@ #define DEF_MENUCLICK 0.25 #endif -typedef enum _Eet_Union -{ - EET_SCREEN_INFO_11 = (int)((1 << 16) | 1), - EET_SCREEN_INFO_12 = (int)((1 << 16) | 2), - EET_SCREEN_INFO_13 = (int)((1 << 16) | 3), - EET_UNKNOWN -} Eet_Union; - -struct { - Eet_Union u; - const char *name; -} eet_mapping[] = { - { EET_SCREEN_INFO_11, "E_Config_Screen_11" }, - { EET_SCREEN_INFO_12, "E_Config_Screen_12" }, - { EET_SCREEN_INFO_12, "E_Config_Screen_13" }, - { EET_UNKNOWN, NULL } -}; +#define RANDR_SERIALIZED_SETUP_11 ((int)((1 << 16) | 1)) +#define RANDR_SERIALIZED_SETUP_12 ((int)((1 << 16) | 2)) +#define RANDR_SERIALIZED_SETUP_13 ((int)((1 << 16) | 3)) EAPI E_Config *e_config = NULL; @@ -34,8 +20,6 @@ static void _e_config_free(E_Config *cfg); static Eina_Bool _e_config_cb_timer(void *data); static int _e_config_eet_close_handle(Eet_File *ef, char *file); static void _e_config_acpi_bindings_add(void); -static const char * _eet_union_type_get(const void *data, Eina_Bool *unknow); -static Eina_Bool _eet_union_type_set(const char *type, void *data, Eina_Bool unknow); /* local subsystem globals */ static int _e_config_save_block = 0; @@ -65,15 +49,16 @@ static E_Config_DD *_e_config_shelf_desk_edd = NULL; static E_Config_DD *_e_config_mime_icon_edd = NULL; static E_Config_DD *_e_config_syscon_action_edd = NULL; static E_Config_DD *_e_config_env_var_edd = NULL; -static E_Config_DD *_e_config_screen_size_edd = NULL; -static E_Config_DD *_e_config_screen_size_mm_edd = NULL; -static E_Config_DD *_e_config_eina_rectangle_edd = NULL; -static E_Config_DD *_e_config_screen_info_edd = NULL; -static E_Config_DD *_e_config_screen_restore_info_11_edd = NULL; -static E_Config_DD *_e_config_screen_restore_info_12_edd = NULL; -static E_Config_DD *_e_config_screen_output_edid_hash_edd = NULL; -static E_Config_DD *_e_config_screen_output_restore_info_edd = NULL; -static E_Config_DD *_e_config_screen_crtc_restore_info_edd = NULL; +static E_Config_DD *_e_config_randr_size_edd = NULL; +static E_Config_DD *_e_config_randr_size_mm_edd = NULL; +static E_Config_DD *_e_config_randr_edid_hash_edd = NULL; +static E_Config_DD *_e_config_randr_serialized_setup_edd = NULL; +static E_Config_DD *_e_config_randr_serialized_setup_11_edd = NULL; +static E_Config_DD *_e_config_randr_serialized_setup_12_edd = NULL; +static E_Config_DD *_e_config_randr_serialized_output_policy_edd = NULL; +static E_Config_DD *_e_config_randr_serialized_output_edd = NULL; +static E_Config_DD *_e_config_randr_serialized_mode_info_edd = NULL; +static E_Config_DD *_e_config_randr_serialized_crtc_edd = NULL; EAPI int E_EVENT_CONFIG_ICON_THEME = 0; @@ -526,107 +511,109 @@ e_config_init(void) E_CONFIG_VAL(D, T, val, STR); E_CONFIG_VAL(D, T, unset, UCHAR); - _e_config_screen_size_edd = E_CONFIG_DD_NEW("Ecore_X_Randr_Screen_Size", Ecore_X_Randr_Screen_Size); + _e_config_randr_size_edd = E_CONFIG_DD_NEW("Ecore_X_Randr_Screen_Size", Ecore_X_Randr_Screen_Size); #undef T #undef D #define T Ecore_X_Randr_Screen_Size -#define D _e_config_screen_size_edd +#define D _e_config_randr_size_edd E_CONFIG_VAL(D, T, width, INT); E_CONFIG_VAL(D, T, height, INT); E_CONFIG_VAL(D, T, width, INT); E_CONFIG_VAL(D, T, height, INT); - _e_config_screen_size_mm_edd = E_CONFIG_DD_NEW("Ecore_X_Randr_Screen_Size_MM", Ecore_X_Randr_Screen_Size_MM); + _e_config_randr_size_mm_edd = E_CONFIG_DD_NEW("Ecore_X_Randr_Screen_Size_MM", Ecore_X_Randr_Screen_Size_MM); #undef T #undef D #define T Ecore_X_Randr_Screen_Size_MM -#define D _e_config_screen_size_mm_edd +#define D _e_config_randr_size_mm_edd E_CONFIG_VAL(D, T, width, INT); E_CONFIG_VAL(D, T, height, INT); E_CONFIG_VAL(D, T, width_mm, INT); E_CONFIG_VAL(D, T, height_mm, INT); - _e_config_screen_restore_info_11_edd = E_CONFIG_DD_NEW("E_Randr_Screen_Restore_Info_11", E_Randr_Screen_Restore_Info_11); + _e_config_randr_edid_hash_edd = E_CONFIG_DD_NEW("E_Randr_Edid_Hash", E_Randr_Edid_Hash); #undef T #undef D -#define T E_Randr_Screen_Restore_Info_11 -#define D _e_config_screen_restore_info_11_edd - E_CONFIG_SUB(D, T, size, _e_config_screen_size_edd); +#define T E_Randr_Edid_Hash +#define D _e_config_randr_edid_hash_edd + E_CONFIG_VAL(D, T, hash, INT); + + _e_config_randr_serialized_setup_11_edd = E_CONFIG_DD_NEW("E_Randr_Serialized_Setup_11", E_Randr_Serialized_Setup_11); +#undef T +#undef D +#define T E_Randr_Serialized_Setup_11 +#define D _e_config_randr_serialized_setup_11_edd + E_CONFIG_SUB(D, T, size, _e_config_randr_size_edd); E_CONFIG_VAL(D, T, orientation, INT); E_CONFIG_VAL(D, T, refresh_rate, SHORT); - _e_config_eina_rectangle_edd = E_CONFIG_DD_NEW("Eina_Rectangle", Eina_Rectangle); + _e_config_randr_serialized_output_policy_edd = E_CONFIG_DD_NEW("E_Randr_Serialized_Output_Policy", E_Randr_Serialized_Output_Policy); #undef T #undef D -#define T Eina_Rectangle -#define D _e_config_eina_rectangle_edd - E_CONFIG_VAL(D, T, x, INT); - E_CONFIG_VAL(D, T, y, INT); - E_CONFIG_VAL(D, T, w, INT); - E_CONFIG_VAL(D, T, h, INT); +#define T E_Randr_Serialized_Output_Policy +#define D _e_config_randr_serialized_output_policy_edd + E_CONFIG_VAL(D, T, name, STR); + E_CONFIG_VAL(D, T, name_length, INT); + E_CONFIG_VAL(D, T, policy, INT); - _e_config_screen_output_edid_hash_edd = E_CONFIG_DD_NEW("E_Randr_Output_Edid_Hash", E_Randr_Output_Edid_Hash); + _e_config_randr_serialized_output_edd = E_CONFIG_DD_NEW("E_Randr_Serialized_Output", E_Randr_Serialized_Output); #undef T #undef D -#define T E_Randr_Output_Edid_Hash -#define D _e_config_screen_output_edid_hash_edd - E_CONFIG_VAL(D, T, hash, INT); - - // FIXME: need to totally re-do this randr config stuff - remove the - // union stuff. do this differently to not use unions really. not - // intended for how it is used here really. - _e_config_screen_output_restore_info_edd = E_CONFIG_DD_NEW("E_Randr_Output_Restore_Info", E_Randr_Output_Restore_Info); -#undef T -#undef D -#define T E_Randr_Output_Restore_Info -#define D _e_config_screen_output_restore_info_edd - E_CONFIG_SUB(D, T, edid_hash, _e_config_screen_output_edid_hash_edd); +#define T E_Randr_Serialized_Output +#define D _e_config_randr_serialized_output_edd + E_CONFIG_VAL(D, T, name, STR); + E_CONFIG_VAL(D, T, name_length, INT); + E_CONFIG_SUB(D, T, edid_hash, _e_config_randr_edid_hash_edd); E_CONFIG_VAL(D, T, backlight_level, DOUBLE); - _e_config_screen_crtc_restore_info_edd = E_CONFIG_DD_NEW("E_Randr_Crtc_Restore_Info", E_Randr_Crtc_Restore_Info); + _e_config_randr_serialized_mode_info_edd = E_CONFIG_DD_NEW("E_Randr_Serialized_Mode_Info", Ecore_X_Randr_Mode_Info); #undef T #undef D -#define T E_Randr_Crtc_Restore_Info -#define D _e_config_screen_crtc_restore_info_edd - E_CONFIG_SUB(D, T, geometry, _e_config_eina_rectangle_edd); - E_CONFIG_LIST(D, T, outputs, _e_config_screen_output_restore_info_edd); +#define T Ecore_X_Randr_Mode_Info +#define D _e_config_randr_serialized_mode_info_edd + E_CONFIG_VAL(D, T, width, INT); + E_CONFIG_VAL(D, T, height, INT); + E_CONFIG_VAL(D, T, dotClock, LL); + E_CONFIG_VAL(D, T, hSyncStart, INT); + E_CONFIG_VAL(D, T, hSyncEnd, INT); + E_CONFIG_VAL(D, T, hTotal, INT); + E_CONFIG_VAL(D, T, hSkew, INT); + E_CONFIG_VAL(D, T, vSyncStart, INT); + E_CONFIG_VAL(D, T, vSyncEnd, INT); + E_CONFIG_VAL(D, T, vTotal, INT); + E_CONFIG_VAL(D, T, name, STR); + E_CONFIG_VAL(D, T, nameLength, INT); + E_CONFIG_VAL(D, T, modeFlags, LL); + + _e_config_randr_serialized_crtc_edd = E_CONFIG_DD_NEW("E_Randr_Serialized_Crtc", E_Randr_Serialized_Crtc); +#undef T +#undef D +#define T E_Randr_Serialized_Crtc +#define D _e_config_randr_serialized_crtc_edd + E_CONFIG_LIST(D, T, serialized_outputs, _e_config_randr_serialized_output_edd); + E_CONFIG_SUB(D, T, mode_info, _e_config_randr_serialized_mode_info_edd); + E_CONFIG_VAL(D, T, pos.x, INT); + E_CONFIG_VAL(D, T, pos.y, INT); + EET_DATA_DESCRIPTOR_ADD_LIST_STRING(D, T, "Crtc_Possible_Outputs_Names", possible_outputs_names); E_CONFIG_VAL(D, T, orientation, INT); - _e_config_screen_restore_info_12_edd = E_CONFIG_DD_NEW("E_Randr_Screen_Restore_Info_12", E_Randr_Screen_Restore_Info_12); + _e_config_randr_serialized_setup_12_edd = E_CONFIG_DD_NEW("E_Randr_Serialized_Setup_12", E_Randr_Serialized_Setup_12); #undef T #undef D -#define T E_Randr_Screen_Restore_Info_12 -#define D _e_config_screen_restore_info_12_edd - E_CONFIG_LIST(D, T, crtcs, _e_config_screen_crtc_restore_info_edd); - E_CONFIG_LIST(D, T, outputs_edid_hashes, _e_config_screen_output_edid_hash_edd); - E_CONFIG_VAL(D, T, noutputs, INT); - E_CONFIG_VAL(D, T, output_policy, INT); - E_CONFIG_VAL(D, T, alignment, INT); +#define T E_Randr_Serialized_Setup_12 +#define D _e_config_randr_serialized_setup_12_edd + E_CONFIG_VAL(D, T, timestamp, DOUBLE); + E_CONFIG_LIST(D, T, serialized_crtcs, _e_config_randr_serialized_crtc_edd); + E_CONFIG_LIST(D, T, serialized_edid_hashes, _e_config_randr_edid_hash_edd); + _e_config_randr_serialized_setup_edd = E_CONFIG_DD_NEW("E_Randr_Serialized_Setup", E_Randr_Serialized_Setup); #undef T #undef D -#define T E_Randr_Screen_Restore_Info -#define D _e_config_screen_info_edd - Eet_Data_Descriptor_Class eddc; - Eet_Data_Descriptor *unified, *edd_11_info, *edd_12_info; - EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, T); - D = eet_data_descriptor_stream_new(&eddc); - eddc.version = EET_DATA_DESCRIPTOR_CLASS_VERSION; - eddc.func.type_get = _eet_union_type_get; - eddc.func.type_set = _eet_union_type_set; - //virtual types to work around EET's inability to differentiate when it - //comes to pointers (a->b) vs. values (a.b) in union mappings - edd_11_info = E_CONFIG_DD_NEW("E_Randr_Screen_Restore_Info_11_Struct", E_Randr_Screen_Restore_Info_11); - E_CONFIG_SUB(edd_11_info, E_Randr_Screen_Restore_Info_Union, restore_info_11, _e_config_screen_restore_info_11_edd); - edd_12_info = E_CONFIG_DD_NEW("E_Randr_Screen_Restore_Info_12_Struct", E_Randr_Screen_Restore_Info_12); - E_CONFIG_LIST(edd_12_info, E_Randr_Screen_Restore_Info_Union, restore_info_12, _e_config_screen_restore_info_12_edd); - unified = eet_data_descriptor_stream_new(&eddc); - EET_DATA_DESCRIPTOR_ADD_MAPPING(unified, "E_Config_Screen_11", edd_11_info); - EET_DATA_DESCRIPTOR_ADD_MAPPING(unified, "E_Config_Screen_12", edd_12_info); - EET_DATA_DESCRIPTOR_ADD_MAPPING(unified, "E_Config_Screen_13", edd_12_info); -// DISABLE! crashie crashie long time -// EET_DATA_DESCRIPTOR_ADD_UNION(D, T, "E_Randr_Screen_Restore_Info_Union", rrvd_restore_info, randr_version, unified); - E_CONFIG_VAL(D, T, randr_version, INT); +#define T E_Randr_Serialized_Setup +#define D _e_config_randr_serialized_setup_edd + E_CONFIG_SUB(D, T, serialized_setup_11, _e_config_randr_serialized_setup_11_edd); + E_CONFIG_LIST(D, T, serialized_setups_12, _e_config_randr_serialized_setup_12_edd); + E_CONFIG_LIST(D, T, serialized_outputs_policies, _e_config_randr_serialized_output_policy_edd); _e_config_edd = E_CONFIG_DD_NEW("E_Config", E_Config); #undef T @@ -787,7 +774,8 @@ e_config_init(void) E_CONFIG_VAL(D, T, desklock_ask_presentation, UCHAR); E_CONFIG_VAL(D, T, desklock_ask_presentation_timeout, DOUBLE); - E_CONFIG_LIST(D, T, screen_info, _e_config_screen_info_edd); + //randr specifics + E_CONFIG_SUB(D, T, randr_serialized_setup, _e_config_randr_serialized_setup_edd); E_CONFIG_VAL(D, T, screensaver_enable, INT); E_CONFIG_VAL(D, T, screensaver_timeout, INT); @@ -959,7 +947,7 @@ e_config_shutdown(void) E_CONFIG_DD_FREE(_e_config_mime_icon_edd); E_CONFIG_DD_FREE(_e_config_syscon_action_edd); E_CONFIG_DD_FREE(_e_config_env_var_edd); - E_CONFIG_DD_FREE(_e_config_screen_info_edd); + E_CONFIG_DD_FREE(_e_config_randr_serialized_setup_edd); return 1; } @@ -1920,10 +1908,13 @@ _e_config_free(E_Config *ecf) E_Color_Class *cc; E_Path_Dir *epd; E_Remember *rem; - E_Randr_Screen_Restore_Info *screen_info; - E_Randr_Crtc_Restore_Info *crtc_info; - E_Randr_Output_Info *output_info; - E_Randr_Screen_Restore_Info_12 *restore_info_12; + E_Randr_Serialized_Setup *serialized_setup; + E_Randr_Serialized_Setup_12 *serialized_setup_12; + E_Randr_Serialized_Crtc *serialized_crtc; + E_Randr_Serialized_Output_Policy *serialized_output_policy; + E_Randr_Serialized_Output *serialized_output; + E_Randr_Edid_Hash *edid_hash; + char *output_name; E_Config_Env_Var *evr; @@ -2074,37 +2065,50 @@ _e_config_free(E_Config *ecf) if (sca->icon) eina_stringshare_del(sca->icon); E_FREE(sca); } - if (ecf->screen_info) + if(ecf->randr_serialized_setup) { - EINA_LIST_FREE(ecf->screen_info, screen_info) - { - switch (screen_info->randr_version) - { - case EET_SCREEN_INFO_11: - free(screen_info->rrvd_restore_info.restore_info_11); - break; - case EET_SCREEN_INFO_12: - case EET_SCREEN_INFO_13: - EINA_LIST_FREE(screen_info->rrvd_restore_info.restore_info_12, restore_info_12) - { - EINA_LIST_FREE(restore_info_12->crtcs, crtc_info) - { - EINA_LIST_FREE(crtc_info->outputs, output_info) - { - free(output_info->name); - free(output_info->edid); - free (output_info); - } - free (crtc_info); - } - free(restore_info_12); - } - eina_list_free(screen_info->rrvd_restore_info.restore_info_12); - break; - } - free(screen_info); - } + if(ecf->randr_serialized_setup->serialized_setup_11) + free(serialized_setup->serialized_setup_11); + else if(ecf->randr_serialized_setup->serialized_setups_12) + { + EINA_LIST_FREE(ecf->randr_serialized_setup->serialized_setups_12, serialized_setup_12) + { + EINA_LIST_FREE(serialized_setup_12->serialized_crtcs, serialized_crtc) + { + if (!serialized_crtc) continue; + EINA_LIST_FREE(serialized_crtc->serialized_outputs, serialized_output) + { + if (serialized_output) + { + if (serialized_output->name) free(serialized_output); + free(serialized_output); + } + } + EINA_LIST_FREE(serialized_crtc->possible_outputs_names, output_name) + { + if (output_name) free(output_name); + } + if (serialized_crtc->mode_info.name) + free(serialized_crtc->mode_info.name); + free(serialized_crtc); + } + EINA_LIST_FREE(serialized_setup_12->serialized_edid_hashes, edid_hash) + { + if (edid_hash) free(edid_hash); + } + free(serialized_setup_12); + } + } + EINA_LIST_FREE(ecf->randr_serialized_setup->serialized_outputs_policies, serialized_output_policy) + { + if (!serialized_output) continue; + if (serialized_output_policy->name) + free(serialized_output_policy->name); + free(serialized_output_policy); + } + free(ecf->randr_serialized_setup); } + EINA_LIST_FREE(ecf->env_vars, evr) { if (evr->var) eina_stringshare_del(evr->var); @@ -2121,14 +2125,14 @@ _e_config_free(E_Config *ecf) E_FREE(ecf); } -static Eina_Bool + static Eina_Bool _e_config_cb_timer(void *data) { e_util_dialog_show(_("Settings Upgraded"), "%s", (char *)data); return 0; } -static int + static int _e_config_eet_close_handle(Eet_File *ef, char *file) { Eet_Error err; @@ -2138,16 +2142,16 @@ _e_config_eet_close_handle(Eet_File *ef, char *file) switch (err) { case EET_ERROR_NONE: - /* all good - no error */ - break; + /* all good - no error */ + break; case EET_ERROR_BAD_OBJECT: - erstr = _("The EET file handle is bad."); - break; + erstr = _("The EET file handle is bad."); + break; case EET_ERROR_EMPTY: - erstr = _("The file data is empty."); - break; + erstr = _("The file data is empty."); + break; case EET_ERROR_NOT_WRITABLE: - erstr = _("The file is not writable. Perhaps the disk is read-only
or you lost permissions to your files."); + erstr = _("The file is not writable. Perhaps the disk is read-only
or you lost permissions to your files."); break; case EET_ERROR_OUT_OF_MEMORY: erstr = _("Memory ran out while preparing the write.
Please free up memory."); @@ -2285,36 +2289,3 @@ _e_config_acpi_bindings_add(void) bind->params = eina_stringshare_add("now"); e_config->acpi_bindings = eina_list_append(e_config->acpi_bindings, bind); } - -static const char * -_eet_union_type_get(const void *data, Eina_Bool *unknow) -{ - const Eet_Union *u = data; - int i; - - if (unknow) *unknow = EINA_FALSE; - for (i = 0; eet_mapping[i].name; ++i) - if (*u == eet_mapping[i].u) - return eet_mapping[i].name; - - if (unknow) *unknow = EINA_TRUE; - return NULL; -} - -static Eina_Bool -_eet_union_type_set(const char *type, void *data, Eina_Bool unknow) -{ - Eet_Union *u = data; - int i; - - if (unknow) return EINA_FALSE; - - for (i = 0; eet_mapping[i].name; ++i) - if (strcmp(eet_mapping[i].name, type) == 0) - { - *u = eet_mapping[i].u; - return EINA_TRUE; - } - - return EINA_FALSE; -} diff --git a/src/bin/e_config.h b/src/bin/e_config.h index 123843b2f..e793cb733 100644 --- a/src/bin/e_config.h +++ b/src/bin/e_config.h @@ -33,7 +33,7 @@ typedef struct _E_Event_Config_Icon_Theme E_Event_Config_Icon_Theme; /* increment this whenever a new set of config values are added but the users * config doesn't need to be wiped - simply new values need to be put in */ -#define E_CONFIG_FILE_GENERATION 0x0145 +#define E_CONFIG_FILE_GENERATION 0x0146 #define E_CONFIG_FILE_VERSION ((E_CONFIG_FILE_EPOCH << 16) | E_CONFIG_FILE_GENERATION) #define E_EVAS_ENGINE_DEFAULT 0 @@ -250,7 +250,7 @@ struct _E_Config int mouse_accel_denominator; // GUI int mouse_accel_threshold; // GUI - Eina_List *screen_info; // GUI + E_Randr_Serialized_Setup *randr_serialized_setup; // GUI int border_raise_on_mouse_action; // GUI int border_raise_on_focus; // GUI diff --git a/src/bin/e_randr.c b/src/bin/e_randr.c index bb64ecf14..4b8cc7660 100644 --- a/src/bin/e_randr.c +++ b/src/bin/e_randr.c @@ -11,6 +11,13 @@ #define Ecore_X_Randr_Unset -1 /* + * 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 */ @@ -57,6 +64,11 @@ 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); +EAPI void e_randr_store_configuration(E_Randr_Screen_Info *screen_info); +EAPI Eina_Bool e_randr_try_restore_configuration(E_Randr_Screen_Info *screen_info); 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); @@ -66,12 +78,8 @@ 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 Eina_Bool _e_randr_outputs_connected(Eina_List *outputs_info); static Ecore_X_Randr_Output *_e_randr_outputs_to_array(Eina_List *outputs_info); -//static int _e_randr_config_find_suiting_config_11(E_Randr_Screen_Restore_Info_11** restore_info); -static E_Randr_Screen_Restore_Info_12 * _e_randr_config_find_suiting_config_12(void); -//static Eina_Bool _e_randr_config_enable_11(int size_index, Ecore_X_Randr_Refresh_Rate refresh_rate, Ecore_X_Randr_Orientation orientation); -//static Eina_Bool _e_randr_config_enable_12(const E_Randr_Screen_Restore_Info_12 *restore_info); +static E_Randr_Serialized_Setup_12 * _e_randr_config_find_suiting_config_12(void); 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); @@ -90,7 +98,7 @@ 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); -E_Randr_Screen_Info *e_randr_screen_info = NULL; +EAPI E_Randr_Screen_Info *e_randr_screen_info = NULL; static Eina_List *_e_randr_event_handlers = NULL; EINTERN Eina_Bool @@ -270,7 +278,6 @@ _e_randr_screen_info_12_new(void) .crtcs = NULL, .outputs = NULL, .primary_output = NULL, - .output_policy = ECORE_X_RANDR_OUTPUT_POLICY_NONE, .alignment = ECORE_X_RANDR_RELATIVE_ALIGNMENT_NONE }; @@ -459,7 +466,8 @@ _e_randr_output_info_new(int nrequested) .connector_type = Ecore_X_Randr_Unset, .connection_status = ECORE_X_RANDR_CONNECTION_STATUS_DISCONNECTED, .subpixel_order= Ecore_X_Randr_Unset, - .compatible_outputs = NULL + .compatible_outputs = NULL, + .policy = ECORE_X_RANDR_OUTPUT_POLICY_NONE }; if (!(ret = malloc(sizeof(E_Randr_Output_Info) * nrequested))) return NULL; @@ -760,7 +768,7 @@ _e_randr_event_cb(void *data __UNUSED__, int type, void *ev) else if (type == ECORE_X_EVENT_RANDR_OUTPUT_CHANGE) { Ecore_X_Event_Randr_Output_Change *event = ev; - const E_Randr_Screen_Restore_Info_12 *restore_info; + const E_Randr_Serialized_Setup_12 *restore_info; E_Randr_Output_Info* output_info = NULL; /* available information: struct _Ecore_X_Event_Randr_Output_Change @@ -807,11 +815,13 @@ _e_randr_event_cb(void *data __UNUSED__, int type, void *ev) _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 ((restore_info = _e_randr_config_find_suiting_config_12())) //maybe we have a suiting configuration //_e_randr_config_enable_12(restore_info); ; else + */ enabled = _e_randr_try_enable_output(output_info, EINA_FALSE); //maybe give a success message? } _e_randr_notify_output_change(output_info); @@ -841,11 +851,13 @@ _e_randr_event_cb(void *data __UNUSED__, int type, void *ev) _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); @@ -865,6 +877,7 @@ _e_randr_event_cb(void *data __UNUSED__, int type, void *ev) }; */ } + e_randr_try_restore_configuration(e_randr_screen_info); on_exit: return ECORE_CALLBACK_RENEW; } @@ -999,146 +1012,602 @@ _e_randr_policy_crtc_get(E_Randr_Crtc_Info *but, E_Randr_Crtc_Info *hint __UNUSE return ret; } -/* -static Eina_Bool -_e_randr_outputs_connected(Eina_List *outputs_info) +// Setup store functions + Eina_Bool +_e_randr_copy_mode_info(Ecore_X_Randr_Mode_Info *dest, Ecore_X_Randr_Mode_Info *src) { - Eina_List *iter; - E_Randr_Output_Info *output_info; - EINA_LIST_FOREACH(outputs_info, iter, output_info) - if (output_info->connection_status == ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED) return EINA_TRUE; - return EINA_FALSE; -} + if (!dest || !src) return EINA_FALSE; -static Eina_Bool -_e_randr_config_enable_11(int size_index, Ecore_X_Randr_Refresh_Rate refresh_rate, Ecore_X_Randr_Orientation orientation) -{ - E_Randr_Screen_Info_11 *current_info_11; - - if (E_RANDR_NO_11 || (size_index < 0) || (refresh_rate < 0) || - (orientation < 0)) return EINA_FALSE; - - if (!ecore_x_randr_screen_primary_output_size_set(e_randr_screen_info->root, size_index) - || !ecore_x_randr_screen_primary_output_orientation_set(e_randr_screen_info->root, orientation) - || !ecore_x_randr_screen_primary_output_refresh_rate_set(e_randr_screen_info->root, size_index, refresh_rate)) return EINA_FALSE; - - //TODO: move this to the screen event later. - current_info_11 = e_randr_screen_info->rrvd_info.randr_info_11; - - current_info_11->csize_index = size_index; - current_info_11->corientation = orientation; - current_info_11->current_rate = refresh_rate; + 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; -} -static Eina_Bool -_e_randr_config_enable_12(const E_Randr_Screen_Restore_Info_12 *restore_info __UNUSED__) -{ - if (E_RANDR_NO_12 || !restore_info) return EINA_FALSE; - E_Randr_Screen_Info_12 *current_info_12; - E_Randr_Screen_Restore_Info_12 *restore_info_12 = NULL; - E_Randr_Crtc_Restore_Info *crtc_restore_info = NULL; - E_Randr_Crtc_Info *crtc_info; - E_Randr_Output_Info *output_info; - Eina_List *crtc_restore_iter; - - current_info_12 = (e_randr_screen_info->rrvd_info).randr_info_12; - EINA_LIST_FOREACH(restore_info_12->crtcs, crtc_restore_iter, crtc_restore_info) - { - ; - } - current_info_12->alignment = restore_info_12->alignment; - current_info_12->output_policy = restore_info_12->output_policy; - return EINA_TRUE; +_e_randr_copy_mode_info_fail_free_name: + free(dest->name); return EINA_FALSE; } -static int -_e_randr_config_find_suiting_config_11(E_Randr_Screen_Restore_Info_11 **restore_info) + void +_e_randr_free_serialized_mode_info(Ecore_X_Randr_Mode_Info *mode_info) { - E_RANDR_NO_11_RET(Ecore_X_Randr_None); - Eina_List *cfg_screen_restore_info_iter; - E_Randr_Screen_Restore_Info *screen_restore_info; - - E_Randr_Screen_Restore_Info_11 *restore_info_11; - Ecore_X_Randr_Screen_Size_MM *sizes; - Ecore_X_Randr_Refresh_Rate *rates = NULL; - int i = 0, j = 0, nsizes = 0, nrates = 0; - - EINA_LIST_FOREACH(e_config->screen_info, cfg_screen_restore_info_iter, screen_restore_info) - { - // 'screen_restore_info' should _never_ be NULL, since this functions shouldn't be called due to randr init failing. - if (!screen_restore_info) continue; - if (screen_restore_info->randr_version != ECORE_X_RANDR_1_1) continue; - restore_info_11 = screen_restore_info->rrvd_restore_info.restore_info_11; - if((sizes = ecore_x_randr_screen_primary_output_sizes_get(e_randr_screen_info->root, &nsizes))) - { - for (i = 0; i < nsizes; i++) - { - if ((restore_info_11->size.width == sizes[i].width) - && (restore_info_11->size.height == sizes[i].height)) - { - if ((rates = ecore_x_randr_screen_primary_output_refresh_rates_get(e_randr_screen_info->root, i, &nrates))) - { - for (j = 0; j < nrates; j++) - if (rates[j] == restore_info_11->refresh_rate) - { - if (restore_info) *restore_info = restore_info_11; - free(rates); - free(sizes); - return i; - } - free(rates); - } - } - } - if (sizes) free(sizes); - } - } - return Ecore_X_Randr_Unset; + if (mode_info->name) free(mode_info->name); } -*/ -/** - * @Brief find configuration with the most hardware currently available - */ -static E_Randr_Screen_Restore_Info_12 * -_e_randr_config_find_suiting_config_12(void) + Eina_List +*_e_randr_create_outputs_policies_list(Eina_List *outputs) { - //TODO: write geometry based loading - /* - Eina_List *cfg_screen_restore_info_iter; - E_Randr_Screen_Restore_Info *screen_restore_info; + Eina_List *iter, *list = NULL; + E_Randr_Output_Info *oi; + E_Randr_Serialized_Output_Policy *sop; + char *name; - E_Randr_Screen_Info_12 *current_info_12; - E_Randr_Screen_Restore_Info_12 *restore_info_12, *most_matches = NULL; - E_Randr_Output_Info *output_info; - E_Randr_Crtc_Restore_Info *crtc_restore_info; - Ecore_X_Randr_Output *outputs_xids; - Ecore_X_Randr_Crtc *crtcs_xids; - Eina_List *restore_info_12_iter, *output_iter, *restore_crtcs_iter; + 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; + } - if (e_randr_screen_info && e_config && e_config->screen_info) - { - - EINA_LIST_FOREACH(e_config->screen_info, cfg_screen_restore_info_iter, screen_restore_info) - { - if (screen_restore_info->randr_version < ECORE_X_RANDR_1_2) continue; - - //HINT: use eina_list_clone and a sort callback to find proper - //crtcs and outputs - - //current_info_12 = e_randr_screen_info->rrvd_info.randr_info_12; - } - - } - */ + 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; } -static Ecore_X_Randr_Output * +void +_e_randr_free_serialized_output_policy(E_Randr_Serialized_Output_Policy *sop) +{ + if (!sop) return; + if (sop->name) free(sop->name); + free(sop); +} + + Eina_List +*_e_randr_update_serialized_outputs_policies(E_Randr_Screen_Info_12 *si_12, Eina_List *sops) +{ + E_Randr_Serialized_Output_Policy *sop; + + EINA_LIST_FREE(sops, sop) + { + _e_randr_free_serialized_output_policy(sop); + } + + 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; + size_t len; + + 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); +} + + 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); + } +_e_randr_create_serialized_setup_12_failed_free_ss: + free(ss); +} + +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; + E_Randr_Serialized_Output *so; + E_Randr_Output_Info *oi; + E_Randr_Edid_Hash *edid_hash; + + 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); + } + 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) +{ + E_Randr_Serialized_Setup *ss; + E_Randr_Serialized_Setup_12 *ss_12; + + if (!(ss = E_NEW(E_Randr_Serialized_Setup, 1))) return NULL; + if ((screen_info->randr_version == ECORE_X_RANDR_1_1) && !(ss->serialized_setup_11 = _e_randr_create_serialized_setup_11(screen_info->rrvd_info.randr_info_11))) goto _e_randr_create_serialized_setup_failed_free_ss; + if ((screen_info->randr_version >= ECORE_X_RANDR_1_2)) + { + if (!(ss_12 = _e_randr_create_serialized_setup_12(screen_info->rrvd_info.randr_info_12)) + || !(ss->serialized_setups_12 = eina_list_append(ss->serialized_setups_12, ss_12))) + goto _e_randr_create_serialized_setup_failed_free_ss; + } + + return ss; + +_e_randr_create_serialized_setup_failed_free_ss: + free(ss); +} + + EAPI void +e_randr_store_configuration(E_Randr_Screen_Info *screen_info) +{ + if (E_RANDR_NO_11) return; + + if (!e_config->randr_serialized_setup) + { + e_config->randr_serialized_setup = _e_randr_create_serialized_setup(screen_info); + e_config_save_queue(); + return; + } + + if (screen_info->randr_version == ECORE_X_RANDR_1_1) + { + 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 if (screen_info->randr_version >= ECORE_X_RANDR_1_2) + { + 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); + + //Also, 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) +{ + E_Manager *man; + 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)) + { + man = e_manager_current_get(); + return ecore_x_randr_screen_primary_output_size_set(man->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_Bool name_found; + + 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) + { + name_found = EINA_FALSE; + EINA_LIST_FOREACH(ci->possible_outputs, p_output_iter, oi) + { + if (!strncmp(s_output_name, oi->name, oi->name_length)) + { + name_found = EINA_TRUE; + break; + } + } + if (!name_found) + break; + } + if (name_found) + break; + } + if (name_found) + return ci; + else + 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) + { + EINA_LIST_FOREACH(ois, r_output_iter, oi) + { + 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; +} + + 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, *outputs_list; + Eina_Bool ret = EINA_TRUE; + E_Manager *man; + + if(!(ss_12 = _e_randr_find_matching_serialized_setup(e_config->randr_serialized_setup->serialized_setups_12, si_12))) return EINA_FALSE; + + man = e_manager_current_get(); + + 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); + mi = _e_randr_find_matching_mode_info(si_12->modes, &sc->mode_info); + ret &= ecore_x_randr_crtc_mode_set(man->root, ci->xid, outputs_array, eina_list_count(outputs_list), mi->xid); + ret &= ecore_x_randr_crtc_pos_set(man->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; @@ -1148,7 +1617,7 @@ _e_randr_outputs_to_array(Eina_List *outputs_info) 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! */ + /* output_info == NULL should _not_ be possible! */ ret[i++] = output_info ? output_info->xid : Ecore_X_Randr_None; return ret; } @@ -1191,7 +1660,7 @@ _e_randr_try_enable_output(E_Randr_Output_Info *output_info, Eina_Bool force) if (!usable_crtc) return EINA_FALSE; //get the CRTC we will refer to, dependend on policy - switch (e_randr_screen_info->rrvd_info.randr_info_12->output_policy) + switch (output_info->policy) { case ECORE_X_RANDR_OUTPUT_POLICY_NONE: return EINA_TRUE; @@ -1386,15 +1855,19 @@ 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, e_randr_screen_info->rrvd_info.randr_info_12->output_policy))) return EINA_TRUE; + if(!(crtc_rel = _e_randr_policy_crtc_get(new_crtc, NULL, last_output->policy))) return EINA_TRUE; //following is policy dependend. - switch (e_randr_screen_info->rrvd_info.randr_info_12->output_policy) + switch (last_output->policy) { case ECORE_X_RANDR_OUTPUT_POLICY_ABOVE: dy = (crtc_rel->geometry.y - new_crtc->geometry.h); @@ -1425,7 +1898,7 @@ _e_randr_crtc_move_policy(E_Randr_Crtc_Info *new_crtc) default: break; } - ret &= ecore_x_randr_crtc_pos_relative_set(e_randr_screen_info->root, new_crtc->xid, crtc_rel->xid, e_randr_screen_info->rrvd_info.randr_info_12->output_policy, e_randr_screen_info->rrvd_info.randr_info_12->alignment); + 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; } @@ -1686,6 +2159,8 @@ _e_randr_output_info_hw_info_set(E_Randr_Output_Info *output_info) _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(output_info->edid, output_info->edid_length); //get the outputs we can use on the same CRTC alongside this one. if ((outputs = ecore_x_randr_output_clones_get(e_randr_screen_info->root, output_info->xid, &num))) { @@ -1711,6 +2186,10 @@ _e_randr_output_info_hw_info_set(E_Randr_Output_Info *output_info) } free(crtcs); } + else + { + fprintf(stderr, "E_RANDR: Output %x does not have a single possible CRTC.\n", output_info->xid); + } } /* diff --git a/src/bin/e_randr.h b/src/bin/e_randr.h index 448fc8901..7f04d0283 100644 --- a/src/bin/e_randr.h +++ b/src/bin/e_randr.h @@ -1,24 +1,26 @@ #ifdef E_TYPEDEFS typedef struct _E_Randr_Crtc_Info E_Randr_Crtc_Info; +typedef struct _E_Randr_Edid_Hash E_Randr_Edid_Hash; typedef struct _E_Randr_Output_Info E_Randr_Output_Info; typedef struct _E_Randr_Screen_Info_11 E_Randr_Screen_Info_11; typedef struct _E_Randr_Screen_Info_12 E_Randr_Screen_Info_12; typedef union _E_Randr_Screen_RRVD_Info E_Randr_Screen_RRVD_Info; typedef struct _E_Randr_Screen_Info E_Randr_Screen_Info; -typedef struct _E_Randr_Output_Edid_Hash E_Randr_Output_Edid_Hash; -typedef struct _E_Randr_Output_Restore_Info E_Randr_Output_Restore_Info; -typedef struct _E_Randr_Crtc_Restore_Info E_Randr_Crtc_Restore_Info; -typedef struct _E_Randr_Screen_Restore_Info_11 E_Randr_Screen_Restore_Info_11; -typedef struct _E_Randr_Screen_Restore_Info_12 E_Randr_Screen_Restore_Info_12; -typedef union _E_Randr_Screen_Restore_Info_Union E_Randr_Screen_Restore_Info_Union; -typedef struct _E_Randr_Screen_Restore_Info E_Randr_Screen_Restore_Info; +typedef struct _E_Randr_Serialized_Output_Policy E_Randr_Serialized_Output_Policy; +typedef struct _E_Randr_Serialized_Output E_Randr_Serialized_Output; +typedef struct _E_Randr_Serialized_Crtc E_Randr_Serialized_Crtc; +typedef struct _E_Randr_Serialized_Setup_11 E_Randr_Serialized_Setup_11; +typedef struct _E_Randr_Serialized_Setup_12 E_Randr_Serialized_Setup_12; +typedef struct _E_Randr_Serialized_Setup E_Randr_Serialized_Setup; + +EAPI void e_randr_store_configuration(E_Randr_Screen_Info *screen_info); #else #ifndef E_RANDR_H #define E_RANDR_H -struct _E_Randr_Crtc_Info +struct _E_Randr_Crtc_Info { Ecore_X_ID xid; Eina_Rectangle geometry; @@ -35,7 +37,12 @@ struct _E_Randr_Crtc_Info Ecore_X_Randr_Mode_Info *current_mode; }; -struct _E_Randr_Output_Info +struct _E_Randr_Edid_Hash +{ + int hash; +}; + +struct _E_Randr_Output_Info { Ecore_X_ID xid; char *name; @@ -47,6 +54,7 @@ struct _E_Randr_Output_Info int connector_number; Ecore_X_Randr_Connector_Type connector_type; Ecore_X_Randr_Connection_Status connection_status; + Ecore_X_Randr_Output_Policy policy; /* * Attached Monitor specific: */ @@ -57,6 +65,7 @@ struct _E_Randr_Output_Info Ecore_X_Randr_Screen_Size size_mm; unsigned char *edid; unsigned long edid_length; + E_Randr_Edid_Hash edid_hash; int max_backlight; double backlight_level; Ecore_X_Render_Subpixel_Order subpixel_order; @@ -75,7 +84,7 @@ struct _E_Randr_Screen_Info_11 Ecore_X_Randr_Refresh_Rate current_rate; }; -struct _E_Randr_Screen_Info_12 +struct _E_Randr_Screen_Info_12 { Ecore_X_Randr_Screen_Size min_size; Ecore_X_Randr_Screen_Size max_size; @@ -84,18 +93,17 @@ struct _E_Randr_Screen_Info_12 Eina_List *crtcs; Eina_List *outputs; E_Randr_Output_Info *primary_output; - Ecore_X_Randr_Output_Policy output_policy; Ecore_X_Randr_Relative_Alignment alignment; }; //RRVD == RandR(R) Version Depended -union _E_Randr_Screen_RRVD_Info +union _E_Randr_Screen_RRVD_Info { E_Randr_Screen_Info_11 *randr_info_11; E_Randr_Screen_Info_12 *randr_info_12; }; -struct _E_Randr_Screen_Info +struct _E_Randr_Screen_Info { Ecore_X_Window root; int randr_version; @@ -103,56 +111,67 @@ struct _E_Randr_Screen_Info }; //Following stuff is just for configuration purposes -struct _E_Randr_Output_Edid_Hash { - int hash; + +struct _E_Randr_Serialized_Output_Policy +{ + char *name; + int name_length; + Ecore_X_Randr_Output_Policy policy; }; -struct _E_Randr_Output_Restore_Info +struct _E_Randr_Serialized_Output { - E_Randr_Output_Edid_Hash edid_hash; + char *name; + int name_length; + E_Randr_Edid_Hash edid_hash; double backlight_level; }; -struct _E_Randr_Crtc_Restore_Info +struct _E_Randr_Serialized_Crtc { - Eina_Rectangle geometry; + //List of E_Randr_Serialized_Output objects that were used on the same output + Eina_List *serialized_outputs; + //the serialized mode_info misses its xid value + Ecore_X_Randr_Mode_Info mode_info; + Evas_Coord_Point pos; + //List of all possible outputs' names + //e.g. "LVDS", "CRT-0", "VGA" + Eina_List *possible_outputs_names; Ecore_X_Randr_Orientation orientation; - //list of the outputs; - Eina_List *outputs; }; -struct _E_Randr_Screen_Restore_Info_11 +struct _E_Randr_Serialized_Setup_12 { - Ecore_X_Randr_Screen_Size size; + double timestamp; + //List of E_Randr_Serialized_Crtc objects + Eina_List *serialized_crtcs; + /* + * List of E_Randr_Edid_Hash elements of monitors, + * that were enabled, when the setup was stored + */ + Eina_List *serialized_edid_hashes; +}; + +struct _E_Randr_Serialized_Setup_11 +{ + Ecore_X_Randr_Screen_Size_MM size; Ecore_X_Randr_Refresh_Rate refresh_rate; Ecore_X_Randr_Orientation orientation; }; -struct _E_Randr_Screen_Restore_Info_12 +struct _E_Randr_Serialized_Setup { - Eina_List *outputs_edid_hashes; - int noutputs; - Eina_List *crtcs; - Ecore_X_Randr_Output_Policy output_policy; - Ecore_X_Randr_Relative_Alignment alignment; -}; - -union _E_Randr_Screen_Restore_Info_Union -{ - E_Randr_Screen_Restore_Info_11 *restore_info_11; - Eina_List *restore_info_12; -}; - -struct _E_Randr_Screen_Restore_Info -{ - int randr_version; - E_Randr_Screen_Restore_Info_Union rrvd_restore_info; + E_Randr_Serialized_Setup_11 *serialized_setup_11; + //List of E_Randr_Serialized_Setup_12 objects + Eina_List *serialized_setups_12; + //List of E_Randr_Serialized_Output_Policy objects + Eina_List *serialized_outputs_policies; }; EINTERN Eina_Bool e_randr_init(void); EINTERN int e_randr_shutdown(void); -extern E_Randr_Screen_Info *e_randr_screen_info; +EAPI extern E_Randr_Screen_Info *e_randr_screen_info; #endif #endif diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index 7eadd76a1..56498e786 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -126,6 +126,10 @@ if USE_MODULE_CONF_INTERACTION SUBDIRS += conf_interaction endif +if USE_MODULE_CONF_RANDR +SUBDIRS += conf_randr +endif + if USE_MODULE_GADMAN SUBDIRS += gadman endif diff --git a/src/modules/conf_display/e_int_config_display.c b/src/modules/conf_display/e_int_config_display.c index 27b62844b..6733c9429 100644 --- a/src/modules/conf_display/e_int_config_display.c +++ b/src/modules/conf_display/e_int_config_display.c @@ -23,8 +23,6 @@ static int _sort_resolutions (const void *d1, const void *d2); typedef struct _Resolution Resolution; typedef struct _SureBox SureBox; -static E_Randr_Screen_Restore_Info_11 *e_screen_config_11 = NULL; - struct _Resolution { int id; @@ -96,13 +94,8 @@ _surebox_dialog_cb_yes(void *data, E_Dialog *dia) ecore_x_randr_screen_primary_output_current_size_get(man->root, &c_size.width, &c_size.height, NULL, NULL, NULL); c_rate = ecore_x_randr_screen_primary_output_current_refresh_rate_get(man->root); - if (e_screen_config_11) - { - e_screen_config_11->size.width = c_size.width; - e_screen_config_11->size.height = c_size.height; - e_screen_config_11->refresh_rate = c_rate; - e_config_save_queue(); - } + e_randr_store_configuration(e_randr_screen_info); + _fill_data(sb->cfdata); _load_resolutions(sb->cfdata); /* No need to load rates as the currently selected resolution has not been @@ -242,7 +235,6 @@ static void _fill_data(E_Config_Dialog_Data *cfdata) { E_Manager *man; - E_Randr_Screen_Restore_Info *restore_info; Eina_List *iter; int rots; @@ -250,36 +242,6 @@ _fill_data(E_Config_Dialog_Data *cfdata) ecore_x_randr_screen_primary_output_current_size_get(man->root, &cfdata->orig_size.width, &cfdata->orig_size.height, NULL, NULL, &cfdata->orig_size_index); cfdata->orig_rate = ecore_x_randr_screen_primary_output_current_refresh_rate_get(man->root); - EINA_LIST_FOREACH(e_config->screen_info, iter, restore_info) - { - if (restore_info->randr_version == RANDR_11) - { - e_screen_config_11 = restore_info->rrvd_restore_info.restore_info_11; - break; - } - } - - if(!e_screen_config_11) - { - if ((restore_info = E_NEW(E_Randr_Screen_Restore_Info, 1))) - { - restore_info->randr_version = RANDR_11; - if ((e_screen_config_11 = E_NEW(E_Randr_Screen_Restore_Info_11, 1))) - { - restore_info->rrvd_restore_info.restore_info_11 = e_screen_config_11; - if (!(e_config->screen_info = eina_list_append(e_config->screen_info, restore_info))) - { - free(e_screen_config_11); - free(restore_info); - } - } - else - { - free (restore_info); - } - } - } - rots = ecore_x_randr_screen_primary_output_orientations_get(man->root); if ((rots) && (rots != ECORE_X_RANDR_ORIENTATION_ROT_0)) { @@ -352,7 +314,6 @@ _basic_check_changed(E_Config_Dialog *cfd __UNUSED__, E_Config_Dialog_Data *cfda rt = eina_list_nth(res->rates, r); if (!rt) return 0; - if (!e_screen_config_11) return EINA_FALSE; return ((res->size.width != cfdata->orig_size.width) || (res->size.height != cfdata->orig_size.height) || (cfdata->has_rates && (*rt != cfdata->orig_rate)) || @@ -414,14 +375,7 @@ _basic_apply_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) (cfdata->orientation | cfdata->flip)); cfdata->orig_orientation = cfdata->orientation; cfdata->orig_flip = cfdata->flip; - if (e_screen_config_11) - e_screen_config_11->orientation = (cfdata->orientation | cfdata->flip); } - else - if (e_screen_config_11) - e_screen_config_11->orientation = 0; - - e_config_save_queue(); return 1; } diff --git a/src/modules/conf_randr/Makefile.am b/src/modules/conf_randr/Makefile.am new file mode 100644 index 000000000..f54929fda --- /dev/null +++ b/src/modules/conf_randr/Makefile.am @@ -0,0 +1,51 @@ +MAINTAINERCLEANFILES = Makefile.in module.desktop +MODULE = conf_randr + +EDJE_CC = @edje_cc@ +EDJE_FLAGS = -v \ + -id $(top_srcdir)/src/modules/$(MODULE)/images \ + @EDJE_DEF@ + +# data files for the module +filesdir = $(libdir)/enlightenment/modules/$(MODULE) +files_DATA = \ + e-module-$(MODULE).edj \ + module.desktop + +EXTRA_DIST = \ + e-module-$(MODULE).edc \ + module.desktop.in + +# the module .so file +INCLUDES = -I. \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src/modules/$(MODULE) \ + -I$(top_srcdir)/src/bin \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/modules \ + @e_cflags@ +pkgdir = $(libdir)/enlightenment/modules/$(MODULE)/$(MODULE_ARCH) +pkg_LTLIBRARIES = module.la +module_la_SOURCES = e_mod_main.c \ + e_mod_main.h \ + e_int_config_randr_orientation.c \ + e_int_config_randr_resolutions.c \ + e_int_config_randr_arrangement.c \ + e_int_config_randr_policies.c \ + e_int_config_randr.c \ + e_int_config_randr.h + +module_la_LIBADD = @e_libs@ @dlopen_libs@ +module_la_LDFLAGS = -module -avoid-version +module_la_DEPENDENCIES = $(top_builddir)/config.h + +e-module-$(MODULE).edj: Makefile $(EXTRA_DIST) + $(EDJE_CC) $(EDJE_FLAGS) \ + $(top_srcdir)/src/modules/$(MODULE)/e-module-$(MODULE).edc \ + $(top_builddir)/src/modules/$(MODULE)/e-module-$(MODULE).edj + +clean-local: + rm -f *.edj + +uninstall: + rm -rf $(DESTDIR)$(libdir)/enlightenment/modules/$(MODULE) diff --git a/src/modules/conf_randr/e-module-conf_randr.edc b/src/modules/conf_randr/e-module-conf_randr.edc new file mode 100644 index 000000000..61f2e219f --- /dev/null +++ b/src/modules/conf_randr/e-module-conf_randr.edc @@ -0,0 +1,945 @@ +#define BORDERSIZE 1 +#define SUGGESTION_TIMEOUT 2.5 + +images { + image: "icon.png" COMP; + image: "video-display.svg" COMP; + image: "display.png" COMP; + image: "display-glass-shine.png" COMP; +} + +collections { + + // The icon used in the settings dialog + group { + name: "icon"; + parts { + part { + name: "image"; + mouse_events: 0; + description { + state: "default" 0.0; + aspect: 1.0 1.0; + aspect_preference: BOTH; + image.normal: "icon.png"; + } + } + } + } + + /**********************************************/ + /*********Subdialog - Arrangement**************/ + /**********************************************/ + + //The graphical representation of a single monitor, including its decorations + group { + name: "e/conf/randr/dialog/subdialog/arrangement/output"; + + styles { + style { + name: "display_name_text"; + base: "font=Sans:style=Bold font_size=10 text_class=tb_plain align=center valign=center color=#fff style=soft_shadow shadow_color=#0000001f wrap=word"; + tag: "br" "\n"; + tag: "hilight" "+ font=Sans:style=Bold text_class=tb_light"; + } + } + + + parts { + + part { + name: "display"; + type: IMAGE; + mouse_events: 0; + + description { + state: "default" 0.0; + image.normal: "display.png"; + rel1.relative: 0.0 0.0; + rel2.relative: 1.0 1.0; + } + } + + part { + name: "e.swallow.content"; + type: SWALLOW; // background of CRTC's zone + + description { + state: "default" 0.0; + aspect_preference: BOTH; + color: 255 255 255 255; + rel1 { + to: "display"; + relative: 0.047379 0.049303; + } + rel2 { + to: "display"; + //relative: 0.97 0.657804; + relative: 0.975 0.66; + } + } + + description { + state: "disabled" 0.0; + inherit: "default" 0.0; + color: 255 255 255 128; + } + } + + part { + name: "output_selected_frame_clip"; + type: RECT; + mouse_events: 0; + + description { + state: "default" 0.0; + color: 255 255 255 0; + + rel1.relative: 0.0 0.0; + rel2.relative: 1.0 1.0; + } + description { + state: "selected" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + } + } + + part { + name: "output_selected_frame_border_top"; + type: RECT; + clip_to: "output_selected_frame_clip"; + mouse_events: 0; + + description { + state: "default" 0.0; + color: 128 128 128 255; + min: 0 2; + fixed: 0 1; + align: 0.5 0.0; + + rel1 { + to: "output_selected_frame_clip"; + relative: 0.0 0.0; + } + rel2 { + to: "output_selected_frame_clip"; + relative: 1.0 0.0; + offset: 0 BORDERSIZE; + } + } + } + + part { + name: "output_selected_frame_border_right"; + type: RECT; + clip_to: "output_selected_frame_clip"; + mouse_events: 0; + + description { + state: "default" 0.0; + color: 128 128 128 255; + min: 1 0; + fixed: 1 0; + align: 1.0 0.5; + + rel1 { + to_x: "output_selected_frame_clip"; + to_y: "output_selected_frame_border_top"; + relative: 1.0 1.0; + offset: (-BORDERSIZE-1) 0; + } + rel2 { + to_x: "output_selected_frame_clip"; + to_y: "output_selected_frame_border_bottom"; + relative: 1.0 0.0; + } + } + } + + part { + name: "output_selected_frame_border_bottom"; + type: RECT; + clip_to: "output_selected_frame_clip"; + mouse_events: 0; + + description { + state: "default" 0.0; + color: 128 128 128 255; + min: 0 2; + fixed: 0 1; + align: 0.5 1.0; + + rel1 { + to: "output_selected_frame_clip"; + relative: 0.0 1.0; + offset: 0 (-BORDERSIZE-1); + } + rel2 { + to: "output_selected_frame_clip"; + relative: 1.0 1.0; + } + } + } + + part { + name: "output_selected_frame_border_left"; + type: RECT; + clip_to: "output_selected_frame_clip"; + mouse_events: 0; + + description { + state: "default" 0.0; + color: 128 128 128 255; + min: 1 0; + fixed: 1 0; + align: 0.0 0.5; + + rel1 { + to_x: "output_selected_frame_clip"; + to_y: "output_selected_frame_border_top"; + relative: 0.0 1.0; + } + rel2 { + to_x: "output_selected_frame_clip"; + to_y: "output_selected_frame_border_bottom"; + relative: 0.0 0.0; + offset: BORDERSIZE 0; + } + } + } + + part { + name: "output_selected_frm_inside"; + type: RECT; + clip_to: "output_selected_frame_clip"; + mouse_events: 0; + + description { + state: "default" 0.0; + color: 255 255 255 120; + + rel1 { + to: "output_selected_frame_clip"; + offset: BORDERSIZE BORDERSIZE; + } + rel2 { + to: "output_selected_frame_clip"; + offset: -BORDERSIZE -BORDERSIZE; + } + } + } + + part { + name: "output_txt_bg"; + type: RECT; + //clip_to: "output_txt_clip"; + mouse_events: 0; + + description { + state: "default" 0.0; + color: 255 255 255 128; + align: 0.5 0.5; + + rel1 { + to: "output_txt"; + relative: 0.0 0.0; + offset: -5 -5; + } + rel2 { + to: "output_txt"; + relative: 1.0 1.0; + offset: 5 5; + } + } + description { + state: "selected" 0.0; + inherit: "default" 0.0; + color: 0 0 0 255; + } + } + + part { + name: "output_txt"; + type: TEXTBLOCK; + //clip_to: "output_txt_clip"; + mouse_events: 0; + + description { + align: 0.5 0.5; + state: "default" 0.0; + color: 0 0 0 255; + // define part coordinates: + //rel1.to: "output_txt_clip"; + //rel2.to: "output_txt_clip"; + rel1.to: "e.swallow.content"; + rel2.to: "e.swallow.content"; + + text { + style: "display_name_text"; + text: "output name"; + min: 1.0 1.0; + max: 1.0 1.0; + } + } + description { + state: "selected" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + } + } + + part { + name: "selected_toggle_on"; + type: RECT; + mouse_events: 1; + + description { + state: "default" 0.0; + color: 0 0 0 0; + visible: 1; + + rel1 { + to: "e.swallow.content"; + relative: 0.0 0.0; + } + rel2 { + to: "e.swallow.content"; + relative: 1.0 1.0; + } + } + + description { + state: "disable" 0.0; + inherit: "default" 0.0; + visible: 0; + } + } + + part { + name: "selected_toggle_off"; + type: RECT; + mouse_events: 1; + + description { + state: "default" 0.0; + color: 0 0 0 0; + visible: 0; + + rel1 { + to: "selected_toggle_on"; + relative: 0.0 0.0; + } + rel2 { + to: "selected_toggle_on"; + relative: 1.0 1.0; + } + } + + description { + state: "disable" 0.0; + inherit: "default" 0.0; + visible: 1; + } + } + + part { + name: "display-glass-shine"; + type: IMAGE; + + description { + state: "default" 0.0; + image.normal: "display-glass-shine.png"; + } + } + + programs { + program { + name: "highlight"; + signal: "mouse,down,1"; + source: "selected_toggle_on"; + + action: STATE_SET "selected" 0.0; + transition: LINEAR 0.1; + //target: "e.swallow.content"; + //target: "output_selected_clip"; + target: "output_txt"; + target: "output_txt_bg"; + target: "output_selected_frame_clip"; + } + + program { + name: "normal"; + signal: "mouse,clicked,1"; + source: "selected_toggle_off"; + + action: STATE_SET "default" 0.0; + transition: LINEAR 0.1; + //target: "e.swallow.content"; + //target: "output_selected_clip"; + target: "output_txt"; + target: "output_txt_bg"; + target: "output_selected_frame_clip"; + } + + program { + name: "selected_toggle_off_on"; + signal: "mouse,clicked,1"; + source: "selected_toggle_on"; + action: STATE_SET "disable" 1.0; + target: "selected_toggle_on"; + target: "selected_toggle_off"; + } + + program { + name: "selected_toggle_on_off"; + signal: "mouse,clicked,1"; + source: "selected_toggle_off"; + action: STATE_SET "default" 1.0; + target: "selected_toggle_on"; + target: "selected_toggle_off"; + } + + program { + name: "emit_highlight"; + signal: "select"; + source: "e"; + after: "highlight"; + } + + program { + name: "emit_normal"; + signal: "deselect"; + source: "e"; + after: "normal"; + } + + program { + name: "disable"; + signal: "disabled"; + source: "e"; + action: STATE_SET "disabled" 0.0; + target: "e.swallow.content"; + } + + program { + name: "enable"; + signal: "enabled"; + source: "e"; + action: STATE_SET "default" 0.0; + target: "e.swallow.content"; + } + + program { + name: "init"; + after: "normal"; + } + } + } + } + + //This group describes the look of the suggestion entity used, when a monitor + //representation is dragged. Its size matches the size of the monitor + //dragged. + group{ + name: "e/conf/randr/dialog/subdialog/arrangement/suggestion"; + data { + item: "distance_min" "20"; + } + + /* + script { + public fade_out_timer_id = 0; + + public suggestion_fade_out () + { + cancel_timer(get_int(fade_out_timer_id)); + run_program(PROGRAM:"hide"); + } + } + */ + + parts { + part { + name: "shape_clip"; + type: RECT; + description { + state: "default" 0.0; + color: 255 255 255 0; + rel1.relative: 0.0 0.0; + rel2.relative: 1.0 1.0; + } + description { + state: "visible" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + } + } + part { + name: "shape"; + type: RECT; + mouse_events: 0; + clip_to: "shape_clip"; + description { + state: "default" 0.0; + color: 0 0 0 100; + rel1.to: "shape_clip"; + rel2.to: "shape_clip"; + } + } + } + programs { + program { + name: "show_transition"; + signal: "show"; + source: "e"; + action: STATE_SET "visible" 0.0; + target: "shape_clip"; + transition: LINEAR 0.2; + } + /* + program { + name: "set_timeout"; + signal: "show"; + source: "e"; + script { + new i = timer(SUGGESTION_TIMEOUT, "suggestion_fade_out", 0); + set_int(fade_out_timer_id, i); + } + } + */ + program { + name: "hide"; + signal: "hide"; + source: "e"; + action: STATE_SET "default" 0.0; + target: "shape_clip"; + transition: LINEAR 0.2; + } + } + } + + /**********************************************/ + /************Subdialog - Policies**************/ + /**********************************************/ + group{ + name: "e/conf/randr/dialog/subdialog/policies"; + parts { + part { + name: "current_displays_setup/clipper"; + type: RECT; + mouse_events: 0; + description { + state: "default" 0.0; + color: 0 0 0 0; + rel1.relative: 0.25 0.25; + rel2.relative: 0.75 0.75; + } + description { + state: "above" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + rel1.relative: 0.25 0.5; + rel2.relative: 0.75 1.0; + } + description { + state: "right" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + rel1.relative: 0.0 0.25; + rel2.relative: 0.5 0.75; + } + description { + state: "below" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + rel1.relative: 0.25 0.0; + rel2.relative: 0.75 0.5; + } + description { + state: "left" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + rel1.relative: 0.5 0.25; + rel2.relative: 1.0 0.75; + } + description { + state: "clone" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + } + description { + state: "none" 0.0; + inherit: "default" 0.0; + color: 255 255 255 255; + } + } + part { + name: "current_displays_setup.swallow.content"; + type: SWALLOW; + clip_to: "current_displays_setup/clipper"; + description { + state: "default" 0.0; + rel1.to: "current_displays_setup/clipper"; + rel2.to: "current_displays_setup/clipper"; + } + } + part { + name: "new_display/clipper"; + type: RECT; + mouse_events: 0; + description { + state: "default" 0.0; + color: 0 0 0 0; + rel1.relative: 0.25 0.25; + rel2.relative: 0.75 0.75; + } + description { + state: "above" 0.0; + inherit: "default" 0.0; + color: 0 0 0 0; + rel1.relative: 0.25 0.0; + rel2.relative: 0.75 0.5; + } + description { + state: "above_visible" 0.0; + inherit: "above" 0.0; + color: 255 255 255 255; + } + description { + state: "right" 0.0; + inherit: "default" 0.0; + color: 0 0 0 0; + rel1.relative: 0.5 0.25; + rel2.relative: 1.0 0.75; + } + description { + state: "right_visible" 0.0; + inherit: "right" 0.0; + color: 255 255 255 255; + } + description { + state: "below" 0.0; + inherit: "default" 0.0; + color: 0 0 0 0; + rel1.relative: 0.25 0.5; + rel2.relative: 0.75 1.0; + } + description { + state: "below_visible" 0.0; + inherit: "below" 0.0; + color: 255 255 255 255; + } + description { + state: "left" 0.0; + inherit: "default" 0.0; + color: 0 0 0 0; + rel1.relative: 0.0 0.25; + rel2.relative: 0.5 0.75; + } + description { + state: "left_visible" 0.0; + inherit: "left" 0.0; + color: 255 255 255 255; + } + description { + state: "clone" 0.0; + inherit: "default" 0.0; + color: 0 0 0 0; + } + description { + state: "clone_visible" 0.0; + inherit: "clone" 0.0; + color: 255 255 255 255; + } + description { + state: "none" 0.0; + inherit: "default" 0.0; + color: 0 0 0 0; + } + description { + state: "none_visible" 0.0; + inherit: "none" 0.0; + color: 255 255 255 255; + } + } + part { + name: "new_display.swallow.content"; + type: SWALLOW; + clip_to: "new_display/clipper"; + description { + state: "default" 0.0; + rel1.to: "new_display/clipper"; + rel2.to: "new_display/clipper"; + } + } + } + /* + * The signals emitted to the UI are encoded as their corresponding value + * in Ecore_X + * + * Policy = Signal emitted + * ECORE_X_RANDR_OUTPUT_POLICY_ABOVE = 1 + * ECORE_X_RANDR_OUTPUT_POLICY_RIGHT = 2 + * ECORE_X_RANDR_OUTPUT_POLICY_BELOW = 3 + * ECORE_X_RANDR_OUTPUT_POLICY_LEFT = 4 + * ECORE_X_RANDR_OUTPUT_POLICY_CLONE = 5 + * ECORE_X_RANDR_OUTPUT_POLICY_NONE = 6 + */ + programs { + program { + name: "new_display_hide"; + signal: "conf,randr,dialog,policies,*"; + source: "e"; + action: STATE_SET "default" 0.0; + target: "new_display/clipper"; + } + program { + name: "current_displays_setup_clipper_above"; + signal: "conf,randr,dialog,policies,1"; + source: "e"; + action: STATE_SET "above" 0.0; + target: "current_displays_setup/clipper"; + target: "new_display/clipper"; + after: "new_display_above_visible_set"; + transition: LINEAR 0.5; + } + program { + name: "new_display_above_visible_set"; + action: STATE_SET "above_visible" 0.0; + target: "new_display/clipper"; + transition: LINEAR 0.5; + } + program { + name: "current_displays_setup_clipper_right"; + signal: "conf,randr,dialog,policies,2"; + source: "e"; + action: STATE_SET "right" 0.0; + target: "current_displays_setup/clipper"; + target: "new_display/clipper"; + after: "new_display_right_visible_set"; + transition: LINEAR 0.5; + } + program { + name: "new_display_right_visible_set"; + action: STATE_SET "right_visible" 0.0; + target: "new_display/clipper"; + transition: LINEAR 0.5; + } + program { + name: "current_displays_setup_clipper_below"; + signal: "conf,randr,dialog,policies,3"; + source: "e"; + action: STATE_SET "below" 0.0; + target: "current_displays_setup/clipper"; + target: "new_display/clipper"; + after: "new_display_below_visible_set"; + transition: LINEAR 0.5; + } + program { + name: "new_display_below_visible_set"; + action: STATE_SET "below_visible" 0.0; + target: "new_display/clipper"; + transition: LINEAR 0.5; + } + program { + name: "current_displays_setup_clipper_left"; + signal: "conf,randr,dialog,policies,4"; + source: "e"; + action: STATE_SET "left" 0.0; + target: "current_displays_setup/clipper"; + target: "new_display/clipper"; + after: "new_display_left_visible_set"; + transition: LINEAR 0.5; + } + program { + name: "new_display_left_visible_set"; + action: STATE_SET "left_visible" 0.0; + target: "new_display/clipper"; + transition: LINEAR 0.5; + } + program { + name: "current_displays_setup_clipper_clone"; + signal: "conf,randr,dialog,policies,5"; + source: "e"; + action: STATE_SET "clone" 0.0; + target: "current_displays_setup/clipper"; + target: "new_display/clipper"; + after: "new_display_clone_visible_set"; + transition: LINEAR 0.5; + } + program { + name: "new_display_clone_visible_set"; + action: STATE_SET "clone_visible" 0.0; + target: "new_display/clipper"; + transition: LINEAR 0.5; + } + program { + name: "current_displays_setup_clipper_none"; + signal: "conf,randr,dialog,policies,6"; + source: "e"; + action: STATE_SET "none" 0.0; + target: "current_displays_setup/clipper"; + target: "new_display/clipper"; + //after: "new_display_none_visible_set"; + transition: LINEAR 0.5; + } + /* + * following is an analog program for none, but we don't want to show + * it anyway. + program { + name: "new_display_none_visible_set"; + action: STATE_SET "none_visible" 0.0; + target: "new_display/clipper"; + transition: LINEAR 0.5; + } + */ + } + } + + // Text objects for rotation and reflection + group { + name: "e/conf/randr/dialog/subdialog/orientation"; + + parts { + part { + name: "clip"; + type: RECT; + mouse_events: 0; + description { + state: "default" 0.0; + rel1.relative: 0.0 0.0; + rel2.relative: 1.0 1.0; + } + } + part { + name: "display"; + clip_to: "clip"; + mouse_events: 0; + description { + state: "default" 0.0; + aspect: 1.0 1.0; + aspect_preference: BOTH; + image.normal: "video-display.svg"; + } + } + part { + name: "orientation_text"; + clip_to: "clip"; + type: TEXT; + mouse_events: 0; + scale: 1; + description { + state: "default" 0.0; + rel1.relative: 0.0 0.0; + rel2.relative: 1.0 0.8; + color: 0 0 0 255; + text { + //Maybe use some default theme label text style later + text: "Orientation"; + font: "Sans:style=Bold"; + /* Use the Bold style + * of the Sans font from + * fontconfig */ + size: 10; + /* size in pixels - 10 */ + min: 0 1; + /* the text will not determine minimum horizontal + * size but WILL determine minimal vertical size + * (thus 0 1 - horiz then vert flags) */ + /* align text to top-left of the region + * given */ + text_class: "title_bar"; + /* text class - so font and size + * can be changed by users */ + } + map { + on: 1; + rotation { + x: 0.0; + y: 0.0; + z: 0.0; + } + } + } + description { + state: "rotate" 0.90; + inherit: "default" 0.0; + map.rotation.z: 270.0; + } + description { + state: "rotate" 0.180; + inherit: "default" 0.0; + map.rotation.z: 180.0; + } + description { + state: "rotate" 0.270; + inherit: "default" 0.0; + map.rotation.z: 90.0; + } + description { + state: "reflect_horizontal" 0.0; + inherit: "default" 0.0; + map.rotation.y: 180.0; + } + description { + state: "reflect_vertical" 0.0; + inherit: "default" 0.0; + map.rotation.x: 180.0; + } + } + } + programs { + program { + name: "rot0"; + signal: "conf,randr,dialog,orientation,current,1"; + source: "e"; + action: STATE_SET "default" 0.0; + target: "orientation_text"; + transition: LINEAR 0.5; + } + program { + name: "rot90"; + signal: "conf,randr,dialog,orientation,current,2"; + source: "e"; + action: STATE_SET "rotate" 0.90; + target: "orientation_text"; + transition: LINEAR 0.5; + } + program { + name: "rot180"; + signal: "conf,randr,dialog,orientation,current,4"; + source: "e"; + action: STATE_SET "rotate" 0.180; + target: "orientation_text"; + transition: LINEAR 0.5; + } + program { + name: "rot270"; + signal: "conf,randr,dialog,orientation,current,8"; + source: "e"; + action: STATE_SET "rotate" 0.270; + target: "orientation_text"; + transition: LINEAR 0.5; + } + program { + name: "ref_x"; + signal: "conf,randr,dialog,orientation,current,16"; + source: "e"; + action: STATE_SET "reflect_horizontal" 0.0; + target: "orientation_text"; + transition: LINEAR 0.5; + } + program { + name: "ref_y"; + signal: "conf,randr,dialog,orientation,current,32"; + source: "e"; + action: STATE_SET "reflect_vertical" 0.0; + target: "orientation_text"; + transition: LINEAR 0.5; + } + } + + } + +} diff --git a/src/modules/conf_randr/e_int_config_randr.c b/src/modules/conf_randr/e_int_config_randr.c new file mode 100644 index 000000000..3a525e708 --- /dev/null +++ b/src/modules/conf_randr/e_int_config_randr.c @@ -0,0 +1,424 @@ +#include "e_int_config_randr.h" +#include "e_widget_toolbook.h" +#include "e.h" +#include "e_randr.h" + +/* + * BUGS: + * - ethumb sometimes returns garbage objects leading to a segv + * + * TODO: + * - write 1.2 per monitor configuration + * - write Smart object, so crtcs representations can be properly freed (events, + * etc.) + * + * IMPROVABLE: + * See comments starting with 'IMPROVABLE' + */ +#ifndef ECORE_X_RANDR_1_2 +#define ECORE_X_RANDR_1_2 ((1 << 16) | 2) +#endif +#ifndef ECORE_X_RANDR_1_3 +#define ECORE_X_RANDR_1_3 ((1 << 16) | 3) +#endif + + +#ifdef Ecore_X_Randr_None +#undef Ecore_X_Randr_None +#define Ecore_X_Randr_None 0 +#else +#define Ecore_X_Randr_None 0 +#endif +#ifdef Ecore_X_Randr_Unset +#undef Ecore_X_Randr_Unset +#define Ecore_X_Randr_Unset -1 +#else +#define Ecore_X_Randr_Unset -1 +#endif + +#define THEME_FILENAME "/e-module-conf_randr.edj" +#define TOOLBAR_ICONSIZE 16 + +static void *create_data (E_Config_Dialog *cfd); +static void free_cfdata (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +static int basic_check_changed (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +static int basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +static Evas_Object *basic_create_widgets (E_Config_Dialog *cfd, Evas *evas, E_Config_Dialog_Data *cfdata); +static Eina_Bool _deferred_noxrandr_error (void *data); +static Eina_Bool _deferred_norates_error (void *data); + +// Functions for the arrangement subdialog interaction +extern Eina_Bool e_config_randr_dialog_subdialog_arrangement_create_data (E_Config_Dialog_Data *cfdata); +extern Evas_Object *e_config_randr_dialog_subdialog_arrangement_basic_create_widgets (Evas *canvas); +extern Eina_Bool e_config_randr_dialog_subdialog_arrangement_basic_check_changed (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +extern Eina_Bool e_config_randr_dialog_subdialog_arrangement_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +extern void e_config_randr_dialog_subdialog_arrangement_free_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +extern void e_config_randr_dialog_subdialog_arrangement_keep_changes (E_Config_Dialog_Data *cfdata); +extern void e_config_randr_dialog_subdialog_arrangement_discard_changes (E_Config_Dialog_Data *cfdata); + +// Functions for the policies subdialog interaction +extern Eina_Bool e_config_randr_dialog_subdialog_policies_create_data (E_Config_Dialog_Data *cfdata); +extern Evas_Object *e_config_randr_dialog_subdialog_policies_basic_create_widgets (Evas *canvas); +extern Eina_Bool e_config_randr_dialog_subdialog_policies_basic_check_changed (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +extern Eina_Bool e_config_randr_dialog_subdialog_policies_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +extern void e_config_randr_dialog_subdialog_policies_keep_changes (E_Config_Dialog_Data *cfdata); +extern void e_config_randr_dialog_subdialog_policies_discard_changes (E_Config_Dialog_Data *cfdata); + +// Functions for the resolutions subdialog interaction +extern Eina_Bool e_config_randr_dialog_subdialog_resolutions_create_data (E_Config_Dialog_Data *cfdata); +extern Evas_Object *e_config_randr_dialog_subdialog_resolutions_basic_create_widgets (Evas *canvas); +extern Eina_Bool e_config_randr_dialog_subdialog_resolutions_basic_check_changed (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +extern Eina_Bool e_config_randr_dialog_subdialog_resolutions_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +extern void e_config_randr_dialog_subdialog_resolutions_update_list (Evas *canvas, Evas_Object *crtc); +extern void e_config_randr_dialog_subdialog_resolutions_keep_changes (E_Config_Dialog_Data *cfdata); +extern void e_config_randr_dialog_subdialog_resolutions_discard_changes (E_Config_Dialog_Data *cfdata); + +// Functions for the orientation subdialog interaction +extern Eina_Bool e_config_randr_dialog_subdialog_orientation_create_data (E_Config_Dialog_Data *cfdata); +extern Evas_Object *e_config_randr_dialog_subdialog_orientation_basic_create_widgets (Evas *canvas); +extern Eina_Bool e_config_randr_dialog_subdialog_orientation_basic_check_changed (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +extern Eina_Bool e_config_randr_dialog_subdialog_orientation_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +extern void e_config_randr_dialog_subdialog_orientation_update_radio_buttons (Evas_Object *crtc); +extern void e_config_randr_dialog_subdialog_orientation_update_edje (Evas_Object *crtc); +extern void e_config_randr_dialog_subdialog_orientation_keep_changes (E_Config_Dialog_Data *cfdata); +extern void e_config_randr_dialog_subdialog_orientation_discard_changes (E_Config_Dialog_Data *cfdata); + +/* actual module specifics */ +E_Config_Dialog_Data *e_config_runtime_info = NULL; +extern E_Module *conf_randr_module; +char _theme_file_path[PATH_MAX]; + + E_Config_Randr_Dialog_Output_Dialog_Data * +_e_config_randr_dialog_output_dialog_data_new(E_Randr_Crtc_Info *crtc_info, E_Randr_Output_Info *output_info) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *dialog_data; + + if ((!crtc_info && !output_info) || !(dialog_data = E_NEW(E_Config_Randr_Dialog_Output_Dialog_Data, 1))) return NULL; + if (crtc_info) + { + //already enabled screen + dialog_data->crtc = crtc_info; + } + else if (output_info) + { + //disabled monitor + dialog_data->output = output_info; + } + return dialog_data; + +_e_conf_randr_dialog_create_output_data_failed: + free(dialog_data); + return NULL; +} + + + static void * +create_data(E_Config_Dialog *cfd) +{ + Eina_List *iter; + E_Randr_Output_Info *output_info; + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + + // Prove we got all things to get going + EINA_SAFETY_ON_TRUE_RETURN_VAL(!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2), NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL(!(e_config_runtime_info = E_NEW(E_Config_Dialog_Data, 1)), NULL); + + e_config_runtime_info->cfd = cfd; + + //Compose theme's file path and name + snprintf(_theme_file_path, sizeof(_theme_file_path), "%s%s", conf_randr_module->dir, THEME_FILENAME); + + e_config_runtime_info->manager = e_manager_current_get(); + EINA_LIST_FOREACH(e_randr_screen_info->rrvd_info.randr_info_12->outputs, iter, output_info) + { + //Create basic data struct for every connected output. + //Data would have to be recreated if a monitor is connected while dialog + //is open. + if (output_info->connection_status != ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED) + continue; + if ((odd = _e_config_randr_dialog_output_dialog_data_new(output_info->crtc, output_info))) + EINA_SAFETY_ON_FALSE_GOTO((e_config_runtime_info->output_dialog_data_list = eina_list_append(e_config_runtime_info->output_dialog_data_list, odd)), _e_conf_randr_create_data_failed_free_data); + } + + fprintf(stderr, "CONF_RANDR: Added %d output data structs.\n", eina_list_count(e_config_runtime_info->output_dialog_data_list)); + //FIXME: Properly (stack-like) free data when creation fails + EINA_SAFETY_ON_FALSE_GOTO(e_config_randr_dialog_subdialog_arrangement_create_data(e_config_runtime_info), _e_conf_randr_create_data_failed_free_data); + EINA_SAFETY_ON_FALSE_GOTO(e_config_randr_dialog_subdialog_resolutions_create_data(e_config_runtime_info), _e_conf_randr_create_data_failed_free_data); + EINA_SAFETY_ON_FALSE_GOTO(e_config_randr_dialog_subdialog_policies_create_data(e_config_runtime_info), _e_conf_randr_create_data_failed_free_data); + EINA_SAFETY_ON_FALSE_GOTO(e_config_randr_dialog_subdialog_orientation_create_data(e_config_runtime_info), _e_conf_randr_create_data_failed_free_data); + + return e_config_runtime_info; + +_e_conf_randr_create_data_failed_free_data: + free(e_config_runtime_info); + return NULL; +} + + static void +free_cfdata(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + EINA_SAFETY_ON_TRUE_RETURN(!e_randr_screen_info); + e_config_randr_dialog_subdialog_arrangement_free_data(cfd, cfdata); + + if (cfdata) free(cfdata); + cfdata = NULL; +} + + static Eina_Bool +_e_conf_randr_confirmation_dialog_timer_cb(void *data) +{ + E_Config_Randr_Dialog_Confirmation_Dialog_Data *cdd = (E_Config_Randr_Dialog_Confirmation_Dialog_Data*)data; + char buf[4096]; + + if (!cdd) return ECORE_CALLBACK_CANCEL; + + --cdd->countdown; + + if (cdd->countdown > 0) + { + snprintf(buf, sizeof(buf), + _("Does this look OK? Click Keep if it does, or Restore if not.
" + "If you do not press a button, the previous settings will be restored in %d seconds."), cdd->countdown); + } + else + { + snprintf(buf, sizeof(buf), + _("Does this look OK? Click Keep if it does, or Restore if not.
" + "If you do not press a button, the previous settings will be restored IMMEDIATELY.")); + } + + e_dialog_text_set(cdd->dialog, buf); + + return (cdd->countdown > 0) ? ECORE_CALLBACK_RENEW : ECORE_CALLBACK_CANCEL; +} + + static void +_e_conf_randr_confirmation_dialog_delete_cb(E_Win *win) +{ + E_Dialog *dia; + E_Config_Randr_Dialog_Confirmation_Dialog_Data *cd; + E_Config_Dialog *cfd; + + dia = win->data; + cd = dia->data; + cd->cfdata->gui.confirmation_dialog = NULL; + cfd = cd->cfdata->cfd; + if (cd->timer) ecore_timer_del(cd->timer); + cd->timer = NULL; + free(cd); + e_object_del(E_OBJECT(dia)); + e_object_unref(E_OBJECT(cfd)); +} + + static void +_e_conf_randr_confirmation_dialog_keep_cb(void *data, E_Dialog *dia) +{ + E_Config_Randr_Dialog_Confirmation_Dialog_Data *cdd = (E_Config_Randr_Dialog_Confirmation_Dialog_Data*)data; + + if (!cdd) return; + + e_config_randr_dialog_subdialog_arrangement_keep_changes(cdd->cfdata); + e_config_randr_dialog_subdialog_orientation_keep_changes(cdd->cfdata); + e_config_randr_dialog_subdialog_policies_keep_changes(cdd->cfdata); + e_config_randr_dialog_subdialog_resolutions_keep_changes(cdd->cfdata); + _e_conf_randr_confirmation_dialog_delete_cb(dia->win); +} + + static void +_e_conf_randr_confirmation_dialog_discard_cb(void *data, E_Dialog *dia) +{ + E_Config_Randr_Dialog_Confirmation_Dialog_Data *cdd = (E_Config_Randr_Dialog_Confirmation_Dialog_Data*)data; + + if (!cdd) return; + + e_config_randr_dialog_subdialog_arrangement_discard_changes(cdd->cfdata); + e_config_randr_dialog_subdialog_orientation_discard_changes(cdd->cfdata); + e_config_randr_dialog_subdialog_policies_discard_changes(cdd->cfdata); + e_config_randr_dialog_subdialog_resolutions_discard_changes(cdd->cfdata); + _e_conf_randr_confirmation_dialog_delete_cb(dia->win); +} + + static void +_e_conf_randr_confirmation_dialog_store_cb(void *data, E_Dialog *dia) +{ + E_Config_Randr_Dialog_Confirmation_Dialog_Data *cdd = (E_Config_Randr_Dialog_Confirmation_Dialog_Data*)data; + + if (!cdd) return; + + _e_conf_randr_confirmation_dialog_keep_cb(data, dia); + e_randr_store_configuration(e_randr_screen_info); +} + + static void +_e_conf_randr_confirmation_dialog_new(E_Config_Dialog *cfd) +{ + E_Config_Randr_Dialog_Confirmation_Dialog_Data *cd = E_NEW(E_Config_Randr_Dialog_Confirmation_Dialog_Data, 1); + + if (!cd) return; + + cd->cfd = cfd; + + if((cd->dialog = e_dialog_new(cfd->con, "E", "e_randr_confirmation_dialog"))) + { + e_dialog_title_set(cd->dialog, _("New settings confirmation")); + cd->cfdata = cfd->cfdata; + cd->timer = ecore_timer_add(1.0, _e_conf_randr_confirmation_dialog_timer_cb, cd); + cd->countdown = 15; + cd->dialog->data = cd; + e_dialog_icon_set(cd->dialog, "preferences-system-screen-resolution", 48); + e_win_delete_callback_set(cd->dialog->win, _e_conf_randr_confirmation_dialog_delete_cb); + e_dialog_button_add(cd->dialog, _("Keep"), NULL, _e_conf_randr_confirmation_dialog_keep_cb, cd); + e_dialog_button_add(cd->dialog, _("Store Permanently"), NULL, _e_conf_randr_confirmation_dialog_store_cb, cd); + e_dialog_button_add(cd->dialog, _("Restore"), NULL, _e_conf_randr_confirmation_dialog_discard_cb, cd); + e_dialog_button_focus_num(cd->dialog, 1); + e_win_centered_set(cd->dialog->win, 1); + e_win_borderless_set(cd->dialog->win, 1); + e_win_layer_set(cd->dialog->win, 6); + e_win_sticky_set(cd->dialog->win, 1); + e_dialog_show(cd->dialog); + e_object_ref(E_OBJECT(cfd)); + } + +} + + static Evas_Object * +basic_create_widgets (E_Config_Dialog *cfd, Evas *canvas, E_Config_Dialog_Data *cfdata) +{ + Evas_Object *table = NULL, *wl = NULL; + + EINA_SAFETY_ON_TRUE_RETURN_VAL (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2), NULL); + EINA_SAFETY_ON_TRUE_RETURN_VAL((!canvas || !cfdata), NULL); + + if(!(cfdata->gui.subdialogs.arrangement.dialog = e_config_randr_dialog_subdialog_arrangement_basic_create_widgets(canvas))) goto _e_config_randr_dialog_create_subdialog_arrangement_fail; + if(!(cfdata->gui.subdialogs.policies.dialog = e_config_randr_dialog_subdialog_policies_basic_create_widgets(canvas))) goto _e_config_randr_dialog_create_subdialog_policies_fail; + if(!(cfdata->gui.subdialogs.resolutions.dialog = e_config_randr_dialog_subdialog_resolutions_basic_create_widgets(canvas))) goto _e_config_randr_dialog_create_subdialog_resolutions_fail; + if(!(cfdata->gui.subdialogs.orientation.dialog = e_config_randr_dialog_subdialog_orientation_basic_create_widgets(canvas))) goto _e_config_randr_dialog_create_subdialog_orientation_fail; + + EINA_SAFETY_ON_FALSE_GOTO((table = e_widget_table_add(canvas, EINA_FALSE)), _e_config_randr_dialog_create_widgets_fail); + EINA_SAFETY_ON_FALSE_GOTO((wl = e_widget_list_add(canvas, EINA_FALSE, EINA_TRUE)), _e_config_randr_dialog_create_widget_list_fail); + + //e_widget_table_object_append(Evas_Object *obj, Evas_Object *sobj, int col, int row, int colspan, int rowspan, int fill_w, int fill_h, int expand_w, int expand_h); + e_widget_table_object_append(table, cfdata->gui.subdialogs.arrangement.dialog, 1, 1, 1, 1, EVAS_HINT_FILL, EVAS_HINT_FILL, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + /* + e_widget_table_object_append(table, cfdata->gui.subdialogs.policies.dialog, 1, 2, 1, 1, 0, 0, 0, 0); + e_widget_table_object_append(table, cfdata->gui.subdialogs.orientation.dialog, 2, 2, 1, 1, 0, 0, 0, 0); + e_widget_table_object_append(table, cfdata->gui.subdialogs.resolutions.dialog, 3, 2, 1, 1, EVAS_HINT_FILL, EVAS_HINT_FILL, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + */ + //e_widget_list_object_append(Evas_Object *obj, Evas_Object *sobj, int fill, int expand, double align); + e_widget_list_object_append(wl, cfdata->gui.subdialogs.policies.dialog, 0, 0, 0.0); + e_widget_list_object_append(wl, cfdata->gui.subdialogs.orientation.dialog, 0, 0, 0.0); + e_widget_list_object_append(wl, cfdata->gui.subdialogs.resolutions.dialog, EVAS_HINT_FILL, EVAS_HINT_EXPAND, 1.0); + e_widget_table_object_append(table, wl, 1, 2, 1, 1, EVAS_HINT_FILL, EVAS_HINT_FILL, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + cfdata->gui.widget_list = wl; + + cfdata->gui.dialog = table; + + e_dialog_resizable_set(cfd->dia, EINA_TRUE); + + return cfdata->gui.dialog; + +_e_config_randr_dialog_create_widget_list_fail: + evas_object_del(table); +_e_config_randr_dialog_create_widgets_fail: + evas_object_del(cfdata->gui.subdialogs.orientation.dialog); +_e_config_randr_dialog_create_subdialog_orientation_fail: + evas_object_del(cfdata->gui.subdialogs.resolutions.dialog); +_e_config_randr_dialog_create_subdialog_resolutions_fail: + evas_object_del(cfdata->gui.subdialogs.policies.dialog); +_e_config_randr_dialog_create_subdialog_policies_fail: + evas_object_del(cfdata->gui.subdialogs.arrangement.dialog); +_e_config_randr_dialog_create_subdialog_arrangement_fail: + return NULL; +} + +static int + basic_apply_data +(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + Eina_Bool ret = EINA_TRUE; + + fprintf(stderr, "CONF_RANDR: New configuration is beeing applied.\n"); + //this is a special case, where the function is called, before the + //configuration data is created. + if (!cfdata) return EINA_FALSE; + + //the order matters except for policies! + if (e_config_randr_dialog_subdialog_policies_basic_check_changed(cfd, cfdata)) + { + ret &= e_config_randr_dialog_subdialog_policies_basic_apply_data(cfd, cfdata); + if (!ret) return EINA_FALSE; + } + + if (e_config_randr_dialog_subdialog_resolutions_basic_check_changed(cfd, cfdata)) + { + ret &= e_config_randr_dialog_subdialog_resolutions_basic_apply_data(cfd, cfdata); + if (!ret) return EINA_FALSE; + } + + if (e_config_randr_dialog_subdialog_arrangement_basic_check_changed(cfd, cfdata)) + { + ret &= e_config_randr_dialog_subdialog_arrangement_basic_apply_data(cfd, cfdata); + if (!ret) return EINA_FALSE; + } + + if (e_config_randr_dialog_subdialog_orientation_basic_check_changed(cfd, cfdata)) + ret &= e_config_randr_dialog_subdialog_orientation_basic_apply_data(cfd, cfdata); + + _e_conf_randr_confirmation_dialog_new(cfd); + + return ret; +} + +E_Config_Dialog * +e_int_config_randr(E_Container *con, const char *params __UNUSED__){ + E_Config_Dialog *cfd; + E_Config_Dialog_View *v; + + if (!e_randr_screen_info || (e_randr_screen_info->randr_version < ECORE_X_RANDR_1_2)) + { + ecore_timer_add(0.5, _deferred_noxrandr_error, NULL); + fprintf(stderr, "CONF_RANDR: XRandR version >= 1.2 necessary to work.\n"); + return NULL; + } + + //Dialog already opened? + if (e_config_dialog_find("E", "screen/screen_setup")) return NULL; + + v = E_NEW(E_Config_Dialog_View, 1); + v->create_cfdata = create_data; + v->free_cfdata = free_cfdata; + v->basic.apply_cfdata = basic_apply_data; + v->basic.create_widgets = basic_create_widgets; + v->basic.check_changed = basic_check_changed; + //v->override_auto_apply = 0; + + cfd = e_config_dialog_new(con, _("Screen Setup"), + "E", "screen/screen_setup", + "preferences-system-screen-setup", 0, v, NULL); + return cfd; +} + + static Eina_Bool +_deferred_noxrandr_error(void *data __UNUSED__) +{ + e_util_dialog_show(_("Missing Features"), + _("Your X Display Server is missing support for
" + "the XRandR (X Resize and Rotate) extension version 1.2 or above.
" + "You cannot change screen resolutions without
" + "the support of this extension. It could also be
" + "that at the time ecore was built, there
" + "was no XRandR support detected.")); + return ECORE_CALLBACK_CANCEL; +} + + static int +basic_check_changed (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + if (!cfdata) return EINA_FALSE; + else + return (e_config_randr_dialog_subdialog_arrangement_basic_check_changed(cfd, cfdata) + || e_config_randr_dialog_subdialog_policies_basic_check_changed(cfd, cfdata) + || e_config_randr_dialog_subdialog_orientation_basic_check_changed(cfd, cfdata) + || e_config_randr_dialog_subdialog_resolutions_basic_check_changed(cfd, cfdata)); +} + diff --git a/src/modules/conf_randr/e_int_config_randr.h b/src/modules/conf_randr/e_int_config_randr.h new file mode 100644 index 000000000..5356e2494 --- /dev/null +++ b/src/modules/conf_randr/e_int_config_randr.h @@ -0,0 +1,73 @@ +#ifdef E_TYPEDEFS +#else +#ifndef E_INT_CONFIG_RANDR_H +#define E_INT_CONFIG_RANDR_H + +#include "e.h" + +typedef struct _E_Config_Randr_Dialog_Output_Dialog_Data E_Config_Randr_Dialog_Output_Dialog_Data; +typedef struct _E_Config_Randr_Dialog_Confirmation_Dialog_Data E_Config_Randr_Dialog_Confirmation_Dialog_Data; + +struct _E_Config_Dialog_Data +{ + E_Config_Dialog *cfd; + + //list of E_Config_Randr_Dialog_Output_Dialog_Data + Eina_List *output_dialog_data_list; + E_Manager *manager; + struct { + Evas_Object *dialog, *widget_list, *selected_eo; + E_Config_Randr_Dialog_Output_Dialog_Data *selected_output_dd; + E_Config_Randr_Dialog_Confirmation_Dialog_Data *confirmation_dialog; + struct { + struct { + Evas_Object *dialog, *swallowing_edje, *smart_parent, *suggestion, *clipper; + Evas_Coord_Point previous_pos, relative_zero; + int suggestion_dist_min; + } arrangement; + struct { + Evas_Object *dialog; + //Evas_Object *swallowing_edje; + Evas_Object *radio_above, *radio_right, *radio_below, *radio_left, *radio_clone, *radio_none; + int radio_val; + //Evas_Object *current_displays_setup, *current_displays_setup_background, *new_display, *new_display_background; + } policies; + struct { + Evas_Object *dialog; + } resolutions; + struct { + Evas_Object *dialog; + //Evas_Object *swallowing_edje; + Evas_Object *radio_normal, *radio_rot90, *radio_rot180, *radio_rot270, *radio_reflect_horizontal, *radio_reflect_vertical; + int radio_val; + } orientation; + } subdialogs; + } gui; + Ecore_X_Randr_Screen_Size screen_size; + +}; + +struct _E_Config_Randr_Dialog_Output_Dialog_Data +{ + E_Randr_Crtc_Info *crtc; + E_Randr_Output_Info *output; + Evas_Coord_Point previous_pos, new_pos; + Ecore_X_Randr_Mode_Info *previous_mode, *new_mode, *preferred_mode; + Ecore_X_Randr_Orientation previous_orientation, new_orientation; + Ecore_X_Randr_Output_Policy previous_policy, new_policy; + Evas_Object *bg; +}; + +struct _E_Config_Randr_Dialog_Confirmation_Dialog_Data +{ + E_Config_Dialog *cfd; + E_Config_Dialog_Data *cfdata; + E_Dialog *dialog; + Ecore_Timer *timer; + int countdown; +}; + +E_Config_Dialog *e_int_config_randr(E_Container *con, const char *params __UNUSED__); + +#endif +#endif diff --git a/src/modules/conf_randr/e_int_config_randr_arrangement.c b/src/modules/conf_randr/e_int_config_randr_arrangement.c new file mode 100644 index 000000000..d30260ace --- /dev/null +++ b/src/modules/conf_randr/e_int_config_randr_arrangement.c @@ -0,0 +1,806 @@ +#include "e_int_config_randr.h" +#include "e_randr.h" +#include "Ecore_X.h" + +#ifndef ECORE_X_RANDR_1_2 +#define ECORE_X_RANDR_1_2 ((1 << 16) | 2) +#endif +#ifndef ECORE_X_RANDR_1_3 +#define ECORE_X_RANDR_1_3 ((1 << 16) | 3) +#endif + +#ifndef Ecore_X_Randr_Unset +#define Ecore_X_Randr_Unset -1 +#endif + +#define DOUBLECLICK_TIMEOUT 0.2 +#define CRTC_THUMB_SIZE_W 300 +#define CRTC_THUMB_SIZE_H 300 + +Eina_Bool e_config_randr_dialog_subdialog_arrangement_create_data (E_Config_Dialog_Data *e_config_runtime_info); +Evas_Object *e_config_randr_dialog_subdialog_arrangement_basic_create_widgets (Evas *canvas); +Eina_Bool e_config_randr_dialog_subdialog_arrangement_basic_check_changed (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +Eina_Bool e_config_randr_dialog_subdialog_arrangement_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +void e_config_randr_dialog_subdialog_arrangement_free_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +static inline Eina_List *_e_config_randr_dialog_subdialog_arrangement_neighbors_get (Evas_Object *obj); +static void _e_config_randr_dialog_subdialog_arrangement_determine_positions_recursive (Evas_Object *obj); + +static inline E_Config_Randr_Dialog_Output_Dialog_Data *_e_config_randr_dialog_subdialog_arrangement_output_dialog_data_new (E_Randr_Crtc_Info *crtc_info, E_Randr_Output_Info *output_info); +static inline void _e_config_randr_dialog_subdialog_arrangement_suggestion_add (Evas *evas); +static inline void _e_config_randr_dialog_subdialog_arrangement_make_suggestion (Evas_Object *obj); +static void _e_config_randr_dialog_subdialog_arrangement_smart_class_resize (Evas_Object *obj, Evas_Coord w, Evas_Coord h); +static Evas_Object *_e_config_randr_dialog_subdialog_arrangement_output_add (Evas *canvas, E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data); +static void _e_config_randr_dialog_subdialog_arrangement_output_mouse_down_cb (void *data, Evas *e, Evas_Object *obj, void *event_info); +static void _e_config_randr_dialog_subdialog_arrangement_output_mouse_move_cb (void *data, Evas *e, Evas_Object *obj, void *event_info); +static void _e_config_randr_dialog_subdialog_arrangement_output_mouse_up_cb (void *data, Evas *e, Evas_Object *obj, void *event_info); + +// Function for the resolutions subdialog interaction +extern void e_config_randr_dialog_subdialog_resolutions_update_list (Evas_Object *crtc); +// Function for the orientation subdialog interaction +extern void e_config_randr_dialog_subdialog_orientation_update_radio_buttons (Evas_Object *crtc); +extern void e_config_randr_dialog_subdialog_orientation_update_edje (Evas_Object *crtc); +// Functions for the orientation subdialog interaction +extern void e_config_randr_dialog_subdialog_policies_update_radio_buttons (Evas_Object *crtc); + +Evas_Smart_Class screen_setup_smart_class = EVAS_SMART_CLASS_INIT_NAME_VERSION("EvasObjectSmartScreenSetup"); +Evas_Smart *screen_setup_smart; + +extern E_Config_Dialog_Data *e_config_runtime_info; +extern char _theme_file_path[]; + + static void +_e_config_randr_dialog_subdialog_arrangement_output_dialog_data_fill(E_Config_Randr_Dialog_Output_Dialog_Data *odd) +{ + if (!odd) return; + + if (odd->crtc) + { + //already enabled screen + odd->previous_pos.x = odd->crtc->geometry.x; + odd->previous_pos.y = odd->crtc->geometry.y; + odd->previous_mode = odd->crtc->current_mode; + } + else if (odd->output) + { + //disabled monitor + //try to get a mode from the preferred list, else use default list + if(!(odd->preferred_mode = (Ecore_X_Randr_Mode_Info*)eina_list_data_get(eina_list_last(odd->output->preferred_modes)))) + odd->preferred_mode = (Ecore_X_Randr_Mode_Info*)eina_list_data_get(eina_list_last(odd->output->modes)); + odd->previous_pos.x = Ecore_X_Randr_Unset; + odd->previous_pos.y = Ecore_X_Randr_Unset; + } + + odd->new_pos.x = Ecore_X_Randr_Unset; + odd->new_pos.y = Ecore_X_Randr_Unset; +} + + Eina_Bool +e_config_randr_dialog_subdialog_arrangement_create_data(E_Config_Dialog_Data *data) +{ + Eina_List *iter; + E_Config_Randr_Dialog_Output_Dialog_Data *dialog_data; + + EINA_LIST_FOREACH(data->output_dialog_data_list, iter, dialog_data) + { + _e_config_randr_dialog_subdialog_arrangement_output_dialog_data_fill(dialog_data); + } + + return EINA_TRUE; +} + +//IMPROVABLE: Clean up properly if instances can't be created + Evas_Object * +e_config_randr_dialog_subdialog_arrangement_basic_create_widgets(Evas *canvas) +{ + Evas_Object *subdialog, *crtc; + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + Eina_List *iter; + + if (!canvas || !e_config_runtime_info || !e_config_runtime_info->output_dialog_data_list) return NULL; + + //initialize smart object + evas_object_smart_clipped_smart_set(&screen_setup_smart_class); + screen_setup_smart_class.resize = _e_config_randr_dialog_subdialog_arrangement_smart_class_resize; + screen_setup_smart = evas_smart_class_new(&screen_setup_smart_class); + + subdialog = evas_object_smart_add(canvas, screen_setup_smart); + e_config_runtime_info->gui.subdialogs.arrangement.clipper = evas_object_smart_clipped_clipper_get(subdialog); + fprintf(stderr, "CONF_RANDR: Arrangement subdialog added (%p).\n", subdialog); + + + //only use information we can restore. + EINA_LIST_FOREACH(e_config_runtime_info->output_dialog_data_list, iter, output_dialog_data) + { + if ((!output_dialog_data->crtc && !output_dialog_data->output)) + continue; + crtc = _e_config_randr_dialog_subdialog_arrangement_output_add(canvas, output_dialog_data); + + if (!crtc) continue; + evas_object_show(crtc); + + evas_object_event_callback_add (crtc, EVAS_CALLBACK_MOUSE_DOWN, _e_config_randr_dialog_subdialog_arrangement_output_mouse_down_cb, NULL); + evas_object_event_callback_add (crtc, EVAS_CALLBACK_MOUSE_MOVE, _e_config_randr_dialog_subdialog_arrangement_output_mouse_move_cb, NULL); + evas_object_event_callback_add (crtc, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_arrangement_output_mouse_up_cb, NULL); + + + evas_object_smart_member_add(crtc, subdialog); + fprintf(stderr, "CONF_RANDR: CRTC representation (%p) added to arrangement subdialog (%p).\n", crtc, subdialog); + } + + e_config_runtime_info->gui.subdialogs.arrangement.smart_parent = subdialog; + + return subdialog; +} + + static Evas_Object * +_e_config_randr_dialog_subdialog_arrangement_output_add(Evas *canvas, E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data) +{ + E_Randr_Output_Info *output_info; + Evas_Object *output, *bg; + const char* output_name = NULL; + + if (!canvas || !output_dialog_data || !e_config_runtime_info) return NULL; + + EINA_SAFETY_ON_FALSE_RETURN_VAL((output = edje_object_add(canvas)), NULL); + + //set instance data for output + evas_object_data_set(output, "output_info", output_dialog_data); + + //set theme for monitor representation + EINA_SAFETY_ON_FALSE_GOTO(edje_object_file_set(output, _theme_file_path, "e/conf/randr/dialog/subdialog/arrangement/output"), _e_config_randr_dialog_subdialog_arrangement_output_add_edje_set_fail); + //indicate monitor state + if (!output_dialog_data->crtc || (output_dialog_data->crtc && !output_dialog_data->previous_mode)) + edje_object_signal_emit(output, "disabled", "e"); + else + edje_object_signal_emit(output, "enabled", "e"); + //for now use deskpreview widget as background of output, maybe change this to + //live image from comp module + output_dialog_data->bg = e_widget_deskpreview_add(canvas, 1, 1); + edje_object_part_swallow(output, "e.swallow.content", output_dialog_data->bg); + + //Try to get the name of the monitor connected to the output's last output via edid + //else use the output's name + if (output_dialog_data->crtc) + output_info = (E_Randr_Output_Info*)eina_list_data_get(eina_list_last(output_dialog_data->crtc->outputs)); + else + output_info = output_dialog_data->output; + if (output_info) + { + if (ecore_x_randr_edid_has_valid_header(output_info->edid, output_info->edid_length)) + output_name = ecore_x_randr_edid_display_name_get(output_info->edid, output_info->edid_length); + else if (output_info->name) + output_name = output_info->name; + } + if (output_name) + edje_object_part_text_set(output, "output_txt", output_name); + + //set output orientation + e_config_randr_dialog_subdialog_orientation_update_edje(output); + return output; + +_e_config_randr_dialog_subdialog_arrangement_output_add_edje_set_fail: + evas_object_del(output); + return NULL; +} + + static void +_e_config_randr_dialog_subdialog_arrangement_smart_class_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h) +{ + Evas_Object *output; + Evas_Coord real_sum_w = 0 , real_sum_h = 0; + Eina_Rectangle parent_geo, new_geo; + Evas_Coord_Point offset = {.x = 0, .y = 0}; + Evas_Coord offset_x_max = 0; + float scaling_factor = 0.1; + Eina_List *lst, *itr; + const E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + + evas_object_geometry_get(obj, &parent_geo.x, &parent_geo.y, &parent_geo.w, &parent_geo.h); + fprintf(stderr, "CONF_RANDR: Arrangement dialog shall be resized to %d x %d\n", w, h); + fprintf(stderr, "CONF_RANDR: Arrangement dialog Smart object geo: %d x %d, %d x %d\n", parent_geo.x, parent_geo.y, parent_geo.w, parent_geo.h); + if ((w < 1) || (h < 1)) return; + + lst = evas_object_smart_members_get(obj); + //Calc average aspect ratio from all available monitors + EINA_LIST_FOREACH(lst, itr, output) + { + if ((output == e_config_runtime_info->gui.subdialogs.arrangement.clipper) || !(output_dialog_data = evas_object_data_get(output, "output_info")) || (!output_dialog_data->previous_mode && !output_dialog_data->preferred_mode)) continue; + if (output_dialog_data->previous_mode) + { + real_sum_w += output_dialog_data->previous_mode->width; + real_sum_h += output_dialog_data->previous_mode->height; + } + else + { + real_sum_w += output_dialog_data->preferred_mode->width; + real_sum_h += output_dialog_data->preferred_mode->height; + } + } + + scaling_factor = (((float)parent_geo.w / (float)real_sum_w) < ((float)parent_geo.h / (float)real_sum_h)) ? ((float)parent_geo.w / (float)real_sum_w) : ((float)parent_geo.h / (float)real_sum_h); + scaling_factor *= e_scale; + + EINA_LIST_FOREACH(lst, itr, output) + { + //Skip elements that are either the clipped smart object or falsely added + //to the list of outputs (which should not happen) + if ((output == e_config_runtime_info->gui.subdialogs.arrangement.clipper) || !(output_dialog_data = evas_object_data_get(output, "output_info"))) continue; + if (output_dialog_data->previous_mode) + { + new_geo.w = (int)((float)output_dialog_data->previous_mode->width * scaling_factor); + new_geo.h = (int)((float)output_dialog_data->previous_mode->height * scaling_factor); + } + else if (output_dialog_data->preferred_mode) + { + new_geo.w = (int)((float)output_dialog_data->preferred_mode->width * scaling_factor); + new_geo.h = (int)((float)output_dialog_data->preferred_mode->height * scaling_factor); + } + else + { + fprintf(stderr, "CONF_RANDR: Can't resize thumb, as neither mode nor preferred mode are avavailable for %x\n", (output_dialog_data->crtc ? output_dialog_data->crtc->xid : output_dialog_data->output->xid)); + continue; + } + if ((new_geo.w <= 0) || (new_geo.h <= 0)) + { + //this is an effect, occuring during dialog closing. + //If we don't return here, e_thumb will segfault! + return; + } + if ((output_dialog_data->previous_pos.x == Ecore_X_Randr_Unset) || (output_dialog_data->previous_pos.y == Ecore_X_Randr_Unset)) + { + //this is a non enabled monitor + new_geo.x = parent_geo.x + parent_geo.w - new_geo.w - offset.x; + new_geo.y = parent_geo.y + offset.y; + offset.y = new_geo.y + new_geo.h; + if (offset_x_max < new_geo.w) + { + //adopt new max value for x + offset_x_max = new_geo.w; + } + if ((offset.y + new_geo.h) > (parent_geo.y + parent_geo.h)) + { + //reset offset.y and adjust offset.x + offset.y = 0; + offset.x += offset_x_max; + } + } + else + { + new_geo.x = ((int)((float)output_dialog_data->previous_pos.x * scaling_factor)) + parent_geo.x; + new_geo.y = ((int)((float)output_dialog_data->previous_pos.y * scaling_factor)) + parent_geo.y; + } + //resize edje element + evas_object_resize(output, new_geo.w, new_geo.h); + //also resize bg + e_thumb_icon_size_set(output_dialog_data->bg, new_geo.w, new_geo.h); //need to clarify the usage of e_thumb. Usable without e_thumb_icon_file_set??!! + evas_object_move(output, new_geo.x, new_geo.y); + fprintf(stderr, "CONF_RANDR: output representation %p was resized to %d x %d\n", output, new_geo.w, new_geo.h); + fprintf(stderr, "CONF_RANDR: output representation %p was moved to %d x %d\n", output, new_geo.x, new_geo.y); + } +} + + static void +_e_config_randr_dialog_subdialog_arrangement_output_mouse_down_cb (void *data, Evas *e, Evas_Object *obj, void *event_info) +{ + Evas_Event_Mouse_Down *ev = (Evas_Event_Mouse_Down*)event_info; + Evas_Object *element = NULL; + Eina_List *iter; + Eina_Bool crtc_selected = EINA_FALSE; + + EINA_LIST_FOREACH(evas_object_smart_members_get(evas_object_smart_parent_get(obj)), iter, element) + { + if (e_config_runtime_info->gui.subdialogs.arrangement.clipper == obj) continue; + if (element != obj) + edje_object_signal_emit(element, "deselect", "e"); + else + { + edje_object_signal_emit(element, "select", "e"); + //update data for other dialogs + e_config_runtime_info->gui.selected_eo = obj; + + //update resolutions list + e_config_randr_dialog_subdialog_resolutions_update_list(obj); + + //update orientation radio buttons + e_config_randr_dialog_subdialog_orientation_update_radio_buttons(obj); + + //update policy radio buttons + e_config_randr_dialog_subdialog_policies_update_radio_buttons(obj); + + crtc_selected = EINA_TRUE; + } + } + if (!crtc_selected) + { + //update data for other dialogs + e_config_runtime_info->gui.selected_eo = NULL; + + //update resolutions list + e_config_randr_dialog_subdialog_resolutions_update_list(NULL); + + //update orientation radio buttons + e_config_randr_dialog_subdialog_orientation_update_radio_buttons(NULL); + + //update policy radio buttons + e_config_randr_dialog_subdialog_policies_update_radio_buttons(NULL); + } + + evas_object_geometry_get(obj, &e_config_runtime_info->gui.subdialogs.arrangement.previous_pos.x, &e_config_runtime_info->gui.subdialogs.arrangement.previous_pos.y, NULL, NULL); +} + + static void +_e_config_randr_dialog_subdialog_arrangement_output_mouse_move_cb (void *data, Evas *e, Evas_Object *obj, void *event_info) +{ + Evas_Event_Mouse_Move *ev = event_info; + Eina_Rectangle geo, parent; + Evas_Coord_Point delta, new; + + if (ev->buttons == 1) + { + evas_object_geometry_get (obj, &geo.x, &geo.y, &geo.w, &geo.h); + evas_object_geometry_get (evas_object_smart_parent_get(obj), &parent.x, &parent.y, &parent.w, &parent.h); + delta.x = ev->cur.canvas.x - ev->prev.canvas.x; + delta.y = ev->cur.canvas.y - ev->prev.canvas.y; + + new.x = geo.x + delta.x; + new.y = geo.y + delta.y; + //respect container borders + if (new.x < parent.x + 1) + new.x = parent.x + 1; + else if (new.x > parent.x + parent.w - geo.w) + new.x = parent.x + parent.w - geo.w; + if (new.y < parent.y + 1) + new.y = parent.y + 1; + else if (new.y > parent.y + parent.h - geo.h) + new.y = parent.y + parent.h - geo.h; + //only take action if position changed + if ((geo.x != new.x) || (geo.y != new.y)) + { + evas_object_move(obj, new.x, new.y); + _e_config_randr_dialog_subdialog_arrangement_make_suggestion(obj); + } + } +} + + static void +_e_config_randr_dialog_subdialog_arrangement_output_mouse_up_cb (void *data, Evas *e, Evas_Object *obj, void *event_info) +{ + Evas_Coord_Point coords; + + if (evas_object_visible_get(e_config_runtime_info->gui.subdialogs.arrangement.suggestion)) + { + edje_object_signal_emit(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, "hide", "e"); + evas_object_geometry_get(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, &coords.x, &coords.y, NULL, NULL); + evas_object_move(obj, coords.x, coords.y); + } + else + { + evas_object_move(obj, e_config_runtime_info->gui.subdialogs.arrangement.previous_pos.x, e_config_runtime_info->gui.subdialogs.arrangement.previous_pos.y); + } + +} + +void +_e_config_randr_dialog_subdialog_arrangement_suggestion_add(Evas *evas) +{ + const char *theme_data_item = NULL; + + e_config_runtime_info->gui.subdialogs.arrangement.suggestion = edje_object_add(evas); + edje_object_file_set(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, _theme_file_path, "e/conf/randr/dialog/subdialog/arrangement/suggestion"); + if ((theme_data_item = edje_object_data_get(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, "distance_min"))) + e_config_runtime_info->gui.subdialogs.arrangement.suggestion_dist_min = MIN(MAX(atoi(theme_data_item), 0), 100); + else + e_config_runtime_info->gui.subdialogs.arrangement.suggestion_dist_min = 20; +} + + void +_e_config_randr_dialog_subdialog_arrangement_make_suggestion (Evas_Object *obj) +{ + Eina_List *li, *crtcs = evas_object_smart_members_get(evas_object_smart_parent_get(obj)); + Evas_Object *crtc = NULL; + Eina_Rectangle p_geo, geo, crtc_geo, s_geo; + int dxa = 10000, dya = 10000, tmp, min_dist; + + if (!obj) return; + + if (!e_config_runtime_info->gui.subdialogs.arrangement.suggestion) + { + _e_config_randr_dialog_subdialog_arrangement_suggestion_add(evas_object_evas_get(obj)); + evas_object_show(e_config_runtime_info->gui.subdialogs.arrangement.suggestion); + } + + min_dist = e_config_runtime_info->gui.subdialogs.arrangement.suggestion_dist_min; + + evas_object_geometry_get(evas_object_smart_parent_get(obj), &p_geo.x, &p_geo.y, &p_geo.w, &p_geo.h); + evas_object_geometry_get(obj, &geo.x, &geo.y, &geo.w, &geo.h); + + s_geo.x = geo.x; + s_geo.y = geo.y; + s_geo.w = geo.w; + s_geo.h = geo.h; + + //compare possible positions + //aritifical (relative) 0x0 element + tmp = s_geo.x; + if ((tmp < dxa) && (tmp < min_dist)) + { + s_geo.x = p_geo.x; + dxa = tmp; + } + tmp = s_geo.y; + if ((tmp < dya) && (tmp < min_dist)) + { + s_geo.y = p_geo.y; + dya = tmp; + } + //iterate crtc list + EINA_LIST_FOREACH(crtcs, li, crtc) + { + if ((crtc == obj) || (crtc == e_config_runtime_info->gui.subdialogs.arrangement.clipper)) continue; + evas_object_geometry_get(crtc, &crtc_geo.x, &crtc_geo.y, &crtc_geo.w, &crtc_geo.h); + //X-Axis + tmp = abs(s_geo.x - crtc_geo.x); + if ((tmp < dxa) && (tmp < min_dist)) + { + s_geo.x = crtc_geo.x; + dxa = abs(s_geo.x - crtc_geo.x); + } + + tmp = abs(s_geo.x - (crtc_geo.x + crtc_geo.w)); + if ((tmp < dxa) && (tmp < min_dist)) + { + s_geo.x = (crtc_geo.x + crtc_geo.w); + dxa = tmp; + } + + tmp = abs((s_geo.x + s_geo.w) - (crtc_geo.x - 1)); + if ((tmp < dxa) && (tmp < min_dist)) + { + s_geo.x = (crtc_geo.x - s_geo.w); + dxa = tmp; + } + + tmp = abs((s_geo.x + s_geo.w) - (crtc_geo.x + crtc_geo.w)); + if ((tmp < dxa) && (tmp < min_dist)) + { + s_geo.x = (crtc_geo.x + crtc_geo.w - s_geo.w); + dxa = tmp; + } + + //Y-Axis + tmp = abs(s_geo.y - crtc_geo.y); + if ((tmp < dya) && (tmp < min_dist)) + { + s_geo.y = crtc_geo.y; + dya = abs(s_geo.y - crtc_geo.y); + } + + tmp = abs(s_geo.y - (crtc_geo.y + crtc_geo.h)); + if ((tmp < dya) && (tmp < min_dist)) + { + s_geo.y = (crtc_geo.y + crtc_geo.h); + dya = tmp; + } + + tmp = abs((s_geo.y + s_geo.h) - (crtc_geo.y - 1)); + if ((tmp < dya) && (tmp < min_dist)) + { + s_geo.y = (crtc_geo.y - s_geo.h); + dya = tmp; + } + + tmp = abs((s_geo.y + s_geo.h) - (crtc_geo.y + crtc_geo.h)); + if ((tmp < dya) && (tmp < min_dist)) + { + s_geo.y = (crtc_geo.y + crtc_geo.h - s_geo.h); + dya = tmp; + } + + } + + + if ((s_geo.x != geo.x) && (s_geo.y != geo.y)) + { + if (s_geo.x < p_geo.x) s_geo.x = p_geo.x; + if ((s_geo.x + s_geo.w) > (p_geo.x + p_geo.w)) s_geo.x = ((p_geo.x + p_geo.w) - s_geo.w); + if (s_geo.y < p_geo.y) s_geo.y = p_geo.y; + if ((s_geo.y + s_geo.h) > (p_geo.y + p_geo.h)) s_geo.y = ((p_geo.y + p_geo.h) - s_geo.h); + + if (!evas_object_visible_get(e_config_runtime_info->gui.subdialogs.arrangement.suggestion)) + { + evas_object_show(e_config_runtime_info->gui.subdialogs.arrangement.suggestion); + edje_object_signal_emit(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, "show", "e"); + } + + evas_object_resize(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, s_geo.w, s_geo.h); + evas_object_move(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, s_geo.x, s_geo.y); + } + else + { + edje_object_signal_emit(e_config_runtime_info->gui.subdialogs.arrangement.suggestion, "hide", "e"); + evas_object_hide(e_config_runtime_info->gui.subdialogs.arrangement.suggestion); + } +} + + void +e_config_randr_dialog_subdialog_arrangement_free_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *dialog_data; + + EINA_SAFETY_ON_NULL_RETURN(cfdata); + + EINA_LIST_FREE(cfdata->output_dialog_data_list, dialog_data) + { + if (dialog_data) + { + if (dialog_data->bg) + { + evas_object_del(dialog_data->bg); + dialog_data->bg = NULL; + } + free(dialog_data); + dialog_data = NULL; + } + } +} + + static Eina_List +*_e_config_randr_dialog_subdialog_arrangement_neighbors_get(Evas_Object *obj) +{ + Evas_Object *smart_parent, *crtc; + Eina_List *crtcs, *iter, *neighbors = NULL; + Eina_Rectangle geo, neighbor_geo; + E_Config_Randr_Dialog_Output_Dialog_Data *dialog_data, *neighbor_info; + + smart_parent = evas_object_smart_parent_get(obj); + crtcs = evas_object_smart_members_get(smart_parent); + + EINA_SAFETY_ON_FALSE_RETURN_VAL((dialog_data = evas_object_data_get(obj, "output_info")), NULL); + evas_object_geometry_get(obj, &geo.x, &geo.y, &geo.w, &geo.h); + EINA_LIST_FOREACH(crtcs, iter, crtc) + { + if ((crtc == obj) + || (crtc == e_config_runtime_info->gui.subdialogs.arrangement.clipper)) continue; + evas_object_geometry_get(crtc, &neighbor_geo.x, &neighbor_geo.y, &neighbor_geo.w, &neighbor_geo.h); + if(!(neighbor_info = evas_object_data_get(crtc, "output_info"))) continue; + + if (((geo.x + geo.w) == neighbor_geo.x) + || (geo.x == (neighbor_geo.x + neighbor_geo.w)) + || (geo.x == neighbor_geo.x) + || ((geo.x + geo.w) == (neighbor_geo.x + neighbor_geo.w)) + || ((geo.y + geo.h) == neighbor_geo.y) + || (geo.y == (neighbor_geo.y + neighbor_geo.h)) + || (geo.y == neighbor_geo.y) + || ((geo.y + geo.h) == (neighbor_geo.y + neighbor_geo.h))) + { + neighbors = eina_list_append(neighbors, crtc); + } + } + + return neighbors; +} + + static void +_e_config_randr_dialog_subdialog_arrangement_determine_positions_recursive(Evas_Object *obj) +{ + Eina_List *neighbors, *iter; + Evas_Object *smart_parent, *crtc; + E_Config_Randr_Dialog_Output_Dialog_Data *dialog_data, *neighbor_info; + Eina_Rectangle geo, neighbor_geo, smart_geo; + + // Each object is seen as a tree. All its edges are compared to their + // neighbors and wandered recusively. + EINA_SAFETY_ON_NULL_RETURN(obj); + + smart_parent = e_config_runtime_info->gui.subdialogs.arrangement.smart_parent; + evas_object_geometry_get(smart_parent, &smart_geo.x, &smart_geo.y, &smart_geo.w, &smart_geo.h); + //fprintf(stderr, "CONF_RANDR: Smart Parent is at %dx%d\n", smart_geo.x, smart_geo.y); + neighbors = _e_config_randr_dialog_subdialog_arrangement_neighbors_get(obj); + + EINA_SAFETY_ON_FALSE_RETURN((dialog_data = evas_object_data_get(obj, "output_info"))); + evas_object_geometry_get(obj, &geo.x, &geo.y, &geo.w, &geo.h); + + //fprintf(stderr, "CONF_RANDR: Traversed element (%p) is at %dx%d\n", obj, geo.x, geo.y); + if (geo.x == e_config_runtime_info->gui.subdialogs.arrangement.relative_zero.x) dialog_data->new_pos.x = 0; + if (geo.y == e_config_runtime_info->gui.subdialogs.arrangement.relative_zero.y) dialog_data->new_pos.y = 0; + + if ((dialog_data->new_pos.x != 0) || (dialog_data->new_pos.y != 0)) + { + // Find neighbor object we can calculate our own coordinates from + EINA_LIST_FOREACH(neighbors, iter, crtc) + { + evas_object_geometry_get(crtc, &neighbor_geo.x, &neighbor_geo.y, &neighbor_geo.w, &neighbor_geo.h); + if (!(neighbor_info = evas_object_data_get(crtc, "output_info"))) continue; + + evas_object_geometry_get(crtc, &neighbor_geo.x, &neighbor_geo.y, &neighbor_geo.w, &neighbor_geo.h); + + if ((dialog_data->new_pos.x == Ecore_X_Randr_Unset) && (neighbor_info->new_pos.x != Ecore_X_Randr_Unset)) + { + if ((geo.x + geo.w) == neighbor_geo.x) + { + dialog_data->new_pos.x = neighbor_info->new_pos.x - dialog_data->previous_mode->width; + } + + if (geo.x == (neighbor_geo.x + neighbor_geo.w)) + { + dialog_data->new_pos.x = neighbor_info->new_pos.x + neighbor_info->previous_mode->width; + } + + if (geo.x == neighbor_geo.x) + { + dialog_data->new_pos.x = neighbor_info->new_pos.x; + } + + if ((geo.x + geo.w) == (neighbor_geo.x + neighbor_geo.w)) + { + dialog_data->new_pos.x = (neighbor_info->new_pos.x + neighbor_info->previous_mode->width) - dialog_data->previous_mode->width; + } + } + + if ((dialog_data->new_pos.y == Ecore_X_Randr_Unset) && (neighbor_info->new_pos.y != Ecore_X_Randr_Unset)) + { + if ((geo.y + geo.h) == neighbor_geo.y) + { + dialog_data->new_pos.y = neighbor_info->new_pos.y - dialog_data->previous_mode->height; + } + + if (geo.y == (neighbor_geo.y + neighbor_geo.h)) + { + dialog_data->new_pos.y = neighbor_info->new_pos.y + neighbor_info->previous_mode->height; + } + + if (geo.y == neighbor_geo.y) + { + dialog_data->new_pos.y = neighbor_info->new_pos.y; + } + + if ((geo.y + geo.h) == (neighbor_geo.y + neighbor_geo.h)) + { + dialog_data->new_pos.y = (neighbor_info->new_pos.y + neighbor_info->previous_mode->height) - dialog_data->previous_mode->height; + } + } + if ((dialog_data->new_pos.x != Ecore_X_Randr_Unset) + && (dialog_data->new_pos.y != Ecore_X_Randr_Unset)) + { + //fprintf(stderr, "CONF_RANDR: Determined position for %p: %dx%d\n", obj, dialog_data->new_pos.x, dialog_data->new_pos.y); + break; + } + } + } + if ((dialog_data->new_pos.x != Ecore_X_Randr_Unset) || (dialog_data->new_pos.y != Ecore_X_Randr_Unset)) + { + //Only wander all neighbors recursively, if they can use the current + //element as reference for their position + EINA_LIST_FOREACH(neighbors, iter, crtc) + { + neighbor_info = evas_object_data_get(crtc, "output_info"); + if ((neighbor_info->new_pos.x == Ecore_X_Randr_Unset) + || (neighbor_info->new_pos.y == Ecore_X_Randr_Unset)) + { + //fprintf(stderr, "CONF_RANDR: Now going to travel %p.\n", crtc); + _e_config_randr_dialog_subdialog_arrangement_determine_positions_recursive(crtc); + } + } + } + eina_list_free(neighbors); +} + + Eina_Bool +e_config_randr_dialog_subdialog_arrangement_basic_apply_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + Eina_List *crtcs, *iter; + Evas_Object *smart_parent, *crtc, *top_left = NULL; + Eina_Rectangle geo, smart_geo; + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + Evas_Coord_Point relz = { .x = 10000, .y = 10000}; + Eina_Bool arrangement_failed = EINA_FALSE; + + smart_parent = e_config_runtime_info->gui.subdialogs.arrangement.smart_parent; + evas_object_geometry_get(smart_parent, &smart_geo.x, &smart_geo.y, &smart_geo.w, &smart_geo.h); + crtcs = evas_object_smart_members_get(smart_parent); + + //Create virtual borders around the displayed representations by finding + //relative x and y as virtual 0x0 + EINA_LIST_FOREACH(crtcs, iter, crtc) + { + if (crtc == e_config_runtime_info->gui.subdialogs.arrangement.clipper) continue; + //Already reset values for upcoming calculation + if (!(odd = evas_object_data_get(crtc, "output_info"))) continue; + odd->new_pos.x = Ecore_X_Randr_Unset; + odd->new_pos.y = Ecore_X_Randr_Unset; + odd = NULL; + + //See whether this element is closer to 0x0 than any before + evas_object_geometry_get(crtc, &geo.x, &geo.y, &geo.w, &geo.h); + if (geo.x < relz.x) + { + relz.x = geo.x; + top_left = crtc; + } + if (geo.y < relz.y) + { + relz.y = geo.y; + top_left = crtc; + } + } + e_config_runtime_info->gui.subdialogs.arrangement.relative_zero.x = relz.x; + e_config_runtime_info->gui.subdialogs.arrangement.relative_zero.y = relz.y; + if (top_left) _e_config_randr_dialog_subdialog_arrangement_determine_positions_recursive(top_left); + + EINA_LIST_FOREACH(crtcs, iter, crtc) + { + if ((crtc == e_config_runtime_info->gui.subdialogs.arrangement.clipper) || !(odd = evas_object_data_get(crtc, "output_info")) || !odd->crtc + || ((odd->new_pos.x == Ecore_X_Randr_Unset) || (odd->new_pos.y == Ecore_X_Randr_Unset))) continue; + if ((odd->previous_pos.x != odd->new_pos.x) || (odd->previous_pos.y != odd->new_pos.y)) + { + fprintf(stderr, "CONF_RANDR: CRTC %x is moved to %dx%d\n", odd->crtc->xid, odd->new_pos.x, odd->new_pos.y); + if (!ecore_x_randr_crtc_pos_set(cfd->con->manager->root, odd->crtc->xid, odd->new_pos.x, odd->new_pos.y)) + { + arrangement_failed = EINA_TRUE; + break; + } + } + } + if (arrangement_failed) + return EINA_FALSE; + else + { + ecore_x_randr_screen_reset(cfd->con->manager->root); + return EINA_TRUE; + } +} + + Eina_Bool +e_config_randr_dialog_subdialog_arrangement_basic_check_changed(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + Eina_List *iter; + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, output_dialog_data) + { + if ((output_dialog_data->previous_pos.x != output_dialog_data->new_pos.x) + || (output_dialog_data->previous_pos.y != output_dialog_data->new_pos.y) + ) return EINA_TRUE; + } + return EINA_FALSE; +} + + void +e_config_randr_dialog_subdialog_arrangement_keep_changes(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + Eina_List *iter; + + if (!cfdata) return; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + if (!odd->crtc || ((odd->new_pos.x == Ecore_X_Randr_Unset) || (odd->new_pos.y == Ecore_X_Randr_Unset))) continue; + odd->previous_pos.x = odd->new_pos.x; + odd->previous_pos.y = odd->new_pos.y; + odd->new_pos.x = Ecore_X_Randr_Unset; + odd->new_pos.y = Ecore_X_Randr_Unset; + } +} + + void +e_config_randr_dialog_subdialog_arrangement_discard_changes(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + Eina_List *iter; + + if (!cfdata) return; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + if (!odd->crtc || ((odd->previous_pos.x == Ecore_X_Randr_Unset) || (odd->previous_pos.y == Ecore_X_Randr_Unset))) continue; + if (ecore_x_randr_crtc_pos_set(cfdata->manager->root, odd->crtc->xid, odd->previous_pos.x, odd->previous_pos.y)) + { + odd->new_pos.x = odd->previous_pos.x; + odd->new_pos.y = odd->previous_pos.y; + odd->previous_pos.x = Ecore_X_Randr_Unset; + odd->previous_pos.y = Ecore_X_Randr_Unset; + ecore_x_randr_screen_reset(cfdata->manager->root); + } + } +} diff --git a/src/modules/conf_randr/e_int_config_randr_orientation.c b/src/modules/conf_randr/e_int_config_randr_orientation.c new file mode 100644 index 000000000..3a0121baf --- /dev/null +++ b/src/modules/conf_randr/e_int_config_randr_orientation.c @@ -0,0 +1,336 @@ +#include "e_int_config_randr.h" +#include "e_randr.h" + +#ifndef Ecore_X_Randr_Unset +#define Ecore_X_Randr_Unset -1 +#endif + +#define RANDR_DIALOG_ORIENTATION_ALL (ECORE_X_RANDR_ORIENTATION_ROT_0 | ECORE_X_RANDR_ORIENTATION_ROT_90 | ECORE_X_RANDR_ORIENTATION_ROT_180 | ECORE_X_RANDR_ORIENTATION_ROT_270 | ECORE_X_RANDR_ORIENTATION_ROT_270 | ECORE_X_RANDR_ORIENTATION_FLIP_X | ECORE_X_RANDR_ORIENTATION_FLIP_Y) + +Eina_Bool e_config_randr_dialog_subdialog_orientation_create_data (E_Config_Dialog_Data *cfdata); +Evas_Object *e_config_randr_dialog_subdialog_orientation_basic_create_widgets(Evas *canvas); +Eina_Bool e_config_randr_dialog_subdialog_orientation_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +Eina_Bool e_config_randr_dialog_subdialog_orientation_basic_check_changed(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +Eina_Bool e_config_randr_dialog_subdialog_orientation_basic_apply_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +void e_config_randr_dialog_subdialog_orientation_update_radio_buttons(Evas_Object *crtc); +void e_config_randr_dialog_subdialog_orientation_update_edje(Evas_Object *crtc); + +static void _e_config_randr_dialog_subdialog_orientation_policy_mouse_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info); +extern E_Config_Dialog_Data *e_config_runtime_info; +extern char _theme_file_path[]; + +/* +static void +_e_config_randr_dialog_subdialog_orientation_radio_add_callbacks(void) +{ + evas_object_event_callback_add (e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_vertical, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_orientation_policy_mouse_up_cb, NULL); + evas_object_event_callback_add (e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_horizontal, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_orientation_policy_mouse_up_cb, NULL); + evas_object_event_callback_add (e_config_runtime_info->gui.subdialogs.orientation.radio_rot270, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_orientation_policy_mouse_up_cb, NULL); + evas_object_event_callback_add (e_config_runtime_info->gui.subdialogs.orientation.radio_rot180, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_orientation_policy_mouse_up_cb, NULL); + evas_object_event_callback_add (e_config_runtime_info->gui.subdialogs.orientation.radio_rot90, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_orientation_policy_mouse_up_cb, NULL); + evas_object_event_callback_add (e_config_runtime_info->gui.subdialogs.orientation.radio_normal, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_orientation_policy_mouse_up_cb, NULL); +} +*/ + + Eina_Bool +e_config_randr_dialog_subdialog_orientation_create_data(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + E_Randr_Crtc_Info *ci; + Eina_List *iter; + + if (!cfdata || !cfdata->output_dialog_data_list) return EINA_FALSE; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + if (!ci || !ci->current_mode) continue; + odd->new_orientation = Ecore_X_Randr_Unset; + odd->previous_orientation = ci->current_orientation; + } + + return EINA_TRUE; +} + +Evas_Object * +e_config_randr_dialog_subdialog_orientation_basic_create_widgets(Evas *canvas) +{ + Evas_Object *subdialog; + E_Radio_Group *rg; + //char signal[29]; + + if (!canvas || !e_config_runtime_info) return NULL; + if (e_config_runtime_info->gui.subdialogs.orientation.dialog) return e_config_runtime_info->gui.subdialogs.orientation.dialog; + + if (!(subdialog = e_widget_framelist_add(canvas, _("Display Orientation"), EINA_FALSE))) return NULL; + + // Add radio buttons + if(!(rg = e_widget_radio_group_new(&e_config_runtime_info->gui.subdialogs.orientation.radio_val))) goto _e_config_randr_dialog_subdialog_orientation_radio_add_fail; + + //IMPROVABLE: use enum to determine objects via 'switch'-statement + e_config_runtime_info->gui.subdialogs.orientation.radio_normal = e_widget_radio_add(canvas, _("Normal"), ECORE_X_RANDR_OUTPUT_POLICY_ABOVE, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.orientation.radio_normal); + + e_config_runtime_info->gui.subdialogs.orientation.radio_rot90 = e_widget_radio_add(canvas, _("Rotated, 90°"), ECORE_X_RANDR_OUTPUT_POLICY_RIGHT, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.orientation.radio_rot90); + + e_config_runtime_info->gui.subdialogs.orientation.radio_rot180 = e_widget_radio_add(canvas, _("Rotated, 180°"), ECORE_X_RANDR_OUTPUT_POLICY_BELOW, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.orientation.radio_rot180); + + e_config_runtime_info->gui.subdialogs.orientation.radio_rot270 = e_widget_radio_add(canvas, _("Rotated, 270°"), ECORE_X_RANDR_OUTPUT_POLICY_LEFT, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.orientation.radio_rot270); + + e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_horizontal = e_widget_radio_add(canvas, _("Flipped, horizontally"), ECORE_X_RANDR_OUTPUT_POLICY_CLONE, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_horizontal); + + e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_vertical = e_widget_radio_add(canvas, _("Flipped, vertically"), ECORE_X_RANDR_OUTPUT_POLICY_NONE, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_vertical); + + //_e_config_randr_dialog_subdialog_orientation_radio_add_callbacks(); + + /* + // Add orientation demonstration edje + if (!(e_config_runtime_info->gui.subdialogs.orientation.swallowing_edje = edje_object_add(canvas))) + goto _e_config_randr_dialog_subdialog_orientation_edje_add_fail; + if (!edje_object_file_set(e_config_runtime_info->gui.subdialogs.orientation.swallowing_edje, _theme_file_path, "e/conf/randr/dialog/subdialog/orientation")) + goto _e_config_randr_dialog_subdialog_orientation_edje_set_fail; + + e_widget_table_object_align_append(subdialog, e_config_runtime_info->gui.subdialogs.orientation.swallowing_edje, 1, 0, 1, 1, 1, 1, 1, 1, 1.0, 1.0); + */ + + //disable widgets, if no CRTC is selected + e_config_randr_dialog_subdialog_orientation_update_radio_buttons(e_config_runtime_info->gui.selected_eo); + + //evas_object_show(e_config_runtime_info->gui.subdialogs.orientation.swallowing_edje); + + return subdialog; + + /* +_e_config_randr_dialog_subdialog_orientation_edje_set_fail: + evas_object_del(ol); + evas_object_del(e_config_runtime_info->gui.subdialogs.orientation.swallowing_edje); +_e_config_randr_dialog_subdialog_orientation_edje_add_fail: + fprintf(stderr, "CONF_RANDR: Couldn't set edj for orientation subdialog!\n"); + evas_object_del(subdialog); + return NULL; + */ +_e_config_randr_dialog_subdialog_orientation_radio_add_fail: + evas_object_del(subdialog); + fprintf(stderr, "CONF_RANDR: Could not add radio group!\n"); + return NULL; +} + +/* + static void +_e_config_randr_dialog_subdialog_orientation_policy_mouse_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info) +{ + char signal[40]; + int orientation = ECORE_X_RANDR_ORIENTATION_ROT_0; + + /* + * IMPROVABLE: + * "sadly" the evas callbacks are called before radio_val is set to its new + * value. If that is ever changed, remove the used code below and just use the + * 1-liner below. + * snprintf(signal, sizeof(signal), "conf,randr,dialog,orientation,%d", e_config_runtime_info->gui.subdialogs.orientation.radio_val); + * / + if (obj == e_config_runtime_info->gui.subdialogs.orientation.radio_normal) orientation = ECORE_X_RANDR_ORIENTATION_ROT_0; + if (obj == e_config_runtime_info->gui.subdialogs.orientation.radio_rot90) orientation = ECORE_X_RANDR_ORIENTATION_ROT_90; + if (obj == e_config_runtime_info->gui.subdialogs.orientation.radio_rot180) orientation = ECORE_X_RANDR_ORIENTATION_ROT_180; + if (obj == e_config_runtime_info->gui.subdialogs.orientation.radio_rot270) orientation = ECORE_X_RANDR_ORIENTATION_ROT_270; + if (obj == e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_horizontal) orientation = ECORE_X_RANDR_ORIENTATION_FLIP_X; + if (obj == e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_vertical) orientation = ECORE_X_RANDR_ORIENTATION_FLIP_Y; + + snprintf(signal, sizeof(signal), "conf,randr,dialog,orientation,%d", orientation); + + edje_object_signal_emit(e_config_runtime_info->gui.subdialogs.orientation.swallowing_edje, signal, "e"); + + fprintf(stderr, "CONF_RANDR: mouse button released. Emitted signal to orientation: %s\n", signal); +} +*/ + + void +e_config_randr_dialog_subdialog_orientation_update_radio_buttons(Evas_Object *crtc) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + Ecore_X_Randr_Orientation supported_oris, ori; + char signal[40]; + + //disable widgets, if no crtc is selected + if (!crtc) + { + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_normal, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot90, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot180, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot270, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_horizontal, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_vertical, EINA_TRUE); + return; + } + + if (!(output_dialog_data = evas_object_data_get(crtc, "output_info"))) return; + + if (output_dialog_data->crtc) + { + //enabled monitor + supported_oris = output_dialog_data->crtc->orientations; + ori = output_dialog_data->crtc->current_orientation; + } + else + { + //disabled monitor + //assume all orientations are supported + supported_oris = RANDR_DIALOG_ORIENTATION_ALL; + ori = ECORE_X_RANDR_ORIENTATION_ROT_0; + } + + if (supported_oris & ECORE_X_RANDR_ORIENTATION_ROT_0) + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_normal, EINA_FALSE); + else + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_normal, EINA_TRUE); + + if (supported_oris & ECORE_X_RANDR_ORIENTATION_ROT_90) + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot90, EINA_FALSE); + else + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot90, EINA_TRUE); + + if (supported_oris & ECORE_X_RANDR_ORIENTATION_ROT_180) + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot180, EINA_FALSE); + else + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot180, EINA_TRUE); + + if (supported_oris & ECORE_X_RANDR_ORIENTATION_ROT_270) + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot270, EINA_FALSE); + else + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot270, EINA_TRUE); + + if (supported_oris & ECORE_X_RANDR_ORIENTATION_FLIP_X) + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_horizontal, EINA_FALSE); + else + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_horizontal, EINA_TRUE); + + if (supported_oris & ECORE_X_RANDR_ORIENTATION_FLIP_Y) + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_vertical, EINA_FALSE); + else + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_vertical, EINA_TRUE); + + //toggle the switch of the currently used orientation + switch (ori) + { + case ECORE_X_RANDR_ORIENTATION_ROT_90: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot90, EINA_TRUE); + break; + case ECORE_X_RANDR_ORIENTATION_ROT_180: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot180, EINA_TRUE); + break; + case ECORE_X_RANDR_ORIENTATION_ROT_270: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.orientation.radio_rot270, EINA_TRUE); + break; + case ECORE_X_RANDR_ORIENTATION_FLIP_X: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_horizontal, EINA_TRUE); + break; + case ECORE_X_RANDR_ORIENTATION_FLIP_Y: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.orientation.radio_reflect_vertical, EINA_TRUE); + break; + default: //== ECORE_X_RANDR_ORIENTATION_ROT_0: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.orientation.radio_normal, EINA_TRUE); + } +} + + void +e_config_randr_dialog_subdialog_orientation_update_edje(Evas_Object *crtc) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + Ecore_X_Randr_Orientation supported_oris, ori; + char signal[40]; + + if (!e_config_runtime_info->gui.selected_eo || !(output_dialog_data = evas_object_data_get(crtc, "output_info"))) return; + + if (output_dialog_data->crtc) + { + //enabled monitor + supported_oris = output_dialog_data->crtc->orientations; + ori = output_dialog_data->crtc->current_orientation; + } + else + { + //disabled monitor + //assume all orientations are supported + supported_oris = RANDR_DIALOG_ORIENTATION_ALL; + ori = ECORE_X_RANDR_ORIENTATION_ROT_0; + } + //Send signal to the edje, to represent the supported and current set orientation + snprintf(signal, sizeof(signal), "conf,randr,dialog,orientation,supported,%d", supported_oris); + edje_object_signal_emit(crtc, signal, "e"); + snprintf(signal, sizeof(signal), "conf,randr,dialog,orientation,current,%d", ori); + edje_object_signal_emit(crtc, signal, "e"); +} + + Eina_Bool +e_config_randr_dialog_subdialog_orientation_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + Ecore_X_Randr_Orientation orientation; + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + + if (!e_config_runtime_info->gui.subdialogs.orientation.dialog || !e_config_runtime_info->gui.selected_eo || !(output_dialog_data = evas_object_data_get(e_config_runtime_info->gui.selected_eo, "output_info")) || !output_dialog_data->crtc) return EINA_FALSE; + + orientation = e_config_runtime_info->gui.subdialogs.orientation.radio_val; + + fprintf(stderr, "CONF_RANDR: Change orientation of crtc %x to %d.\n", output_dialog_data->crtc->xid, orientation); + + if (ecore_x_randr_crtc_orientation_set(cfd->con->manager->root, output_dialog_data->crtc->xid, orientation)) + { + ecore_x_randr_screen_reset(cfd->con->manager->root); + output_dialog_data->previous_orientation = output_dialog_data->new_orientation; + output_dialog_data->new_orientation = orientation; + return EINA_TRUE; + } + else + return EINA_FALSE; +} + + Eina_Bool +e_config_randr_dialog_subdialog_orientation_basic_check_changed(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + Ecore_X_Randr_Orientation orientation = ECORE_X_RANDR_ORIENTATION_ROT_0; + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + + if (!e_config_runtime_info->gui.subdialogs.orientation.dialog || !e_config_runtime_info->gui.selected_eo || !(output_dialog_data = evas_object_data_get(e_config_runtime_info->gui.selected_eo, "output_info"))) return EINA_FALSE; + + return (output_dialog_data->previous_orientation != e_config_runtime_info->gui.subdialogs.orientation.radio_val); +} + + void +e_config_randr_dialog_subdialog_orientation_keep_changes(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + Eina_List *iter; + + if (!cfdata) return; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + if (!odd || (odd->previous_orientation == Ecore_X_Randr_Unset)) continue; + odd->previous_orientation = odd->new_orientation; + odd->new_orientation = Ecore_X_Randr_Unset; + } +} + + void +e_config_randr_dialog_subdialog_orientation_discard_changes(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + Eina_List *iter; + + if (!cfdata) return; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + if (!odd->crtc || (odd->previous_orientation == Ecore_X_Randr_Unset)) continue; + if (ecore_x_randr_crtc_orientation_set(cfdata->manager->root, odd->crtc->xid, odd->previous_orientation)) + { + odd->new_orientation = odd->previous_orientation; + odd->previous_orientation = Ecore_X_Randr_Unset; + ecore_x_randr_screen_reset(cfdata->manager->root); + } + } +} diff --git a/src/modules/conf_randr/e_int_config_randr_policies.c b/src/modules/conf_randr/e_int_config_randr_policies.c new file mode 100644 index 000000000..77b204c5e --- /dev/null +++ b/src/modules/conf_randr/e_int_config_randr_policies.c @@ -0,0 +1,302 @@ +#include "e_int_config_randr.h" +#include "e_randr.h" + +#ifndef ECORE_X_RANDR_1_2 +#define ECORE_X_RANDR_1_2 ((1 << 16) | 2) +#endif +#ifndef ECORE_X_RANDR_1_3 +#define ECORE_X_RANDR_1_3 ((1 << 16) | 3) +#endif + +#ifndef Ecore_X_Randr_Unset +#define Ecore_X_Randr_Unset -1 +#endif + +Evas_Object *e_config_randr_dialog_subdialog_policies_basic_create_widgets(Evas *canvas); +Eina_Bool e_config_randr_dialog_subdialog_policies_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +Eina_Bool e_config_randr_dialog_subdialog_policies_basic_check_changed(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +Eina_Bool e_config_randr_dialog_subdialog_policies_basic_apply_data(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +void e_config_randr_dialog_subdialog_policies_update_radio_buttons(Evas_Object *crtc); + +static void _e_config_randr_dialog_subdialog_policies_policy_mouse_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info); +extern E_Config_Dialog_Data *e_config_runtime_info; +extern char _theme_file_path[]; + +/* +static void +_e_config_randr_dialog_subdialog_policies_radio_add_callbacks(void) +{ + evas_object_event_callback_add(e_config_runtime_info->gui.subdialogs.policies.radio_none, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_policies_policy_mouse_up_cb, NULL); + evas_object_event_callback_add(e_config_runtime_info->gui.subdialogs.policies.radio_clone, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_policies_policy_mouse_up_cb, NULL); + evas_object_event_callback_add(e_config_runtime_info->gui.subdialogs.policies.radio_left, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_policies_policy_mouse_up_cb, NULL); + evas_object_event_callback_add(e_config_runtime_info->gui.subdialogs.policies.radio_below, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_policies_policy_mouse_up_cb, NULL); + evas_object_event_callback_add(e_config_runtime_info->gui.subdialogs.policies.radio_above, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_policies_policy_mouse_up_cb, NULL); + evas_object_event_callback_add(e_config_runtime_info->gui.subdialogs.policies.radio_right, EVAS_CALLBACK_MOUSE_UP, _e_config_randr_dialog_subdialog_policies_policy_mouse_up_cb, NULL); +} +*/ + + Eina_Bool +e_config_randr_dialog_subdialog_policies_create_data(E_Config_Dialog_Data *e_config_runtime_info) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + E_Randr_Output_Info *oi; + Eina_List *iter; + + if (!e_config_runtime_info || !e_config_runtime_info->output_dialog_data_list) return EINA_FALSE; + + EINA_LIST_FOREACH(e_config_runtime_info->output_dialog_data_list, iter, odd) + { + if (odd->crtc) + oi = eina_list_data_get(eina_list_last(odd->crtc->outputs)); + else if (odd->output) + oi = odd->output; + if (!oi) continue; + odd->previous_policy = oi->policy; + odd->new_policy = Ecore_X_Randr_Unset; + } + + return EINA_TRUE; +} + +Evas_Object * +e_config_randr_dialog_subdialog_policies_basic_create_widgets(Evas *canvas) +{ + Evas_Object *subdialog; + E_Radio_Group *rg; + //char signal[29]; + + if (!canvas || !e_config_runtime_info) return NULL; + + if (e_config_runtime_info->gui.subdialogs.policies.dialog) return e_config_runtime_info->gui.subdialogs.policies.dialog; + + if(!(subdialog = e_widget_framelist_add(canvas, _("Screen attachement policy"), EINA_FALSE))) return NULL; + + // Add radio buttons + if (!(rg = e_widget_radio_group_new(&e_config_runtime_info->gui.subdialogs.policies.radio_val))) goto _e_config_randr_dialog_subdialog_policies_radio_add_fail; + + //IMPROVABLE: use enum to determine objects via 'switch'-statement + e_config_runtime_info->gui.subdialogs.policies.radio_above = e_widget_radio_add(canvas, _("Above"), ECORE_X_RANDR_OUTPUT_POLICY_ABOVE, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.policies.radio_above); + + e_config_runtime_info->gui.subdialogs.policies.radio_right = e_widget_radio_add(canvas, _("Right"), ECORE_X_RANDR_OUTPUT_POLICY_RIGHT, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.policies.radio_right); + + e_config_runtime_info->gui.subdialogs.policies.radio_below = e_widget_radio_add(canvas, _("Below"), ECORE_X_RANDR_OUTPUT_POLICY_BELOW, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.policies.radio_below); + + e_config_runtime_info->gui.subdialogs.policies.radio_left = e_widget_radio_add(canvas, _("Left"), ECORE_X_RANDR_OUTPUT_POLICY_LEFT, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.policies.radio_left); + + e_config_runtime_info->gui.subdialogs.policies.radio_clone = e_widget_radio_add(canvas, _("Clone display content"), ECORE_X_RANDR_OUTPUT_POLICY_CLONE, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.policies.radio_clone); + + e_config_runtime_info->gui.subdialogs.policies.radio_none = e_widget_radio_add(canvas, _("No reaction"), ECORE_X_RANDR_OUTPUT_POLICY_NONE, rg); + e_widget_framelist_object_append(subdialog, e_config_runtime_info->gui.subdialogs.policies.radio_none); + + //_e_config_randr_dialog_subdialog_policies_radio_add_callbacks(); + + /* + // Add policies demonstration edje + if (!(e_config_runtime_info->gui.subdialogs.policies.swallowing_edje = edje_object_add(canvas))) + { + goto _e_config_randr_dialog_subdialog_policies_edje_add_fail; + + } + if (!edje_object_file_set(e_config_runtime_info->gui.subdialogs.policies.swallowing_edje, _theme_file_path, "e/conf/randr/dialog/subdialog/policies")) + { + goto _e_config_randr_dialog_subdialog_policies_edje_set_fail; + } + + e_widget_table_object_align_append(subdialog, e_config_runtime_info->gui.subdialogs.policies.swallowing_edje, 1, 0, 1, 1, 1, 1, 1, 1, 1.0, 1.0); + */ + + /* + evas_object_show(e_config_runtime_info->gui.subdialogs.policies.swallowing_edje); + + //emit signal to edje so a demonstration can be shown + snprintf(signal, sizeof(signal), "conf,randr,dialog,policies,%d", e_randr_screen_info->rrvd_info.randr_info_12->output_policy); + edje_object_signal_emit(e_config_runtime_info->gui.subdialogs.policies.swallowing_edje, signal, "e"); + fprintf(stderr, "CONF_RANDR: Initial signal emitted to policy dialog: %s\n", signal); + + //Use theme's background as screen representation + e_config_runtime_info->gui.subdialogs.policies.new_display = edje_object_add(canvas); + e_theme_edje_object_set(e_config_runtime_info->gui.subdialogs.policies.new_display, "base/theme/widgets", "e/widgets/frame"); + e_config_runtime_info->gui.subdialogs.policies.new_display_background = edje_object_add(canvas); + e_theme_edje_object_set(e_config_runtime_info->gui.subdialogs.policies.new_display_background, "base/theme/background", "e/desktop/background"); + edje_object_part_swallow(e_config_runtime_info->gui.subdialogs.policies.new_display, "e.swallow.content", e_config_runtime_info->gui.subdialogs.policies.new_display_background); + edje_object_part_text_set(e_config_runtime_info->gui.subdialogs.policies.new_display, "e.text.label", _("New display")); + edje_object_part_swallow(e_config_runtime_info->gui.subdialogs.policies.swallowing_edje, "new_display.swallow.content", e_config_runtime_info->gui.subdialogs.policies.new_display); + //add theme's frame + //for now use the theme's background for the new display as well + e_config_runtime_info->gui.subdialogs.policies.current_displays_setup = edje_object_add(canvas); + e_theme_edje_object_set(e_config_runtime_info->gui.subdialogs.policies.current_displays_setup, "base/theme/widgets", "e/widgets/frame"); + e_config_runtime_info->gui.subdialogs.policies.current_displays_setup_background = edje_object_add(canvas); + e_theme_edje_object_set(e_config_runtime_info->gui.subdialogs.policies.current_displays_setup_background, "base/theme/background", "e/desktop/background"); + edje_object_part_swallow(e_config_runtime_info->gui.subdialogs.policies.current_displays_setup, "e.swallow.content", e_config_runtime_info->gui.subdialogs.policies.current_displays_setup_background); + edje_object_part_text_set(e_config_runtime_info->gui.subdialogs.policies.current_displays_setup, "e.text.label", _("Used display")); + edje_object_part_swallow(e_config_runtime_info->gui.subdialogs.policies.swallowing_edje, "current_displays_setup.swallow.content", e_config_runtime_info->gui.subdialogs.policies.current_displays_setup); + */ + + evas_object_show(subdialog); + + return subdialog; + + /* +_e_config_randr_dialog_subdialog_policies_edje_set_fail: + evas_object_del(e_config_runtime_info->gui.subdialogs.policies.swallowing_edje); +_e_config_randr_dialog_subdialog_policies_edje_add_fail: + fprintf(stderr, "CONF_RANDR: Couldn't set edj for policies subdialog!\n"); + evas_object_del(subdialog); + return NULL; + */ +_e_config_randr_dialog_subdialog_policies_radio_add_fail: + evas_object_del(subdialog); + return NULL; +} + + static void +_e_config_randr_dialog_subdialog_policies_policy_mouse_up_cb(void *data, Evas *e, Evas_Object *obj, void *event_info) +{ + char signal[29]; + int policy = ECORE_X_RANDR_OUTPUT_POLICY_NONE; + + /* + * IMPROVABLE: + * "sadly" the evas callbacks are called before radio_val is set to its new + * value. If that is ever changed, remove the used code below and just use the + * 1-liner below. + * snprintf(signal, sizeof(signal), "conf,randr,dialog,policies,%d", e_config_runtime_info->gui.subdialogs.policies.radio_val); + */ + if (obj == e_config_runtime_info->gui.subdialogs.policies.radio_above) policy = ECORE_X_RANDR_OUTPUT_POLICY_ABOVE; + if (obj == e_config_runtime_info->gui.subdialogs.policies.radio_right) policy = ECORE_X_RANDR_OUTPUT_POLICY_RIGHT; + if (obj == e_config_runtime_info->gui.subdialogs.policies.radio_below) policy = ECORE_X_RANDR_OUTPUT_POLICY_BELOW; + if (obj == e_config_runtime_info->gui.subdialogs.policies.radio_left) policy = ECORE_X_RANDR_OUTPUT_POLICY_LEFT; + if (obj == e_config_runtime_info->gui.subdialogs.policies.radio_clone) policy = ECORE_X_RANDR_OUTPUT_POLICY_CLONE; + if (obj == e_config_runtime_info->gui.subdialogs.policies.radio_none) policy = ECORE_X_RANDR_OUTPUT_POLICY_NONE; + + snprintf(signal, sizeof(signal), "conf,randr,dialog,policies,%d", policy); + + //edje_object_signal_emit(e_config_runtime_info->gui.subdialogs.policies.swallowing_edje, signal, "e"); + + fprintf(stderr, "CONF_RANDR: mouse button released. Emitted signal to policy: %s\n", signal); +} + + Eina_Bool +e_config_randr_dialog_subdialog_policies_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + E_Randr_Output_Info *output_info; + + if (!e_randr_screen_info || !e_config_runtime_info->gui.selected_output_dd) return EINA_FALSE; + + //policy update + e_config_runtime_info->gui.selected_output_dd->previous_policy = e_config_runtime_info->gui.selected_output_dd->new_policy; + e_config_runtime_info->gui.selected_output_dd->new_policy = e_config_runtime_info->gui.subdialogs.policies.radio_val; + fprintf(stderr, "CONF_RANDR: 'New display attached'-policy set to %d\n", e_config_runtime_info->gui.selected_output_dd->new_policy); + + return EINA_TRUE; +} + + Eina_Bool +e_config_randr_dialog_subdialog_policies_basic_check_changed(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + if (!e_randr_screen_info || !cfdata || !cfdata->gui.selected_output_dd) return EINA_FALSE; + + return (cfdata->gui.selected_output_dd->previous_policy != cfdata->gui.subdialogs.policies.radio_val); +} + + void +e_config_randr_dialog_subdialog_policies_update_radio_buttons(Evas_Object *crtc) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + E_Randr_Output_Info *output; + Ecore_X_Randr_Output_Policy policy; + char signal[40]; + + //disable widgets, if no crtc is selected + if (!crtc || !(output_dialog_data = evas_object_data_get(crtc, "output_info"))) + { + //Evas_Object *radio_above, *radio_right, *radio_below, *radio_left, *radio_clone, *radio_none; + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_above, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_right, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_below, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_left, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_clone, EINA_TRUE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_none, EINA_TRUE); + return; + } + else + { + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_above, EINA_FALSE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_right, EINA_FALSE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_below, EINA_FALSE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_left, EINA_FALSE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_clone, EINA_FALSE); + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.policies.radio_none, EINA_FALSE); + } + + if (output_dialog_data->crtc && output_dialog_data->crtc->outputs) + { + output = (E_Randr_Output_Info*)eina_list_data_get(eina_list_last(output_dialog_data->crtc->outputs)); + } + else if (output_dialog_data->output) + { + output = output_dialog_data->output; + } + + if (!output) return; + policy = output->policy; + e_config_runtime_info->gui.selected_output_dd = output_dialog_data; + //toggle the switch of the currently used policies + switch (policy) + { + case ECORE_X_RANDR_OUTPUT_POLICY_RIGHT: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.policies.radio_right, EINA_TRUE); + break; + case ECORE_X_RANDR_OUTPUT_POLICY_BELOW: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.policies.radio_below, EINA_TRUE); + break; + case ECORE_X_RANDR_OUTPUT_POLICY_LEFT: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.policies.radio_left, EINA_TRUE); + break; + case ECORE_X_RANDR_OUTPUT_POLICY_CLONE: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.policies.radio_clone, EINA_TRUE); + break; + case ECORE_X_RANDR_OUTPUT_POLICY_NONE: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.policies.radio_none, EINA_TRUE); + break; + default: //== ECORE_X_RANDR_OUTPUT_POLICY_ABOVE: + e_widget_radio_toggle_set(e_config_runtime_info->gui.subdialogs.policies.radio_above, EINA_TRUE); + } +} + + void +e_config_randr_dialog_subdialog_policies_keep_changes(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + Eina_List *iter; + + if (!cfdata) return; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + if (!odd || (odd->previous_policy == Ecore_X_Randr_Unset)) continue; + odd->previous_policy = odd->new_policy; + odd->new_policy = Ecore_X_Randr_Unset; + } +} + + void +e_config_randr_dialog_subdialog_policies_discard_changes(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + Eina_List *iter; + + if (!cfdata) return; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + if (!odd->crtc || (odd->previous_policy == Ecore_X_Randr_Unset)) continue; + odd->new_policy = odd->previous_policy; + odd->previous_policy = Ecore_X_Randr_Unset; + } +} diff --git a/src/modules/conf_randr/e_int_config_randr_resolutions.c b/src/modules/conf_randr/e_int_config_randr_resolutions.c new file mode 100644 index 000000000..ea3bb9362 --- /dev/null +++ b/src/modules/conf_randr/e_int_config_randr_resolutions.c @@ -0,0 +1,265 @@ +#include "e_int_config_randr.h" +#include "e_randr.h" +#include "e_widget_ilist.h" + +#ifdef Ecore_X_Randr_Unset +#undef Ecore_X_Randr_Unset +#define Ecore_X_Randr_Unset -1 +#else +#define Ecore_X_Randr_Unset -1 +#endif + +#ifdef Ecore_X_Randr_None +#undef Ecore_X_Randr_None +#endif +#define Ecore_X_Randr_None 0 + +#define ICON_WIDTH 10 +#define ICON_HEIGHT 10 +#define RESOLUTION_TXT_MAX_LENGTH 50 + +Evas_Object *e_config_randr_dialog_subdialog_resolutions_basic_create_widgets(Evas *canvas); +Eina_Bool e_config_randr_dialog_subdialog_resolutions_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +Eina_Bool e_config_randr_dialog_subdialog_resolutions_basic_check_changed(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata); +void e_config_randr_dialog_subdialog_resolutions_update_list(Evas_Object *crtc); +void e_config_randr_dialog_subdialog_resolutions_keep_changes(E_Config_Dialog_Data *cfdata); +void e_config_randr_dialog_subdialog_resolutions_discard_changes(E_Config_Dialog_Data *cfdata); + +extern E_Config_Dialog_Data *e_config_runtime_info; + + Eina_Bool +e_config_randr_dialog_subdialog_resolutions_create_data(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + E_Randr_Crtc_Info *ci; + Ecore_X_Randr_Mode_Info *mi; + Eina_List *iter; + + if (!cfdata || !cfdata->output_dialog_data_list) return EINA_FALSE; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + if (odd->previous_mode || odd->preferred_mode) + { + //this means, that mode info is already filled + //(by the display arrangement code) + break; + } + if (odd->crtc) + { + if(!(mi = odd->crtc->current_mode)) + mi = (Ecore_X_Randr_Mode_Info*)eina_list_data_get(eina_list_last(odd->crtc->outputs_common_modes)); + odd->previous_mode = mi; + } + else if (odd->output) + { + odd->preferred_mode = (Ecore_X_Randr_Mode_Info*)eina_list_data_get(eina_list_last(odd->output->preferred_modes)); + } + } + + return EINA_TRUE; +} + + Evas_Object * +e_config_randr_dialog_subdialog_resolutions_basic_create_widgets(Evas *canvas) +{ + Evas_Object *subdialog; + + if (!canvas || !e_config_runtime_info || e_config_runtime_info->gui.subdialogs.resolutions.dialog || !(subdialog = e_widget_ilist_add(canvas, ICON_WIDTH * e_scale, ICON_HEIGHT * e_scale, NULL))) return NULL; + + e_widget_ilist_multi_select_set(subdialog, EINA_FALSE); + e_widget_disabled_set(subdialog, EINA_TRUE); + + evas_object_show(subdialog); + + return subdialog; +} + + Eina_Bool +e_config_randr_dialog_subdialog_resolutions_basic_apply_data (E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + //Apply new mode + Ecore_X_Randr_Mode_Info* selected_mode; + Ecore_X_ID selected_mode_xid; + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + Ecore_X_Randr_Output *output = NULL; + E_Randr_Crtc_Info *crtc_info = NULL, *crtc_iter; + Eina_List *iter; + int noutputs = Ecore_X_Randr_Unset; + + if (!e_config_runtime_info->gui.selected_eo || !(output_dialog_data = evas_object_data_get(e_config_runtime_info->gui.selected_eo, "output_info"))) + { + fprintf(stderr, "CONF_RADNR: no crtc was selected or no output info could be retrieved for the selected crtc element (%p).\n", e_config_runtime_info->gui.selected_eo); + return EINA_FALSE; + } + + if (output_dialog_data->crtc) + { + //CRTC is already asssigned, easy one! + crtc_info = output_dialog_data->crtc; + } + else if (output_dialog_data->output) + { + //CRTC not assigned yet. Let's try to find a non occupied one. + fprintf(stderr, "CONF_RANDR: Trying to find a CRTC for output %x, %d crtcs are possible.\n", output_dialog_data->output->xid, eina_list_count(output_dialog_data->output->possible_crtcs)); + output = &output_dialog_data->output->xid; + noutputs = 1; + EINA_LIST_FOREACH(output_dialog_data->output->possible_crtcs, iter, crtc_iter) + { + if (!crtc_iter->outputs) + { + //CRTC is not occupied yet + crtc_info = crtc_iter; + break; + } + } + } + if (!crtc_info) + { + fprintf(stderr, "CONF_RANDR: Changing mode failed, no unoccupied CRTC found!\n"); + return EINA_FALSE; + } + //get selected mode + if ((selected_mode = (Ecore_X_Randr_Mode_Info*)e_widget_ilist_selected_data_get(e_config_runtime_info->gui.subdialogs.resolutions.dialog))) + { + selected_mode_xid = selected_mode->xid; + } + else + { + selected_mode_xid = Ecore_X_Randr_None; + } + + fprintf(stderr, "CONF_RANDR: Change mode of crtc %x to %x.\n", crtc_info->xid, selected_mode_xid); + + if (ecore_x_randr_crtc_mode_set(cfd->con->manager->root, crtc_info->xid, output, noutputs, selected_mode_xid)) + { + //remove unused space + ecore_x_randr_screen_reset(cfd->con->manager->root); + //update information + if (!output_dialog_data->crtc) + output_dialog_data->crtc = crtc_info; + output_dialog_data->new_mode = selected_mode; + return EINA_TRUE; + } + + return EINA_FALSE; +} + + Eina_Bool +e_config_randr_dialog_subdialog_resolutions_basic_check_changed(E_Config_Dialog *cfd, E_Config_Dialog_Data *cfdata) +{ + Ecore_X_Randr_Mode_Info* selected_mode; + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + + if (!e_config_runtime_info->gui.selected_eo || !(selected_mode = (Ecore_X_Randr_Mode_Info*)e_widget_ilist_selected_data_get(e_config_runtime_info->gui.subdialogs.resolutions.dialog)) || !(output_dialog_data = evas_object_data_get(e_config_runtime_info->gui.selected_eo, "output_info"))) return EINA_FALSE; + + return (selected_mode != output_dialog_data->previous_mode); +} + + void +e_config_randr_dialog_subdialog_resolutions_update_list(Evas_Object *crtc) +{ + Eina_List *iter, *modelist = NULL; + E_Config_Randr_Dialog_Output_Dialog_Data *output_dialog_data; + Ecore_X_Randr_Mode_Info *mode_info, *current_mode; + char resolution_text[RESOLUTION_TXT_MAX_LENGTH]; + float rate; + int str_ret, i = 0; + + e_widget_ilist_freeze(e_config_runtime_info->gui.subdialogs.resolutions.dialog); + e_widget_ilist_clear(e_config_runtime_info->gui.subdialogs.resolutions.dialog); + if (!crtc) + { + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.resolutions.dialog, EINA_TRUE); + return; + } + if (!(output_dialog_data = evas_object_data_get(crtc, "output_info"))) + return; + + //select correct mode list + if (output_dialog_data->crtc) + { + current_mode = output_dialog_data->crtc->current_mode; + modelist = output_dialog_data->crtc->outputs_common_modes; + } + else if (output_dialog_data->output) + { + current_mode = NULL; + if (output_dialog_data->output->modes) + modelist = output_dialog_data->output->modes; + else + modelist = output_dialog_data->output->modes; + } + EINA_LIST_FOREACH(modelist, iter, mode_info) + { + //calculate refresh rate + if (!mode_info) continue; + if (mode_info->hTotal && mode_info->vTotal) + rate = ((float) mode_info->dotClock / + ((float) mode_info->hTotal * (float) mode_info->vTotal)); + else + rate = 0.0; + + str_ret = snprintf(resolution_text, RESOLUTION_TXT_MAX_LENGTH, "%dx%d@%.1fHz", mode_info->width, mode_info->height, rate); + if (str_ret < 0 || str_ret > (RESOLUTION_TXT_MAX_LENGTH - 1)) + { + fprintf(stderr, "CONF_RANDR: Resolution text could not be created."); + continue; + } + e_widget_ilist_append(e_config_runtime_info->gui.subdialogs.resolutions.dialog, NULL, resolution_text, NULL, mode_info, NULL); + + //select currently enabled mode + if (mode_info == current_mode) + e_widget_ilist_selected_set(e_config_runtime_info->gui.subdialogs.resolutions.dialog, i); + i++; + } + + //append 'disabled' mode + e_widget_ilist_append(e_config_runtime_info->gui.subdialogs.resolutions.dialog, NULL, _("Disabled"), NULL, NULL, NULL); + + //reenable widget + e_widget_disabled_set(e_config_runtime_info->gui.subdialogs.resolutions.dialog, EINA_FALSE); + e_widget_ilist_go(e_config_runtime_info->gui.subdialogs.resolutions.dialog); + e_widget_ilist_thaw(e_config_runtime_info->gui.subdialogs.resolutions.dialog); +} + + +void +e_config_randr_dialog_subdialog_resolutions_keep_changes(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + Eina_List *iter; + + if (!cfdata) return; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + if (odd && odd->new_mode && (odd->new_mode != odd->previous_mode)) + { + odd->previous_mode = odd->new_mode; + odd->new_mode = NULL; + } + } +} + + void +e_config_randr_dialog_subdialog_resolutions_discard_changes(E_Config_Dialog_Data *cfdata) +{ + E_Config_Randr_Dialog_Output_Dialog_Data *odd; + Eina_List *iter; + + if (!cfdata) return; + + EINA_LIST_FOREACH(cfdata->output_dialog_data_list, iter, odd) + { + //for now, there is no way to redisable an output during discartion + if (!odd->crtc || !odd->previous_mode) continue; + //use currently used outputs (noutputs == Ecore_X_Randr_Unset) + if (ecore_x_randr_crtc_mode_set(cfdata->manager->root, odd->crtc->xid, NULL, Ecore_X_Randr_Unset, odd->previous_mode->xid)) + { + odd->new_mode = odd->previous_mode; + odd->previous_mode = NULL; + ecore_x_randr_screen_reset(cfdata->manager->root); + } + } +} diff --git a/src/modules/conf_randr/e_mod_main.c b/src/modules/conf_randr/e_mod_main.c new file mode 100644 index 000000000..6358594d7 --- /dev/null +++ b/src/modules/conf_randr/e_mod_main.c @@ -0,0 +1,42 @@ +/* + * vim:ts=8:sw=3:sts=8:expandtab:cino=>5n-3f0^-2{2 + */ +#include "e.h" +#include "e_mod_main.h" + +/* actual module specifics */ +E_Module *conf_randr_module = NULL; + +/* module setup */ +EAPI E_Module_Api e_modapi = +{ + E_MODULE_API_VERSION, + "Settings - Screen Setup" +}; + +EAPI void * +e_modapi_init(E_Module *m) +{ + e_configure_registry_category_add("screen", 30, _("Screen"), NULL, "preferences-desktop-display"); + e_configure_registry_item_add("screen/randr", 20, _("Screen Setup"), NULL, "preferences-system-screen-resolution", e_int_config_randr); + conf_randr_module = m; + e_module_delayed_set(m, 1); + return m; +} + +EAPI int +e_modapi_shutdown(E_Module *m) +{ + E_Config_Dialog *cfd; + while ((cfd = e_config_dialog_get("E", "screen/randr"))) e_object_del(E_OBJECT(cfd)); + e_configure_registry_item_del("screen/randr"); + e_configure_registry_category_del("screen"); + conf_randr_module = NULL; + return 1; +} + +EAPI int +e_modapi_save(E_Module *m) +{ + return 1; +} diff --git a/src/modules/conf_randr/e_mod_main.h b/src/modules/conf_randr/e_mod_main.h new file mode 100644 index 000000000..8735ba71d --- /dev/null +++ b/src/modules/conf_randr/e_mod_main.h @@ -0,0 +1,19 @@ +/* + * vim:ts=8:sw=3:sts=8:expandtab:cino=>5n-3f0^-2{2 + */ +#ifndef E_MOD_MAIN_H +#define E_MOD_MAIN_H + +#define E_TYPEDEFS 1 +#include "e_int_config_randr.h" + +#undef E_TYPEDEFS +#include "e_int_config_randr.h" + +EAPI extern E_Module_Api e_modapi; + +EAPI void *e_modapi_init (E_Module *m); +EAPI int e_modapi_shutdown (E_Module *m); +EAPI int e_modapi_save (E_Module *m); + +#endif diff --git a/src/modules/conf_randr/images/display-glass-shine.png b/src/modules/conf_randr/images/display-glass-shine.png new file mode 100644 index 0000000000000000000000000000000000000000..0f299a631bc3617de542bb13bc2e1cdf6a1d5e3e GIT binary patch literal 3719 zcmeAS@N?(olHy`uVBq!ia0y~yV159^OF7tpB5ybKE&@`F#X;^)4C~IxyaaMM3p^r= z85p>QL70(Y)*K0-AbW|YuPgg)E@p9KRWH4-jSLKYlAbP(Ar-gY9%N($DrlJSZ~Z$C zkS(KNGz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLttctKm%J#E(4=R(y1hne?49O KT-G@yGywqI!yxwn literal 0 HcmV?d00001 diff --git a/src/modules/conf_randr/images/display.png b/src/modules/conf_randr/images/display.png new file mode 100644 index 0000000000000000000000000000000000000000..589385f5cff71edbd87391ea73c78dea999c7c32 GIT binary patch literal 105928 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

wn%%x)eLZ^U>1jlF30+7bAqQ=NDQI_? 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/conf_randr/module.desktop.in b/src/modules/conf_randr/module.desktop.in new file mode 100644 index 000000000..e0b1c0773 --- /dev/null +++ b/src/modules/conf_randr/module.desktop.in @@ -0,0 +1,32 @@ +[Desktop Entry] +Type=Link +Name=Settings - Screen Setup +Name[cs]=Nastavení - rozlišení obrazovky +Name[de]=Konfiguration - Bildschirm +Name[eo]=Agordo - Ekrandistingivo +Name[es]=Configuración - Resolución de pantalla +Name[fr]=Configuration - Résolution de l'écran +Name[hu]=Beállítások - Képernyő felbontása +Name[it]=Configurazione - Risoluzione schermo +Name[ja]= +Name[pt]= +Name[pt_BR]= +Name[tr]=Ayarlar - Ekran Çözünürlüğü +Name[zh_CN]= +Name[zh_TW]= +Icon=e-module-conf_randr +Comment=Used to configure your screen's resolution. +Comment[cs]=Použit k nastavení rozlišení obrazovky. +Comment[de]= +Comment[eo]=Agordi sian ekrandistingivon. +Comment[es]=Usado para configurar su resolución de pantalla. +Comment[fr]=Permet de configurer la résolution de l'écran. +Comment[hu]=Segítségével beállíthatod a képernyőd felbontását. +Comment[it]=Usato per configurare la risoluzione del vostro schermo. +Comment[ja]= +Comment[pt]= +Comment[pt_BR]= +Comment[tr]=Ekranınızın çözünürlüğünü yapılandırır. +Comment[zh_CN]= +Comment[zh_TW]= +X-Enlightenment-ModuleType=settings