#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<br>"
                               "been wiped and a new set of defaults initialized. This<br>"
                               "will happen regularly during development, so don't report a<br>"
                               "bug. This simply means Enlightenment needs new settings<br>"
                               "data by default for usable functionality that your old<br>"
                               "settings simply lack. This new set of defaults will fix<br>"
                               "that by adding it in. You can re-configure things now to your<br>"
                               "liking. Sorry for the hiccup in your settings.<br>"));
          }
        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<br>"
                               "strange. This should not happen unless you downgraded<br>"
                               "Enlightenment or copied the settings from a place where<br>"
                               "a newer version of Enlightenment was running. This is bad and<br>"
                               "as a precaution your settings have been now restored to<br>"
                               "defaults. Sorry for the inconvenience.<br>"));
          }
     }

   /* 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;
	  }
     }
}
