/* cec.cpp
*
* Copyright (C) 2013 Austin Hendrix
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/*
* This file provides the python cec module, which is a python wrapper
* around libcec
*
* Author: Austin Hendrix
*/
// request the std format macros
#define __STDC_FORMAT_MACROS
#define PY_SSIZE_T_CLEAN
#include
#include
#include
#include
#include
#include
#include "device.h"
using namespace CEC;
// Basic design/usage:
//
// ( cec.add_callback(event, handler) )
// ???
//
// ( cec.remove_callback(event, handler) )
// ???
//
// ( cec.transmit( ??? ) )
// call ICECAdapter::Transmit()
//
// Events:
// - log message
// message.message[1024]
// message.level (bitfield)
// message.time (int64)
// - key press
// key.keycode (int/enum)
// key.duration (int)
// - command
// command.initiator (logical address)
// command.destination (logical address)
// command.ack
// command.eom
// command.opcode (enum)
// command.parameters
// command.opcode_set (flag for when opcode is set; should probably use
// this to set opcode to None)
// command.transmit_timeout
// - cec configuration changed (adapter/library config change)
// - alert
// - menu state changed
// this is potentially thorny; see libcec source and CEC spec
// ???
// - source activated
//
#define EVENT_LOG 0x0001
#define EVENT_KEYPRESS 0x0002
#define EVENT_COMMAND 0x0004
#define EVENT_CONFIG_CHANGE 0x0008
#define EVENT_ALERT 0x0010
#define EVENT_MENU_CHANGED 0x0020
#define EVENT_ACTIVATED 0x0040
#define EVENT_VALID 0x007F
#define EVENT_ALL 0x007F
//#define DEBUG 1
#ifdef DEBUG
# define debug(...) printf("CEC DEBUG: " __VA_ARGS__)
#else
# define debug(...)
#endif
// cec_adapter_descriptor and DetectAdapters were introduced in 2.1.0
#if CEC_LIB_VERSION_MAJOR >= 3 || (CEC_LIB_VERSION_MAJOR >= 2 && CEC_LIB_VERSION_MINOR >= 1)
#define CEC_ADAPTER_TYPE cec_adapter_descriptor
#define CEC_FIND_ADAPTERS DetectAdapters
#define HAVE_CEC_ADAPTER_DESCRIPTOR 1
#else
#define CEC_ADAPTER_TYPE cec_adapter
#define CEC_FIND_ADAPTERS FindAdapters
#define HAVE_CEC_ADAPTER_DESCRIPTOR 0
#endif
int parse_physical_addr(const char * addr) {
int a, b, c, d;
if( sscanf(addr, "%x.%x.%x.%x", &a, &b, &c, &d) == 4 ) {
if( a > 0xF || b > 0xF || c > 0xF || d > 0xF ) return -1;
if( a < 0 || b < 0 || c < 0 || d < 0 ) return -1;
return (a CEC_FIND_ADAPTERS(dev_list, cec_count);
count = (std::min)(count, cec_count);
}
for( int i=0; istrComName));
/* Convert all of the fields
PyList_Append(result, Py_BuildValue("sshhhhii",
itr->strComName,
itr->strComPath,
itr->iVendorId,
itr->iProductId,
itr->iFirmwareVersion,
itr->iPhysicalAddress,
itr->iFirmwareBuildDate,
itr->adapterType
));
*/
#else
PyList_Append(result, Py_BuildValue("s", itr->comm));
/* Convert all of the fields
PyList_Append(result, Py_BuildValue("sshhhhii",
itr->comm,
itr->strComPath,
itr->iVendorId,
itr->iProductId,
itr->iFirmwareVersion,
itr->iPhysicalAddress,
itr->iFirmwareBuildDate,
itr->adapterType
));
*/
#endif
}
}
return result;
}
static PyObject * init(PyObject * self, PyObject * args) {
PyObject * result = NULL;
const char * dev = NULL;
if( PyArg_ParseTuple(args, "|s:init", &dev) ) {
if( !dev ) {
std::list devs = get_adapters();
if( devs.size() > 0 ) {
#if HAVE_CEC_ADAPTER_DESCRIPTOR
dev = devs.front().strComName;
#else
dev = devs.front().comm;
#endif
} else {
PyErr_SetString(PyExc_Exception, "No default adapter found");
}
}
}
if( dev ) {
bool success = false;
Py_BEGIN_ALLOW_THREADS
success = CEC_adapter->Open(dev);
Py_END_ALLOW_THREADS
if( success ) {
Py_INCREF(Py_None);
result = Py_None;
} else {
Py_BEGIN_ALLOW_THREADS
CECDestroy(CEC_adapter);
CEC_adapter = NULL;
Py_END_ALLOW_THREADS
char errstr[1024];
snprintf(errstr, 1024, "CEC failed to open %s", dev);
PyErr_SetString(PyExc_IOError, errstr);
}
}
return result;
}
static PyObject * close(PyObject * self, PyObject * args) {
if( CEC_adapter != NULL ) {
Py_BEGIN_ALLOW_THREADS
CEC_adapter->Close();
Py_END_ALLOW_THREADS
}
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * list_devices(PyObject * self, PyObject * args) {
PyObject * result = NULL;
if( PyArg_ParseTuple(args, ":list_devices") ) {
cec_logical_addresses devices;
Py_BEGIN_ALLOW_THREADS
devices = CEC_adapter->GetActiveDevices();
Py_END_ALLOW_THREADS
//result = PyList_New(0);
result = PyDict_New();
for( uint8_t i=0; icb == callback ) {
// clear out the given events for this callback
itr->event &= ~(events);
if( itr->event == 0 ) {
// if this callback has no events, remove it
itr = callbacks.erase(itr);
Py_DECREF(callback);
}
}
}
}
Py_INCREF(Py_None);
return Py_None;
}
static PyObject * make_bound_method_args(PyObject * self, PyObject * args) {
Py_ssize_t count = 0;
if( PyTuple_Check(args) ) {
count = PyTuple_Size(args);
}
PyObject * result = PyTuple_New(count+1);
if( result == NULL ) {
return NULL;
}
assert(self != NULL);
Py_INCREF(self);
PyTuple_SetItem(result, 0, self);
for( Py_ssize_t i=0; ievent);
if( itr->event & event ) {
//debug("Calling callback %d\n", i);
PyObject * callback = itr->cb;
PyObject * arguments = args;
if( PyMethod_Check(itr->cb) ) {
callback = PyMethod_Function(itr->cb);
PyObject * self = PyMethod_Self(itr->cb);
if( self ) {
// bound method, prepend self/cls to argument tuple
arguments = make_bound_method_args(self, args);
}
}
// see also: PyObject_CallFunction(...) which can take C args
PyObject * temp = PyObject_CallObject(callback, arguments);
if( arguments != args ) {
Py_XDECREF(arguments);
}
if( temp ) {
debug("Callback succeeded\n");
Py_DECREF(temp);
} else {
debug("Callback failed\n");
Py_DECREF(Py_None);
return NULL;
}
}
i++;
}
return result;
}
static PyObject * transmit(PyObject * self, PyObject * args) {
unsigned char initiator = 'g';
unsigned char destination;
unsigned char opcode;
const char * params = NULL;
Py_ssize_t param_count = 0;
if( PyArg_ParseTuple(args, "bb|s#b:transmit", &destination, &opcode,
¶ms, ¶m_count, &initiator) ) {
if( destination < 0 || destination > 15 ) {
PyErr_SetString(PyExc_ValueError, "Logical address must be between 0 and 15");
return NULL;
}
if( initiator != 'g' ) {
if( initiator < 0 || initiator > 15 ) {
PyErr_SetString(PyExc_ValueError, "Logical address must be between 0 and 15");
return NULL;
}
} else {
initiator = CEC_adapter->GetLogicalAddresses().primary;
}
if( param_count > CEC_MAX_DATA_PACKET_SIZE ) {
char errstr[1024];
snprintf(errstr, 1024, "Too many parameters, maximum is %d",
CEC_MAX_DATA_PACKET_SIZE);
PyErr_SetString(PyExc_ValueError, errstr);
return NULL;
}
cec_command data;
bool success;
Py_BEGIN_ALLOW_THREADS
data.initiator = (cec_logical_address)initiator;
data.destination = (cec_logical_address)destination;
data.opcode = (cec_opcode)opcode;
data.opcode_set = 1;
if( params ) {
for( Py_ssize_t i=0; iTransmit(data);
Py_END_ALLOW_THREADS
RETURN_BOOL(success);
}
return NULL;
}
static PyObject * is_active_source(PyObject * self, PyObject * args) {
unsigned char addr;
if( PyArg_ParseTuple(args, "b:is_active_source", &addr) ) {
if( addr < 0 || addr > 15 ) {
PyErr_SetString(PyExc_ValueError, "Logical address must be between 0 and 15");
return NULL;
} else {
RETURN_BOOL(CEC_adapter->IsActiveSource((cec_logical_address)addr));
}
}
return NULL;
}
static PyObject * set_active_source(PyObject * self, PyObject * args) {
unsigned char devtype = (unsigned char)CEC_DEVICE_TYPE_RESERVED;
if( PyArg_ParseTuple(args, "|b:set_active_source", &devtype) ) {
if( devtype < 0 || devtype > 5 ) {
PyErr_SetString(PyExc_ValueError, "Device type must be between 0 and 5");
return NULL;
} else {
RETURN_BOOL(CEC_adapter->SetActiveSource((cec_device_type)devtype));
}
}
return NULL;
}
static PyObject * volume_up(PyObject * self, PyObject * args) {
if( PyArg_ParseTuple(args, ":volume_up") )
RETURN_BOOL(CEC_adapter->VolumeUp());
return NULL;
}
static PyObject * volume_down(PyObject * self, PyObject * args) {
if( PyArg_ParseTuple(args, ":volume_up") )
RETURN_BOOL(CEC_adapter->VolumeDown());
return NULL;
}
#if CEC_LIB_VERSION_MAJOR > 1
static PyObject * toggle_mute(PyObject * self, PyObject * args) {
if( PyArg_ParseTuple(args, ":toggle_mute") )
RETURN_BOOL(CEC_adapter->AudioToggleMute());
return NULL;
}
#endif
static PyObject * set_stream_path(PyObject * self, PyObject * args) {
PyObject * arg;
if( PyArg_ParseTuple(args, "O:set_stream_path", &arg) ) {
Py_INCREF(arg);
#if PY_MAJOR_VERSION >= 3
if(PyLong_Check(arg)) {
long arg_l = PyLong_AsLong(arg);
#else
if(PyInt_Check(arg)) {
long arg_l = PyInt_AsLong(arg);
#endif
Py_DECREF(arg);
if( arg_l < 0 || arg_l > 15 ) {
PyErr_SetString(PyExc_ValueError, "Logical address must be between 0 and 15");
return NULL;
} else {
RETURN_BOOL(CEC_adapter->SetStreamPath((cec_logical_address)arg_l));
}
#if PY_MAJOR_VERSION >= 3
} else if(PyUnicode_Check(arg)) {
const char * arg_s = PyUnicode_AsUTF8(arg);
#else
} else if(PyString_Check(arg)) {
char * arg_s = PyString_AsString(arg);
#endif
if( arg_s ) {
int pa = parse_physical_addr(arg_s);
Py_DECREF(arg);
if( pa < 0 ) {
PyErr_SetString(PyExc_ValueError, "Invalid physical address");
return NULL;
} else {
RETURN_BOOL(CEC_adapter->SetStreamPath((uint16_t)pa));
}
} else {
Py_DECREF(arg);
return NULL;
}
} else if(PyUnicode_Check(arg)) {
// Convert from Unicode to ASCII
PyObject* ascii_arg = PyUnicode_AsASCIIString(arg);
if (NULL == ascii_arg) {
// Means the string can't be converted to ASCII, the codec failed
PyErr_SetString(PyExc_ValueError,
"Could not convert address to ASCII");
return NULL;
}
// Get the actual bytes as a C string
char * arg_s = PyByteArray_AsString(ascii_arg);
if( arg_s ) {
int pa = parse_physical_addr(arg_s);
Py_DECREF(arg);
if( pa < 0 ) {
PyErr_SetString(PyExc_ValueError, "Invalid physical address");
return NULL;
} else {
RETURN_BOOL(CEC_adapter->SetStreamPath((uint16_t)pa));
}
} else {
Py_DECREF(arg);
return NULL;
}
} else {
PyErr_SetString(PyExc_TypeError, "parameter must be string or int");
return NULL;
}
}
return NULL;
}
PyObject * set_physical_addr(PyObject * self, PyObject * args) {
char * addr_s;
if( PyArg_ParseTuple(args, "s:set_physical_addr", &addr_s) ) {
int addr = parse_physical_addr(addr_s);
if( addr >= 0 ) {
RETURN_BOOL(CEC_adapter->SetPhysicalAddress((uint16_t)addr));
} else {
PyErr_SetString(PyExc_ValueError, "Invalid physical address");
return NULL;
}
}
return NULL;
}
PyObject * set_port(PyObject * self, PyObject * args) {
unsigned char dev, port;
if( PyArg_ParseTuple(args, "bb", &dev, &port) ) {
if( dev > 15 ) {
PyErr_SetString(PyExc_ValueError, "Invalid logical address");
return NULL;
}
if( port > 15 ) {
PyErr_SetString(PyExc_ValueError, "Invalid port");
return NULL;
}
RETURN_BOOL(CEC_adapter->SetHDMIPort((cec_logical_address)dev, port));
}
return NULL;
}
PyObject * can_persist_config(PyObject * self, PyObject * args) {
if( PyArg_ParseTuple(args, ":can_persist_config") ) {
#if CEC_LIB_VERSION_MAJOR >= 5
RETURN_BOOL(CEC_adapter->CanSaveConfiguration());
#else
RETURN_BOOL(CEC_adapter->CanPersistConfiguration());
#endif
}
return NULL;
}
PyObject * persist_config(PyObject * self, PyObject * args) {
if( PyArg_ParseTuple(args, ":persist_config") ) {
#if CEC_LIB_VERSION_MAJOR >= 5
if( ! CEC_adapter->CanSaveConfiguration() ) {
#else
if( ! CEC_adapter->CanPersistConfiguration() ) {
#endif
PyErr_SetString(PyExc_NotImplementedError,
"Cannot persist configuration");
return NULL;
}
libcec_configuration config;
if( ! CEC_adapter->GetCurrentConfiguration(&config) ) {
PyErr_SetString(PyExc_IOError, "Could not get configuration");
return NULL;
}
#if CEC_LIB_VERSION_MAJOR >= 5
RETURN_BOOL(CEC_adapter->SetConfiguration(&config));
#else
RETURN_BOOL(CEC_adapter->PersistConfiguration(&config));
#endif
}
return NULL;
}
static PyMethodDef CecMethods[] = {
{"list_adapters", list_adapters, METH_VARARGS, "List available adapters"},
{"init", init, METH_VARARGS, "Open an adapter"},
{"close", close, METH_NOARGS, "Close an adapter"},
{"list_devices", list_devices, METH_VARARGS, "List devices"},
{"add_callback", add_callback, METH_VARARGS, "Add a callback"},
{"remove_callback", remove_callback, METH_VARARGS, "Remove a callback"},
{"transmit", transmit, METH_VARARGS, "Transmit a raw CEC command"},
{"is_active_source", is_active_source, METH_VARARGS, "Check active source"},
{"set_active_source", set_active_source, METH_VARARGS, "Set active source"},
{"volume_up", volume_up, METH_VARARGS, "Volume Up"},
{"volume_down", volume_down, METH_VARARGS, "Volume Down"},
#if CEC_LIB_VERSION_MAJOR > 1
{"toggle_mute", toggle_mute, METH_VARARGS, "Toggle Mute"},
#endif
{"set_stream_path", set_stream_path, METH_VARARGS, "Set HDMI stream path"},
{"set_physical_addr", set_physical_addr, METH_VARARGS,
"Set HDMI physical address"},
{"can_persist_config", can_persist_config, METH_VARARGS,
"return true if the current adapter can persist the CEC configuration"},
{"persist_config", persist_config, METH_VARARGS,
"persist CEC configuration to adapter"},
{"set_port", set_port, METH_VARARGS, "Set upstream HDMI port"},
{NULL, NULL, 0, NULL}
};
libcec_configuration * CEC_config;
ICECCallbacks * CEC_callbacks;
#if CEC_LIB_VERSION_MAJOR >= 4
void log_cb(void * self, const cec_log_message* message) {
#else
int log_cb(void * self, const cec_log_message message) {
#endif
debug("got log callback\n");
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
#if CEC_LIB_VERSION_MAJOR >= 4
int level = message->level;
long int time = message->time;
const char* msg = message->message;
#else
int level = message.level;
long int time = message.time;
const char* msg = message.message;
#endif
// decode message ignoring invalid characters
PyObject * umsg = PyUnicode_DecodeASCII(msg, strlen(msg), "ignore");
PyObject * args = Py_BuildValue("(iilO)", EVENT_LOG,
level,
time,
umsg);
if( args ) {
trigger_event(EVENT_LOG, args);
Py_DECREF(args);
}
Py_XDECREF(umsg);
PyGILState_Release(gstate);
#if CEC_LIB_VERSION_MAJOR >= 4
return;
#else
return 1;
#endif
}
#if CEC_LIB_VERSION_MAJOR >= 4
void keypress_cb(void * self, const cec_keypress* key) {
#else
int keypress_cb(void * self, const cec_keypress key) {
#endif
debug("got keypress callback\n");
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
#if CEC_LIB_VERSION_MAJOR >= 4
cec_user_control_code keycode = key->keycode;
unsigned int duration = key->duration;
#else
cec_user_control_code keycode = key.keycode;
unsigned int duration = key.duration;
#endif
PyObject * args = Py_BuildValue("(iBI)", EVENT_KEYPRESS,
keycode,
duration);
if( args ) {
trigger_event(EVENT_KEYPRESS, args);
Py_DECREF(args);
}
PyGILState_Release(gstate);
#if CEC_LIB_VERSION_MAJOR >= 4
return;
#else
return 1;
#endif
}
static PyObject * convert_cmd(const cec_command* cmd) {
#if PY_MAJOR_VERSION >= 3
return Py_BuildValue("{sBsBsOsOsBsy#sOsi}",
#else
return Py_BuildValue("{sBsBsOsOsBss#sOsi}",
#endif
"initiator", cmd->initiator,
"destination", cmd->destination,
"ack", cmd->ack ? Py_True : Py_False,
"eom", cmd->eom ? Py_True : Py_False,
"opcode", cmd->opcode,
"parameters", cmd->parameters.data, cmd->parameters.size,
"opcode_set", cmd->opcode_set ? Py_True : Py_False,
"transmit_timeout", cmd->transmit_timeout);
}
#if CEC_LIB_VERSION_MAJOR >= 4
void command_cb(void * self, const cec_command* command) {
#else
int command_cb(void * self, const cec_command command) {
#endif
debug("got command callback\n");
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
#if CEC_LIB_VERSION_MAJOR >= 4
const cec_command * cmd = command;
#else
const cec_command * cmd = &command;
#endif
PyObject * args = Py_BuildValue("(iO&)", EVENT_COMMAND, convert_cmd, cmd);
if( args ) {
trigger_event(EVENT_COMMAND, args);
Py_DECREF(args);
}
PyGILState_Release(gstate);
#if CEC_LIB_VERSION_MAJOR >= 4
return;
#else
return 1;
#endif
}
#if CEC_LIB_VERSION_MAJOR >= 4
void config_cb(void * self, const libcec_configuration*) {
#else
int config_cb(void * self, const libcec_configuration) {
#endif
debug("got config callback\n");
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
// TODO: figure out how to pass these as parameters
// yeah... right.
// we'll probably have to come up with some functions for converting the
// libcec_configuration class into a python Object
// this will probably be _lots_ of work and should probably wait until
// a later release, or when it becomes necessary.
PyObject * args = Py_BuildValue("(i)", EVENT_CONFIG_CHANGE);
if( args ) {
// don't bother triggering an event until we can actually pass arguments
//trigger_event(EVENT_CONFIG_CHANGE, args);
Py_DECREF(args);
}
PyGILState_Release(gstate);
#if CEC_LIB_VERSION_MAJOR >= 4
return;
#else
return 1;
#endif
}
#if CEC_LIB_VERSION_MAJOR >= 4
void alert_cb(void * self, const libcec_alert alert, const libcec_parameter p) {
#else
int alert_cb(void * self, const libcec_alert alert, const libcec_parameter p) {
#endif
debug("got alert callback\n");
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
PyObject * param = Py_None;
if( p.paramType == CEC_PARAMETER_TYPE_STRING ) {
param = Py_BuildValue("s", p.paramData);
} else {
Py_INCREF(param);
}
PyObject * args = Py_BuildValue("(iiN)", EVENT_ALERT, alert, param);
if( args ) {
trigger_event(EVENT_ALERT, args);
Py_DECREF(args);
}
PyGILState_Release(gstate);
#if CEC_LIB_VERSION_MAJOR >= 4
return;
#else
return 1;
#endif
}
int menu_cb(void * self, const cec_menu_state menu) {
debug("got menu callback\n");
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
PyObject * args = Py_BuildValue("(ii)", EVENT_MENU_CHANGED, menu);
if( args ) {
trigger_event(EVENT_MENU_CHANGED, args);
Py_DECREF(args);
}
PyGILState_Release(gstate);
return 1;
}
void activated_cb(void * self, const cec_logical_address logical_address,
const uint8_t state) {
debug("got activated callback\n");
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
PyObject * active = (state == 1) ? Py_True : Py_False;
PyObject * args = Py_BuildValue("(iOi)", EVENT_ACTIVATED, active,
logical_address);
if( args ) {
trigger_event(EVENT_ACTIVATED, args);
Py_DECREF(args);
}
PyGILState_Release(gstate);
return;
}
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"cec",
NULL,
-1,
CecMethods,
NULL,
NULL,
NULL,
NULL
};
#endif
#if PY_MAJOR_VERSION >= 3
#define INITERROR return NULL
#else
#define INITERROR return
#endif
#if PY_MAJOR_VERSION >= 3
PyMODINIT_FUNC PyInit_cec(void) {
#else
PyMODINIT_FUNC initcec(void) {
#endif
// Make sure threads are enabled in the python interpreter
// this also acquires the global interpreter lock
PyEval_InitThreads();
// set up libcec
// libcec config
CEC_config = new libcec_configuration();
CEC_config->Clear();
snprintf(CEC_config->strDeviceName, 13, "python-cec");
// CEC_CLIENT_VERSION_CURRENT was introduced in 2.0.4
// just use 2.1.0 because the conditional is simpler
#if CEC_LIB_VERSION_MAJOR >= 3
CEC_config->clientVersion = LIBCEC_VERSION_CURRENT;
#elif CEC_LIB_VERSION_MAJOR >= 2 && CEC_LIB_VERSION_MINOR >= 1
CEC_config->clientVersion = CEC_CLIENT_VERSION_CURRENT;
#else
// fall back to 1.6.0 since it's the lowest common denominator shipped with
// Ubuntu
CEC_config->clientVersion = CEC_CLIENT_VERSION_1_6_0;
#endif
CEC_config->bActivateSource = 0;
CEC_config->deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
// libcec callbacks
CEC_callbacks = new ICECCallbacks();
#if CEC_LIB_VERSION_MAJOR > 1 || ( CEC_LIB_VERSION_MAJOR == 1 && CEC_LIB_VERSION_MINOR >= 7 )
CEC_callbacks->Clear();
#endif
#if CEC_LIB_VERSION_MAJOR >= 4
CEC_callbacks->logMessage = log_cb;
CEC_callbacks->keyPress = keypress_cb;
CEC_callbacks->commandReceived = command_cb;
CEC_callbacks->configurationChanged = config_cb;
CEC_callbacks->alert = alert_cb;
CEC_callbacks->menuStateChanged = menu_cb;
CEC_callbacks->sourceActivated = activated_cb;
#else
CEC_callbacks->CBCecLogMessage = log_cb;
CEC_callbacks->CBCecKeyPress = keypress_cb;
CEC_callbacks->CBCecCommand = command_cb;
CEC_callbacks->CBCecConfigurationChanged = config_cb;
CEC_callbacks->CBCecAlert = alert_cb;
CEC_callbacks->CBCecMenuStateChanged = menu_cb;
CEC_callbacks->CBCecSourceActivated = activated_cb;
#endif
CEC_config->callbacks = CEC_callbacks;
CEC_adapter = (ICECAdapter*)CECInitialise(CEC_config);
if( !CEC_adapter ) {
PyErr_SetString(PyExc_IOError, "Failed to initialize libcec");
INITERROR;
}
#if CEC_LIB_VERSION_MAJOR > 1 || ( CEC_LIB_VERSION_MAJOR == 1 && CEC_LIB_VERSION_MINOR >= 8 )
CEC_adapter->InitVideoStandalone();
#endif
// set up python module
PyTypeObject * dev = DeviceTypeInit(CEC_adapter);
Device = (PyObject*)dev;
if(PyType_Ready(dev) < 0 ) INITERROR;
#if PY_MAJOR_VERSION >= 3
PyObject * m = PyModule_Create(&moduledef);
#else
PyObject * m = Py_InitModule("cec", CecMethods);
#endif
if( m == NULL ) INITERROR;
Py_INCREF(dev);
PyModule_AddObject(m, "Device", (PyObject*)dev);
// constants for event types
PyModule_AddIntMacro(m, EVENT_LOG);
PyModule_AddIntMacro(m, EVENT_KEYPRESS);
PyModule_AddIntMacro(m, EVENT_COMMAND);
PyModule_AddIntMacro(m, EVENT_CONFIG_CHANGE);
PyModule_AddIntMacro(m, EVENT_ALERT);
PyModule_AddIntMacro(m, EVENT_MENU_CHANGED);
PyModule_AddIntMacro(m, EVENT_ACTIVATED);
PyModule_AddIntMacro(m, EVENT_ALL);
// constants for alert types
PyModule_AddIntConstant(m, "CEC_ALERT_SERVICE_DEVICE",
CEC_ALERT_SERVICE_DEVICE);
PyModule_AddIntConstant(m, "CEC_ALERT_CONNECTION_LOST",
CEC_ALERT_CONNECTION_LOST);
PyModule_AddIntConstant(m, "CEC_ALERT_PERMISSION_ERROR",
CEC_ALERT_PERMISSION_ERROR);
PyModule_AddIntConstant(m, "CEC_ALERT_PORT_BUSY",
CEC_ALERT_PORT_BUSY);
PyModule_AddIntConstant(m, "CEC_ALERT_PHYSICAL_ADDRESS_ERROR",
CEC_ALERT_PHYSICAL_ADDRESS_ERROR);
PyModule_AddIntConstant(m, "CEC_ALERT_TV_POLL_FAILED",
CEC_ALERT_TV_POLL_FAILED);
// constants for menu events
PyModule_AddIntConstant(m, "CEC_MENU_STATE_ACTIVATED",
CEC_MENU_STATE_ACTIVATED);
PyModule_AddIntConstant(m, "CEC_MENU_STATE_DEACTIVATED",
CEC_MENU_STATE_DEACTIVATED);
// constants for device types
PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_TV",
CEC_DEVICE_TYPE_TV);
PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_RECORDING_DEVICE",
CEC_DEVICE_TYPE_RECORDING_DEVICE);
PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_RESERVED",
CEC_DEVICE_TYPE_RESERVED);
PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_TUNER",
CEC_DEVICE_TYPE_TUNER);
PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_PLAYBACK_DEVICE",
CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_AUDIO_SYSTEM",
CEC_DEVICE_TYPE_AUDIO_SYSTEM);
// constants for logical addresses
PyModule_AddIntConstant(m, "CECDEVICE_UNKNOWN",
CECDEVICE_UNKNOWN);
PyModule_AddIntConstant(m, "CECDEVICE_TV",
CECDEVICE_TV);
PyModule_AddIntConstant(m, "CECDEVICE_RECORDINGDEVICE1",
CECDEVICE_RECORDINGDEVICE1);
PyModule_AddIntConstant(m, "CECDEVICE_RECORDINGDEVICE2",
CECDEVICE_RECORDINGDEVICE2);
PyModule_AddIntConstant(m, "CECDEVICE_TUNER1",
CECDEVICE_TUNER1);
PyModule_AddIntConstant(m, "CECDEVICE_PLAYBACKDEVICE1",
CECDEVICE_PLAYBACKDEVICE1);
PyModule_AddIntConstant(m, "CECDEVICE_AUDIOSYSTEM",
CECDEVICE_AUDIOSYSTEM);
PyModule_AddIntConstant(m, "CECDEVICE_TUNER2",
CECDEVICE_TUNER2);
PyModule_AddIntConstant(m, "CECDEVICE_TUNER3",
CECDEVICE_TUNER3);
PyModule_AddIntConstant(m, "CECDEVICE_PLAYBACKDEVICE2",
CECDEVICE_PLAYBACKDEVICE2);
PyModule_AddIntConstant(m, "CECDEVICE_RECORDINGDEVICE3",
CECDEVICE_RECORDINGDEVICE3);
PyModule_AddIntConstant(m, "CECDEVICE_TUNER4",
CECDEVICE_TUNER4);
PyModule_AddIntConstant(m, "CECDEVICE_PLAYBACKDEVICE3",
CECDEVICE_PLAYBACKDEVICE3);
PyModule_AddIntConstant(m, "CECDEVICE_RESERVED1",
CECDEVICE_RESERVED1);
PyModule_AddIntConstant(m, "CECDEVICE_RESERVED2",
CECDEVICE_RESERVED2);
PyModule_AddIntConstant(m, "CECDEVICE_FREEUSE",
CECDEVICE_FREEUSE);
PyModule_AddIntConstant(m, "CECDEVICE_UNREGISTERED",
CECDEVICE_UNREGISTERED);
PyModule_AddIntConstant(m, "CECDEVICE_BROADCAST",
CECDEVICE_BROADCAST);
// constants for opcodes
PyModule_AddIntConstant(m, "CEC_OPCODE_ACTIVE_SOURCE",
CEC_OPCODE_ACTIVE_SOURCE);
PyModule_AddIntConstant(m, "CEC_OPCODE_IMAGE_VIEW_ON",
CEC_OPCODE_IMAGE_VIEW_ON);
PyModule_AddIntConstant(m, "CEC_OPCODE_TEXT_VIEW_ON",
CEC_OPCODE_TEXT_VIEW_ON);
PyModule_AddIntConstant(m, "CEC_OPCODE_INACTIVE_SOURCE",
CEC_OPCODE_INACTIVE_SOURCE);
PyModule_AddIntConstant(m, "CEC_OPCODE_REQUEST_ACTIVE_SOURCE",
CEC_OPCODE_REQUEST_ACTIVE_SOURCE);
PyModule_AddIntConstant(m, "CEC_OPCODE_ROUTING_CHANGE",
CEC_OPCODE_ROUTING_CHANGE);
PyModule_AddIntConstant(m, "CEC_OPCODE_ROUTING_INFORMATION",
CEC_OPCODE_ROUTING_INFORMATION);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_STREAM_PATH",
CEC_OPCODE_SET_STREAM_PATH);
PyModule_AddIntConstant(m, "CEC_OPCODE_STANDBY",
CEC_OPCODE_STANDBY);
PyModule_AddIntConstant(m, "CEC_OPCODE_RECORD_OFF",
CEC_OPCODE_RECORD_OFF);
PyModule_AddIntConstant(m, "CEC_OPCODE_RECORD_ON",
CEC_OPCODE_RECORD_ON);
PyModule_AddIntConstant(m, "CEC_OPCODE_RECORD_STATUS",
CEC_OPCODE_RECORD_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_RECORD_TV_SCREEN",
CEC_OPCODE_RECORD_TV_SCREEN);
PyModule_AddIntConstant(m, "CEC_OPCODE_CLEAR_ANALOGUE_TIMER",
CEC_OPCODE_CLEAR_ANALOGUE_TIMER);
PyModule_AddIntConstant(m, "CEC_OPCODE_CLEAR_DIGITAL_TIMER",
CEC_OPCODE_CLEAR_DIGITAL_TIMER);
PyModule_AddIntConstant(m, "CEC_OPCODE_CLEAR_EXTERNAL_TIMER",
CEC_OPCODE_CLEAR_EXTERNAL_TIMER);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_ANALOGUE_TIMER",
CEC_OPCODE_SET_ANALOGUE_TIMER);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_DIGITAL_TIMER",
CEC_OPCODE_SET_DIGITAL_TIMER);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_EXTERNAL_TIMER",
CEC_OPCODE_SET_EXTERNAL_TIMER);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_TIMER_PROGRAM_TITLE",
CEC_OPCODE_SET_TIMER_PROGRAM_TITLE);
PyModule_AddIntConstant(m, "CEC_OPCODE_TIMER_CLEARED_STATUS",
CEC_OPCODE_TIMER_CLEARED_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_TIMER_STATUS",
CEC_OPCODE_TIMER_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_CEC_VERSION",
CEC_OPCODE_CEC_VERSION);
PyModule_AddIntConstant(m, "CEC_OPCODE_GET_CEC_VERSION",
CEC_OPCODE_GET_CEC_VERSION);
PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_PHYSICAL_ADDRESS",
CEC_OPCODE_GIVE_PHYSICAL_ADDRESS);
PyModule_AddIntConstant(m, "CEC_OPCODE_GET_MENU_LANGUAGE",
CEC_OPCODE_GET_MENU_LANGUAGE);
PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_PHYSICAL_ADDRESS",
CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_MENU_LANGUAGE",
CEC_OPCODE_SET_MENU_LANGUAGE);
PyModule_AddIntConstant(m, "CEC_OPCODE_DECK_CONTROL",
CEC_OPCODE_DECK_CONTROL);
PyModule_AddIntConstant(m, "CEC_OPCODE_DECK_STATUS",
CEC_OPCODE_DECK_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_DECK_STATUS",
CEC_OPCODE_GIVE_DECK_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_PLAY",
CEC_OPCODE_PLAY);
PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS",
CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_SELECT_ANALOGUE_SERVICE",
CEC_OPCODE_SELECT_ANALOGUE_SERVICE);
PyModule_AddIntConstant(m, "CEC_OPCODE_SELECT_DIGITAL_SERVICE",
CEC_OPCODE_SELECT_DIGITAL_SERVICE);
PyModule_AddIntConstant(m, "CEC_OPCODE_TUNER_DEVICE_STATUS",
CEC_OPCODE_TUNER_DEVICE_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_TUNER_STEP_DECREMENT",
CEC_OPCODE_TUNER_STEP_DECREMENT);
PyModule_AddIntConstant(m, "CEC_OPCODE_TUNER_STEP_INCREMENT",
CEC_OPCODE_TUNER_STEP_INCREMENT);
PyModule_AddIntConstant(m, "CEC_OPCODE_DEVICE_VENDOR_ID",
CEC_OPCODE_DEVICE_VENDOR_ID);
PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_DEVICE_VENDOR_ID",
CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
PyModule_AddIntConstant(m, "CEC_OPCODE_VENDOR_COMMAND",
CEC_OPCODE_VENDOR_COMMAND);
PyModule_AddIntConstant(m, "CEC_OPCODE_VENDOR_COMMAND_WITH_ID",
CEC_OPCODE_VENDOR_COMMAND_WITH_ID);
PyModule_AddIntConstant(m, "CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN",
CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN);
PyModule_AddIntConstant(m, "CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP",
CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_OSD_STRING",
CEC_OPCODE_SET_OSD_STRING);
PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_OSD_NAME",
CEC_OPCODE_GIVE_OSD_NAME);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_OSD_NAME",
CEC_OPCODE_SET_OSD_NAME);
PyModule_AddIntConstant(m, "CEC_OPCODE_MENU_REQUEST",
CEC_OPCODE_MENU_REQUEST);
PyModule_AddIntConstant(m, "CEC_OPCODE_MENU_STATUS",
CEC_OPCODE_MENU_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_USER_CONTROL_PRESSED",
CEC_OPCODE_USER_CONTROL_PRESSED);
PyModule_AddIntConstant(m, "CEC_OPCODE_USER_CONTROL_RELEASE",
CEC_OPCODE_USER_CONTROL_RELEASE);
PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_DEVICE_POWER_STATUS",
CEC_OPCODE_GIVE_DEVICE_POWER_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_POWER_STATUS",
CEC_OPCODE_REPORT_POWER_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_FEATURE_ABORT",
CEC_OPCODE_FEATURE_ABORT);
PyModule_AddIntConstant(m, "CEC_OPCODE_ABORT",
CEC_OPCODE_ABORT);
PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_AUDIO_STATUS",
CEC_OPCODE_GIVE_AUDIO_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS",
CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_AUDIO_STATUS",
CEC_OPCODE_REPORT_AUDIO_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_SYSTEM_AUDIO_MODE",
CEC_OPCODE_SET_SYSTEM_AUDIO_MODE);
PyModule_AddIntConstant(m, "CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST",
CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST);
PyModule_AddIntConstant(m, "CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS",
CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS);
PyModule_AddIntConstant(m, "CEC_OPCODE_SET_AUDIO_RATE",
CEC_OPCODE_SET_AUDIO_RATE);
PyModule_AddIntConstant(m, "CEC_OPCODE_START_ARC",
CEC_OPCODE_START_ARC);
PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_ARC_STARTED",
CEC_OPCODE_REPORT_ARC_STARTED);
PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_ARC_ENDED",
CEC_OPCODE_REPORT_ARC_ENDED);
PyModule_AddIntConstant(m, "CEC_OPCODE_REQUEST_ARC_START",
CEC_OPCODE_REQUEST_ARC_START);
PyModule_AddIntConstant(m, "CEC_OPCODE_REQUEST_ARC_END",
CEC_OPCODE_REQUEST_ARC_END);
PyModule_AddIntConstant(m, "CEC_OPCODE_END_ARC",
CEC_OPCODE_END_ARC);
PyModule_AddIntConstant(m, "CEC_OPCODE_CDC",
CEC_OPCODE_CDC);
PyModule_AddIntConstant(m, "CEC_OPCODE_NONE",
CEC_OPCODE_NONE);
// expose whether or not we're using the new cec_adapter_descriptor API
// this should help debugging by exposing which version was detected and
// which adapter detection API was used at compile time
PyModule_AddIntMacro(m, HAVE_CEC_ADAPTER_DESCRIPTOR);
#if PY_MAJOR_VERSION >= 3
return m;
#endif
}