1 /* cec.cpp
2  *
3  * Copyright (C) 2013 Austin Hendrix <namniart@gmail.com>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * This file provides the python cec module, which is a python wrapper
21  *  around libcec
22  *
23  * Author: Austin Hendrix <namniart@gmail.com>
24  */
25 
26 // request the std format macros
27 #define __STDC_FORMAT_MACROS
28 
29 #include <Python.h>
30 #include <stdlib.h>
31 #include <inttypes.h>
32 #include <libcec/cec.h>
33 #include <algorithm>
34 #include <list>
35 
36 #include "device.h"
37 
38 
39 using namespace CEC;
40 
41 // Basic design/usage:
42 //
43 // ( cec.add_callback(event, handler) )
44 //    ???
45 //
46 // ( cec.remove_callback(event, handler) )
47 //    ???
48 //
49 // ( cec.transmit( ??? ) )
50 //    call ICECAdapter::Transmit()
51 //
52 // Events:
53 //    - log message
54 //       message.message[1024]
55 //       message.level (bitfield)
56 //       message.time (int64)
57 //    - key press
58 //       key.keycode (int/enum)
59 //       key.duration (int)
60 //    - command
61 //       command.initiator (logical address)
62 //       command.destination (logical address)
63 //       command.ack
64 //       command.eom
65 //       command.opcode (enum)
66 //       command.parameters
67 //       command.opcode_set (flag for when opcode is set; should probably use
68 //          this to set opcode to None)
69 //       command.transmit_timeout
70 //    - cec configuration changed (adapter/library config change)
71 //    - alert
72 //    - menu state changed
73 //       this is potentially thorny; see libcec source and CEC spec
74 //       ???
75 //    - source activated
76 //
77 
78 #define EVENT_LOG           0x0001
79 #define EVENT_KEYPRESS      0x0002
80 #define EVENT_COMMAND       0x0004
81 #define EVENT_CONFIG_CHANGE 0x0008
82 #define EVENT_ALERT         0x0010
83 #define EVENT_MENU_CHANGED  0x0020
84 #define EVENT_ACTIVATED     0x0040
85 #define EVENT_VALID         0x007F
86 #define EVENT_ALL           0x007F
87 
88 //#define DEBUG 1
89 
90 #ifdef DEBUG
91 # define debug(...) printf("CEC DEBUG: " __VA_ARGS__)
92 #else
93 # define debug(...)
94 #endif
95 
96 // cec_adapter_descriptor and DetectAdapters were introduced in 2.1.0
97 #if CEC_LIB_VERSION_MAJOR >= 3 || (CEC_LIB_VERSION_MAJOR >= 2 && CEC_LIB_VERSION_MINOR >= 1)
98 #define CEC_ADAPTER_TYPE cec_adapter_descriptor
99 #define CEC_FIND_ADAPTERS DetectAdapters
100 #define HAVE_CEC_ADAPTER_DESCRIPTOR 1
101 #else
102 #define CEC_ADAPTER_TYPE cec_adapter
103 #define CEC_FIND_ADAPTERS FindAdapters
104 #define HAVE_CEC_ADAPTER_DESCRIPTOR 0
105 #endif
106 
parse_physical_addr(const char * addr)107 int parse_physical_addr(const char * addr) {
108    int a, b, c, d;
109    if( sscanf(addr, "%x.%x.%x.%x", &a, &b, &c, &d) == 4 ) {
110       if( a > 0xF || b > 0xF || c > 0xF || d > 0xF ) return -1;
111       if( a < 0 || b < 0 || c < 0 || d < 0 ) return -1;
112       return (a << 12) | (b << 8) | (c << 4) | d;
113    } else {
114       return -1;
115    }
116 }
117 
118 #define RETURN_BOOL(arg) do { \
119   bool result; \
120   Py_BEGIN_ALLOW_THREADS \
121   result = (arg); \
122   Py_END_ALLOW_THREADS \
123   PyObject * ret = (result)?Py_True:Py_False; \
124   Py_INCREF(ret); \
125   return ret; \
126 } while(0)
127 
parse_test()128 void parse_test() {
129    assert(parse_physical_addr("0.0.0.0") == 0);
130    assert(parse_physical_addr("F.0.0.0") == 0xF000);
131    assert(parse_physical_addr("0.F.0.0") == 0x0F00);
132    assert(parse_physical_addr("0.0.F.0") == 0x00F0);
133    assert(parse_physical_addr("0.0.0.F") == 0x000F);
134    assert(parse_physical_addr("-1.0.0.0") == -1);
135    assert(parse_physical_addr("0.-1.0.0") == -1);
136    assert(parse_physical_addr("0.0.-1.0") == -1);
137    assert(parse_physical_addr("0.0.0.-1") == -1);
138    assert(parse_physical_addr("foo") == -1);
139    assert(parse_physical_addr("F.F.F.F") == 0xFFFF);
140    assert(parse_physical_addr("f.f.f.f") == 0xFFFF);
141 }
142 
143 ICECAdapter * CEC_adapter;
144 PyObject * Device;
145 
get_adapters()146 std::list<CEC_ADAPTER_TYPE> get_adapters() {
147    std::list<CEC_ADAPTER_TYPE> res;
148    // release the Global Interpreter lock
149    Py_BEGIN_ALLOW_THREADS
150    // get adapters
151    int cec_count = 10;
152    CEC_ADAPTER_TYPE * dev_list = (CEC_ADAPTER_TYPE*)malloc(
153          cec_count * sizeof(CEC_ADAPTER_TYPE));
154    int count = CEC_adapter->CEC_FIND_ADAPTERS(dev_list, cec_count);
155    if( count > cec_count ) {
156       cec_count = count;
157       dev_list = (CEC_ADAPTER_TYPE*)realloc(dev_list,
158          cec_count * sizeof(CEC_ADAPTER_TYPE));
159       count = CEC_adapter->CEC_FIND_ADAPTERS(dev_list, cec_count);
160       count = (std::min)(count, cec_count);
161    }
162 
163    for( int i=0; i<count; i++ ) {
164       res.push_back(dev_list[i]);
165    }
166 
167    free(dev_list);
168    // acquire the GIL before returning to code that uses python objects
169    Py_END_ALLOW_THREADS
170    return res;
171 }
172 
list_adapters(PyObject * self,PyObject * args)173 static PyObject * list_adapters(PyObject * self, PyObject * args) {
174    PyObject * result = NULL;
175 
176    if( PyArg_ParseTuple(args, ":list_adapters") ) {
177       std::list<CEC_ADAPTER_TYPE> dev_list = get_adapters();
178       // set up our result list
179       result = PyList_New(0);
180 
181       // populate our result list
182       std::list<CEC_ADAPTER_TYPE>::const_iterator itr;
183       for( itr = dev_list.begin(); itr != dev_list.end(); itr++ ) {
184 #if HAVE_CEC_ADAPTER_DESCRIPTOR
185          PyList_Append(result, Py_BuildValue("s", itr->strComName));
186          /* Convert all of the fields
187          PyList_Append(result, Py_BuildValue("sshhhhii",
188                   itr->strComName,
189                   itr->strComPath,
190                   itr->iVendorId,
191                   itr->iProductId,
192                   itr->iFirmwareVersion,
193                   itr->iPhysicalAddress,
194                   itr->iFirmwareBuildDate,
195                   itr->adapterType
196                   ));
197                   */
198 #else
199          PyList_Append(result, Py_BuildValue("s", itr->comm));
200          /* Convert all of the fields
201          PyList_Append(result, Py_BuildValue("sshhhhii",
202                   itr->comm,
203                   itr->strComPath,
204                   itr->iVendorId,
205                   itr->iProductId,
206                   itr->iFirmwareVersion,
207                   itr->iPhysicalAddress,
208                   itr->iFirmwareBuildDate,
209                   itr->adapterType
210                   ));
211                   */
212 #endif
213       }
214    }
215 
216    return result;
217 }
218 
init(PyObject * self,PyObject * args)219 static PyObject * init(PyObject * self, PyObject * args) {
220    PyObject * result = NULL;
221    const char * dev = NULL;
222 
223    if( PyArg_ParseTuple(args, "|s:init", &dev) ) {
224       if( !dev ) {
225          std::list<CEC_ADAPTER_TYPE> devs = get_adapters();
226          if( devs.size() > 0 ) {
227 #if HAVE_CEC_ADAPTER_DESCRIPTOR
228             dev = devs.front().strComName;
229 #else
230             dev = devs.front().comm;
231 #endif
232          } else {
233             PyErr_SetString(PyExc_Exception, "No default adapter found");
234          }
235       }
236    }
237 
238    if( dev ) {
239       bool success = false;
240       Py_BEGIN_ALLOW_THREADS
241       success = CEC_adapter->Open(dev);
242       Py_END_ALLOW_THREADS
243       if( success ) {
244          Py_INCREF(Py_None);
245          result = Py_None;
246       } else {
247          CECDestroy(CEC_adapter);
248          CEC_adapter = NULL;
249 
250          char errstr[1024];
251          snprintf(errstr, 1024, "CEC failed to open %s", dev);
252          PyErr_SetString(PyExc_IOError, errstr);
253       }
254    }
255 
256    return result;
257 }
258 
list_devices(PyObject * self,PyObject * args)259 static PyObject * list_devices(PyObject * self, PyObject * args) {
260    PyObject * result = NULL;
261 
262    if( PyArg_ParseTuple(args, ":list_devices") ) {
263       cec_logical_addresses devices;
264       Py_BEGIN_ALLOW_THREADS
265       devices = CEC_adapter->GetActiveDevices();
266       Py_END_ALLOW_THREADS
267 
268       //result = PyList_New(0);
269       result = PyDict_New();
270       for( uint8_t i=0; i<32; i++ ) {
271          if( devices[i] ) {
272             PyObject * ii = Py_BuildValue("(b)", i);
273             PyObject * dev = PyObject_CallObject(Device, ii);
274             Py_DECREF(ii);
275             if( dev ) {
276                //PyList_Append(result, dev);
277                PyDict_SetItem(result, Py_BuildValue("b", i), dev);
278             } else {
279                Py_DECREF(result);
280                result = NULL;
281                break;
282             }
283          }
284       }
285    }
286 
287    return result;
288 }
289 
290 struct Callback {
291    public:
292       long int event;
293       PyObject * cb;
294 
CallbackCallback295       Callback(long int e, PyObject * c) : event(e), cb(c) {
296       }
297 };
298 
299 
300 typedef std::list<Callback> cb_list;
301 cb_list callbacks;
302 
add_callback(PyObject * self,PyObject * args)303 static PyObject * add_callback(PyObject * self, PyObject * args) {
304    PyObject * result = NULL;
305    PyObject * callback;
306    long int events = EVENT_ALL; // default to all events
307 
308    if( PyArg_ParseTuple(args, "O|i:add_callback", &callback, &events) ) {
309       // check that event is one of the allowed events
310       if( events & ~(EVENT_VALID) ) {
311          PyErr_SetString(PyExc_TypeError, "Invalid event(s) for callback");
312          return NULL;
313       }
314       if( !PyCallable_Check(callback)) {
315          PyErr_SetString(PyExc_TypeError, "parameter must be callable");
316          return NULL;
317       }
318 
319       Py_INCREF(callback);
320       Callback new_cb(events, callback);
321 
322       debug("Adding callback for event %ld\n", events);
323       callbacks.push_back(new_cb);
324 
325       Py_INCREF(Py_None);
326       result = Py_None;
327    }
328    return result;
329 }
330 
remove_callback(PyObject * self,PyObject * args)331 static PyObject * remove_callback(PyObject * self, PyObject * args) {
332   PyObject * callback;
333   long int events = EVENT_ALL; // default to all events
334 
335   if( PyArg_ParseTuple(args, "O|i:remove_callback", &callback, &events) ) {
336      for( cb_list::iterator itr = callbacks.begin();
337            itr != callbacks.end();
338            ++itr ) {
339         if( itr->cb == callback ) {
340            // clear out the given events for this callback
341            itr->event &= ~(events);
342            if( itr->event == 0 ) {
343               // if this callback has no events, remove it
344               itr = callbacks.erase(itr);
345               Py_DECREF(callback);
346            }
347         }
348      }
349 
350   }
351   Py_INCREF(Py_None);
352   return Py_None;
353 }
354 
make_bound_method_args(PyObject * self,PyObject * args)355 static PyObject * make_bound_method_args(PyObject * self, PyObject * args) {
356    Py_ssize_t count = 0;
357    if( PyTuple_Check(args) ) {
358       count = PyTuple_Size(args);
359    }
360    PyObject * result = PyTuple_New(count+1);
361    if( result == NULL ) {
362       return NULL;
363    }
364    assert(self != NULL);
365    Py_INCREF(self);
366    PyTuple_SetItem(result, 0, self);
367    for( int i=0; i<count; i++ ) {
368       PyObject * arg = PyTuple_GetItem(args, i);
369       if( arg == NULL ) {
370          Py_DECREF(result);
371          return NULL;
372       }
373       Py_INCREF(arg);
374       PyTuple_SetItem(result, i+1, arg);
375    }
376    return result;
377 }
378 
trigger_event(long int event,PyObject * args)379 static PyObject * trigger_event(long int event, PyObject * args) {
380    assert(event & EVENT_ALL);
381    Py_INCREF(Py_None);
382    PyObject * result = Py_None;
383 
384    //debug("Triggering event %ld\n", event);
385 
386    int i=0;
387    for( cb_list::const_iterator itr = callbacks.begin();
388          itr != callbacks.end();
389          ++itr ) {
390       //debug("Checking callback %d with events %ld\n", i, itr->event);
391       if( itr->event & event ) {
392          //debug("Calling callback %d\n", i);
393          PyObject * callback = itr->cb;
394          PyObject * arguments = args;
395          if( PyMethod_Check(itr->cb) ) {
396             callback = PyMethod_Function(itr->cb);
397             PyObject * self = PyMethod_Self(itr->cb);
398             if( self ) {
399                // bound method, prepend self/cls to argument tuple
400                arguments = make_bound_method_args(self, args);
401             }
402          }
403          // see also: PyObject_CallFunction(...) which can take C args
404          PyObject * temp = PyObject_CallObject(callback, arguments);
405          if( arguments != args ) {
406             Py_XDECREF(arguments);
407          }
408          if( temp ) {
409             debug("Callback succeeded\n");
410             Py_DECREF(temp);
411          } else {
412             debug("Callback failed\n");
413             Py_DECREF(Py_None);
414             return NULL;
415          }
416       }
417       i++;
418    }
419 
420    return result;
421 }
422 
transmit(PyObject * self,PyObject * args)423 static PyObject * transmit(PyObject * self, PyObject * args) {
424    unsigned char destination;
425    unsigned char opcode;
426    const char * params = NULL;
427    int param_count = 0;
428 
429    if( PyArg_ParseTuple(args, "bb|s#:transmit", &destination, &opcode,
430          &params, &param_count) ) {
431       if( destination < 0 || destination > 15 ) {
432          PyErr_SetString(PyExc_ValueError, "Logical address must be between 0 and 15");
433          return NULL;
434       }
435       if( param_count > CEC_MAX_DATA_PACKET_SIZE ) {
436          char errstr[1024];
437          snprintf(errstr, 1024, "Too many parameters, maximum is %d",
438             CEC_MAX_DATA_PACKET_SIZE);
439          PyErr_SetString(PyExc_ValueError, errstr);
440          return NULL;
441       }
442       cec_command data;
443       bool success;
444       Py_BEGIN_ALLOW_THREADS
445       data.initiator = CEC_adapter->GetLogicalAddresses().primary;
446       data.destination = (cec_logical_address)destination;
447       data.opcode = (cec_opcode)opcode;
448       data.opcode_set = 1;
449       if( params ) {
450          for( int i=0; i<param_count; i++ ) {
451             data.PushBack(((uint8_t *)params)[i]);
452          }
453       }
454       success = CEC_adapter->Transmit(data);
455       Py_END_ALLOW_THREADS
456       RETURN_BOOL(success);
457    }
458 
459    return NULL;
460 }
461 
is_active_source(PyObject * self,PyObject * args)462 static PyObject * is_active_source(PyObject * self, PyObject * args) {
463    unsigned char addr;
464 
465    if( PyArg_ParseTuple(args, "b:is_active_source", &addr) ) {
466       if( addr < 0 || addr > 15 ) {
467          PyErr_SetString(PyExc_ValueError, "Logical address must be between 0 and 15");
468          return NULL;
469       } else {
470          RETURN_BOOL(CEC_adapter->IsActiveSource((cec_logical_address)addr));
471       }
472    }
473    return NULL;
474 }
475 
set_active_source(PyObject * self,PyObject * args)476 static PyObject * set_active_source(PyObject * self, PyObject * args) {
477    unsigned char devtype = (unsigned char)CEC_DEVICE_TYPE_RESERVED;
478 
479    if( PyArg_ParseTuple(args, "|b:set_active_source", &devtype) ) {
480       if( devtype < 0 || devtype > 5 ) {
481          PyErr_SetString(PyExc_ValueError, "Device type must be between 0 and 5");
482          return NULL;
483       } else {
484          RETURN_BOOL(CEC_adapter->SetActiveSource((cec_device_type)devtype));
485       }
486    }
487    return NULL;
488 }
489 
volume_up(PyObject * self,PyObject * args)490 static PyObject * volume_up(PyObject * self, PyObject * args) {
491    if( PyArg_ParseTuple(args, ":volume_up") )
492       RETURN_BOOL(CEC_adapter->VolumeUp());
493    return NULL;
494 }
495 
volume_down(PyObject * self,PyObject * args)496 static PyObject * volume_down(PyObject * self, PyObject * args) {
497    if( PyArg_ParseTuple(args, ":volume_up") )
498       RETURN_BOOL(CEC_adapter->VolumeDown());
499    return NULL;
500 }
501 
502 #if CEC_LIB_VERSION_MAJOR > 1
toggle_mute(PyObject * self,PyObject * args)503 static PyObject * toggle_mute(PyObject * self, PyObject * args) {
504    if( PyArg_ParseTuple(args, ":toggle_mute") )
505       RETURN_BOOL(CEC_adapter->AudioToggleMute());
506    return NULL;
507 }
508 #endif
509 
set_stream_path(PyObject * self,PyObject * args)510 static PyObject * set_stream_path(PyObject * self, PyObject * args) {
511    PyObject * arg;
512 
513    if( PyArg_ParseTuple(args, "O:set_stream_path", &arg) ) {
514       Py_INCREF(arg);
515 #if PY_MAJOR_VERSION >= 3
516       if(PyLong_Check(arg)) {
517          long arg_l = PyLong_AsLong(arg);
518 #else
519       if(PyInt_Check(arg)) {
520          long arg_l = PyInt_AsLong(arg);
521 #endif
522          Py_DECREF(arg);
523          if( arg_l < 0 || arg_l > 15 ) {
524             PyErr_SetString(PyExc_ValueError, "Logical address must be between 0 and 15");
525             return NULL;
526          } else {
527             RETURN_BOOL(CEC_adapter->SetStreamPath((cec_logical_address)arg_l));
528          }
529 #if PY_MAJOR_VERSION >= 3
530       } else if(PyUnicode_Check(arg)) {
531          const char * arg_s = PyUnicode_AsUTF8(arg);
532 #else
533       } else if(PyString_Check(arg)) {
534          char * arg_s = PyString_AsString(arg);
535 #endif
536          if( arg_s ) {
537             int pa = parse_physical_addr(arg_s);
538             Py_DECREF(arg);
539             if( pa < 0 ) {
540                PyErr_SetString(PyExc_ValueError, "Invalid physical address");
541                return NULL;
542             } else {
543                RETURN_BOOL(CEC_adapter->SetStreamPath((uint16_t)pa));
544             }
545          } else {
546             Py_DECREF(arg);
547             return NULL;
548          }
549       } else if(PyUnicode_Check(arg)) {
550          // Convert from Unicode to ASCII
551          PyObject* ascii_arg = PyUnicode_AsASCIIString(arg);
552          if (NULL == ascii_arg) {
553             // Means the string can't be converted to ASCII, the codec failed
554             PyErr_SetString(PyExc_ValueError,
555                "Could not convert address to ASCII");
556             return NULL;
557          }
558 
559          // Get the actual bytes as a C string
560          char * arg_s = PyByteArray_AsString(ascii_arg);
561          if( arg_s ) {
562             int pa = parse_physical_addr(arg_s);
563             Py_DECREF(arg);
564             if( pa < 0 ) {
565                PyErr_SetString(PyExc_ValueError, "Invalid physical address");
566                return NULL;
567             } else {
568                RETURN_BOOL(CEC_adapter->SetStreamPath((uint16_t)pa));
569             }
570          } else {
571             Py_DECREF(arg);
572             return NULL;
573          }
574       } else {
575          PyErr_SetString(PyExc_TypeError, "parameter must be string or int");
576          return NULL;
577       }
578    }
579 
580    return NULL;
581 }
582 
583 PyObject * set_physical_addr(PyObject * self, PyObject * args) {
584    char * addr_s;
585    if( PyArg_ParseTuple(args, "s:set_physical_addr", &addr_s) ) {
586       int addr = parse_physical_addr(addr_s);
587       if( addr >= 0 ) {
588          RETURN_BOOL(CEC_adapter->SetPhysicalAddress((uint16_t)addr));
589       } else {
590          PyErr_SetString(PyExc_ValueError, "Invalid physical address");
591          return NULL;
592       }
593    }
594    return NULL;
595 }
596 
597 PyObject * set_port(PyObject * self, PyObject * args) {
598    unsigned char dev, port;
599    if( PyArg_ParseTuple(args, "bb", &dev, &port) ) {
600       if( dev > 15 ) {
601          PyErr_SetString(PyExc_ValueError, "Invalid logical address");
602          return NULL;
603       }
604       if( port > 15 ) {
605          PyErr_SetString(PyExc_ValueError, "Invalid port");
606          return NULL;
607       }
608       RETURN_BOOL(CEC_adapter->SetHDMIPort((cec_logical_address)dev, port));
609    }
610    return NULL;
611 }
612 
613 PyObject * can_persist_config(PyObject * self, PyObject * args) {
614    if( PyArg_ParseTuple(args, ":can_persist_config") ) {
615 #if CEC_LIB_VERSION_MAJOR >= 5
616       RETURN_BOOL(CEC_adapter->CanSaveConfiguration());
617 #else
618       RETURN_BOOL(CEC_adapter->CanPersistConfiguration());
619 #endif
620    }
621    return NULL;
622 }
623 
624 PyObject * persist_config(PyObject * self, PyObject * args) {
625    if( PyArg_ParseTuple(args, ":persist_config") ) {
626 #if CEC_LIB_VERSION_MAJOR >= 5
627       if( ! CEC_adapter->CanSaveConfiguration() ) {
628 #else
629       if( ! CEC_adapter->CanPersistConfiguration() ) {
630 #endif
631          PyErr_SetString(PyExc_NotImplementedError,
632                "Cannot persist configuration");
633          return NULL;
634       }
635       libcec_configuration config;
636       if( ! CEC_adapter->GetCurrentConfiguration(&config) ) {
637          PyErr_SetString(PyExc_IOError, "Could not get configuration");
638          return NULL;
639       }
640 #if CEC_LIB_VERSION_MAJOR >= 5
641       RETURN_BOOL(CEC_adapter->SetConfiguration(&config));
642 #else
643       RETURN_BOOL(CEC_adapter->PersistConfiguration(&config));
644 #endif
645    }
646    return NULL;
647 }
648 
649 static PyMethodDef CecMethods[] = {
650    {"list_adapters", list_adapters, METH_VARARGS, "List available adapters"},
651    {"init", init, METH_VARARGS, "Open an adapter"},
652    {"list_devices", list_devices, METH_VARARGS, "List devices"},
653    {"add_callback", add_callback, METH_VARARGS, "Add a callback"},
654    {"remove_callback", remove_callback, METH_VARARGS, "Remove a callback"},
655    {"transmit", transmit, METH_VARARGS, "Transmit a raw CEC command"},
656    {"is_active_source", is_active_source, METH_VARARGS, "Check active source"},
657    {"set_active_source", set_active_source, METH_VARARGS, "Set active source"},
658    {"volume_up",   volume_up,   METH_VARARGS, "Volume Up"},
659    {"volume_down", volume_down, METH_VARARGS, "Volume Down"},
660 #if CEC_LIB_VERSION_MAJOR > 1
661    {"toggle_mute", toggle_mute, METH_VARARGS, "Toggle Mute"},
662 #endif
663    {"set_stream_path", set_stream_path, METH_VARARGS, "Set HDMI stream path"},
664    {"set_physical_addr", set_physical_addr, METH_VARARGS,
665       "Set HDMI physical address"},
666    {"can_persist_config", can_persist_config, METH_VARARGS,
667       "return true if the current adapter can persist the CEC configuration"},
668    {"persist_config", persist_config, METH_VARARGS,
669       "persist CEC configuration to adapter"},
670    {"set_port", set_port, METH_VARARGS, "Set upstream HDMI port"},
671    {NULL, NULL, 0, NULL}
672 };
673 
674 libcec_configuration * CEC_config;
675 ICECCallbacks * CEC_callbacks;
676 
677 #if CEC_LIB_VERSION_MAJOR >= 4
678 void log_cb(void * self, const cec_log_message* message) {
679 #else
680 int log_cb(void * self, const cec_log_message message) {
681 #endif
682    debug("got log callback\n");
683    PyGILState_STATE gstate;
684    gstate = PyGILState_Ensure();
685 #if CEC_LIB_VERSION_MAJOR >= 4
686    int level = message->level;
687    long int time = message->time;
688    const char* msg = message->message;
689 #else
690    int level = message.level;
691    long int time = message.time;
692    const char* msg = message.message;
693 #endif
694    // decode message ignoring invalid characters
695    PyObject * umsg = PyUnicode_DecodeASCII(msg, strlen(msg), "ignore");
696    PyObject * args = Py_BuildValue("(iilO)", EVENT_LOG,
697          level,
698          time,
699          umsg);
700    if( args ) {
701       trigger_event(EVENT_LOG, args);
702       Py_DECREF(args);
703    }
704    Py_XDECREF(umsg);
705    PyGILState_Release(gstate);
706 #if CEC_LIB_VERSION_MAJOR >= 4
707    return;
708 #else
709    return 1;
710 #endif
711 }
712 
713 #if CEC_LIB_VERSION_MAJOR >= 4
714 void keypress_cb(void * self, const cec_keypress* key) {
715 #else
716 int keypress_cb(void * self, const cec_keypress key) {
717 #endif
718    debug("got keypress callback\n");
719    PyGILState_STATE gstate;
720    gstate = PyGILState_Ensure();
721 #if CEC_LIB_VERSION_MAJOR >= 4
722    cec_user_control_code keycode = key->keycode;
723    unsigned int duration = key->duration;
724 #else
725    cec_user_control_code keycode = key.keycode;
726    unsigned int duration = key.duration;
727 #endif
728    PyObject * args = Py_BuildValue("(iBI)", EVENT_KEYPRESS,
729          keycode,
730          duration);
731    if( args ) {
732       trigger_event(EVENT_KEYPRESS, args);
733       Py_DECREF(args);
734    }
735    PyGILState_Release(gstate);
736 #if CEC_LIB_VERSION_MAJOR >= 4
737    return;
738 #else
739    return 1;
740 #endif
741 }
742 
743 static PyObject * convert_cmd(const cec_command* cmd) {
744 #if PY_MAJOR_VERSION >= 3
745    return Py_BuildValue("{sBsBsOsOsBsy#sOsi}",
746 #else
747    return Py_BuildValue("{sBsBsOsOsBss#sOsi}",
748 #endif
749          "initiator", cmd->initiator,
750          "destination", cmd->destination,
751          "ack", cmd->ack ? Py_True : Py_False,
752          "eom", cmd->eom ? Py_True : Py_False,
753          "opcode", cmd->opcode,
754          "parameters", cmd->parameters.data, cmd->parameters.size,
755          "opcode_set", cmd->opcode_set ? Py_True : Py_False,
756          "transmit_timeout", cmd->transmit_timeout);
757 }
758 
759 #if CEC_LIB_VERSION_MAJOR >= 4
760 void command_cb(void * self, const cec_command* command) {
761 #else
762 int command_cb(void * self, const cec_command command) {
763 #endif
764    debug("got command callback\n");
765    PyGILState_STATE gstate;
766    gstate = PyGILState_Ensure();
767 #if CEC_LIB_VERSION_MAJOR >= 4
768    const cec_command * cmd = command;
769 #else
770    const cec_command * cmd = &command;
771 #endif
772    PyObject * args = Py_BuildValue("(iO&)", EVENT_COMMAND, convert_cmd, cmd);
773    if( args ) {
774       trigger_event(EVENT_COMMAND, args);
775       Py_DECREF(args);
776    }
777    PyGILState_Release(gstate);
778 #if CEC_LIB_VERSION_MAJOR >= 4
779    return;
780 #else
781    return 1;
782 #endif
783 }
784 
785 #if CEC_LIB_VERSION_MAJOR >= 4
786 void config_cb(void * self, const libcec_configuration*) {
787 #else
788 int config_cb(void * self, const libcec_configuration) {
789 #endif
790    debug("got config callback\n");
791    PyGILState_STATE gstate;
792    gstate = PyGILState_Ensure();
793    // TODO: figure out how to pass these as parameters
794    // yeah... right.
795    //  we'll probably have to come up with some functions for converting the
796    //  libcec_configuration class into a python Object
797    //  this will probably be _lots_ of work and should probably wait until
798    //  a later release, or when it becomes necessary.
799    PyObject * args = Py_BuildValue("(i)", EVENT_CONFIG_CHANGE);
800    if( args ) {
801       // don't bother triggering an event until we can actually pass arguments
802       //trigger_event(EVENT_CONFIG_CHANGE, args);
803       Py_DECREF(args);
804    }
805    PyGILState_Release(gstate);
806 #if CEC_LIB_VERSION_MAJOR >= 4
807    return;
808 #else
809    return 1;
810 #endif
811 }
812 
813 #if CEC_LIB_VERSION_MAJOR >= 4
814 void alert_cb(void * self, const libcec_alert alert, const libcec_parameter p) {
815 #else
816 int alert_cb(void * self, const libcec_alert alert, const libcec_parameter p) {
817 #endif
818    debug("got alert callback\n");
819    PyGILState_STATE gstate;
820    gstate = PyGILState_Ensure();
821    PyObject * param = Py_None;
822    if( p.paramType == CEC_PARAMETER_TYPE_STRING ) {
823       param = Py_BuildValue("s", p.paramData);
824    } else {
825       Py_INCREF(param);
826    }
827    PyObject * args = Py_BuildValue("(iiN)", EVENT_ALERT, alert, param);
828    if( args ) {
829       trigger_event(EVENT_ALERT, args);
830       Py_DECREF(args);
831    }
832    PyGILState_Release(gstate);
833 #if CEC_LIB_VERSION_MAJOR >= 4
834    return;
835 #else
836    return 1;
837 #endif
838 }
839 
840 int menu_cb(void * self, const cec_menu_state menu) {
841    debug("got menu callback\n");
842    PyGILState_STATE gstate;
843    gstate = PyGILState_Ensure();
844    PyObject * args = Py_BuildValue("(ii)", EVENT_MENU_CHANGED, menu);
845    if( args ) {
846       trigger_event(EVENT_MENU_CHANGED, args);
847       Py_DECREF(args);
848    }
849    PyGILState_Release(gstate);
850    return 1;
851 }
852 
853 void activated_cb(void * self, const cec_logical_address logical_address,
854       const uint8_t state) {
855    debug("got activated callback\n");
856    PyGILState_STATE gstate;
857    gstate = PyGILState_Ensure();
858    PyObject * active = (state == 1) ? Py_True : Py_False;
859    PyObject * args = Py_BuildValue("(iOi)", EVENT_ACTIVATED, active,
860       logical_address);
861    if( args ) {
862       trigger_event(EVENT_ACTIVATED, args);
863       Py_DECREF(args);
864    }
865    PyGILState_Release(gstate);
866    return;
867 }
868 
869 #if PY_MAJOR_VERSION >= 3
870 static struct PyModuleDef moduledef = {
871    PyModuleDef_HEAD_INIT,
872    "cec",
873    NULL,
874    -1,
875    CecMethods,
876    NULL,
877    NULL,
878    NULL,
879    NULL
880 };
881 #endif
882 
883 #if PY_MAJOR_VERSION >= 3
884 #define INITERROR return NULL
885 #else
886 #define INITERROR return
887 #endif
888 
889 #if PY_MAJOR_VERSION >= 3
890 PyMODINIT_FUNC PyInit_cec(void) {
891 #else
892 PyMODINIT_FUNC initcec(void) {
893 #endif
894    // Make sure threads are enabled in the python interpreter
895    // this also acquires the global interpreter lock
896    PyEval_InitThreads();
897 
898    // set up libcec
899    //  libcec config
900    CEC_config = new libcec_configuration();
901    CEC_config->Clear();
902 
903    snprintf(CEC_config->strDeviceName, 13, "python-cec");
904    // CEC_CLIENT_VERSION_CURRENT was introduced in 2.0.4
905    // just use 2.1.0 because the conditional is simpler
906 #if CEC_LIB_VERSION_MAJOR >= 3
907    CEC_config->clientVersion = LIBCEC_VERSION_CURRENT;
908 #elif CEC_LIB_VERSION_MAJOR >= 2 && CEC_LIB_VERSION_MINOR >= 1
909    CEC_config->clientVersion = CEC_CLIENT_VERSION_CURRENT;
910 #else
911    // fall back to 1.6.0 since it's the lowest common denominator shipped with
912    // Ubuntu
913    CEC_config->clientVersion = CEC_CLIENT_VERSION_1_6_0;
914 #endif
915    CEC_config->bActivateSource = 0;
916    CEC_config->deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
917 
918 
919    //  libcec callbacks
920    CEC_callbacks = new ICECCallbacks();
921 #if CEC_LIB_VERSION_MAJOR > 1 || ( CEC_LIB_VERSION_MAJOR == 1 && CEC_LIB_VERSION_MINOR >= 7 )
922    CEC_callbacks->Clear();
923 #endif
924 #if CEC_LIB_VERSION_MAJOR >= 4
925    CEC_callbacks->logMessage = log_cb;
926    CEC_callbacks->keyPress = keypress_cb;
927    CEC_callbacks->commandReceived = command_cb;
928    CEC_callbacks->configurationChanged = config_cb;
929    CEC_callbacks->alert = alert_cb;
930    CEC_callbacks->menuStateChanged = menu_cb;
931    CEC_callbacks->sourceActivated = activated_cb;
932 #else
933    CEC_callbacks->CBCecLogMessage = log_cb;
934    CEC_callbacks->CBCecKeyPress = keypress_cb;
935    CEC_callbacks->CBCecCommand = command_cb;
936    CEC_callbacks->CBCecConfigurationChanged = config_cb;
937    CEC_callbacks->CBCecAlert = alert_cb;
938    CEC_callbacks->CBCecMenuStateChanged = menu_cb;
939    CEC_callbacks->CBCecSourceActivated = activated_cb;
940 #endif
941 
942    CEC_config->callbacks = CEC_callbacks;
943 
944    CEC_adapter = (ICECAdapter*)CECInitialise(CEC_config);
945 
946    if( !CEC_adapter ) {
947       PyErr_SetString(PyExc_IOError, "Failed to initialize libcec");
948       INITERROR;
949    }
950 
951 #if CEC_LIB_VERSION_MAJOR > 1 || ( CEC_LIB_VERSION_MAJOR == 1 && CEC_LIB_VERSION_MINOR >= 8 )
952    CEC_adapter->InitVideoStandalone();
953 #endif
954 
955    // set up python module
956    PyTypeObject * dev = DeviceTypeInit(CEC_adapter);
957    Device = (PyObject*)dev;
958    if(PyType_Ready(dev) < 0 ) INITERROR;
959 
960 #if PY_MAJOR_VERSION >= 3
961    PyObject * m = PyModule_Create(&moduledef);
962 #else
963    PyObject * m = Py_InitModule("cec", CecMethods);
964 #endif
965 
966    if( m == NULL ) INITERROR;
967 
968    Py_INCREF(dev);
969    PyModule_AddObject(m, "Device", (PyObject*)dev);
970 
971    // constants for event types
972    PyModule_AddIntMacro(m, EVENT_LOG);
973    PyModule_AddIntMacro(m, EVENT_KEYPRESS);
974    PyModule_AddIntMacro(m, EVENT_COMMAND);
975    PyModule_AddIntMacro(m, EVENT_CONFIG_CHANGE);
976    PyModule_AddIntMacro(m, EVENT_ALERT);
977    PyModule_AddIntMacro(m, EVENT_MENU_CHANGED);
978    PyModule_AddIntMacro(m, EVENT_ACTIVATED);
979    PyModule_AddIntMacro(m, EVENT_ALL);
980 
981    // constants for alert types
982    PyModule_AddIntConstant(m, "CEC_ALERT_SERVICE_DEVICE",
983          CEC_ALERT_SERVICE_DEVICE);
984    PyModule_AddIntConstant(m, "CEC_ALERT_CONNECTION_LOST",
985          CEC_ALERT_CONNECTION_LOST);
986    PyModule_AddIntConstant(m, "CEC_ALERT_PERMISSION_ERROR",
987          CEC_ALERT_PERMISSION_ERROR);
988    PyModule_AddIntConstant(m, "CEC_ALERT_PORT_BUSY",
989          CEC_ALERT_PORT_BUSY);
990    PyModule_AddIntConstant(m, "CEC_ALERT_PHYSICAL_ADDRESS_ERROR",
991          CEC_ALERT_PHYSICAL_ADDRESS_ERROR);
992    PyModule_AddIntConstant(m, "CEC_ALERT_TV_POLL_FAILED",
993          CEC_ALERT_TV_POLL_FAILED);
994 
995    // constants for menu events
996    PyModule_AddIntConstant(m, "CEC_MENU_STATE_ACTIVATED",
997          CEC_MENU_STATE_ACTIVATED);
998    PyModule_AddIntConstant(m, "CEC_MENU_STATE_DEACTIVATED",
999          CEC_MENU_STATE_DEACTIVATED);
1000 
1001    // constants for device types
1002    PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_TV",
1003          CEC_DEVICE_TYPE_TV);
1004    PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_RECORDING_DEVICE",
1005          CEC_DEVICE_TYPE_RECORDING_DEVICE);
1006    PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_RESERVED",
1007          CEC_DEVICE_TYPE_RESERVED);
1008    PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_TUNER",
1009          CEC_DEVICE_TYPE_TUNER);
1010    PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_PLAYBACK_DEVICE",
1011          CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
1012    PyModule_AddIntConstant(m, "CEC_DEVICE_TYPE_AUDIO_SYSTEM",
1013          CEC_DEVICE_TYPE_AUDIO_SYSTEM);
1014 
1015    // constants for logical addresses
1016    PyModule_AddIntConstant(m, "CECDEVICE_UNKNOWN",
1017          CECDEVICE_UNKNOWN);
1018    PyModule_AddIntConstant(m, "CECDEVICE_TV",
1019          CECDEVICE_TV);
1020    PyModule_AddIntConstant(m, "CECDEVICE_RECORDINGDEVICE1",
1021          CECDEVICE_RECORDINGDEVICE1);
1022    PyModule_AddIntConstant(m, "CECDEVICE_RECORDINGDEVICE2",
1023          CECDEVICE_RECORDINGDEVICE2);
1024    PyModule_AddIntConstant(m, "CECDEVICE_TUNER1",
1025          CECDEVICE_TUNER1);
1026    PyModule_AddIntConstant(m, "CECDEVICE_PLAYBACKDEVICE1",
1027          CECDEVICE_PLAYBACKDEVICE1);
1028    PyModule_AddIntConstant(m, "CECDEVICE_AUDIOSYSTEM",
1029          CECDEVICE_AUDIOSYSTEM);
1030    PyModule_AddIntConstant(m, "CECDEVICE_TUNER2",
1031          CECDEVICE_TUNER2);
1032    PyModule_AddIntConstant(m, "CECDEVICE_TUNER3",
1033          CECDEVICE_TUNER3);
1034    PyModule_AddIntConstant(m, "CECDEVICE_PLAYBACKDEVICE2",
1035          CECDEVICE_PLAYBACKDEVICE2);
1036    PyModule_AddIntConstant(m, "CECDEVICE_RECORDINGDEVICE3",
1037          CECDEVICE_RECORDINGDEVICE3);
1038    PyModule_AddIntConstant(m, "CECDEVICE_TUNER4",
1039          CECDEVICE_TUNER4);
1040    PyModule_AddIntConstant(m, "CECDEVICE_PLAYBACKDEVICE3",
1041          CECDEVICE_PLAYBACKDEVICE3);
1042    PyModule_AddIntConstant(m, "CECDEVICE_RESERVED1",
1043          CECDEVICE_RESERVED1);
1044    PyModule_AddIntConstant(m, "CECDEVICE_RESERVED2",
1045          CECDEVICE_RESERVED2);
1046    PyModule_AddIntConstant(m, "CECDEVICE_FREEUSE",
1047          CECDEVICE_FREEUSE);
1048    PyModule_AddIntConstant(m, "CECDEVICE_UNREGISTERED",
1049          CECDEVICE_UNREGISTERED);
1050    PyModule_AddIntConstant(m, "CECDEVICE_BROADCAST",
1051          CECDEVICE_BROADCAST);
1052 
1053    // constants for opcodes
1054    PyModule_AddIntConstant(m, "CEC_OPCODE_ACTIVE_SOURCE",
1055          CEC_OPCODE_ACTIVE_SOURCE);
1056    PyModule_AddIntConstant(m, "CEC_OPCODE_IMAGE_VIEW_ON",
1057          CEC_OPCODE_IMAGE_VIEW_ON);
1058    PyModule_AddIntConstant(m, "CEC_OPCODE_TEXT_VIEW_ON",
1059          CEC_OPCODE_TEXT_VIEW_ON);
1060    PyModule_AddIntConstant(m, "CEC_OPCODE_INACTIVE_SOURCE",
1061          CEC_OPCODE_INACTIVE_SOURCE);
1062    PyModule_AddIntConstant(m, "CEC_OPCODE_REQUEST_ACTIVE_SOURCE",
1063          CEC_OPCODE_REQUEST_ACTIVE_SOURCE);
1064    PyModule_AddIntConstant(m, "CEC_OPCODE_ROUTING_CHANGE",
1065          CEC_OPCODE_ROUTING_CHANGE);
1066    PyModule_AddIntConstant(m, "CEC_OPCODE_ROUTING_INFORMATION",
1067          CEC_OPCODE_ROUTING_INFORMATION);
1068    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_STREAM_PATH",
1069          CEC_OPCODE_SET_STREAM_PATH);
1070    PyModule_AddIntConstant(m, "CEC_OPCODE_STANDBY",
1071          CEC_OPCODE_STANDBY);
1072    PyModule_AddIntConstant(m, "CEC_OPCODE_RECORD_OFF",
1073          CEC_OPCODE_RECORD_OFF);
1074    PyModule_AddIntConstant(m, "CEC_OPCODE_RECORD_ON",
1075          CEC_OPCODE_RECORD_ON);
1076    PyModule_AddIntConstant(m, "CEC_OPCODE_RECORD_STATUS",
1077          CEC_OPCODE_RECORD_STATUS);
1078    PyModule_AddIntConstant(m, "CEC_OPCODE_RECORD_TV_SCREEN",
1079          CEC_OPCODE_RECORD_TV_SCREEN);
1080    PyModule_AddIntConstant(m, "CEC_OPCODE_CLEAR_ANALOGUE_TIMER",
1081          CEC_OPCODE_CLEAR_ANALOGUE_TIMER);
1082    PyModule_AddIntConstant(m, "CEC_OPCODE_CLEAR_DIGITAL_TIMER",
1083          CEC_OPCODE_CLEAR_DIGITAL_TIMER);
1084    PyModule_AddIntConstant(m, "CEC_OPCODE_CLEAR_EXTERNAL_TIMER",
1085          CEC_OPCODE_CLEAR_EXTERNAL_TIMER);
1086    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_ANALOGUE_TIMER",
1087          CEC_OPCODE_SET_ANALOGUE_TIMER);
1088    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_DIGITAL_TIMER",
1089          CEC_OPCODE_SET_DIGITAL_TIMER);
1090    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_EXTERNAL_TIMER",
1091          CEC_OPCODE_SET_EXTERNAL_TIMER);
1092    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_TIMER_PROGRAM_TITLE",
1093          CEC_OPCODE_SET_TIMER_PROGRAM_TITLE);
1094    PyModule_AddIntConstant(m, "CEC_OPCODE_TIMER_CLEARED_STATUS",
1095          CEC_OPCODE_TIMER_CLEARED_STATUS);
1096    PyModule_AddIntConstant(m, "CEC_OPCODE_TIMER_STATUS",
1097          CEC_OPCODE_TIMER_STATUS);
1098    PyModule_AddIntConstant(m, "CEC_OPCODE_CEC_VERSION",
1099          CEC_OPCODE_CEC_VERSION);
1100    PyModule_AddIntConstant(m, "CEC_OPCODE_GET_CEC_VERSION",
1101          CEC_OPCODE_GET_CEC_VERSION);
1102    PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_PHYSICAL_ADDRESS",
1103          CEC_OPCODE_GIVE_PHYSICAL_ADDRESS);
1104    PyModule_AddIntConstant(m, "CEC_OPCODE_GET_MENU_LANGUAGE",
1105          CEC_OPCODE_GET_MENU_LANGUAGE);
1106    PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_PHYSICAL_ADDRESS",
1107          CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
1108    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_MENU_LANGUAGE",
1109          CEC_OPCODE_SET_MENU_LANGUAGE);
1110    PyModule_AddIntConstant(m, "CEC_OPCODE_DECK_CONTROL",
1111          CEC_OPCODE_DECK_CONTROL);
1112    PyModule_AddIntConstant(m, "CEC_OPCODE_DECK_STATUS",
1113          CEC_OPCODE_DECK_STATUS);
1114    PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_DECK_STATUS",
1115          CEC_OPCODE_GIVE_DECK_STATUS);
1116    PyModule_AddIntConstant(m, "CEC_OPCODE_PLAY",
1117          CEC_OPCODE_PLAY);
1118    PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS",
1119          CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS);
1120    PyModule_AddIntConstant(m, "CEC_OPCODE_SELECT_ANALOGUE_SERVICE",
1121          CEC_OPCODE_SELECT_ANALOGUE_SERVICE);
1122    PyModule_AddIntConstant(m, "CEC_OPCODE_SELECT_DIGITAL_SERVICE",
1123          CEC_OPCODE_SELECT_DIGITAL_SERVICE);
1124    PyModule_AddIntConstant(m, "CEC_OPCODE_TUNER_DEVICE_STATUS",
1125          CEC_OPCODE_TUNER_DEVICE_STATUS);
1126    PyModule_AddIntConstant(m, "CEC_OPCODE_TUNER_STEP_DECREMENT",
1127          CEC_OPCODE_TUNER_STEP_DECREMENT);
1128    PyModule_AddIntConstant(m, "CEC_OPCODE_TUNER_STEP_INCREMENT",
1129          CEC_OPCODE_TUNER_STEP_INCREMENT);
1130    PyModule_AddIntConstant(m, "CEC_OPCODE_DEVICE_VENDOR_ID",
1131          CEC_OPCODE_DEVICE_VENDOR_ID);
1132    PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_DEVICE_VENDOR_ID",
1133          CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
1134    PyModule_AddIntConstant(m, "CEC_OPCODE_VENDOR_COMMAND",
1135          CEC_OPCODE_VENDOR_COMMAND);
1136    PyModule_AddIntConstant(m, "CEC_OPCODE_VENDOR_COMMAND_WITH_ID",
1137          CEC_OPCODE_VENDOR_COMMAND_WITH_ID);
1138    PyModule_AddIntConstant(m, "CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN",
1139          CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN);
1140    PyModule_AddIntConstant(m, "CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP",
1141          CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP);
1142    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_OSD_STRING",
1143          CEC_OPCODE_SET_OSD_STRING);
1144    PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_OSD_NAME",
1145          CEC_OPCODE_GIVE_OSD_NAME);
1146    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_OSD_NAME",
1147          CEC_OPCODE_SET_OSD_NAME);
1148    PyModule_AddIntConstant(m, "CEC_OPCODE_MENU_REQUEST",
1149          CEC_OPCODE_MENU_REQUEST);
1150    PyModule_AddIntConstant(m, "CEC_OPCODE_MENU_STATUS",
1151          CEC_OPCODE_MENU_STATUS);
1152    PyModule_AddIntConstant(m, "CEC_OPCODE_USER_CONTROL_PRESSED",
1153          CEC_OPCODE_USER_CONTROL_PRESSED);
1154    PyModule_AddIntConstant(m, "CEC_OPCODE_USER_CONTROL_RELEASE",
1155          CEC_OPCODE_USER_CONTROL_RELEASE);
1156    PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_DEVICE_POWER_STATUS",
1157          CEC_OPCODE_GIVE_DEVICE_POWER_STATUS);
1158    PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_POWER_STATUS",
1159          CEC_OPCODE_REPORT_POWER_STATUS);
1160    PyModule_AddIntConstant(m, "CEC_OPCODE_FEATURE_ABORT",
1161          CEC_OPCODE_FEATURE_ABORT);
1162    PyModule_AddIntConstant(m, "CEC_OPCODE_ABORT",
1163          CEC_OPCODE_ABORT);
1164    PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_AUDIO_STATUS",
1165          CEC_OPCODE_GIVE_AUDIO_STATUS);
1166    PyModule_AddIntConstant(m, "CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS",
1167          CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
1168    PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_AUDIO_STATUS",
1169          CEC_OPCODE_REPORT_AUDIO_STATUS);
1170    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_SYSTEM_AUDIO_MODE",
1171          CEC_OPCODE_SET_SYSTEM_AUDIO_MODE);
1172    PyModule_AddIntConstant(m, "CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST",
1173          CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST);
1174    PyModule_AddIntConstant(m, "CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS",
1175          CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS);
1176    PyModule_AddIntConstant(m, "CEC_OPCODE_SET_AUDIO_RATE",
1177          CEC_OPCODE_SET_AUDIO_RATE);
1178    PyModule_AddIntConstant(m, "CEC_OPCODE_START_ARC",
1179          CEC_OPCODE_START_ARC);
1180    PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_ARC_STARTED",
1181          CEC_OPCODE_REPORT_ARC_STARTED);
1182    PyModule_AddIntConstant(m, "CEC_OPCODE_REPORT_ARC_ENDED",
1183          CEC_OPCODE_REPORT_ARC_ENDED);
1184    PyModule_AddIntConstant(m, "CEC_OPCODE_REQUEST_ARC_START",
1185          CEC_OPCODE_REQUEST_ARC_START);
1186    PyModule_AddIntConstant(m, "CEC_OPCODE_REQUEST_ARC_END",
1187          CEC_OPCODE_REQUEST_ARC_END);
1188    PyModule_AddIntConstant(m, "CEC_OPCODE_END_ARC",
1189          CEC_OPCODE_END_ARC);
1190    PyModule_AddIntConstant(m, "CEC_OPCODE_CDC",
1191          CEC_OPCODE_CDC);
1192    PyModule_AddIntConstant(m, "CEC_OPCODE_NONE",
1193          CEC_OPCODE_NONE);
1194 
1195    // expose whether or not we're using the new cec_adapter_descriptor API
1196    // this should help debugging by exposing which version was detected and
1197    // which adapter detection API was used at compile time
1198    PyModule_AddIntMacro(m, HAVE_CEC_ADAPTER_DESCRIPTOR);
1199 
1200 #if PY_MAJOR_VERSION >= 3
1201    return m;
1202 #endif
1203 }
1204