aboutsummaryrefslogblamecommitdiffstats
path: root/src/bin/e_randr2.c
blob: fc0375a8c212b9bf9acdca2ba6a0018cbc282866 (plain) (tree)
1
2
3
4
5
6

              
                                

                                                                         
                                                                  








                                                                               
                                                                                             
                                                                                


                                                                                             
                                                                   










                                                                         




                                                                         

                                           
 
                                                

                                                                         


                   

             
                                                                            
                                                                                                              
















                                                                       

                                                








                                                                           

                                                    
                                            
 
                                   

                            
                             
                                       





                                                                     


                                                              
      




                                                             



                    
                                                  

                               
                                                           










                                                                 
                                                  
                                









                                                      


            
               





                                               
          




                           
          



                                         
                                       











                                                                         


                                  
                                                      
                                                  




                                         


                     

























                                                                
                               

         
                        


                                                                     






                                                        
                                                                             






















                                                    
                    

















                                                                
                                       







                                           
                           


















                                                 
                                




                






                                                  









                                                             


                                                






                                            

                                  
                                                










                                  






                                          










                                                                   
                
                              
 

                                                                




                                                                        



                              
                              




                                                                            
                                                                              
                                                 
                                        




                                                            
                                                       




                                                                     













                                                                               



                                                                                                   

                                        












                                                                       
                                  






















                                                                              

                                               












                                                                          
                             


                                                                  
              


           















                                                                    



                                                                 

















                                                         


                                         



           

                                                


                              





                                                     
                                                        
                                                   



                                                            
                                                              
                                         
           




                                           



                                         












                                                             

                                                    





                                                 

















                                                      

                                                                     


















                                                        
                                         
                                                             
                                                     


















                                                             

                                                               

                              
                                          





                                               
                                 
                              
                                                          



                                                                            

                                                             
                                         


                                                                      








                                                                               

                               
                                             
                                                                    
                                          



                                                                         


                                                  






                                                               


































                                                 


























                                                                                                




                               

                           
                                         



                                   
                                                   




                                                                              



























                                                                                  







                                                                             
                                                                















                                                                               

                                                                            
                                                  





                                                                 


                                                             


                            


                                                                      
               













































                                                                           
                                                         





















                                                                             



                                           










                                                                               

                                                                           





                         
                                 


















                                                                





                                     
                       
 
                                                 
                              
      
                                             

               
                        






                                                        






                                                                            
                                                                              





                                                                                             



























































                                                                       































                                                                              
                        




































































                                                              















                                                      
          




                                                                           
                                                 
       
                                                                                    
                               
 
 
                              













                                                                     
 
          



















































































                                                                         

                                              





























                                                                                            























                                                                         


















                                                                    
#include "e.h"

#define E_RANDR_CONFIG_VERSION 2

/////////////////////////////////////////////////////////////////////////
static Eina_Bool               _screen_closed(E_Randr2_Screen *s);
static void                    _animated_apply_abort(void);
static Eina_Bool               _cb_delay_timer(void *data);
static Eina_Bool               _cb_fade_animator(void *data);
static void                    _animated_apply(void);
static void                    _do_apply(void);
static void                    _info_free(E_Randr2 *r);
static E_Config_Randr2        *_config_load(void);
static void                    _config_free(E_Config_Randr2 *cfg);
static Eina_Bool               _config_save(E_Randr2 *r, E_Config_Randr2 *cfg);
static Eina_Bool               _config_update(E_Randr2 *r, E_Config_Randr2 *cfg, Eina_Bool );
static void                    _config_apply(E_Randr2 *r, E_Config_Randr2 *cfg);
static int                     _config_screen_match_count(E_Randr2 *r, E_Config_Randr2 *cfg);
static char                   *_screens_fingerprint(E_Randr2 *r);
static Eina_Bool               _screens_differ(E_Randr2 *r1, E_Randr2 *r2);
static Eina_Bool               _cb_screen_change_delay(void *data);
static E_Randr2_Screen        *_screen_output_find(const char *out);
static E_Randr2_Screen        *_screen_id_find(const char *id);
static void                    _screen_config_takeover(void);
static void                    _screen_config_do(E_Randr2_Screen *s);
static void                    _screen_config_eval(void);
static void                    _screen_config_maxsize(void);

/////////////////////////////////////////////////////////////////////////
static E_Config_DD   *_e_randr2_cfg_edd = NULL;
static E_Config_DD   *_e_randr2_cfg_screen_edd = NULL;
static Eina_List     *_ev_handlers = NULL;
static Ecore_Timer   *_screen_delay_timer = NULL;
static Eina_Bool      event_screen = EINA_FALSE;
static Eina_Bool      event_ignore = EINA_FALSE;

/////////////////////////////////////////////////////////////////////////
E_API E_Config_Randr2 *e_randr2_cfg = NULL;
E_API E_Randr2        *e_randr2 = NULL;

E_API int              E_EVENT_RANDR_CHANGE = 0;

/////////////////////////////////////////////////////////////////////////
EINTERN Eina_Bool
e_randr2_init(void)
{
   int count;

   if (!E_EVENT_RANDR_CHANGE) E_EVENT_RANDR_CHANGE = ecore_event_type_new();
   if ((!e_comp->screen) || (!e_comp->screen->available) || (!e_comp->screen->available())) return EINA_FALSE;
   // create data descriptors for config storage
   _e_randr2_cfg_screen_edd =
     E_CONFIG_DD_NEW("E_Config_Randr2_Screen", E_Config_Randr2_Screen);
#undef T
#undef D
#define T E_Config_Randr2_Screen
#define D _e_randr2_cfg_screen_edd
   E_CONFIG_VAL(D, T, id, STR);
   E_CONFIG_VAL(D, T, rel_to, STR);
   E_CONFIG_VAL(D, T, rel_align, DOUBLE);
   E_CONFIG_VAL(D, T, mode_refresh, DOUBLE);
   E_CONFIG_VAL(D, T, mode_w, UINT);
   E_CONFIG_VAL(D, T, mode_h, INT);
   E_CONFIG_VAL(D, T, rotation, INT);
   E_CONFIG_VAL(D, T, priority, INT);
   E_CONFIG_VAL(D, T, rel_mode, UCHAR);
   E_CONFIG_VAL(D, T, enabled, UCHAR);
   E_CONFIG_VAL(D, T, profile, STR);
   E_CONFIG_VAL(D, T, scale_multiplier, DOUBLE);

   _e_randr2_cfg_edd = E_CONFIG_DD_NEW("E_Config_Randr2", E_Config_Randr2);
#undef T
#undef D
#define T E_Config_Randr2
#define D _e_randr2_cfg_edd
   E_CONFIG_VAL(D, T, version, INT);
   E_CONFIG_LIST(D, T, screens, _e_randr2_cfg_screen_edd);
   E_CONFIG_VAL(D, T, restore, UCHAR);
   E_CONFIG_VAL(D, T, ignore_hotplug_events, UCHAR);
   E_CONFIG_VAL(D, T, ignore_acpi_events, UCHAR);
   E_CONFIG_VAL(D, T, default_policy, UINT);

   // set up events from the driver
   if (e_comp->screen->init)
     e_comp->screen->init();
   // get current screen info
   e_randr2 = e_comp->screen->create();
   // from screen info calculate screen max dimensions
   _screen_config_maxsize();
   // load config and apply it
   e_randr2_cfg = _config_load();
   // only apply if restore is set AND at least one configured screen
   // matches one we have

   count = _config_screen_match_count(e_randr2, e_randr2_cfg);
   if (e_randr2_cfg->restore && count)
     {
        if (count != (int)eina_list_count(e_randr2->screens))
          {
             if (_config_update(e_randr2, e_randr2_cfg, 1))
               e_randr2_config_save();
          }
        _do_apply();
     }
   else
     {
        _config_update(e_randr2, e_randr2_cfg, 0);
        e_randr2_config_save();
     }
   ecore_event_add(E_EVENT_RANDR_CHANGE, NULL, NULL, NULL);
   return EINA_TRUE;
}

EINTERN int
e_randr2_shutdown(void)
{
   _animated_apply_abort();
   // nuke any screen config delay handler
   if (_screen_delay_timer) ecore_timer_del(_screen_delay_timer);
   _screen_delay_timer = NULL;
   // stop listening to driver info
   if (e_comp->screen && e_comp->screen->shutdown)
     e_comp->screen->shutdown();
   // clear up all event handlers
   E_FREE_LIST(_ev_handlers, ecore_event_handler_del);
   // free up screen info
   _info_free(e_randr2);
   e_randr2 = NULL;
   _config_free(e_randr2_cfg);
   e_randr2_cfg = NULL;
   // free up data descriptors
   E_CONFIG_DD_FREE(_e_randr2_cfg_edd);
   E_CONFIG_DD_FREE(_e_randr2_cfg_screen_edd)
   return 1;
}

E_API Eina_Bool
e_randr2_config_save(void)
{
   // save our config
   return _config_save(e_randr2, e_randr2_cfg);
}

E_API void
e_randr2_config_apply(void)
{
   _animated_apply();
}

E_API void
e_randr2_screeninfo_update(void)
{
   // re-fetch/update current screen info
   _info_free(e_randr2);
   e_randr2 = e_comp->screen->create();
   _screen_config_maxsize();
}

/////////////////////////////////////////////////////////////////////////
static double _start_time = 0.0;
static Ecore_Animator *_fade_animator = NULL;
static Ecore_Timer *_apply_delay = NULL;
Eina_Bool _applying = EINA_FALSE;
static int _target_from = 0;
static int _target_to = 0;
static Evas_Object *_fade_obj = NULL;

static Eina_Bool
_screen_closed(E_Randr2_Screen *s)
{
   printf("RRR: check lid for %s...\n", s->info.name);
   if (!e_acpi_lid_is_closed()) return EINA_FALSE;
   if (s->info.is_lid)
     {
        printf("RRR:   is closed lid\n");
        return EINA_TRUE;
     }
   return EINA_FALSE;
}

static void
_animated_apply_abort(void)
{
   if (_apply_delay) ecore_timer_del(_apply_delay);
   if (_fade_animator) ecore_animator_del(_fade_animator);
   _apply_delay = NULL;
   _fade_animator = NULL;
   _applying = EINA_FALSE;
   _fade_obj = NULL;
}

static Eina_Bool
_cb_delay_timer(void *data EINA_UNUSED)
{
   _apply_delay = NULL;
   _target_from = 255;
   _target_to = 0;
   _start_time = ecore_loop_time_get();
   _fade_animator = ecore_animator_add(_cb_fade_animator, NULL);
   return EINA_FALSE;
}

static Eina_Bool
_cb_fade_animator(void *data EINA_UNUSED)
{
   double t = ecore_loop_time_get() - _start_time;
   const double duration = 0.5;
   int v;

   if (t < 0.0) t = 0.0;
   t = t / duration;
   if (t > 1.0) t = 1.0;
   t = ecore_animator_pos_map(t, ECORE_POS_MAP_SINUSOIDAL, 0.0, 0.0);
   v = _target_from + ((_target_to - _target_from) * t);
   if (t >= 1.0) v = _target_to;
   evas_object_color_set(_fade_obj, 0, 0, 0, v);
   if (v == _target_to)
     {
        if (_target_to == 255)
          {
             _apply_delay = ecore_timer_loop_add(1.0, _cb_delay_timer, NULL);
             _do_apply();
          }
        else
          {
             evas_object_del(_fade_obj);
             _fade_obj = NULL;
             _applying = EINA_FALSE;
          }
        _fade_animator = NULL;
        return EINA_FALSE;
     }
   return EINA_TRUE;
}

static void
_animated_apply(void)
{
   Evas *e;

   // fade out, config, wait 3 seconds, fade back in
   if (_applying) return;
   _applying = EINA_TRUE;
   _start_time = ecore_loop_time_get();
   e = e_comp->evas;
   _fade_obj = evas_object_rectangle_add(e);
   evas_object_pass_events_set(_fade_obj, EINA_TRUE);
   evas_object_color_set(_fade_obj, 0, 0, 0, 0);
   evas_object_move(_fade_obj, 0, 0);
   evas_object_resize(_fade_obj, 999999, 999999);
   evas_object_layer_set(_fade_obj, EVAS_LAYER_MAX);
   evas_object_show(_fade_obj);
   _target_from = 0;
   _target_to = 255;
   _fade_animator = ecore_animator_add(_cb_fade_animator, NULL);
}

static void
_do_apply(void)
{
   // take current screen config and apply it to the driver
   printf("RRR: re-get info before applying..\n");
   _info_free(e_randr2);
   e_randr2 = e_comp->screen->create();
   _screen_config_maxsize();
   printf("RRR: apply config...\n");
   _config_apply(e_randr2, e_randr2_cfg);
   printf("RRR: takeover config...\n");
   _screen_config_takeover();
   printf("RRR: eval config...\n");
   _screen_config_eval();
   printf("RRR: really apply config...\n");
   e_comp->screen->apply();
   printf("RRR: done config...\n");
}

static void
_info_free(E_Randr2 *r)
{
   E_Randr2_Screen *s;
   E_Randr2_Mode *m;

   if (!r) return;
   // free up our randr screen data
   EINA_LIST_FREE(r->screens, s)
     {
        free(s->id);
        free(s->info.screen);
        free(s->info.name);
        free(s->info.edid);
        EINA_LIST_FREE(s->info.modes, m) free(m);
        free(s->config.relative.to);
        free(s->config.profile);
        free(s);
     }
   free(r);
}

static void
_config_upgrade(E_Config_Randr2 *cfg)
{
   if (cfg->version < 2)
     cfg->default_policy = E_RANDR2_POLICY_EXTEND;
}

static E_Config_Randr2 *
_config_load(void)
{
   E_Config_Randr2 *cfg;

   // load config and check if version is up to date
   cfg = e_config_domain_load("e_randr2", _e_randr2_cfg_edd);
   if (cfg)
     {
        if (cfg->version < E_RANDR_CONFIG_VERSION)
          _config_upgrade(cfg);
        printf("RRR: loaded existing config\n");
        return cfg;
     }

   // need new config
   cfg = calloc(1, sizeof(E_Config_Randr2));
   cfg->version = E_RANDR_CONFIG_VERSION;
   cfg->screens = NULL;
   cfg->restore = 1;
   cfg->ignore_hotplug_events = 0;
   cfg->ignore_acpi_events = 0;
   cfg->default_policy = E_RANDR2_POLICY_EXTEND;
   printf("RRR: fresh config\n");
   return cfg;
}

static void
_config_free(E_Config_Randr2 *cfg)
{
   E_Config_Randr2_Screen *cs;

   if (!cfg) return;
   // free config data
   EINA_LIST_FREE(cfg->screens, cs)
     {
        eina_stringshare_del(cs->id);
        eina_stringshare_del(cs->rel_to);
        eina_stringshare_del(cs->profile);
        free(cs);
     }
   free(cfg);
}

static Eina_Bool
_config_save(E_Randr2 *r, E_Config_Randr2 *cfg)
{
   if ((!r) || (!cfg)) return EINA_FALSE;
   // save config struct to cfg file
   return e_config_domain_save("e_randr2", _e_randr2_cfg_edd, cfg);
}

static Eina_Bool
_config_ask_dialog(void *data)
{
   e_configure_registry_call("screen/screen_setup", NULL, data);
   free(data);
   return EINA_FALSE;
}

static Eina_Bool
_config_update(E_Randr2 *r, E_Config_Randr2 *cfg, Eina_Bool update_only)
{
   Eina_List *l;
   E_Randr2_Screen *s;
   E_Config_Randr2_Screen *cs;
   Eina_Bool ret = EINA_FALSE;

   printf("--------------------------------------------------\n");
   EINA_LIST_FOREACH(r->screens, l, s)
     {
        printf("RRR: out id=%s:  connected=%i\n", s->id, s->info.connected);
        if ((!s->id) || (!s->info.connected) || (_screen_closed(s))) continue;
        cs = e_randr2_config_screen_find(s, cfg);
        if (cs && update_only) continue;
        if (!cs)
          {
             cs = calloc(1, sizeof(E_Config_Randr2_Screen));
             if (cs)
               {
                  cs->id = eina_stringshare_add(s->id);
                  cfg->screens = eina_list_prepend(cfg->screens, cs);
               }
          }
        if (cs)
          {
             if (update_only)
               {
                  switch (cfg->default_policy)
                    {
                     case E_RANDR2_POLICY_EXTEND:
                       if (s->config.relative.mode < E_RANDR2_RELATIVE_TO_LEFT)
                         cs->rel_mode = E_RANDR2_RELATIVE_TO_RIGHT;
                       else
                         cs->rel_mode = s->config.relative.mode;
                       break;
                     case E_RANDR2_POLICY_CLONE:
                       cs->rel_mode = E_RANDR2_RELATIVE_CLONE;
                       break;
                     case E_RANDR2_POLICY_ASK:
                       if (starting)
                         ecore_timer_loop_add(2, _config_ask_dialog, eina_strdup(s->info.name));
                       else
                         ecore_timer_loop_add(0.01, _config_ask_dialog, eina_strdup(s->info.name));
                       EINA_FALLTHROUGH;
                       /* no break */
                     case E_RANDR2_POLICY_NONE:
                       cs->rel_mode = E_RANDR2_RELATIVE_NONE;
                       break;
                    }
               }
             else
               cs->rel_mode = s->config.relative.mode;
             if (update_only &&
               ((cfg->default_policy == E_RANDR2_POLICY_CLONE) ||
                (cfg->default_policy == E_RANDR2_POLICY_EXTEND)))
               {
                  E_Randr2_Mode *m = eina_list_data_get(s->info.modes);

                  cs->enabled = 1;
                  cs->mode_refresh = m->refresh;
                  cs->mode_w = m->w;
                  cs->mode_h = m->h;
                  if (s->config.relative.to)
                    cs->rel_to = eina_stringshare_add(s->config.relative.to);
                  else
                    {
                       /* find right-most screen */
                       E_Zone *zone = eina_list_last_data_get(e_comp->zones);
                       eina_stringshare_replace(&cs->rel_to, zone->randr2_id);
                    }
                  cs->rel_align = 0;
               }
             else
               {
                  cs->enabled = s->config.enabled;
                  cs->mode_refresh = s->config.mode.refresh;
                  cs->mode_w = s->config.mode.w;
                  cs->mode_h = s->config.mode.h;
                  if (s->config.relative.to)
                    cs->rel_to = eina_stringshare_add(s->config.relative.to);
                  cs->rel_align = s->config.relative.align;
               }
             cs->rotation = s->config.rotation;
             cs->priority = s->config.priority;
             if (cs->profile)
               {
                  printf("RRR: store config profile '%s'\n", cs->profile);
                  free(s->config.profile);
                  s->config.profile = strdup(cs->profile);
               }
             else
               {
                  free(s->config.profile);
                  s->config.profile = NULL;
               }
             printf("RRR: store scale mul %1.5f\n", cs->scale_multiplier);
             s->config.scale_multiplier = cs->scale_multiplier;
             ret = EINA_TRUE;
          }
     }
   printf("--------------------------------------------------\n");
   return ret;
}

static void
_config_really_apply(E_Randr2_Screen *s, E_Config_Randr2_Screen *cs)
{
   if (cs)
     {
        s->config.enabled = EINA_TRUE;
        s->config.mode.w = cs->mode_w;
        s->config.mode.h = cs->mode_h;
        s->config.mode.refresh = cs->mode_refresh;
        s->config.mode.preferred = EINA_FALSE;
        s->config.rotation = cs->rotation;
        s->config.priority = cs->priority;
        free(s->config.relative.to);
        if (cs->rel_to) s->config.relative.to = strdup(cs->rel_to);
        else s->config.relative.to = NULL;
        s->config.relative.mode = cs->rel_mode;
        s->config.relative.align = cs->rel_align;
        free(s->config.profile);
        if (cs->profile) s->config.profile = strdup(cs->profile);
        else s->config.profile = NULL;
        s->config.scale_multiplier = cs->scale_multiplier;
     }
   else
     {
        s->config.enabled = EINA_FALSE;
        s->config.geom.x = 0;
        s->config.geom.y = 0;
        s->config.geom.w = 0;
        s->config.geom.h = 0;
        s->config.mode.w = 0;
        s->config.mode.h = 0;
        s->config.mode.refresh = 0.0;
        s->config.mode.preferred = EINA_FALSE;
        s->config.rotation = 0;
        s->config.priority = 0;
        free(s->config.relative.to);
        s->config.relative.to = NULL;
        s->config.relative.mode = E_RANDR2_RELATIVE_NONE;
        s->config.relative.align = 0.0;
        free(s->config.profile);
        s->config.profile = NULL;
        s->config.scale_multiplier = 0.0;
     }
}

static void
_config_apply(E_Randr2 *r, E_Config_Randr2 *cfg)
{
   Eina_List *l;
   E_Randr2_Screen *s;
   E_Config_Randr2_Screen *cs;

   if ((!r) || (!cfg)) return;
   EINA_LIST_FOREACH(r->screens, l, s)
     {
        printf("RRR: apply '%s'...\n", s->info.name);
        cs = NULL;
        if ((!_screen_closed(s)) && (s->info.connected))
          cs = e_randr2_config_screen_find(s, cfg);
        printf("RRR: connected =  %i\n", s->info.connected);
        if ((cs) && (cs->enabled))
          {
             printf("RRR: ... enabled\n");
             printf("RRR: ... priority = %i\n", cs->priority);
             _config_really_apply(s, cs);
          }
        else
          {
             printf("RRR: ... disabled\n");
             _config_really_apply(s, NULL);
          }
        s->config.configured = EINA_TRUE;
     }
}

static int
_config_screen_match_count(E_Randr2 *r, E_Config_Randr2 *cfg)
{
   Eina_List *l, *ll;
   E_Randr2_Screen *s;
   E_Config_Randr2_Screen *cs;
   int count = 0;

   EINA_LIST_FOREACH(cfg->screens, l, cs)
     {
        if (!cs->id) continue;
        EINA_LIST_FOREACH(r->screens, ll, s)
          {
             if ((!s->id) || (!s->info.connected) ||
                 (_screen_closed(s))) continue;
             if (!strcmp(cs->id, s->id)) count++;
          }
     }
   return count;
}

static char *
_screens_fingerprint(E_Randr2 *r)
{
   Eina_List *l;
   E_Randr2_Screen *s;
   Eina_Strbuf *buf;
   char *str;

   buf = eina_strbuf_new();
   if (!buf) return NULL;
   EINA_LIST_FOREACH(r->screens, l, s)
     {
        if (!s->id) eina_strbuf_append(buf, ":NULL:");
        else
          {
             eina_strbuf_append(buf, ":");
             eina_strbuf_append(buf, s->id);
             eina_strbuf_append(buf, ":");
             if (s->info.lid_closed) eina_strbuf_append(buf, ":LC:");
             else eina_strbuf_append(buf, ":LO:");
          }
     }
   str = eina_strbuf_string_steal(buf);
   eina_strbuf_free(buf);
   return str;
}

static Eina_Bool
_screens_differ(E_Randr2 *r1, E_Randr2 *r2)
{
   char *s1, *s2;
   Eina_Bool changed = EINA_FALSE;
   Eina_List *l, *ll;
   E_Randr2_Screen *s, *ss;

   // check monitor outputs and edids, plugged in things
   s1 = _screens_fingerprint(r1);
   s2 = _screens_fingerprint(r2);
   if ((!s1) && (!s2)) return EINA_FALSE;
   printf("RRR: check fingerprint...\n");
   if ((s1) && (s2) && (strcmp(s1, s2))) changed = EINA_TRUE;
   printf("RRR: ... fingerprint says %i\n", changed);
   free(s1);
   free(s2);
   // check screen config
   EINA_LIST_FOREACH(r2->screens, l, s)
     {
        if (!s->id) continue;
        EINA_LIST_FOREACH(r2->screens, ll, ss)
          {
             if ((ss->id) && (!strcmp(s->id, ss->id))) break;
             ss = NULL;
          }
        if (!ss) changed = EINA_TRUE;
        else if ((s->config.geom.x != ss->config.geom.x) ||
                 (s->config.geom.y != ss->config.geom.y) ||
                 (s->config.geom.w != ss->config.geom.w) ||
                 (s->config.geom.h != ss->config.geom.h) ||
                 (s->config.mode.w != ss->config.mode.w) ||
                 (s->config.mode.h != ss->config.mode.h) ||
                 (s->config.enabled != ss->config.enabled) ||
                 (s->config.rotation != ss->config.rotation) ||
                 (s->info.lid_closed != ss->info.lid_closed))
          changed = EINA_TRUE;
     }
   printf("RRR: changed = %i\n", changed);
   return changed;
}

static Eina_Bool
_cb_screen_change_delay(void *data EINA_UNUSED)
{
   Eina_Bool change = EINA_FALSE;
   _screen_delay_timer = NULL;
   printf("RRR: ... %i %i\n", event_screen, event_ignore);
   // if we had a screen plug/unplug etc. event and we shouldnt ignore it...
   if ((event_screen) && (!event_ignore))
     {
        E_Randr2 *rtemp;

        printf("RRR: reconfigure screens due to event...\n");
        rtemp = e_comp->screen->create();
        if (rtemp)
          {
             if (_screens_differ(e_randr2, rtemp)) change = EINA_TRUE;
             if (e_randr2_cfg->default_policy != E_RANDR2_POLICY_NONE)
               {
                  if (_config_update(rtemp, e_randr2_cfg, 1))
                    {
                       e_randr2_config_save();
                       if (e_randr2_cfg->default_policy != E_RANDR2_POLICY_ASK)
                         change = EINA_TRUE;
                    }
               }
             _info_free(rtemp);
          }
        printf("RRR: change = %i\n", change);
        // we plugged or unplugged some monitor - re-apply config so
        // known screens can be configured
        if (change) e_randr2_config_apply();
     }
   // update screen info after the above apply or due to external changes
   e_randr2_screeninfo_update();
   e_comp_canvas_resize(e_randr2->w, e_randr2->h);
   e_randr2_screens_setup(e_comp->w, e_comp->h);
   e_comp_canvas_update();
   // tell the rest of e some screen reconfigure thing happened
   ecore_event_add(E_EVENT_RANDR_CHANGE, NULL, NULL, NULL);
   event_screen = EINA_FALSE;
   event_ignore = EINA_FALSE;
   return EINA_FALSE;
}

static E_Randr2_Screen *
_screen_output_find(const char *out)
{
   E_Randr2_Screen *s;
   Eina_List *l;
   EINA_LIST_FOREACH(e_randr2->screens, l, s)
     {
        if (!strcmp(s->info.name, out)) return s;
     }
   return NULL;
}

static E_Randr2_Screen *
_screen_id_find(const char *id)
{
   E_Randr2_Screen *s;
   Eina_List *l;
   EINA_LIST_FOREACH(e_randr2->screens, l, s)
     {
        if (!strcmp(s->id, id)) return s;
     }
   return NULL;
}

static void
_screen_config_takeover(void)
{
   Eina_List *l;
   E_Randr2_Screen *s;
   EINA_LIST_FOREACH(e_randr2->screens, l, s)
     {
        s->config.configured = EINA_TRUE;
     }
}

static E_Config_Randr2_Screen *_config_screen_string_find(E_Config_Randr2 *cfg, const char *id);
static E_Randr2_Screen *_screen_fuzzy_fallback_find(E_Config_Randr2 *cfg, const char *id);

static E_Config_Randr2_Screen *
_config_screen_string_find(E_Config_Randr2 *cfg, const char *id)
{
   Eina_List *l;
   E_Config_Randr2_Screen *cs;

   if ((!id) || (!cfg)) return NULL;
   EINA_LIST_FOREACH(cfg->screens, l, cs)
     {
        if (!cs->id) continue;
        if (!strcmp(cs->id, id)) return cs;
     }
   return NULL;
}

static E_Randr2_Screen *
_screen_fuzzy_fallback_find(E_Config_Randr2 *cfg, const char *id)
{
   E_Randr2_Screen *s = NULL;
   char *p, *name;

   // strip out everythng in the string from / on as that is edid
   // and fall back to finding just the output name in the rel
   // to identifier, rather than the specific screen id
   p = strchr(id, '/');
   if (!p) return NULL;
   name = alloca((p - id) + 1);
   strncpy(name, id, p - id);
   name[p - id] = 0;

   s = _screen_id_find(id);
   if (!s) s = _screen_output_find(name);
   if (!s)
     {
        E_Config_Randr2_Screen *cs;

        cs = _config_screen_string_find(cfg, name);
        if ((cs) && (cs->id)) return _screen_fuzzy_fallback_find(cfg, cs->id);
     }
   return s;
}

static E_Config_Randr2_Screen *
_config_screen_clone_resolve(E_Config_Randr2 *cfg, const char *id, int *x, int *y)
{
   E_Config_Randr2_Screen *cs;
   E_Randr2_Screen *s;
   char *p, *name;

   cs = _config_screen_string_find(cfg, id);
   if (!cs) return NULL;

   name = alloca(strlen(cs->id) + 1);
   strcpy(name, cs->id);
   if ((p = strchr(name, '/'))) *p = 0;

   s = _screen_id_find(cs->id);
   if (!s) s = _screen_output_find(name);
   if (!s)
     {
        if ((cs->rel_mode == E_RANDR2_RELATIVE_CLONE) && (cs->rel_to))
          return _config_screen_clone_resolve(cfg, cs->rel_to, x, y);
        return NULL;
     }
   _screen_config_do(s);
   *x = s->config.geom.x;
   *y = s->config.geom.y;
   return cs;
}

static Eina_List *
_screen_clones_find(Eina_List *screens, E_Randr2_Screen *s)
{
   Eina_List *clones = NULL, *l;
   E_Randr2_Screen *s2, *sclone;
   Eina_Bool added;

   // go over all screens and as long as we have found another screen that is
   // cloned from something in the clone set, then keep looking.
   clones = eina_list_append(clones, s);
   added = EINA_TRUE;
   while (added)
     {
        added = EINA_FALSE;
        // og over all screens
        EINA_LIST_FOREACH(screens, l, s2)
          {
             // skip looking at screens we already have in our set
             if (eina_list_data_find(clones, s2)) continue;
             // if this clones another screen... get that as sclone
             if ((s2->config.relative.to) &&
                 (s2->config.relative.mode == E_RANDR2_RELATIVE_CLONE))
               {
                  sclone = _screen_fuzzy_fallback_find(e_randr2_cfg,
                                                       s2->config.relative.to);
                  if (!sclone) continue;
                  // if the screen s2 is relative to is not in our list, add
                  // s2 to our clones list as well
                  if (!eina_list_data_find(clones, sclone))
                    {
                       clones = eina_list_append(clones, sclone);
                       added = EINA_TRUE;
                    }
                  if (!eina_list_data_find(clones, s2))
                    {
                       clones = eina_list_append(clones, s2);
                       added = EINA_TRUE;
                    }
                  if (added)
                    {
                       // break our list walk, and iterate while again
                       break;
                    }
              }
          }
     }
   return clones;
}

static void
_screen_clones_common_sync(Eina_List *clones)
{
   E_Randr2_Screen *s, *sbase = NULL;
   E_Randr2_Mode *m, *m2, *mcommon = NULL;
   Eina_List *modes = NULL, *l, *l2, *l3;
   Eina_Bool common;
   int d, diff = 0x7fffffff;

   // find the base/root/master screen for clones
   EINA_LIST_FOREACH(clones, l, s)
     {
        // simple check - if it doesn't clone something else - then it's
        // the master (doesn't handle missing screens)
        if (s->config.relative.mode != E_RANDR2_RELATIVE_CLONE)
          {
             sbase = s;
             break;
          }
     }
   if (!sbase) return;
   // store all modes that master/base screen has and we'll "weed them out"
   EINA_LIST_FOREACH(sbase->info.modes, l, m)
     {
        modes = eina_list_append(modes, m);
     }
   // ensure it's configured
   _screen_config_do(sbase);
again:
   // we took all modes in the "master"
   EINA_LIST_FOREACH(modes, l, m)
     {
        // find all other screens in the clone list and...
        EINA_LIST_FOREACH(clones, l2, s)
          {
             if (s == sbase) continue; // skip if its the base/master
             // see if mode "m" is common to other clones
             common = EINA_FALSE;
             EINA_LIST_FOREACH(s->info.modes, l3, m2)
               {
                  /// only check res, not refresh
                  if ((m->w == m2->w) && (m->h == m2->h))
                    {
                       common = EINA_TRUE;
                       break;
                    }
               }
             // m is not common with modes in screen s - so removed it
             if (!common)
               {
                  modes = eina_list_remove_list(modes, l);
                  // the list is no longer save to walk - so let's just
                  // walk it again from scratch
                  goto again;
               }
          }
     }
   // no modes in common :(
   if (!modes) return;
   common = EINA_FALSE;
   EINA_LIST_FOREACH(modes, l, m)
     {
        // one of the common modes matches the base config - we are ok
        if ((m->w == sbase->config.mode.w) && (m->h == sbase->config.mode.h))
          {
             modes = eina_list_free(modes);
             return;
          }
     }
   // find a common mode since current config doesn't match
   EINA_LIST_FOREACH(modes, l, m)
     {
        // calculate a "difference" based on a combo of diff in area pixels
        // actual resolutin pixels (squared) and refresh delta * 10 squared
        d = abs((sbase->config.mode.w * sbase->config.mode.h) - (m->w * m->h));
        d += (sbase->config.mode.w - m->w) * (sbase->config.mode.w - m->w);
        d += (sbase->config.mode.h - m->h) * (sbase->config.mode.h - m->h);
        d += ((sbase->config.mode.refresh - m->refresh) * 10) *
             ((sbase->config.mode.refresh - m->refresh) * 10);
        if ((m->w > sbase->config.mode.w) || (m->h > sbase->config.mode.h))
          continue;
        if (d < diff)
          {
             diff = d;
             mcommon = m;
          }
     }
   modes = eina_list_free(modes);
   // no common mode with least difference found
   if (!mcommon) return;
   // we have a common mode - apply it to the base screen
   s = sbase;
   s->config.mode.w = mcommon->w;
   s->config.mode.h = mcommon->h;
   s->config.mode.refresh = mcommon->refresh;
   if ((s->config.rotation == 0) || (s->config.rotation == 180))
     {
        s->config.geom.w = s->config.mode.w;
        s->config.geom.h = s->config.mode.h;
     }
   else
     {
        s->config.geom.w = s->config.mode.h;
        s->config.geom.h = s->config.mode.w;
     }
}

static int _config_do_recurse = 0;

static void
_screen_config_do(E_Randr2_Screen *s)
{
   E_Randr2_Screen *s2 = NULL;
   Eina_List *cloneset;

   printf("RRR: screen do '%s'\n", s->info.name);
   if (_config_do_recurse > 5)
     {
        printf("RRR: screen config loop!\n");
        return;
     }
   _config_do_recurse++;
   // find dependent clones and find a common config res
   cloneset = _screen_clones_find(e_randr2->screens, s);
   if (cloneset)
     {
        _screen_clones_common_sync(cloneset);
        eina_list_free(cloneset);
     }
   // if screen has a dependency...
   if ((s->config.relative.mode != E_RANDR2_RELATIVE_UNKNOWN) &&
       (s->config.relative.mode != E_RANDR2_RELATIVE_NONE) &&
       (s->config.relative.to))
     {
        // if this screen is relative TO something (clone or left/right etc.
        // then calculate what it is relative to first
        s2 = _screen_fuzzy_fallback_find(e_randr2_cfg, s->config.relative.to);
        printf("RRR: '%s' is relative to '%s'\n", s->info.name, s2 ? s2->info.name : "NONE");
        if (s2)
          {
             _screen_config_do(s2);
             if (!s2->config.enabled) s2 = NULL;
          }
     }
   s->config.geom.x = 0;
   s->config.geom.y = 0;
   if ((s->config.rotation == 0) || (s->config.rotation == 180))
     {
        s->config.geom.w = s->config.mode.w;
        s->config.geom.h = s->config.mode.h;
     }
   else
     {
        s->config.geom.w = s->config.mode.h;
        s->config.geom.h = s->config.mode.w;
     }
   if (s2)
     {
        if (s->config.relative.mode == E_RANDR2_RELATIVE_CLONE)
          {
             printf("RRR: clone relative\n");
             s->config.geom.x = s2->config.geom.x;
             s->config.geom.y = s2->config.geom.y;
             s->config.geom.w = s2->config.geom.w;
             s->config.geom.h = s2->config.geom.h;
             s->config.mode.w = s2->config.mode.w;
             s->config.mode.h = s2->config.mode.h;
             s->config.rotation = s2->config.rotation;
             s->config.mode.refresh = s2->config.mode.refresh;
          }
        else if (s->config.relative.mode == E_RANDR2_RELATIVE_TO_LEFT)
          {
             printf("RRR: to left relative\n");
             s->config.geom.x = s2->config.geom.x - s->config.geom.w;
             s->config.geom.y = s2->config.geom.y +
             ((s2->config.geom.h - s->config.geom.h) *
              s->config.relative.align);
          }
        else if (s->config.relative.mode == E_RANDR2_RELATIVE_TO_RIGHT)
          {
             printf("RRR: to right relative\n");
             s->config.geom.x = s2->config.geom.x + s2->config.geom.w;
             s->config.geom.y = s2->config.geom.y +
             ((s2->config.geom.h - s->config.geom.h) *
              s->config.relative.align);
          }
        else if (s->config.relative.mode == E_RANDR2_RELATIVE_TO_ABOVE)
          {
             printf("RRR: to above relative\n");
             s->config.geom.x = s2->config.geom.x +
             ((s2->config.geom.w - s->config.geom.w) *
              s->config.relative.align);
             s->config.geom.y = s2->config.geom.y - s->config.geom.h;
          }
        else if (s->config.relative.mode == E_RANDR2_RELATIVE_TO_BELOW)
          {
             printf("RRR: to below relative\n");
             s->config.geom.x = s2->config.geom.x +
             ((s2->config.geom.w - s->config.geom.w) *
              s->config.relative.align);
             s->config.geom.y = s2->config.geom.y + s2->config.geom.h;
          }
     }
   else
     {
        if ((s->config.relative.mode == E_RANDR2_RELATIVE_CLONE) &&
            (s->config.relative.to))
          {
             E_Config_Randr2_Screen *cs;
             int x = 0, y = 0;

             cs = _config_screen_clone_resolve(e_randr2_cfg,
                                               s->config.relative.to, &x, &y);
             printf("RRR: clone relative - config %p\n", cs);
             if (cs)
               {
                  s->config.geom.x = x;
                  s->config.geom.y = y;
                  s->config.mode.w = cs->mode_w;
                  s->config.mode.h = cs->mode_h;
                  s->config.rotation = cs->rotation;
                  s->config.mode.refresh = cs->mode_refresh;
                  if ((cs->rotation == 0) || (cs->rotation == 180))
                    {
                       s->config.geom.w = s->config.mode.w;
                       s->config.geom.h = s->config.mode.h;
                    }
                  else
                    {
                       s->config.geom.w = s->config.mode.h;
                       s->config.geom.h = s->config.mode.w;
                    }
               }
          }
     }
   _config_do_recurse--;
}

static void
_screen_config_eval(void)
{
   Eina_List *l;
   E_Randr2_Screen *s;
   int minx, miny, maxx, maxy;

   EINA_LIST_FOREACH(e_randr2->screens, l, s)
     {
        if (s->config.configured) _screen_config_do(s);
     }
   minx = 65535;
   miny = 65535;
   maxx = -65536;
   maxy = -65536;
   printf("RRR:--------------------------------\n");
   EINA_LIST_FOREACH(e_randr2->screens, l, s)
     {
        if (!s->config.enabled) continue;
        if (s->config.geom.x < minx) minx = s->config.geom.x;
        if (s->config.geom.y < miny) miny = s->config.geom.y;
        if ((s->config.geom.x + s->config.geom.w) > maxx)
          maxx = s->config.geom.x + s->config.geom.w;
        if ((s->config.geom.y + s->config.geom.h) > maxy)
          maxy = s->config.geom.y + s->config.geom.h;
        printf("RRR: s: '%s' @ %i %i - %ix%i\n",
               s->info.name,
               s->config.geom.x, s->config.geom.y,
               s->config.geom.w, s->config.geom.h);
     }
   printf("RRR:--- %i %i -> %i %i\n", minx, miny, maxx, maxy);
   EINA_LIST_FOREACH(e_randr2->screens, l, s)
     {
        s->config.geom.x -= minx;
        s->config.geom.y -= miny;
     }
   e_randr2->w = maxx - minx;
   e_randr2->h = maxy - miny;
}

static void
_screen_config_maxsize(void)
{
   Eina_List *l;
   E_Randr2_Screen *s;
   int maxx, maxy;

   maxx = -65536;
   maxy = -65536;
   printf("RRR:-------------------------------- 2\n");
   EINA_LIST_FOREACH(e_randr2->screens, l, s)
     {
        if (!s->config.enabled) continue;
        if ((s->config.geom.x + s->config.geom.w) > maxx)
          maxx = s->config.geom.x + s->config.geom.w;
        if ((s->config.geom.y + s->config.geom.h) > maxy)
          maxy = s->config.geom.y + s->config.geom.h;
        printf("RRR: '%s': %i %i %ix%i\n",
               s->info.name,
               s->config.geom.x, s->config.geom.y,
               s->config.geom.w, s->config.geom.h);
     }
   printf("RRR: result max: %ix%i\n", maxx, maxy);
   e_randr2->w = maxx;
   e_randr2->h = maxy;
}

static int
_screen_sort_cb(const void *data1, const void *data2)
{
   const E_Randr2_Screen *s1 = data1, *s2 = data2;
   int dif;

   dif = -(s1->config.priority - s2->config.priority);
   if (dif == 0)
     {
        dif = s1->config.geom.x - s2->config.geom.x;
        if (dif == 0)
          dif = s1->config.geom.y - s2->config.geom.y;
     }
   return dif;
}

E_API void
e_randr2_screen_refresh_queue(Eina_Bool lid_event)
{
   // delay handling of screen shances as they can come in in a series over
   // time and thus we can batch up responding to them by waiting 1.0 sec
   if (_screen_delay_timer)
     ecore_timer_loop_reset(_screen_delay_timer);
   else
     _screen_delay_timer = ecore_timer_loop_add(1.0, _cb_screen_change_delay, NULL);
   event_screen |= !!lid_event;
}

E_API E_Config_Randr2_Screen *
e_randr2_config_screen_find(E_Randr2_Screen *s, E_Config_Randr2 *cfg)
{
   Eina_List *l;
   E_Config_Randr2_Screen *cs;

   if ((!s) || (!cfg)) return NULL;
   if (!s->id) return NULL;
   EINA_LIST_FOREACH(cfg->screens, l, cs)
     {
        if (!cs->id) continue;
        if (!strcmp(cs->id, s->id)) return cs;
     }
   return NULL;
}

E_API void
e_randr2_screens_setup(int rw, int rh)
{
   int i;
   E_Screen *screen;
   Eina_List *screens = NULL, *screens_rem;
   Eina_List *all_screens = NULL;
   Eina_List *l, *ll;
   E_Randr2_Screen *s, *s2, *s_chosen;
   Eina_Bool removed;

   if ((!e_randr2) || (!e_randr2->screens)) goto out;
   // put screens in tmp list
   EINA_LIST_FOREACH(e_randr2->screens, l, s)
     {
        if ((s->config.enabled) &&
            (s->config.geom.w > 0) &&
            (s->config.geom.h > 0))
          {
             screens = eina_list_append(screens, s);
          }
     }
   // remove overlapping screens - if a set of screens overlap, keep the
   // smallest/lowest res
   do
     {
        removed = EINA_FALSE;

        EINA_LIST_FOREACH(screens, l, s)
          {
             screens_rem = NULL;

             EINA_LIST_FOREACH(l->next, ll, s2)
               {
                  if (E_INTERSECTS(s->config.geom.x, s->config.geom.y,
                                   s->config.geom.w, s->config.geom.h,
                                   s2->config.geom.x, s2->config.geom.y,
                                   s2->config.geom.w, s2->config.geom.h))
                    {
                       if (!screens_rem)
                         screens_rem = eina_list_append(screens_rem, s);
                       screens_rem = eina_list_append(screens_rem, s2);
                    }
               }
             // we have intersecting screens - choose the lowest res one
             if (screens_rem)
               {
                  removed = EINA_TRUE;
                  // find the smallest screen (chosen one)
                  s_chosen = NULL;
                  EINA_LIST_FOREACH(screens_rem, ll, s2)
                    {
                       if (!s_chosen) s_chosen = s2;
                       else
                         {
                            if ((s_chosen->config.geom.w *
                                 s_chosen->config.geom.h) >
                                (s2->config.geom.w *
                                 s2->config.geom.h))
                              s_chosen = s2;
                         }
                    }
                  // remove all from screens but the chosen one
                  EINA_LIST_FREE(screens_rem, s2)
                    {
                       if (s2 != s_chosen)
                         screens = eina_list_remove_list(screens, l);
                    }
                  // break our list walk and try again
                  break;
               }
          }
     }
   while (removed);
   // sort screens by priority etc.
   screens = eina_list_sort(screens, 0, _screen_sort_cb);
   i = 0;
   EINA_LIST_FOREACH(screens, l, s)
     {
        screen = E_NEW(E_Screen, 1);
        screen->escreen = screen->screen = i;
        screen->x = s->config.geom.x;
        screen->y = s->config.geom.y;
        screen->w = s->config.geom.w;
        screen->h = s->config.geom.h;
        if (s->id) screen->id = strdup(s->id);

        all_screens = eina_list_append(all_screens, screen);
        printf("xinerama screen %i %i %ix%i\n", screen->x, screen->y, screen->w, screen->h);
        INF("E INIT: XINERAMA SCREEN: [%i][%i], %ix%i+%i+%i",
            i, i, screen->w, screen->h, screen->x, screen->y);
        i++;
     }
   eina_list_free(screens);
   // if we have NO screens at all (above - i will be 0) AND we have no
   // existing screens set up in xinerama - then just say root window size
   // is the entire screen. this should handle the case where you unplug ALL
   // screens from an existing setup (unplug external monitors and/or close
   // laptop lid), in which case as long as at least one screen is configured
   // in xinerama, it will be left-as is until next time we re-eval screen
   // setup and have at least one screen
   printf("xinerama setup............... %i %p\n", i, e_xinerama_screens_all_get());
   if ((i == 0) && (!e_xinerama_screens_all_get()))
     {
out:
        screen = E_NEW(E_Screen, 1);
        screen->escreen = screen->screen = 0;
        screen->x = 0;
        screen->y = 0;
        if ((rw > 0) && (rh > 0))
          screen->w = rw, screen->h = rh;
        else
          ecore_evas_screen_geometry_get(e_comp->ee, NULL, NULL, &screen->w, &screen->h);
        all_screens = eina_list_append(all_screens, screen);
     }
   e_xinerama_screens_set(all_screens);
}

E_API E_Randr2_Screen *
e_randr2_screen_id_find(const char *id)
{
   Eina_List *l;
   E_Randr2_Screen *s;

   EINA_LIST_FOREACH(e_randr2->screens, l, s)
     {
        if (!strcmp(id, s->id)) return s;
     }
   return NULL;
}

E_API double
e_randr2_screen_dpi_get(E_Randr2_Screen *s)
{
   double dpi1, dpi2;

   if ((s->info.size.w <= 0) || (s->info.size.h <= 0)) return 0.0;
   dpi1 = (25.4 * (double)(s->config.mode.w)) / (double)(s->info.size.w);
   dpi2 = (25.4 * (double)(s->config.mode.h)) / (double)(s->info.size.h);
   return (dpi1 + dpi2) / 2.0;
}

static int
_modelist_sort(const void *a, const void *b)
{
   const E_Randr2_Mode *ma = a, *mb = b;

   /* largest resolutions first */
   if ((ma->w * ma->h) > (mb->w * mb->h)) return -1;
   /* highest refresh first */
   if (ma->refresh > mb->refresh) return -1;
   return 1;
}

EAPI void
e_randr2_screen_modes_sort(E_Randr2_Screen *s)
{
   EINA_SAFETY_ON_NULL_RETURN(s);
   s->info.modes = eina_list_sort(s->info.modes, 0, _modelist_sort);
}