#include "e.h" #ifdef HAVE_EEZE # include #endif // FIXME: backlight should be tied per zone but this implementation is just // a signleton right now as thats 99% of use cases. but api supports // doing more. for now make it work in the singleton #define MODE_NONE -1 #define MODE_RANDR 0 #define MODE_SYS 1 static double bl_val = 1.0; static double bl_animval = 1.0; static int sysmode = MODE_NONE; static Ecore_Animator *bl_anim = NULL; static Eina_List *bl_devs = NULL; static Ecore_Event_Handler *_e_backlight_handler_config_mode = NULL; static Ecore_Event_Handler *_e_backlight_handler_border_fullscreen = NULL; static Ecore_Event_Handler *_e_backlight_handler_border_unfullscreen = NULL; static Ecore_Event_Handler *_e_backlight_handler_border_remove = NULL; static Ecore_Event_Handler *_e_backlight_handler_border_iconify = NULL; static Ecore_Event_Handler *_e_backlight_handler_border_uniconify = NULL; static Ecore_Event_Handler *_e_backlight_handler_border_desk_set = NULL; static Ecore_Event_Handler *_e_backlight_handler_desk_show = NULL; static void _e_backlight_update(E_Zone *zone); static void _e_backlight_set(E_Zone *zone, double val); static Eina_Bool _bl_anim(void *data, double pos); static Eina_Bool bl_avail = EINA_FALSE; static Eina_Bool _e_backlight_handler(void *d, int type, void *ev); #ifdef HAVE_EEZE static double bl_delayval = 1.0; static const char *bl_sysval = NULL; static Ecore_Event_Handler *bl_sys_exit_handler = NULL; static Ecore_Exe *bl_sys_set_exe = NULL; static Eina_Bool bl_sys_pending_set = EINA_FALSE; static Eina_Bool bl_sys_set_exe_ready = EINA_TRUE; static void _bl_sys_find(void); static void _bl_sys_level_get(void); static Eina_Bool _e_bl_cb_exit(void *data __UNUSED__, int type __UNUSED__, void *event); static void _bl_sys_level_set(double val); #endif EAPI int E_EVENT_BACKLIGHT_CHANGE = -1; EINTERN int e_backlight_init(void) { #ifdef HAVE_EEZE eeze_init(); #endif // why did someone do this? this makes it ONLY work if xrandr has bl support. // WRONG! // bl_avail = ecore_x_randr_output_backlight_available(); bl_avail = EINA_TRUE; _e_backlight_handler_config_mode = ecore_event_handler_add (E_EVENT_CONFIG_MODE_CHANGED, _e_backlight_handler, NULL); _e_backlight_handler_border_fullscreen = ecore_event_handler_add (E_EVENT_BORDER_FULLSCREEN, _e_backlight_handler, NULL); _e_backlight_handler_border_unfullscreen = ecore_event_handler_add (E_EVENT_BORDER_UNFULLSCREEN, _e_backlight_handler, NULL); _e_backlight_handler_border_remove = ecore_event_handler_add (E_EVENT_BORDER_REMOVE, _e_backlight_handler, NULL); _e_backlight_handler_border_iconify = ecore_event_handler_add (E_EVENT_BORDER_ICONIFY, _e_backlight_handler, NULL); _e_backlight_handler_border_uniconify = ecore_event_handler_add (E_EVENT_BORDER_UNICONIFY, _e_backlight_handler, NULL); _e_backlight_handler_border_desk_set = ecore_event_handler_add (E_EVENT_BORDER_DESK_SET, _e_backlight_handler, NULL); _e_backlight_handler_desk_show = ecore_event_handler_add (E_EVENT_DESK_SHOW, _e_backlight_handler, NULL); if (bl_avail) { e_backlight_update(); if (!getenv("E_RESTART")) { e_backlight_level_set(NULL, 0.0, 0.0); e_backlight_level_set(NULL, e_config->backlight.normal, 0.0); } } E_EVENT_BACKLIGHT_CHANGE = ecore_event_type_new(); return 1; } EINTERN int e_backlight_shutdown(void) { const char *s; if (bl_anim) ecore_animator_del(bl_anim); bl_anim = NULL; if (e_config->backlight.mode != E_BACKLIGHT_MODE_NORMAL) e_backlight_level_set(NULL, e_config->backlight.normal, 0.0); EINA_LIST_FREE(bl_devs, s) eina_stringshare_del(s); #ifdef HAVE_EEZE if (bl_sysval) eina_stringshare_del(bl_sysval); bl_sysval = NULL; if (bl_sys_exit_handler) ecore_event_handler_del(bl_sys_exit_handler); bl_sys_exit_handler = NULL; bl_sys_set_exe = NULL; bl_sys_pending_set = EINA_FALSE; eeze_shutdown(); #endif if (_e_backlight_handler_config_mode) { ecore_event_handler_del(_e_backlight_handler_config_mode); _e_backlight_handler_config_mode = NULL; } if (_e_backlight_handler_border_fullscreen) { ecore_event_handler_del(_e_backlight_handler_border_fullscreen); _e_backlight_handler_border_fullscreen = NULL; } if (_e_backlight_handler_border_unfullscreen) { ecore_event_handler_del(_e_backlight_handler_border_unfullscreen); _e_backlight_handler_border_unfullscreen = NULL; } if (_e_backlight_handler_border_remove) { ecore_event_handler_del(_e_backlight_handler_border_remove); _e_backlight_handler_border_remove = NULL; } if (_e_backlight_handler_border_iconify) { ecore_event_handler_del(_e_backlight_handler_border_iconify); _e_backlight_handler_border_iconify = NULL; } if (_e_backlight_handler_border_uniconify) { ecore_event_handler_del(_e_backlight_handler_border_uniconify); _e_backlight_handler_border_uniconify = NULL; } if (_e_backlight_handler_border_desk_set) { ecore_event_handler_del(_e_backlight_handler_border_desk_set); _e_backlight_handler_border_desk_set = NULL; } if (_e_backlight_handler_desk_show) { ecore_event_handler_del(_e_backlight_handler_desk_show); _e_backlight_handler_desk_show = NULL; } return 1; } EAPI Eina_Bool e_backlight_exists(void) { if (sysmode == MODE_NONE) return EINA_FALSE; return EINA_TRUE; } EAPI void e_backlight_update(void) { Eina_List *m, *c, *z; E_Manager *man; E_Container *con; E_Zone *zone; if (bl_avail == EINA_FALSE) return; EINA_LIST_FOREACH(e_manager_list(), m, man) { EINA_LIST_FOREACH(man->containers, c, con) { EINA_LIST_FOREACH(con->zones, z, zone) { _e_backlight_update(zone); } } } } EAPI void e_backlight_level_set(E_Zone *zone, double val, double tim) { double bl_now; // zone == NULL == everything // set backlight associated with zone to val over period of tim // if tim == 0.0 - then do it instantnly, if time == -1 use some default // transition time if (val < 0.0) val = 0.0; else if (val > 1.0) val = 1.0; if ((val == bl_val) && (!bl_anim)) return; if (!zone) zone = e_util_zone_current_get(e_manager_current_get()); bl_now = bl_val; bl_val = val; // if (e_config->backlight.mode != E_BACKLIGHT_MODE_NORMAL) return; if (e_config->backlight.mode == E_BACKLIGHT_MODE_NORMAL) tim = 0.5; else if (tim < 0.0) tim = e_config->backlight.transition; ecore_event_add(E_EVENT_BACKLIGHT_CHANGE, NULL, NULL, NULL); if (tim == 0.0) { if (bl_anim) { ecore_animator_del(bl_anim); bl_anim = NULL; } _e_backlight_set(zone, val); return; } if (bl_anim) ecore_animator_del(bl_anim); bl_anim = ecore_animator_timeline_add(tim, _bl_anim, zone); bl_animval = bl_now; } EAPI double e_backlight_level_get(E_Zone *zone __UNUSED__) { // zone == NULL == everything return bl_val; } EAPI void e_backlight_mode_set(E_Zone *zone, E_Backlight_Mode mode) { // zone == NULL == everything if (e_config->backlight.mode == mode) return; e_config->backlight.mode = mode; if (e_config->backlight.mode == E_BACKLIGHT_MODE_NORMAL) { e_backlight_level_set(zone, e_config->backlight.normal, -1.0); } else if (e_config->backlight.mode == E_BACKLIGHT_MODE_OFF) { e_backlight_level_set(zone, 0.0, -1.0); } else if (e_config->backlight.mode == E_BACKLIGHT_MODE_DIM) { e_backlight_level_set(zone, e_config->backlight.dim, -1.0); } else if (e_config->backlight.mode == E_BACKLIGHT_MODE_MAX) e_backlight_level_set(zone, 1.0, -1.0); } EAPI E_Backlight_Mode e_backlight_mode_get(E_Zone *zone __UNUSED__) { // zone == NULL == everything return e_config->backlight.mode; } EAPI const Eina_List * e_backlight_devices_get(void) { return bl_devs; } /* local subsystem functions */ static Eina_Bool _e_backlight_handler(void *d __UNUSED__, int type __UNUSED__, void *ev __UNUSED__) { e_backlight_update(); return ECORE_CALLBACK_PASS_ON; } static void _e_backlight_update(E_Zone *zone) { double x_bl = -1.0; Ecore_X_Window root; Ecore_X_Randr_Output *out; int i, num = 0; root = zone->container->manager->root; // try randr out = ecore_x_randr_window_outputs_get(root, &num); if ((out) && (num > 0) && (ecore_x_randr_output_backlight_available())) { char *name; const char *s; Eina_Bool gotten = EINA_FALSE; EINA_LIST_FREE(bl_devs, s) eina_stringshare_del(s); for (i = 0; i < num; i++) { name = ecore_x_randr_output_name_get(root, out[i], NULL); bl_devs = eina_list_append(bl_devs, eina_stringshare_add(name)); if ((name) && (e_config->backlight.sysdev) && (!strcmp(name, e_config->backlight.sysdev))) { x_bl = ecore_x_randr_output_backlight_level_get(root, out[i]); gotten = EINA_TRUE; } free(name); } if (!gotten) x_bl = ecore_x_randr_output_backlight_level_get(root, out[0]); } free(out); if (x_bl >= 0.0) { bl_val = x_bl; sysmode = MODE_RANDR; return; } #ifdef HAVE_EEZE _bl_sys_find(); if (bl_sysval) { sysmode = MODE_SYS; _bl_sys_level_get(); return; } #endif } static void _e_backlight_set(E_Zone *zone, double val) { if (sysmode == MODE_RANDR) { Ecore_X_Window root; Ecore_X_Randr_Output *out; int num = 0, i; char *name; root = zone->container->manager->root; out = ecore_x_randr_window_outputs_get(root, &num); if ((out) && (num > 0)) { Eina_Bool gotten = EINA_FALSE; for (i = 0; i < num; i++) { name = ecore_x_randr_output_name_get(root, out[i], NULL); if (name) { if ((e_config->backlight.sysdev) && (!strcmp(name, e_config->backlight.sysdev))) { ecore_x_randr_output_backlight_level_set(root, out[i], val); gotten = EINA_TRUE; } free(name); } } if (!gotten) { for (i = 0; i < num; i++) ecore_x_randr_output_backlight_level_set(root, out[i], val); } } free(out); } #ifdef HAVE_EEZE else if (sysmode == MODE_SYS) { if (bl_sysval) { _bl_sys_level_set(val); } } #endif } static Eina_Bool _bl_anim(void *data, double pos) { E_Zone *zone = data; double v; // FIXME: if zone is deleted while anim going... bad things. pos = ecore_animator_pos_map(pos, ECORE_POS_MAP_DECELERATE, 0.0, 0.0); v = (bl_animval * (1.0 - pos)) + (bl_val * pos); _e_backlight_set(zone, v); if (pos >= 1.0) { bl_anim = NULL; return EINA_FALSE; } return EINA_TRUE; } #ifdef HAVE_EEZE static void _bl_sys_find(void) { Eina_List *l, *devs, *pdevs = NULL; Eina_Bool use; const char *f, *s; int v; devs = eeze_udev_find_by_filter("backlight", NULL, NULL); if (!devs) { /* FIXME: need to make this more precise so we don't set keyboard LEDs or something */ devs = eeze_udev_find_by_filter("leds", NULL, NULL); if (!devs) return; } if (eina_list_count(devs) > 1) { /* prefer backlights of type "firmware" where available */ EINA_LIST_FOREACH(devs, l, f) { s = eeze_udev_syspath_get_sysattr(f, "type"); use = (s && (!strcmp(s, "firmware"))); eina_stringshare_del(s); if (!use) continue; s = eeze_udev_syspath_get_sysattr(f, "brightness"); if (!s) continue; v = atoi(s); eina_stringshare_del(s); if (v < 0) continue; pdevs = eina_list_append(pdevs, eina_stringshare_add(f)); eina_stringshare_del(f); l->data = NULL; } EINA_LIST_FOREACH(devs, l, f) { if (!l->data) continue; s = eeze_udev_syspath_get_sysattr(f, "brightness"); if (!s) continue; v = atoi(s); eina_stringshare_del(s); if (v < 0) continue; pdevs = eina_list_append(pdevs, eina_stringshare_add(f)); } } if (!pdevs) { /* add the other backlight or led's if none found */ EINA_LIST_FOREACH(devs, l, f) { use = EINA_FALSE; s = eeze_udev_syspath_get_sysattr(f, "brightness"); if (!s) continue; v = atoi(s); eina_stringshare_del(s); if (v < 0) continue; pdevs = eina_list_append(pdevs, eina_stringshare_add(f)); } } /* clear out original devs list now we've filtered */ EINA_LIST_FREE(devs, f) { if (f) eina_stringshare_del(f); } /* clear out old configured bl sysval */ if (bl_sysval) { eina_stringshare_del(bl_sysval); bl_sysval = NULL; } EINA_LIST_FREE(bl_devs, s) eina_stringshare_del(s); /* if configured backlight is there - use it, or if not use first */ EINA_LIST_FOREACH(pdevs, l, f) { bl_devs = eina_list_append(bl_devs, eina_stringshare_add(f)); if (!bl_sysval) { if ((e_config->backlight.sysdev) && (!strcmp(e_config->backlight.sysdev, f))) bl_sysval = eina_stringshare_add(f); } } if (!bl_sysval) { EINA_LIST_FOREACH(pdevs, l, f) { if (!bl_sysval) bl_sysval = eina_stringshare_add(f); } } /* clear out preferred devs list */ EINA_LIST_FREE(pdevs, f) { eina_stringshare_del(f); } } static void _bl_sys_level_get(void) { int maxval, val; const char *str; if (bl_anim) return; str = eeze_udev_syspath_get_sysattr(bl_sysval, "max_brightness"); if (!str) return; maxval = atoi(str); eina_stringshare_del(str); if (maxval < 0) maxval = 255; str = eeze_udev_syspath_get_sysattr(bl_sysval, "brightness"); if (!str) return; val = atoi(str); eina_stringshare_del(str); if ((!maxval) && (!val)) { bl_val = 0; sysmode = MODE_NONE; return; } if (!maxval) maxval = 255; if ((val >= 0) && (val <= maxval)) bl_val = (double)val / (double)maxval; // fprintf(stderr, "GET: %i/%i (%1.3f)\n", val, maxval, bl_val); } static Eina_Bool _e_bl_cb_ext_delay(void *data __UNUSED__) { bl_sys_set_exe_ready = EINA_TRUE; if (bl_sys_pending_set) { bl_sys_pending_set = EINA_FALSE; _bl_sys_level_set(bl_delayval); } return EINA_FALSE; } static Eina_Bool _e_bl_cb_exit(void *data __UNUSED__, int type __UNUSED__, void *event) { Ecore_Exe_Event_Del *ev; ev = event; if (ev->exe == bl_sys_set_exe) { bl_sys_set_exe_ready = EINA_FALSE; bl_sys_set_exe = NULL; ecore_timer_add(0.05, _e_bl_cb_ext_delay, NULL); } return ECORE_CALLBACK_RENEW; } static void _bl_sys_level_set(double val) { char buf[PATH_MAX]; if (!bl_sys_exit_handler) bl_sys_exit_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, _e_bl_cb_exit, NULL); bl_delayval = val; if ((bl_sys_set_exe) || (!bl_sys_set_exe_ready)) { bl_sys_pending_set = EINA_TRUE; return; } // fprintf(stderr, "SET: %1.3f\n", val); snprintf(buf, sizeof(buf), "%s/enlightenment/utils/enlightenment_backlight %i %s", e_prefix_lib_get(), (int)(val * 1000.0), bl_sysval); bl_sys_set_exe = ecore_exe_run(buf, NULL); } #endif