RandR: Fix issues discovered here during testing
authorChris Michael <cp.michael@samsung.com>
Thu, 26 Sep 2013 10:49:45 +0000 (11:49 +0100)
committerChris Michael <cp.michael@samsung.com>
Thu, 26 Sep 2013 11:15:34 +0000 (12:15 +0100)
- Add some more debugging printouts
- When we initially plug in a monitor, set to Clone by default
- During initial clone, we cannot assume the plugged-in device
supports the same Mode so try to find a mode based on size.
- On output changes, test that an output has a crtc assigned.
- If a detached output Was the primary monitor, reset primary in config
to a valid existing monitor.
- Improve output_crtc_find code for better detection of where we can
place a new output.
- Fix "preferred mode" detection error.

Signed-off-by: Chris Michael <cp.michael@samsung.com>
src/bin/e_randr.c

index 79a91d5b6af53babc3d4a9d754face06cf481aea..ec85b6db3d2acc4466eac9573a3b6a9f1d77cb84 100644 (file)
@@ -9,12 +9,13 @@ 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(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);
@@ -729,7 +730,7 @@ _e_randr_event_cb_output_change(void *data EINA_UNUSED, int type EINA_UNUSED, vo
              output_found = EINA_TRUE;
 
              /* is this output still on the same crtc ? */
-             if (output_cfg->crtc != ev->crtc)
+             if ((output_cfg->crtc != ev->crtc) && (ev->crtc != 0))
                {
                   printf("\t\t\t\tOutput Moved Crtc or Removed\n");
 
@@ -744,35 +745,37 @@ _e_randr_event_cb_output_change(void *data EINA_UNUSED, int type EINA_UNUSED, vo
                    * overwrite any of our saved config
                    * 
                    * So for now, just disable it in config by setting exists == FALSE */
-                  if (!ev->crtc)
+                  if (ev->connection == 1)
                     {
-                       /* free this output_cfg */
-                       /* if (output_cfg->clones) free(output_cfg->clones); */
-                       /* if (output_cfg->edid) free(output_cfg->edid); */
-                       /* E_FREE(output_cfg); */
-
-                       /* remove from this crtc */
-                       /* crtc_cfg->outputs = eina_list_remove_list(crtc_cfg->outputs, ll); */
-
                        /* just mark it as not existing */
                        output_cfg->exists = EINA_FALSE;
 
                        /* set flag */
-                       output_removed = EINA_TRUE;
+                      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
+                  else if (ev->connection == 0)
                     {
                        /* output moved to new crtc */
-                       printf("\t\t\tOutput Moved to New Crtc\n");
+                       printf("\t\t\tOutput Moved to New Crtc or Reconnected\n");
                     }
                }
-             else
+             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 (output_found) break;
           }
@@ -784,19 +787,186 @@ _e_randr_event_cb_output_change(void *data EINA_UNUSED, int type EINA_UNUSED, vo
     * then we need to create a new one */
    if ((!output_found) && (ev->connection == 0))
      {
-        printf("\tOutput Not Found In Config: %d\n", ev->output);
+        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 */
+              * '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;
+                 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)
+                                  {
+                                     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);
+
+                           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;
+                                  }
+                             }
+                        }
+
+                      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;
+
+                           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))
+     {
+       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;
@@ -809,7 +979,7 @@ _e_randr_event_cb_output_change(void *data EINA_UNUSED, int type EINA_UNUSED, vo
                   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)))
+                  if ((mode = _e_randr_config_output_preferred_mode_get(output_cfg->xid)))
                     {
                        Evas_Coord mw = 0, mh = 0;
 
@@ -821,10 +991,16 @@ _e_randr_event_cb_output_change(void *data EINA_UNUSED, int type EINA_UNUSED, vo
                        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 */
-                  crtc_cfg->outputs = 
-                    eina_list_append(crtc_cfg->outputs, output_cfg);
+                 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);
@@ -839,6 +1015,7 @@ _e_randr_event_cb_output_change(void *data EINA_UNUSED, int type EINA_UNUSED, vo
                        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++;
                          }
@@ -855,7 +1032,18 @@ _e_randr_event_cb_output_change(void *data EINA_UNUSED, int type EINA_UNUSED, vo
                        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 */
@@ -974,18 +1162,24 @@ _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)))
-     return NULL;
+     {
+       printf("\tNo Possible Crtcs Found From X\n");
+       return NULL;
+     }
 
    if (num == 0)
      {
@@ -993,24 +1187,95 @@ _e_randr_config_output_crtc_find(E_Randr_Output_Config *cfg)
         return NULL;
      }
 
+   printf("\tLooping Possible Crtcs\n");
+
    /* loop the possible crtcs */
    for (i = 0; i < num; i++)
      {
-        /* loop our crtc configs and try to find this one */
-        EINA_LIST_FOREACH(e_randr_cfg->crtcs, l, crtc_cfg)
-          {
-             /* skip if not the one we are looking for */
-             if (crtc_cfg->xid != possible[i]) continue;
-
-             /* check if this crtc already has outputs assigned.
-              * skip if it does because we are trying to find a free crtc */
-             if (eina_list_count(crtc_cfg->outputs) > 0) continue;
+       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;
+     }
 
-             crtc_found = EINA_TRUE;
-             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) break;
+   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);
@@ -1021,19 +1286,24 @@ _e_randr_config_output_crtc_find(E_Randr_Output_Config *cfg)
 }
 
 static Ecore_X_Randr_Mode 
-_e_randr_config_output_preferred_mode_get(E_Randr_Output_Config *cfg)
+_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, cfg->xid, &n, &p)))
-     return 0;
+   if (!(modes = ecore_x_randr_output_modes_get(root, xid, &n, &p)))
+     {
+       printf("\tNo Modes returned from X\n");
+       return 0;
+     }
 
    if (n == 0)
      {
@@ -1041,7 +1311,15 @@ _e_randr_config_output_preferred_mode_get(E_Randr_Output_Config *cfg)
         return 0;
      }
 
-   mode = modes[p - 1];
+   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;
@@ -1279,3 +1557,31 @@ _e_randr_config_mode_geometry(Ecore_X_Randr_Mode mode, Ecore_X_Randr_Orientation
           }
      }
 }
+
+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;
+         }
+     }
+}