1 //===========================================
2 // Lumina-DE source code
3 // Copyright (c) 2016, Ken Moore
4 // Available under the 3-clause BSD license
5 // See the LICENSE file for full details
6 //===========================================
7 #include "LInputDevice.h"
8
9 //Qt Library includes
10 #include <QString>
11 #include <QX11Info>
12 #include <QDebug>
13
14 //XCB Library includes
15 #include <xcb/xcb.h>
16 #include <xcb/xcb_atom.h>
17 #include <xcb/xinput.h>
18 #include <xcb/xproto.h>
19
20 #include <LUtils.h>
21
22 //===================
23 // LInputDevice Class
24 //===================
25 // === PUBLIC ===
LInputDevice(unsigned int id,unsigned int type)26 LInputDevice::LInputDevice(unsigned int id, unsigned int type){
27 devID = id;
28 devType = type;
29 //ATOM_FLOAT = 0; //init this when needed later
30 //devName = name;
31 getProperties(); //need to populate the name/atom correlations for properties
32 readProperties(); //populate the hash with the current values of the properties
33 }
34
~LInputDevice()35 LInputDevice::~LInputDevice(){
36
37 }
38
devNumber()39 unsigned int LInputDevice::devNumber(){
40 return devID;
41 }
42
isPointer()43 bool LInputDevice::isPointer(){
44 return (devType==XCB_INPUT_DEVICE_USE_IS_X_POINTER \
45 || devType==XCB_INPUT_DEVICE_USE_IS_X_EXTENSION_POINTER);
46 }
47
isKeyboard()48 bool LInputDevice::isKeyboard(){
49 return (devType==XCB_INPUT_DEVICE_USE_IS_X_KEYBOARD \
50 || devType==XCB_INPUT_DEVICE_USE_IS_X_EXTENSION_KEYBOARD);
51 }
52
isExtension()53 bool LInputDevice::isExtension(){
54 return (devType==XCB_INPUT_DEVICE_USE_IS_X_EXTENSION_DEVICE \
55 || devType==XCB_INPUT_DEVICE_USE_IS_X_EXTENSION_KEYBOARD \
56 || devType==XCB_INPUT_DEVICE_USE_IS_X_EXTENSION_POINTER);
57 }
58
59 // Property Management
listProperties()60 QList<int> LInputDevice::listProperties(){
61 return devProps.keys();
62 }
63
propertyName(int prop)64 QString LInputDevice::propertyName(int prop){
65 if(devProps.contains(prop)){ return devProps[prop].name; }
66 else{ return ""; }
67 }
68
getPropertyValue(int prop)69 QVariant LInputDevice::getPropertyValue(int prop){
70 if(devProps.contains(prop)){ return devProps[prop].value; }
71 else{ return QVariant(); }
72 }
73
setPropertyValue(int prop,QVariant value)74 bool LInputDevice::setPropertyValue(int prop, QVariant value){
75 if(!devProps.contains(prop)){ return false; }
76 //Need the float atom for some properties - make sure we have that first
77 /*if(ATOM_FLOAT==0){
78 xcb_intern_atom_reply_t *ar = xcb_intern_atom_reply(QX11Info::connection(), \
79 xcb_intern_atom(QX11Info::connection(), 0, 1, "FLOAT"), NULL);
80 if(ar!=0){
81 ATOM_FLOAT = ar->atom;
82 free(ar);
83 }
84 }*/
85 //Now setup the argument
86 bool ok = false;
87 QStringList args;
88 args << "--set-prop";
89 args << QString::number(devID);
90 args << QString::number(prop); //prop ID
91 args << variantToString(value);
92 ok = (0 == LUtils::runCmd("xinput", args) );
93 if(ok){
94 //Need to update the value in the hash as well
95 propData dat = devProps[prop];
96 dat.value = value;
97 devProps.insert(prop, dat);
98 }
99 return ok;
100 }
101
102 // === PRIVATE ===
getProperties()103 void LInputDevice::getProperties(){
104 devProps.clear();
105 xcb_input_list_device_properties_cookie_t cookie = xcb_input_list_device_properties_unchecked(QX11Info::connection(), devID);
106 xcb_input_list_device_properties_reply_t *reply = xcb_input_list_device_properties_reply(QX11Info::connection(), cookie, NULL);
107 //Get the atoms
108 xcb_atom_t *atoms = xcb_input_list_device_properties_atoms(reply);
109 //qDebug() << "Property Response Type:" << reply->response_type; //Always seems to be "1"
110 QList<xcb_get_atom_name_cookie_t> cookies;
111 for(int i=0; i<reply->num_atoms; i++){ cookies << xcb_get_atom_name(QX11Info::connection(), atoms[i]); }
112 for(int i=0; i<reply->num_atoms; i++){
113 xcb_get_atom_name_reply_t *nr = xcb_get_atom_name_reply(QX11Info::connection(), cookies[i], NULL);
114 propData DATA;
115 DATA.name = QString::fromUtf8( xcb_get_atom_name_name(nr), xcb_get_atom_name_name_length(nr) );
116 DATA.atom = atoms[i];
117 DATA.id = (int)(atoms[i]);
118 devProps.insert(DATA.id,DATA);
119 ::free(nr);
120 }
121 //Done with data structure
122 ::free(reply);
123 }
124
readProperties()125 void LInputDevice::readProperties(){
126 QList<int> props = devProps.keys();
127 //XINPUT UTILITY USAGE (alternative to XCB which actually works right now)
128 QStringList info = LUtils::getCmdOutput("xinput list-props "+QString::number(devID));
129 for(int i=0; i<props.length(); i++){
130 propData PROP = devProps[props[i]];
131 QStringList filter = info.filter(" ("+QString::number(PROP.id)+"):");
132 if(filter.length()==1){
133 QString val = filter.first().section("):",1,-1).simplified();
134 //Now figure out what type of value this is and save it into the QVariant
135 QVariant variant;
136 if(val.split(", ").length()>1){
137 //some kind of array
138 QList<QVariant> list;
139 QStringList valList = val.split(", ");
140 for(int j=0; j<valList.length(); j++){ list << valueToVariant(valList[j]); }
141 variant = QVariant(list);
142 }else{
143 variant = valueToVariant(val);
144 }
145 PROP.value = variant;
146 }
147 devProps.insert(props[i], PROP);
148 }
149
150 //XCB Code (non-functional - issue with library itself? 12/6/16 - Ken Moore)
151 /*QVariant result;
152 if(!devProps.contains(prop)){qDebug() << "Invalid Property"; return result; }
153 //Now generate the property request
154 xcb_input_get_device_property_cookie_t cookie = xcb_input_get_device_property_unchecked( QX11Info::connection(), devProps.value(prop).atom, \
155 XCB_ATOM_ATOM, 0, 1000, devID, 0);
156 xcb_input_get_device_property_reply_t *reply = xcb_input_get_device_property_reply(QX11Info::connection(), cookie, NULL);
157 if(reply==0){ qDebug() << "Could not get reply!"; return result; }
158 //Now read off the value of the property
159 if(ATOM_FLOAT==0){
160 xcb_intern_atom_reply_t *ar = xcb_intern_atom_reply(QX11Info::connection(), \
161 xcb_intern_atom(QX11Info::connection(), 0, 1, "FLOAT"), NULL);
162 if(ar!=0){
163 ATOM_FLOAT = ar->atom;
164 free(ar);
165 }
166 }
167 //Turn the reply into the proper items array (depends on format of the return data)
168 xcb_input_get_device_property_items_t items;
169 qDebug() <<QByteArray::fromRawData( (char*)(xcb_input_get_device_property_items(reply) ) , reply->num_items);
170 void *buffer = xcb_input_get_device_property_items(reply);
171 xcb_input_get_device_property_items_serialize( &buffer, reply->num_items, reply->format, &items);
172
173 //if(reply->num_items > 0){
174 //qDebug() << "Format:" << reply->format << "Length:" << length;
175 //qDebug() << "Response Type:" << reply->response_type << "Pads:" << reply->pad0 << reply->pad1;
176 switch(reply->type){
177 case XCB_ATOM_INTEGER:
178 //qDebug() << "Got Integer";
179
180 break;
181 case XCB_ATOM_CARDINAL:
182 //qDebug() << "Got Cardinal";
183
184 break;
185 case XCB_ATOM_STRING:
186 qDebug() << "Got String:";
187 if(reply->format==8){
188 result.setValue( QByteArray::fromRawData( (char*) xcb_input_get_device_property_items_data_8(&items), sizeof(xcb_input_get_device_property_items_data_8(&items))/sizeof(char)) );
189 }
190 break;
191 case XCB_ATOM_ATOM:
192 //qDebug() << "Got Atom";
193
194 break;
195 default:
196 qDebug() << "Other Type:" << reply->type;
197 }
198 //}
199 free(reply); //done with this structure
200 return result;*/
201 }
202
valueToVariant(QString value)203 QVariant LInputDevice::valueToVariant(QString value){
204 //Read through the string and see what type of value it is
205 if(value.count("\"")==2){
206 //String value or atom
207 if(value.endsWith(")")){
208 //ATOM (name string +(atomID))
209 return QVariant(value); //don't strip off the atom number -- keep that within the parenthesis
210 }else{
211 //String
212 value = value.section("\"",1,-2); //everything between the quotes
213 return QVariant(value);
214 }
215 }else if(value.contains(".")){
216 //float/double number
217 return QVariant( value.toDouble() );
218 }else{
219 //integer or boolian (no way to tell right now - assume all int)
220 bool ok = false;
221 int intval = value.toInt(&ok);
222 if(ok){ return QVariant(intval); }
223 }
224 return QVariant();
225 }
226
variantToString(QVariant value)227 QString LInputDevice::variantToString(QVariant value){
228 if( value.canConvert< QList<QVariant> >() ){
229 //List of variants
230 QStringList out;
231 QList<QVariant> list = value.toList();
232 for(int i=0; i<list.length(); i++){ out << variantToString(list[i]); }
233 return out.join(", ");
234 }else{
235 //Single value
236 if(value.canConvert<double>() ){
237 return QString::number(value.toDouble());
238 }else if(value.canConvert<int>() ){
239 return QString::number(value.toInt());
240 }else if( value.canConvert<QString>() ){
241 //See if this is an atom first
242 QString val = value.toString();
243 if(val.contains("(")){ val = val.section("(",1,-1).section(")",0,0); }
244 return val;
245 }
246 }
247 return ""; //nothing to return
248 }
249
250 //======================
251 // LInput Static Functions
252 //======================
listDevices()253 QList<LInputDevice*> LInput::listDevices(){
254 QList<LInputDevice*> devices;
255 xcb_input_list_input_devices_cookie_t cookie = xcb_input_list_input_devices_unchecked(QX11Info::connection());
256 xcb_input_list_input_devices_reply_t *reply = xcb_input_list_input_devices_reply(QX11Info::connection(), cookie, NULL);
257 if(reply==0){ return devices; } //error - nothing returned
258 //Use the iterator for going through the reply
259 //qDebug() << "Create iterator";
260 xcb_input_device_info_iterator_t iter = xcb_input_list_input_devices_devices_iterator(reply);
261 //xcb_str_iterator_t nameiter = xcb_input_list_input_devices_names_iterator(reply);
262
263 //Now step through the reply
264 while(iter.data != 0 ){
265 devices << new LInputDevice(iter.data->device_id, iter.data->device_use);
266 //qDebug() << "Found Input Device:" << iter.data->device_id;
267 //qDebug() << " - num_class_info:" << iter.data->num_class_info;
268 if(iter.rem>0){ xcb_input_device_info_next(&iter); }
269 else{ break; }
270 }
271 //Free the reply (done with it)
272 ::free(reply);
273 //return the information
274 return devices;
275 }
276