#include "e.h" /* local function prototypes */ static Eina_Bool _e_randr_config_load(void); static void _e_randr_config_new(void); static void _e_randr_config_free(void); static Eina_Bool _e_randr_config_cb_timer(void *data); static void _e_randr_config_restore(void); static Eina_Bool _e_randr_config_crtc_update(E_Randr_Crtc_Config *cfg); static Eina_Bool _e_randr_config_output_update(E_Randr_Output_Config *cfg); static E_Randr_Crtc_Config *_e_randr_config_output_crtc_find(E_Randr_Output_Config *cfg); static Ecore_X_Randr_Mode _e_randr_config_output_preferred_mode_get(unsigned int xid); static E_Randr_Output_Config *_e_randr_config_output_new(unsigned int id); static E_Randr_Crtc_Config *_e_randr_config_crtc_find(Ecore_X_Randr_Crtc crtc); static E_Randr_Output_Config *_e_randr_config_output_find(Ecore_X_Randr_Output output); static void _e_randr_config_screen_size_calculate(int *sw, int *sh); static void _e_randr_config_mode_geometry(Ecore_X_Randr_Mode mode, Ecore_X_Randr_Orientation orient, Eina_Rectangle *rect); static void _e_randr_config_primary_update(void); static Eina_Bool _e_randr_event_cb_screen_change(void *data EINA_UNUSED, int type EINA_UNUSED, void *event); static Eina_Bool _e_randr_event_cb_crtc_change(void *data EINA_UNUSED, int type EINA_UNUSED, void *event); static Eina_Bool _e_randr_event_cb_output_change(void *data EINA_UNUSED, int type EINA_UNUSED, void *event); /* local variables */ static Eina_List *_randr_event_handlers = NULL; static E_Config_DD *_e_randr_edd = NULL; static E_Config_DD *_e_randr_crtc_edd = NULL; static E_Config_DD *_e_randr_output_edd = NULL; /* external variables */ EAPI E_Randr_Config *e_randr_cfg = NULL; /* private internal functions */ EINTERN Eina_Bool e_randr_init(void) { /* check if randr is available */ if (!ecore_x_randr_query()) return EINA_FALSE; /* try to load config */ if (!_e_randr_config_load()) { /* NB: We should probably print an error here */ return EINA_FALSE; } /* tell randr that we are interested in receiving events * * NB: Requires RandR >= 1.2 */ if (ecore_x_randr_version_get() >= E_RANDR_VERSION_1_2) { Ecore_X_Window root = 0; if ((root = ecore_x_window_root_first_get())) ecore_x_randr_events_select(root, EINA_TRUE); /* setup randr event listeners */ E_LIST_HANDLER_APPEND(_randr_event_handlers, ECORE_X_EVENT_SCREEN_CHANGE, _e_randr_event_cb_screen_change, NULL); E_LIST_HANDLER_APPEND(_randr_event_handlers, ECORE_X_EVENT_RANDR_CRTC_CHANGE, _e_randr_event_cb_crtc_change, NULL); E_LIST_HANDLER_APPEND(_randr_event_handlers, ECORE_X_EVENT_RANDR_OUTPUT_CHANGE, _e_randr_event_cb_output_change, NULL); } return EINA_TRUE; } EINTERN int e_randr_shutdown(void) { /* check if randr is available */ if (!ecore_x_randr_query()) return 1; if (ecore_x_randr_version_get() >= E_RANDR_VERSION_1_2) { Ecore_X_Window root = 0; /* remove randr event listeners */ E_FREE_LIST(_randr_event_handlers, ecore_event_handler_del); /* tell randr that we are not interested in receiving events anymore */ if ((root = ecore_x_window_root_first_get())) ecore_x_randr_events_select(root, EINA_FALSE); } E_CONFIG_DD_FREE(_e_randr_output_edd); E_CONFIG_DD_FREE(_e_randr_crtc_edd); E_CONFIG_DD_FREE(_e_randr_edd); return 1; } /* public API functions */ EAPI Eina_Bool e_randr_config_save(void) { /* save the new config */ return e_config_domain_save("e_randr", _e_randr_edd, e_randr_cfg); } /* local functions */ static Eina_Bool _e_randr_config_load(void) { E_Randr_Output_Config eroc; Eina_Bool do_restore = EINA_TRUE; /* define edd for output config */ _e_randr_output_edd = E_CONFIG_DD_NEW("E_Randr_Output_Config", E_Randr_Output_Config); #undef T #undef D #define T E_Randr_Output_Config #define D _e_randr_output_edd E_CONFIG_VAL(D, T, xid, UINT); E_CONFIG_VAL(D, T, crtc, UINT); E_CONFIG_VAL(D, T, policy, UINT); E_CONFIG_VAL(D, T, primary, UCHAR); eet_data_descriptor_element_add(D, "edid", EET_T_UCHAR, EET_G_VAR_ARRAY, (char *)(&(eroc.edid)) - (char *)(&(eroc)), (char *)(&(eroc.edid_count)) - (char *)(&(eroc)), NULL, NULL); eet_data_descriptor_element_add(D, "clones", EET_T_UINT, EET_G_VAR_ARRAY, (char *)(&(eroc.clones)) - (char *)(&(eroc)), (char *)(&(eroc.clone_count)) - (char *)(&(eroc)), NULL, NULL); E_CONFIG_VAL(D, T, connected, UCHAR); E_CONFIG_VAL(D, T, exists, UCHAR); /* define edd for crtc config */ _e_randr_crtc_edd = E_CONFIG_DD_NEW("E_Randr_Crtc_Config", E_Randr_Crtc_Config); #undef T #undef D #define T E_Randr_Crtc_Config #define D _e_randr_crtc_edd E_CONFIG_VAL(D, T, xid, UINT); E_CONFIG_VAL(D, T, x, INT); E_CONFIG_VAL(D, T, y, INT); E_CONFIG_VAL(D, T, width, INT); E_CONFIG_VAL(D, T, height, INT); E_CONFIG_VAL(D, T, orient, UINT); E_CONFIG_VAL(D, T, mode, UINT); E_CONFIG_VAL(D, T, exists, UCHAR); E_CONFIG_LIST(D, T, outputs, _e_randr_output_edd); /* define edd for randr config */ _e_randr_edd = E_CONFIG_DD_NEW("E_Randr_Config", E_Randr_Config); #undef T #undef D #define T E_Randr_Config #define D _e_randr_edd E_CONFIG_VAL(D, T, version, INT); E_CONFIG_VAL(D, T, screen.width, INT); E_CONFIG_VAL(D, T, screen.height, INT); E_CONFIG_LIST(D, T, crtcs, _e_randr_crtc_edd); E_CONFIG_VAL(D, T, restore, UCHAR); E_CONFIG_VAL(D, T, poll_interval, INT); E_CONFIG_VAL(D, T, config_timestamp, ULL); E_CONFIG_VAL(D, T, primary, INT); /* try to load the randr config */ if ((e_randr_cfg = e_config_domain_load("e_randr", _e_randr_edd))) { /* check randr config version */ if (e_randr_cfg->version < (E_RANDR_CONFIG_FILE_EPOCH * 1000000)) { /* config is too old */ do_restore = EINA_FALSE; _e_randr_config_free(); ecore_timer_add(1.0, _e_randr_config_cb_timer, _("Settings data needed upgrading. Your old settings have
" "been wiped and a new set of defaults initialized. This
" "will happen regularly during development, so don't report a
" "bug. This simply means Enlightenment needs new settings
" "data by default for usable functionality that your old
" "settings simply lack. This new set of defaults will fix
" "that by adding it in. You can re-configure things now to your
" "liking. Sorry for the hiccup in your settings.
")); } else if (e_randr_cfg->version > E_RANDR_CONFIG_FILE_VERSION) { /* config is too new */ do_restore = EINA_FALSE; _e_randr_config_free(); ecore_timer_add(1.0, _e_randr_config_cb_timer, _("Your settings are NEWER than Enlightenment. This is very
" "strange. This should not happen unless you downgraded
" "Enlightenment or copied the settings from a place where
" "a newer version of Enlightenment was running. This is bad and
" "as a precaution your settings have been now restored to
" "defaults. Sorry for the inconvenience.
")); } } /* if config was too old or too new, then reload a fresh one */ if (!e_randr_cfg) { do_restore = EINA_FALSE; _e_randr_config_new(); } /* e_randr_config_new could return without actually creating a new config */ if (!e_randr_cfg) return EINA_FALSE; if ((do_restore) && (e_randr_cfg->restore)) _e_randr_config_restore(); return EINA_TRUE; } static void _e_randr_config_new(void) { Ecore_X_Window root = 0; Ecore_X_Randr_Crtc *crtcs = NULL; int ncrtcs = 0, i = 0; /* create new randr cfg */ if (!(e_randr_cfg = E_NEW(E_Randr_Config, 1))) return; /* set version */ e_randr_cfg->version = E_RANDR_CONFIG_FILE_VERSION; /* by default, restore config */ e_randr_cfg->restore = EINA_TRUE; /* by default, use 4 sec poll interval */ e_randr_cfg->poll_interval = 32; /* grab the root window once */ root = ecore_x_window_root_first_get(); /* get which output is primary */ e_randr_cfg->primary = ecore_x_randr_primary_output_get(root); /* record the current screen size in our config */ ecore_x_randr_screen_current_size_get(root, &e_randr_cfg->screen.width, &e_randr_cfg->screen.height, NULL, NULL); /* try to get the list of crtcs from x */ if ((crtcs = ecore_x_randr_crtcs_get(root, &ncrtcs))) { /* loop the crtcs */ for (i = 0; i < ncrtcs; i++) { E_Randr_Crtc_Config *crtc_cfg = NULL; Ecore_X_Randr_Output *outputs = NULL; int noutputs = 0; /* try to create new crtc config */ if (!(crtc_cfg = E_NEW(E_Randr_Crtc_Config, 1))) continue; /* assign the xid */ crtc_cfg->xid = crtcs[i]; crtc_cfg->exists = EINA_TRUE; /* fill in crtc_cfg values from X */ _e_randr_config_crtc_update(crtc_cfg); /* try to get any outputs on this crtc */ if ((outputs = ecore_x_randr_crtc_outputs_get(root, crtcs[i], &noutputs))) { int j = 0; for (j = 0; j < noutputs; j++) { E_Randr_Output_Config *output_cfg = NULL; /* try to create new output config */ if (!(output_cfg = _e_randr_config_output_new(outputs[j]))) continue; /* assign crtc for this output */ output_cfg->crtc = crtcs[i]; output_cfg->exists = EINA_TRUE; if ((e_randr_cfg->primary) && ((int)outputs[j] == e_randr_cfg->primary)) output_cfg->primary = EINA_TRUE; if (!e_randr_cfg->primary) { /* X has no primary output set */ if (j == 0) { /* if no primary is set, then we should * use the first output listed by xrandr */ output_cfg->primary = EINA_TRUE; e_randr_cfg->primary = (int)outputs[j]; ecore_x_randr_primary_output_set(root, e_randr_cfg->primary); } } /* add this output to the list for this crtc */ crtc_cfg->outputs = eina_list_append(crtc_cfg->outputs, output_cfg); } free(outputs); } /* append this crtc config to randr config */ e_randr_cfg->crtcs = eina_list_append(e_randr_cfg->crtcs, crtc_cfg); } free(crtcs); } /* update recorded config timestamp */ e_randr_cfg->config_timestamp = ecore_x_randr_config_timestamp_get(root); /* set limits */ E_CONFIG_LIMIT(e_randr_cfg->poll_interval, 1, 1024); /* save the new config */ e_randr_config_save(); } static void _e_randr_config_free(void) { E_Randr_Crtc_Config *crtc = NULL; /* safety check */ if (!e_randr_cfg) return; /* loop the config crtcs and free them */ EINA_LIST_FREE(e_randr_cfg->crtcs, crtc) { E_Randr_Output_Config *output = NULL; /* loop the config outputs on this crtc and free them */ EINA_LIST_FREE(crtc->outputs, output) { if (output->clones) free(output->clones); if (output->edid) free(output->edid); E_FREE(output); } E_FREE(crtc); } /* free the config */ E_FREE(e_randr_cfg); } static Eina_Bool _e_randr_config_cb_timer(void *data) { e_util_dialog_show(_("Randr Settings Upgraded"), "%s", (char *)data); return EINA_FALSE; } static void _e_randr_config_restore(void) { Ecore_X_Window root = 0; Ecore_X_Randr_Crtc *crtcs; /* Ecore_X_Randr_Output primary = 0; */ int ncrtcs = 0; int sw = 0, sh = 0, cw = 0, ch = 0; /* don't try to restore if we have fake screens */ if (e_xinerama_fake_screens_exist()) return; printf("E_RANDR CONFIG RESTORE\n"); /* grab the X server so that we can apply settings without triggering * any randr event updates until we are done */ ecore_x_grab(); /* get the root window */ root = ecore_x_window_root_first_get(); /* get existing primary output */ /* primary = ecore_x_randr_primary_output_get(root); */ /* get existing screen size */ ecore_x_randr_screen_current_size_get(root, &cw, &ch, NULL, NULL); /* calculate new screen size */ _e_randr_config_screen_size_calculate(&sw, &sh); printf("\tCalculated Screen Size: %d %d\n", sw, sh); /* get a list of crtcs from X */ if ((crtcs = ecore_x_randr_crtcs_get(root, &ncrtcs))) { Ecore_X_Randr_Output *outputs; int c = 0, noutputs = 0; /* loop the X crtcs */ for (c = 0; c < ncrtcs; c++) { E_Randr_Crtc_Config *cfg; Evas_Coord x = 0, y = 0, w = 0, h = 0; Ecore_X_Randr_Mode mode = 0; Ecore_X_Randr_Orientation orient = ECORE_X_RANDR_ORIENTATION_ROT_0; Eina_Rectangle rect; /* Firstly, disable any crtcs which are disabled in our config OR * which are larger than the target size */ /* try to find this crtc in our config */ if ((cfg = _e_randr_config_crtc_find(crtcs[c]))) { x = cfg->x; y = cfg->y; w = cfg->width; h = cfg->height; mode = cfg->mode; orient = cfg->orient; } else { /* this crtc is not in our config. get values from X */ Ecore_X_Randr_Crtc_Info *cinfo; /* get crtc info from X */ if ((cinfo = ecore_x_randr_crtc_info_get(root, crtcs[c]))) { x = cinfo->x; y = cinfo->y; w = cinfo->width; h = cinfo->height; mode = cinfo->mode; orient = cinfo->rotation; ecore_x_randr_crtc_info_free(cinfo); } } /* at this point, we should have geometry, mode and orientation. * We can now proceed to calculate crtc size */ _e_randr_config_mode_geometry(mode, orient, &rect); x += rect.x; y += rect.y; w = rect.w; h = rect.h; /* if it fits within the screen and is "enabled", skip it */ if (((x + w) <= sw) && ((y + h) <= sh) && (mode != 0)) continue; /* it does not fit or disabled in our config. disable it in X */ ecore_x_randr_crtc_settings_set(root, crtcs[c], NULL, 0, 0, 0, 0, ECORE_X_RANDR_ORIENTATION_ROT_0); } /* apply the new screen size */ if ((sw != cw) || (sh != ch)) ecore_x_randr_screen_current_size_set(root, sw, sh, -1, -1); /* apply any stored crtc settings */ for (c = 0; c < ncrtcs; c++) { E_Randr_Crtc_Config *cfg; /* try to find this crtc in our config */ if ((cfg = _e_randr_config_crtc_find(crtcs[c]))) { Eina_List *l, *valid = NULL; E_Randr_Output_Config *output_cfg; Ecore_X_Randr_Output *coutputs; int count = 0; /* loop any outputs in this crtc cfg */ EINA_LIST_FOREACH(cfg->outputs, l, output_cfg) { Ecore_X_Randr_Connection_Status status = ECORE_X_RANDR_CONNECTION_STATUS_UNKNOWN; /* get connection status */ status = ecore_x_randr_output_connection_status_get(root, output_cfg->xid); /* skip this output if it is not connected */ if (status != ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED) continue; /* append to the list of valid outputs */ valid = eina_list_append(valid, output_cfg); } if (!valid) continue; count = eina_list_count(valid); /* try to allocate space for x randr outputs */ if ((coutputs = calloc(count, sizeof(Ecore_X_Randr_Output)))) { int o = 0; /* for each entry in valid outputs, place in X list */ EINA_LIST_FOREACH(valid, l, output_cfg) { coutputs[o] = output_cfg->xid; o++; } } /* apply our stored crtc settings */ ecore_x_randr_crtc_settings_set(root, crtcs[c], coutputs, count, cfg->x, cfg->y, cfg->mode, cfg->orient); /* cleanup */ eina_list_free(valid); free(coutputs); } } /* free list of crtcs */ free(crtcs); /* apply primary if we have one set */ if (e_randr_cfg->primary) { Eina_Bool primary_set = EINA_FALSE; /* get list of valid outputs */ if ((outputs = ecore_x_randr_outputs_get(root, &noutputs))) { /* loop valid outputs and check that our primary exists */ for (c = 0; c < noutputs; c++) { Ecore_X_Randr_Connection_Status status = ECORE_X_RANDR_CONNECTION_STATUS_UNKNOWN; /* skip if this output is not one we are looking for */ if ((int)outputs[c] != e_randr_cfg->primary) continue; /* check that this output is actually connected */ status = ecore_x_randr_output_connection_status_get(root, outputs[c]); /* if it is actually connected, set primary */ if (status == ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED) { ecore_x_randr_primary_output_set(root, outputs[c]); primary_set = EINA_TRUE; break; } } /* free list of outputs */ free(outputs); } /* fallback to no primary */ if (!primary_set) ecore_x_randr_primary_output_set(root, 0); } else ecore_x_randr_primary_output_set(root, 0); } /* release the server grab */ ecore_x_ungrab(); } static Eina_Bool _e_randr_event_cb_screen_change(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_X_Event_Screen_Change *ev; Eina_Bool changed = EINA_FALSE; Ecore_X_Randr_Output primary = 0; ev = event; printf("E_RANDR Event: Screen Change: %d %d\n", ev->size.width, ev->size.height); /* check if this event's root window is Our root window */ if (ev->root != e_manager_current_get()->root) return ECORE_CALLBACK_RENEW; primary = ecore_x_randr_primary_output_get(ev->root); if (e_randr_cfg->primary != (int)primary) { e_randr_cfg->primary = (int)primary; changed = EINA_TRUE; } if (e_randr_cfg->screen.width != ev->size.width) { printf("\tWidth Changed\n"); e_randr_cfg->screen.width = ev->size.width; changed = EINA_TRUE; } if (e_randr_cfg->screen.height != ev->size.height) { printf("\tHeight Changed\n"); e_randr_cfg->screen.height = ev->size.height; changed = EINA_TRUE; } if (e_randr_cfg->config_timestamp != ev->config_time) { printf("\tConfig Timestamp Changed\n"); e_randr_cfg->config_timestamp = ev->config_time; changed = EINA_TRUE; } if (changed) e_randr_config_save(); return ECORE_CALLBACK_RENEW; } static Eina_Bool _e_randr_event_cb_crtc_change(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_X_Event_Randr_Crtc_Change *ev; Eina_List *l = NULL; E_Randr_Crtc_Config *crtc_cfg; Eina_Bool crtc_new = EINA_FALSE; Eina_Bool crtc_found = EINA_FALSE; Eina_Bool crtc_changed = EINA_FALSE; ev = event; /* loop our crtc configs and try to find this one */ EINA_LIST_FOREACH(e_randr_cfg->crtcs, l, crtc_cfg) { /* skip if not this crtc */ if (crtc_cfg->xid != ev->crtc) continue; crtc_found = EINA_TRUE; break; } if (!crtc_found) { /* if this crtc is not found in our config, create it */ if ((crtc_cfg = E_NEW(E_Randr_Crtc_Config, 1))) { /* assign id */ crtc_cfg->xid = ev->crtc; crtc_cfg->exists = EINA_TRUE; crtc_new = EINA_TRUE; /* append to randr cfg */ e_randr_cfg->crtcs = eina_list_append(e_randr_cfg->crtcs, crtc_cfg); } } /* check (and update if needed) our crtc config * NB: This will fill in any new ones also */ crtc_changed = _e_randr_config_crtc_update(crtc_cfg); /* save the config if anything changed or we added a new one */ if ((crtc_changed) || (crtc_new)) { printf("E_RANDR Event: Crtc Change\n"); printf("\tCrtc: %d Changed or New. Saving Config\n", ev->crtc); e_randr_config_save(); } return ECORE_CALLBACK_RENEW; } static Eina_Bool _e_randr_event_cb_output_change(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_X_Event_Randr_Output_Change *ev; Eina_List *l = NULL; E_Randr_Crtc_Config *crtc_cfg; E_Randr_Output_Config *output_cfg; Eina_Bool output_new = EINA_FALSE; Eina_Bool output_found = EINA_FALSE; Eina_Bool output_changed = EINA_FALSE; Eina_Bool output_removed = EINA_FALSE; Eina_Bool skip_output = EINA_FALSE; ev = event; /* check if this event's root window is Our root window */ if (ev->win != e_manager_current_get()->root) return ECORE_CALLBACK_RENEW; printf("E_RANDR Event: Output Change\n"); printf("\tOutput: %d\n", ev->output); if (ev->crtc) printf("\t\tCrtc: %lu\n", (unsigned long)ev->crtc); else printf("\t\tNo Crtc\n"); printf("\t\tMode: %d\n", ev->mode); if (ev->connection == 0) printf("\t\tOutput Connected\n"); else if (ev->connection == 1) printf("\t\tOutput Disconnected\n"); /* loop our crtcs and try to find this output */ printf("\tLooping Our Crtc Configs\n"); EINA_LIST_FOREACH(e_randr_cfg->crtcs, l, crtc_cfg) { Eina_List *ll; /* loop the outputs in our crtc cfg and try to find this one */ printf("\t\tLooping Our Output Configs on this Crtc: %d\n", crtc_cfg->xid); EINA_LIST_FOREACH(crtc_cfg->outputs, ll, output_cfg) { /* try to find this output */ if (output_cfg->xid != ev->output) continue; /* FIXME: NB: Hmmm, we may need to also compare edids here (not just X id) */ printf("\t\t\tFound Output %d on Crtc: %d\n", output_cfg->xid, output_cfg->crtc); output_found = EINA_TRUE; /* is this output still on the same crtc ? */ if ((output_cfg->crtc != ev->crtc) && (ev->crtc != 0)) { printf("\t\t\t\tOutput Moved Crtc or Removed\n"); /* if event crtc is 0, then this output is not assigned to any crtc, * so we need to remove it from any existing crtc_cfg Outputs. * * NB: In a typical scenario, we would remove and free this output cfg, * HOWEVER we will NOT do that here. Reasoning is that if someone * replugs this output, we can restore any saved config. * * NB: Do not call _e_randr_config_output_update in this case as that will * overwrite any of our saved config * * So for now, just disable it in config by setting exists == FALSE */ if (ev->connection == 1) { /* just mark it as not existing */ output_cfg->exists = EINA_FALSE; /* set flag */ printf("\t\t\t\t\tOutput Removed\n"); output_removed = EINA_TRUE; /* if this output was set to be the primary when it was unplugged, then * we need to reset the primary monitor in our config */ if (e_randr_cfg->primary == (int)output_cfg->crtc) _e_randr_config_primary_update(); } else if (ev->connection == 0) { /* output moved to new crtc */ printf("\t\t\tOutput Moved to New Crtc or Reconnected\n"); } } else if ((ev->crtc != 0) && (output_cfg->crtc == ev->crtc)) { printf("\t\t\t\tOutput On Same Crtc\n"); /* check (and update if needed) our output config */ output_changed = _e_randr_config_output_update(output_cfg); } else if (ev->crtc == 0) { printf("\t\t\t\tOutput Has No Crtc Assigned\n"); if (!crtc_cfg->mode) { output_found = EINA_TRUE; skip_output = EINA_TRUE; } } if (output_found) break; } if (output_found) break; } /* if the output was not found above, and it is plugged in, * then we need to create a new one */ if ((!output_found) && (ev->connection == 0)) { printf("\tOutput %d Not Found In Our Config\n", ev->output); printf("\t\tCreate New Output Config\n"); if ((output_cfg = _e_randr_config_output_new(ev->output))) { E_Randr_Output_Config *ocfg; output_new = EINA_TRUE; printf("\t\t\tNew Output Config Created: %d\n", output_cfg->xid); /* since this is a new output cfg, the above * output_update function (inside new) will set 'exists' to false * because no crtc has been assigned yet. * * We need to find a valid crtc for this output and set the * 'crtc' and 'exists' properties * * As most users (I think) would expect a newly plugged output to * be a clone of the current desktop, we need to setup this * output to be a clone of the current primary output */ /* try to find the config for the primary output */ if (!(ocfg = _e_randr_config_output_find(e_randr_cfg->primary))) { E_Randr_Crtc_Config *ccfg; Eina_Bool primary_found = EINA_FALSE; /* failed to find config for primary output */ printf("\t\t\t\tFailed to find Primary Output Config\n"); /* try to find the first crtc config */ EINA_LIST_FOREACH(e_randr_cfg->crtcs, l, ccfg) { Eina_List *ll; /* skip if this crtc does not exist */ if (!ccfg->exists) continue; /* skip if no mode set */ if (!ccfg->mode) continue; /* loop the outputs in our crtc cfg and * try to find the first one that exists */ EINA_LIST_FOREACH(ccfg->outputs, ll, ocfg) { if (!ocfg->exists) continue; if (ocfg->connected) { printf("\t\t\t\t\tFound Primary Output %d\n", ocfg->xid); primary_found = EINA_TRUE; break; } } if (primary_found) break; } } if (ocfg) { Ecore_X_Randr_Mode mode = 0; int x = 0, y = 0, orient = 0; printf("\t\t\tHave Primary Output Config %d\n", ocfg->xid); /* grab the important settings we need from the primary */ if ((crtc_cfg = _e_randr_config_crtc_find(ocfg->crtc))) { x = crtc_cfg->x; y = crtc_cfg->y; mode = crtc_cfg->mode; orient = crtc_cfg->orient; } /* find a crtc for this output */ if ((crtc_cfg = _e_randr_config_output_crtc_find(output_cfg))) { Ecore_X_Randr_Mode *modes; int ocount, c = 0; int num = 0, pref = 0; Eina_Bool can_clone = EINA_FALSE; /* append this output_cfg to the crtc_cfg list of outputs */ crtc_cfg->outputs = eina_list_append(crtc_cfg->outputs, output_cfg); printf("\t\t\tUsing Crtc Config %d for Cloning\n", crtc_cfg->xid); /* we found a valid crtc for this output */ output_cfg->crtc = crtc_cfg->xid; output_cfg->exists = (output_cfg->crtc != 0); output_cfg->connected = EINA_TRUE; /* We need to verify that the new output can use this * crtc's mode for cloning * * NB: Hmmm, what to do if it Cannot use this mode ?? */ modes = ecore_x_randr_output_modes_get(ev->win, output_cfg->xid, &num, &pref); if (modes) { for (c = 0; c < num; c++) { if (modes[c] == mode) { printf("\t\t\t\tMode: %d\n", mode); can_clone = EINA_TRUE; break; } } } if (!can_clone) { int mw = 0, mh = 0; /* if we can't clone, then we need to find a * mode of the same size */ ecore_x_randr_mode_size_get(ev->win, mode, &mw, &mh); printf("\t\t\t\tMode: %d - %d x %d\n", mode, mw, mh); for (c = 0; c < num; c++) { int cw, ch; ecore_x_randr_mode_size_get(ev->win, modes[c], &cw, &ch); if ((cw == mw) && (ch == mh)) { mode = modes[c]; break; } else { /* trap for dumb non-standard TV * resolution of 1360 x 768 */ if ((abs(cw - mw) <= 10) && (ch == mh)) { mode = modes[c]; break; } } } } if (modes) free(modes); /* tell X about this new output */ ocount = eina_list_count(crtc_cfg->outputs); printf("\t\t\tNum Outputs: %d\n", ocount); if (ocount > 0) { Ecore_X_Randr_Output *couts; Eina_List *o; E_Randr_Output_Config *out; c = 0; couts = malloc(ocount * sizeof(Ecore_X_Randr_Output)); EINA_LIST_FOREACH(crtc_cfg->outputs, o, out) { couts[c] = out->xid; c++; } printf("\tCrtc Settings: %d %d %d %d\n", crtc_cfg->xid, x, y, mode); ecore_x_randr_crtc_settings_set(ev->win, crtc_cfg->xid, couts, ocount, x, y, mode, orient); free(couts); } } } else printf("NO MAIN OUTPUT CONFIG TO CLONE TO !!!\n"); } } else if ((output_found) && (ev->crtc == 0) && (!skip_output)) { if (ev->connection == 0) { /* previously configured output as been replugged */ printf("\t\t\t\tOutput Replugged: %d\n", output_cfg->xid); if ((crtc_cfg = _e_randr_config_output_crtc_find(output_cfg))) { Ecore_X_Randr_Mode mode; int ocount, c = 0; /* we found a valid crtc for this output */ output_cfg->crtc = crtc_cfg->xid; output_cfg->exists = (output_cfg->crtc != 0); printf("\t\t\tOutput Crtc Is: %d\n", output_cfg->crtc); /* get the preferred mode for this output */ if ((mode = _e_randr_config_output_preferred_mode_get(output_cfg->xid))) { Evas_Coord mw = 0, mh = 0; /* get the size of this mode */ ecore_x_randr_mode_size_get(ev->win, mode, &mw, &mh); /* update crtc config with this mode info */ crtc_cfg->mode = mode; crtc_cfg->width = mw; crtc_cfg->height = mh; } else printf("\t\tNo Mode Found\n"); /* append this output_cfg to the crtc_cfg list of outputs */ if (eina_list_count(crtc_cfg->outputs) == 0) { crtc_cfg->outputs = eina_list_append(crtc_cfg->outputs, output_cfg); output_changed = EINA_TRUE; } /* tell X about this new output */ ocount = eina_list_count(crtc_cfg->outputs); printf("\tNum Outputs: %d\n", ocount); if (ocount > 0) { Ecore_X_Randr_Output *couts; Eina_List *o; E_Randr_Output_Config *out; couts = malloc(ocount * sizeof(Ecore_X_Randr_Output)); EINA_LIST_FOREACH(crtc_cfg->outputs, o, out) { printf("\t\tOutput: %d\n", out->xid); couts[c] = out->xid; c++; } printf("\tCrtc Settings: %d %d %d %d\n", crtc_cfg->xid, crtc_cfg->x, crtc_cfg->y, crtc_cfg->mode); ecore_x_randr_crtc_settings_set(ev->win, crtc_cfg->xid, couts, ocount, crtc_cfg->x, crtc_cfg->y, crtc_cfg->mode, crtc_cfg->orient); free(couts); } } } else { /* output without a crtc asssigned has been unplugged. Nothing to do in this case */ printf("\t\t\t\tOutput Unplugged\n"); /* just mark it as not existing */ output_cfg->exists = EINA_FALSE; /* set flag */ output_removed = EINA_TRUE; } } /* save the config if anything changed or we added a new one */ if ((output_changed) || (output_new) || (output_removed)) { printf("\t\t\t\tOutput Changed, Added, or Removed. Saving Config\n"); e_randr_config_save(); } /* if we added or removed any outputs, we need to reset */ if ((output_new) || (output_removed)) ecore_x_randr_screen_reset(ev->win); return ECORE_CALLBACK_RENEW; } /* This function compares our crtc config against what X has and updates our * view of this crtc. It returns EINA_TRUE is anything changed * * NB: This Does Not Handle Outputs on the Crtc.*/ static Eina_Bool _e_randr_config_crtc_update(E_Randr_Crtc_Config *cfg) { Ecore_X_Window root = 0; Eina_Bool ret = EINA_FALSE; Ecore_X_Randr_Crtc_Info *cinfo; /* grab the root window */ root = ecore_x_window_root_first_get(); /* get crtc info from X */ if ((cinfo = ecore_x_randr_crtc_info_get(root, cfg->xid))) { /* check for changes */ if ((cfg->x != cinfo->x) || (cfg->y != cinfo->y) || (cfg->width != (int)cinfo->width) || (cfg->height != (int)cinfo->height) || (cfg->mode != cinfo->mode) || (cfg->orient != cinfo->rotation)) { cfg->x = cinfo->x; cfg->y = cinfo->y; cfg->width = cinfo->width; cfg->height = cinfo->height; cfg->mode = cinfo->mode; cfg->orient = cinfo->rotation; ret = EINA_TRUE; } ecore_x_randr_crtc_info_free(cinfo); } return ret; } static Eina_Bool _e_randr_config_output_update(E_Randr_Output_Config *cfg) { Ecore_X_Window root = 0; Eina_Bool ret = EINA_FALSE; Ecore_X_Randr_Output primary = 0; Ecore_X_Randr_Crtc crtc; Ecore_X_Randr_Connection_Status status; /* int clone_count = 0; */ /* grab the root window */ root = ecore_x_window_root_first_get(); /* get which output is primary */ primary = ecore_x_randr_primary_output_get(root); /* set this output policy */ cfg->policy = ECORE_X_RANDR_OUTPUT_POLICY_NONE; /* get if this output is the primary */ if (cfg->primary != ((cfg->xid == primary))) { cfg->primary = ((cfg->xid == primary)); ret = EINA_TRUE; } /* get the crtc for this output */ crtc = ecore_x_randr_output_crtc_get(root, cfg->xid); if (cfg->crtc != crtc) { cfg->crtc = crtc; ret = EINA_TRUE; } /* does it exist in X ?? */ if (cfg->exists != (crtc != 0)) { cfg->exists = (crtc != 0); ret = EINA_TRUE; } /* record the edid for this output */ /* cfg->edid = ecore_x_randr_output_edid_get(root, cfg->xid, &cfg->edid_count); */ /* get the clones for this output */ /* cfg->clones = */ /* ecore_x_randr_output_clones_get(root, cfg->xid, &clone_count); */ /* cfg->clone_count = (unsigned long)clone_count; */ status = ecore_x_randr_output_connection_status_get(root, cfg->xid); if (cfg->connected != (status == ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED)) { cfg->connected = (status == ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED); ret = EINA_TRUE; } return ret; } static E_Randr_Crtc_Config * _e_randr_config_output_crtc_find(E_Randr_Output_Config *cfg) { Ecore_X_Window root = 0; E_Randr_Crtc_Config *crtc_cfg = NULL; E_Randr_Output_Config *ocfg = NULL; Ecore_X_Randr_Crtc *possible; int num = 0, i = 0; Eina_List *l; Eina_Bool crtc_found = EINA_FALSE; printf("Find Crtc For Output: %d\n", cfg->xid); /* grab the root window */ root = ecore_x_window_root_first_get(); /* get a list of possible crtcs for this output */ if (!(possible = ecore_x_randr_output_possible_crtcs_get(root, cfg->xid, &num))) { printf("\tNo Possible Crtcs Found From X\n"); return NULL; } if (num == 0) { if (possible) free(possible); return NULL; } printf("\tLooping Possible Crtcs\n"); /* loop the possible crtcs */ for (i = 0; i < num; i++) { if ((crtc_cfg = _e_randr_config_crtc_find(possible[i]))) { printf("\tFound Possible Crtc %d in Config\n", crtc_cfg->xid); /* try to find this output in this crtc config */ EINA_LIST_FOREACH(crtc_cfg->outputs, l, ocfg) { printf("\t\tCrtc Has Output %d\n", ocfg->xid); if (ocfg->xid == cfg->xid) { printf("\t\t\tFound Output %d in Crtc Config\n", cfg->xid); crtc_found = EINA_TRUE; break; } } } if (crtc_found) break; } if (!crtc_found) { /* no existing crtc config was found which contained this output */ printf("\tChecking %d for Clone: %d\n", e_randr_cfg->primary, cfg->xid); /* loop our config and try to find something we can clone to. * starting with the primary output */ if (e_randr_cfg->primary != (int)cfg->xid) { /* E_Randr_Crtc_Config *pcfg = NULL; */ printf("\t\tTrying to get Primary Crtc Config\n"); if ((crtc_cfg = _e_randr_config_crtc_find(e_randr_cfg->primary))) { printf("\t\t\tFound Primary Config\n"); /* found the crtc config for the primary monitor */ crtc_found = EINA_TRUE; } } } if (!crtc_found) { printf("\tNo Crtc Found Yet\n"); /* no existing crtc config was found which contained this output */ /* loop our config and see if we have a crtc which has no outputs existing */ /* loop the possible crtcs */ for (i = 0; i < num; i++) { if ((crtc_cfg = _e_randr_config_crtc_find(possible[i]))) { printf("\tFound Possible Crtc %d in Config\n", crtc_cfg->xid); if (eina_list_count(crtc_cfg->outputs) == 0) { printf("\t\tCrtc Config has No Outputs\n"); crtc_found = EINA_TRUE; if (!crtc_cfg->mode) { Ecore_X_Randr_Mode mode; /* get the preferred mode for this output */ if ((mode = _e_randr_config_output_preferred_mode_get(cfg->xid))) { Evas_Coord mw = 0, mh = 0; ecore_x_randr_mode_size_get(root, mode, &mw, &mh); crtc_cfg->mode = mode; crtc_cfg->width = mw; crtc_cfg->height = mh; } else printf("\t\tNo Mode Found\n"); } break; } } } } free(possible); if (crtc_found) return crtc_cfg; return NULL; } static Ecore_X_Randr_Mode _e_randr_config_output_preferred_mode_get(unsigned int xid) { Ecore_X_Window root = 0; Ecore_X_Randr_Mode *modes; Ecore_X_Randr_Mode mode; int n = 0, p = 0; printf("Get Preferred Mode for Output %d\n", xid); /* grab the root window */ root = ecore_x_window_root_first_get(); /* get the list of modes for this output */ if (!(modes = ecore_x_randr_output_modes_get(root, xid, &n, &p))) { printf("\tNo Modes returned from X\n"); return 0; } if (n == 0) { if (modes) free(modes); return 0; } printf("\tNum Modes: %d\n", n); printf("\tP: %d\n", p); if (p > 0) mode = modes[p - 1]; else mode = modes[0]; printf("\tFound Preferred Mode: %d\n", mode); free(modes); return mode; } static E_Randr_Output_Config * _e_randr_config_output_new(unsigned int id) { E_Randr_Output_Config *cfg = NULL; if ((cfg = E_NEW(E_Randr_Output_Config, 1))) { /* assign output xid */ cfg->xid = id; /* check (and update if needed) our output config */ _e_randr_config_output_update(cfg); } return cfg; } static E_Randr_Crtc_Config * _e_randr_config_crtc_find(Ecore_X_Randr_Crtc crtc) { Eina_List *l; E_Randr_Crtc_Config *crtc_cfg; EINA_LIST_FOREACH(e_randr_cfg->crtcs, l, crtc_cfg) { if (crtc_cfg->xid == crtc) return crtc_cfg; } return NULL; } static E_Randr_Output_Config * _e_randr_config_output_find(Ecore_X_Randr_Output output) { Eina_List *l; E_Randr_Crtc_Config *crtc_cfg; E_Randr_Output_Config *output_cfg; EINA_LIST_FOREACH(e_randr_cfg->crtcs, l, crtc_cfg) { Eina_List *ll; EINA_LIST_FOREACH(crtc_cfg->outputs, ll, output_cfg) { if (output_cfg->xid == output) return output_cfg; } } return NULL; } static void _e_randr_config_screen_size_calculate(int *sw, int *sh) { Ecore_X_Window root = 0; Ecore_X_Randr_Output *outputs; int noutputs = 0; int minw = 0, minh = 0; int maxw = 0, maxh = 0; /* get the root window */ root = ecore_x_window_root_first_get(); /* get the min and max screen size */ ecore_x_randr_screen_size_range_get(root, &minw, &minh, &maxw, &maxh); /* get outputs from X */ if ((outputs = ecore_x_randr_outputs_get(root, &noutputs))) { int i = 0; /* loop X outputs */ for (i = 0; i < noutputs; i++) { E_Randr_Output_Config *output_cfg; Ecore_X_Randr_Connection_Status status = ECORE_X_RANDR_CONNECTION_STATUS_UNKNOWN; Ecore_X_Randr_Orientation orient = ECORE_X_RANDR_ORIENTATION_ROT_0; Ecore_X_Randr_Mode mode = 0; int x = 0, y = 0, w = 0, h = 0; Eina_Rectangle rect; /* get connection status */ status = ecore_x_randr_output_connection_status_get(root, outputs[i]); /* skip this output if it is not connected */ if (status != ECORE_X_RANDR_CONNECTION_STATUS_CONNECTED) continue; /* see if we have this output in our config */ if ((output_cfg = _e_randr_config_output_find(outputs[i]))) { E_Randr_Crtc_Config *crtc_cfg; /* try to find the crtc config for this output */ if ((crtc_cfg = _e_randr_config_crtc_find(output_cfg->crtc))) { /* if this crtc is disabled in our config, skip it * * NB: Since we will end up disabling this crtc, then * we should not use it to calculate screen size */ if (!crtc_cfg->mode) continue; /* get geometry and mode */ x = crtc_cfg->x; y = crtc_cfg->y; w = crtc_cfg->width; h = crtc_cfg->height; mode = crtc_cfg->mode; orient = crtc_cfg->orient; } } /* if we have no config for this output. get values from X */ if ((!w) || (!h)) { Ecore_X_Randr_Crtc crtc = 0; Ecore_X_Randr_Crtc_Info *cinfo; crtc = ecore_x_randr_output_crtc_get(root, outputs[i]); /* get crtc info from X */ if ((cinfo = ecore_x_randr_crtc_info_get(root, crtc))) { x = cinfo->x; y = cinfo->y; w = cinfo->width; h = cinfo->height; mode = cinfo->mode; orient = cinfo->rotation; ecore_x_randr_crtc_info_free(cinfo); } } /* at this point, we should have geometry, mode and orientation. * We can now proceed to calculate screen size */ _e_randr_config_mode_geometry(mode, orient, &rect); x += rect.x; y += rect.y; w = rect.w; h = rect.h; if ((x + w) > *sw) *sw = (x + w); if ((y + h) > *sh) *sh = (y + h); } /* free any space allocated */ free(outputs); } if ((*sw > maxw) || (*sh > maxh)) { printf("Calculated Screen Size %dx%d is Larger Than Max %dx%d!!!\n", *sw, *sh, maxw, maxh); } else { if (*sw < minw) *sw = minw; if (*sh < minh) *sh = minh; } } static void _e_randr_config_mode_geometry(Ecore_X_Randr_Mode mode, Ecore_X_Randr_Orientation orient, Eina_Rectangle *rect) { Ecore_X_Window root = 0; Evas_Point p[4]; int mw = 0, mh = 0; int mode_width = 0, mode_height = 0; int i = 0; Eina_Rectangle tmp; /* get the root window */ root = ecore_x_window_root_first_get(); /* get the size of this mode */ ecore_x_randr_mode_size_get(root, mode, &mode_width, &mode_height); /* based on orientation, calculate mode sizes */ switch (orient) { case ECORE_X_RANDR_ORIENTATION_ROT_0: case ECORE_X_RANDR_ORIENTATION_ROT_180: mw = mode_width; mh = mode_height; break; case ECORE_X_RANDR_ORIENTATION_ROT_90: case ECORE_X_RANDR_ORIENTATION_ROT_270: mw = mode_height; mh = mode_width; break; default: break; } p[0].x = 0; p[0].y = 0; p[1].x = mw; p[1].y = 0; p[2].x = mw; p[2].y = mh; p[3].x = 0; p[3].y = mh; for (i = 0; i < 4; i++) { double x = 0.0, y = 0.0; x = p[i].x; y = p[i].y; eina_rectangle_coords_from(&tmp, floor(x), floor(y), ceil(x), ceil(y)); if (i == 0) *rect = tmp; else { if (tmp.x < rect->x) rect->x = tmp.x; if (tmp.y < rect->y) rect->y = tmp.y; if (tmp.w > rect->w) rect->w = tmp.w; if (tmp.h > rect->h) rect->h = tmp.h; } } } static void _e_randr_config_primary_update(void) { Eina_List *l, *ll; E_Randr_Crtc_Config *crtc_cfg; E_Randr_Output_Config *output_cfg; /* unmark all existing outputs as the primary */ EINA_LIST_FOREACH(e_randr_cfg->crtcs, l, crtc_cfg) { EINA_LIST_FOREACH(crtc_cfg->outputs, ll, output_cfg) output_cfg->primary = EINA_FALSE; } /* find the first existing output and mark it as primary */ EINA_LIST_FOREACH(e_randr_cfg->crtcs, l, crtc_cfg) { if (!crtc_cfg->exists) continue; EINA_LIST_FOREACH(crtc_cfg->outputs, ll, output_cfg) { if (!output_cfg->exists) continue; e_randr_cfg->primary = output_cfg->xid; output_cfg->primary = EINA_TRUE; return; } } }