enlightenment/src/bin/e_acpi.c

312 lines
8.6 KiB
C
Raw Normal View History

#include "e.h"
/* TODO:
*
* Sanatize data received from acpi for message status into something
* meaningful (ie: 00000002 == LID_CLOSED, etc, etc).
*
* Find someone with a WIFI that actually emits ACPI events and add/debug the
* E_EVENT_ACPI for wifi.
*
* Add e_actions for bindings.
*/
/* local structures */
typedef struct _ACPIDevice ACPIDevice; // for mapping device names to type
struct _ACPIDevice
{
const char *name;
int type;
};
/* local function prototypes */
static int _e_acpi_cb_server_del(void *data __UNUSED__, int type __UNUSED__, void *event);
static int _e_acpi_cb_server_data(void *data __UNUSED__, int type __UNUSED__, void *event);
static void _e_acpi_cb_event_free(void *data __UNUSED__, void *event);
static int _e_acpi_lid_status_get(const char *device, const char *bus);
static int _e_acpi_cb_event(void *data __UNUSED__, int type __UNUSED__, void *event);
/* local variables */
static int _e_acpi_events_frozen = 0;
static Ecore_Con_Server *_e_acpid = NULL;
static Eina_List *_e_acpid_hdls = NULL;
static Eina_Hash *_e_acpid_devices = NULL;
static ACPIDevice _devices[] =
{
/* DO NOT TRANSLATE THESE. */
/* standardized ACPI device name, corresponding E_ACPI_TYPE */
{"ac_adapter", E_ACPI_TYPE_AC_ADAPTER},
{"battery", E_ACPI_TYPE_BATTERY},
{"button/lid", E_ACPI_TYPE_LID},
{"button/power", E_ACPI_TYPE_POWER},
{"button/sleep", E_ACPI_TYPE_SLEEP},
{"fan", E_ACPI_TYPE_FAN},
{"processor", E_ACPI_TYPE_PROCESSOR},
{"thermal_zone", E_ACPI_TYPE_THERMAL},
{"video", E_ACPI_TYPE_VIDEO},
{NULL, E_ACPI_TYPE_UNKNOWN}
};
/* public variables */
EAPI int E_EVENT_ACPI_UNKNOWN = 0;
EAPI int E_EVENT_ACPI_AC_ADAPTER = 0;
EAPI int E_EVENT_ACPI_BATTERY = 0;
EAPI int E_EVENT_ACPI_FAN = 0;
EAPI int E_EVENT_ACPI_LID = 0;
EAPI int E_EVENT_ACPI_POWER = 0;
EAPI int E_EVENT_ACPI_PROCESSOR = 0;
EAPI int E_EVENT_ACPI_SLEEP = 0;
EAPI int E_EVENT_ACPI_THERMAL = 0;
EAPI int E_EVENT_ACPI_VIDEO = 0;
EAPI int E_EVENT_ACPI_WIFI = 0;
/* public functions */
EAPI int
e_acpi_init(void)
{
const ACPIDevice *dev;
E_EVENT_ACPI_UNKNOWN = ecore_event_type_new();
E_EVENT_ACPI_AC_ADAPTER = ecore_event_type_new();
E_EVENT_ACPI_BATTERY = ecore_event_type_new();
E_EVENT_ACPI_FAN = ecore_event_type_new();
E_EVENT_ACPI_LID = ecore_event_type_new();
E_EVENT_ACPI_POWER = ecore_event_type_new();
E_EVENT_ACPI_PROCESSOR = ecore_event_type_new();
E_EVENT_ACPI_SLEEP = ecore_event_type_new();
E_EVENT_ACPI_THERMAL = ecore_event_type_new();
E_EVENT_ACPI_VIDEO = ecore_event_type_new();
E_EVENT_ACPI_WIFI = ecore_event_type_new();
/* check for running acpid */
if (!ecore_file_exists("/var/run/acpid.socket")) return 1;
/* try to connect to acpid socket */
_e_acpid = ecore_con_server_connect(ECORE_CON_LOCAL_SYSTEM,
"/var/run/acpid.socket", -1, NULL);
if (!_e_acpid) return 1;
/* create new device hash and fill it */
_e_acpid_devices = eina_hash_string_superfast_new(NULL);
for (dev = _devices; dev->type > E_ACPI_TYPE_UNKNOWN; dev++)
eina_hash_direct_add(_e_acpid_devices, dev->name, dev);
/* setup handlers */
_e_acpid_hdls =
eina_list_append(_e_acpid_hdls,
ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL,
_e_acpi_cb_server_del, NULL));
_e_acpid_hdls =
eina_list_append(_e_acpid_hdls,
ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA,
_e_acpi_cb_server_data, NULL));
/* Add handlers for standard acpi events */
_e_acpid_hdls =
eina_list_append(_e_acpid_hdls,
ecore_event_handler_add(E_EVENT_ACPI_AC_ADAPTER,
_e_acpi_cb_event, NULL));
_e_acpid_hdls =
eina_list_append(_e_acpid_hdls,
ecore_event_handler_add(E_EVENT_ACPI_LID,
_e_acpi_cb_event, NULL));
_e_acpid_hdls =
eina_list_append(_e_acpid_hdls,
ecore_event_handler_add(E_EVENT_ACPI_POWER,
_e_acpi_cb_event, NULL));
_e_acpid_hdls =
eina_list_append(_e_acpid_hdls,
ecore_event_handler_add(E_EVENT_ACPI_SLEEP,
_e_acpi_cb_event, NULL));
return 1;
}
EAPI int
e_acpi_shutdown(void)
{
Ecore_Event_Handler *hdl;
/* destroy the device hash */
if (_e_acpid_devices) eina_hash_free(_e_acpid_devices);
_e_acpid_devices = NULL;
/* cleanup event handlers */
EINA_LIST_FREE(_e_acpid_hdls, hdl)
ecore_event_handler_del(hdl);
/* kill the server if existing */
if (_e_acpid) ecore_con_server_del(_e_acpid);
_e_acpid = NULL;
return 1;
}
EAPI void
e_acpi_events_freeze(void)
{
_e_acpi_events_frozen++;
}
EAPI void
e_acpi_events_thaw(void)
{
_e_acpi_events_frozen--;
}
/* local functions */
static int
_e_acpi_cb_server_del(void *data __UNUSED__, int type __UNUSED__, void *event)
{
Ecore_Con_Event_Server_Del *ev;
Ecore_Event_Handler *hdl;
ev = event;
if (ev->server != _e_acpid) return 1;
/* cleanup event handlers */
EINA_LIST_FREE(_e_acpid_hdls, hdl)
ecore_event_handler_del(hdl);
/* kill the server if existing */
if (_e_acpid) ecore_con_server_del(_e_acpid);
_e_acpid = NULL;
return 1;
}
static int
_e_acpi_cb_server_data(void *data __UNUSED__, int type __UNUSED__, void *event)
{
Ecore_Con_Event_Server_Data *ev;
ACPIDevice *dev;
E_Event_Acpi *acpi_event;
int res, sig, status, event_type;
char device[1024], bus[1024];
ev = event;
/* write out actual acpi received data to stdout for debugging
res = fwrite(ev->data, ev->size, 1, stdout);
*/
/* parse out this acpi string into separate pieces */
if (sscanf(ev->data, "%s %s %d %d", device, bus, &sig, &status) != 4)
return 1;
/* create new event structure to raise */
acpi_event = E_NEW(E_Event_Acpi, 1);
acpi_event->bus_id = eina_stringshare_add(bus);
acpi_event->signal = sig;
acpi_event->status = status;
/* determine which device this event is for */
if ((dev = eina_hash_find(_e_acpid_devices, device)))
{
acpi_event->device = eina_stringshare_add(dev->name);
acpi_event->type = dev->type;
}
else
{
acpi_event->device = eina_stringshare_add(device);
acpi_event->type = E_ACPI_TYPE_UNKNOWN;
}
/* based on device type, determine the event to raise */
switch (acpi_event->type)
{
case E_ACPI_TYPE_AC_ADAPTER:
event_type = E_EVENT_ACPI_AC_ADAPTER;
break;
case E_ACPI_TYPE_BATTERY:
event_type = E_EVENT_ACPI_BATTERY;
break;
case E_ACPI_TYPE_FAN:
event_type = E_EVENT_ACPI_FAN;
break;
case E_ACPI_TYPE_LID:
event_type = E_EVENT_ACPI_LID;
acpi_event->status =
_e_acpi_lid_status_get(acpi_event->device, acpi_event->bus_id);
break;
case E_ACPI_TYPE_POWER:
event_type = E_EVENT_ACPI_POWER;
break;
case E_ACPI_TYPE_PROCESSOR:
event_type = E_EVENT_ACPI_PROCESSOR;
break;
case E_ACPI_TYPE_SLEEP:
event_type = E_EVENT_ACPI_SLEEP;
break;
case E_ACPI_TYPE_THERMAL:
event_type = E_EVENT_ACPI_THERMAL;
break;
case E_ACPI_TYPE_VIDEO:
event_type = E_EVENT_ACPI_VIDEO;
break;
default:
event_type = E_EVENT_ACPI_UNKNOWN;
break;
}
/* actually raise the event */
ecore_event_add(event_type, acpi_event, _e_acpi_cb_event_free, NULL);
return 1;
}
static void
_e_acpi_cb_event_free(void *data __UNUSED__, void *event)
{
E_Event_Acpi *ev;
if (!(ev = event)) return;
if (ev->device) eina_stringshare_del(ev->device);
if (ev->bus_id) eina_stringshare_del(ev->bus_id);
E_FREE(ev);
}
static int
_e_acpi_lid_status_get(const char *device, const char *bus)
{
FILE *f;
int i = 0;
char buff[PATH_MAX], *ret;
/* the acpi driver code in the kernel has a nice acpi function to return
* the lid status easily, but that function is not exposed for user_space
* so we need to check the proc fs to get the actual status */
/* make sure we have a device and bus */
if ((!device) || (!bus)) return E_ACPI_LID_UNKNOWN;
/* open the state file from /proc */
snprintf(buff, sizeof(buff), "/proc/acpi/%s/%s/state", device, bus);
if (!(f = fopen(buff, "r"))) return E_ACPI_LID_UNKNOWN;
/* read the line from state file */
buff[0] = '\0';
ret = fgets(buff, 1024, f);
fclose(f);
/* parse out state file */
i = 0;
while (buff[i] != ':') i++;
while (!isalnum(buff[i])) i++;
/* compare value from state file and return something sane */
if (!strcmp(buff, "open"))
return E_ACPI_LID_OPEN;
else if (!strcmp(buff, "closed"))
return E_ACPI_LID_CLOSED;
else
return E_ACPI_LID_UNKNOWN;
}
static int
_e_acpi_cb_event(void *data __UNUSED__, int type __UNUSED__, void *event)
{
E_Event_Acpi *ev;
ev = event;
if (_e_acpi_events_frozen > 0) return 1;
e_bindings_acpi_event_handle(E_BINDING_CONTEXT_NONE, NULL, ev);
return 1;
}