2
// libavg - Media Playback Engine.
3
// Copyright (C) 2003-2011 Ulrich von Zadow
5
// This library is free software; you can redistribute it and/or
6
// modify it under the terms of the GNU Lesser General Public
7
// License as published by the Free Software Foundation; either
8
// version 2 of the License, or (at your option) any later version.
10
// This library 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 GNU
13
// Lesser General Public License for more details.
15
// You should have received a copy of the GNU Lesser General Public
16
// License along with this library; if not, write to the Free Software
17
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
// Current versions can be found at www.libavg.de
22
#include "XInput21MTInputDevice.h"
24
#include "TouchEvent.h"
27
#include "TouchStatus.h"
28
#include "SDLDisplayEngine.h"
30
#include "../base/Logger.h"
31
#include "../base/Point.h"
32
#include "../base/ObjectCounter.h"
33
#include "../base/Exception.h"
34
#include "../base/OSHelper.h"
35
#include "../base/StringHelper.h"
37
#include <SDL/SDL_syswm.h>
40
#include <X11/extensions/XInput.h>
41
#include <X11/extensions/XInput2.h>
43
#ifndef XI_TouchUpdate
44
// Working with a preliminary spec. Update names to the current spec.
45
#define XI_TouchUpdate XI_TouchMotion
46
#define XI_TouchUpdateUnowned XI_TouchMotionUnowned
53
Display* XInput21MTInputDevice::s_pDisplay = 0;
55
const char* cookieTypeToName(int evtype);
56
string xEventTypeToName(int evtype);
58
XInput21MTInputDevice::XInput21MTInputDevice()
64
XInput21MTInputDevice::~XInput21MTInputDevice()
66
if (m_DeviceID != -1 && m_OldMasterDeviceID != -1) {
67
XIAttachSlaveInfo atInfo;
68
atInfo.type = XIAttachSlave;
69
atInfo.deviceid = m_DeviceID;
70
atInfo.new_master = m_OldMasterDeviceID;
71
XIChangeHierarchy(s_pDisplay, (XIAnyHierarchyChangeInfo *)&atInfo, 1);
75
void XInput21MTInputDevice::start()
78
SDLDisplayEngine * pEngine = Player::get()->getDisplayEngine();
81
SDL_VERSION(&info.version);
82
int rc = SDL_GetWMInfo(&info);
84
s_pDisplay = info.info.x11.display;
85
m_SDLLockFunc = info.info.x11.lock_func;
86
m_SDLUnlockFunc = info.info.x11.unlock_func;
89
// XInput Extension available?
91
bool bOk = XQueryExtension(s_pDisplay, "XInputExtension", &m_XIOpcode,
94
throw Exception(AVG_ERR_MT_INIT,
95
"XInput 2.1 multitouch event source: X Input extension not available.");
98
// Which version of XI2? We need 2.1.
99
int major = 2, minor = 1;
100
status = XIQueryVersion(s_pDisplay, &major, &minor);
101
if (status == BadRequest) {
102
throw Exception(AVG_ERR_MT_INIT,
103
"XInput 2.1 multitouch event source: Server does not support XI2");
105
if (major < 2 || minor < 1) {
106
throw Exception(AVG_ERR_MT_INIT,
107
"XInput 2.1 multitouch event source: Supported version is "
108
+toString(major)+"."+toString(minor)+". 2.1 is needed.");
113
// SDL grabs the pointer in full screen mode. This breaks touchscreen usage.
114
// Can't use SDL_WM_GrabInput(SDL_GRAB_OFF) because it doesn't work in full
115
// screen mode. Get the display connection and do it manually.
116
XUngrabPointer(info.info.x11.display, CurrentTime);
119
mask.deviceid = m_DeviceID;
120
mask.mask_len = XIMaskLen(XI_LASTEVENT);
121
mask.mask = (unsigned char *)calloc(mask.mask_len, sizeof(char));
122
memset(mask.mask, 0, mask.mask_len);
123
XISetMask(mask.mask, XI_TouchBegin);
124
XISetMask(mask.mask, XI_TouchUpdate);
125
XISetMask(mask.mask, XI_TouchEnd);
127
status = XISelectEvents(s_pDisplay, info.info.x11.window, &mask, 1);
128
AVG_ASSERT(status == Success);
132
SDL_SetEventFilter(XInput21MTInputDevice::filterEvent);
135
XIDetachSlaveInfo detInfo;
136
detInfo.type = XIDetachSlave;
137
detInfo.deviceid = m_DeviceID;
138
XIChangeHierarchy(s_pDisplay, (XIAnyHierarchyChangeInfo *)&detInfo, 1);
140
pEngine->setXIMTInputDevice(this);
141
MultitouchInputDevice::start();
142
AVG_TRACE(Logger::CONFIG, "XInput 2.1 Multitouch event source created.");
145
void XInput21MTInputDevice::handleXIEvent(const XEvent& xEvent)
148
XGenericEventCookie* pCookie = (XGenericEventCookie*)&xEvent.xcookie;
149
if (pCookie->type == GenericEvent && pCookie->extension == m_XIOpcode) {
150
XIDeviceEvent* pDevEvent = (XIDeviceEvent*)(pCookie->data);
151
IntPoint pos(pDevEvent->event_x, pDevEvent->event_y);
152
int xid = pDevEvent->detail;
153
switch (pCookie->evtype) {
156
// cerr << "TouchBegin " << xid << ", " << pos << endl;
158
TouchEventPtr pEvent = createEvent(m_LastID, Event::CURSORDOWN, pos);
159
addTouchStatus(xid, pEvent);
164
// cerr << "TouchUpdate " << xid << ", " << pos << endl;
165
TouchEventPtr pEvent = createEvent(0, Event::CURSORMOTION, pos);
166
TouchStatusPtr pTouchStatus = getTouchStatus(xid);
167
AVG_ASSERT(pTouchStatus);
168
pTouchStatus->pushEvent(pEvent);
173
// cerr << "TouchEnd " << xid << ", " << pos << endl;
174
TouchStatusPtr pTouchStatus = getTouchStatus(xid);
175
AVG_ASSERT(pTouchStatus);
176
TouchEventPtr pEvent = createEvent(0, Event::CURSORUP, pos);
177
pTouchStatus->pushEvent(pEvent);
182
// cerr << "Unhandled XInput event, type: "
183
// << cookieTypeToName(pCookie->evtype) << endl;
186
// cerr << "Unhandled X11 Event: " << xEvent.type << endl;
189
XFreeEventData(s_pDisplay, pCookie);
193
std::vector<EventPtr> XInput21MTInputDevice::pollEvents()
196
return MultitouchInputDevice::pollEvents();
199
void XInput21MTInputDevice::findMTDevice()
202
XIDeviceInfo* pDevices;
203
XIDeviceInfo* pDevice;
205
pDevices = XIQueryDevice(s_pDisplay, XIAllDevices, &ndevices);
207
XITouchClassInfo* pTouchClass = 0;
209
for (int i = 0; i < ndevices && !pTouchClass; ++i) {
210
pDevice = &pDevices[i];
211
// cerr << "Device " << pDevice->name << "(id: " << pDevice->deviceid << ")."
213
if (pDevice->use == XISlavePointer || pDevice->use == XIFloatingSlave) {
214
for (int j = 0; j < pDevice->num_classes; ++j) {
215
XIAnyClassInfo * pClass = pDevice->classes[j];
216
if (pClass->type == XITouchClass) {
217
XITouchClassInfo* pTempTouchClass = (XITouchClassInfo *)pClass;
218
if (pTempTouchClass->mode == XIDirectTouch) {
219
pTouchClass = pTempTouchClass;
220
m_sDeviceName = pDevice->name;
221
m_DeviceID = pDevice->deviceid;
222
if (pDevice->use == XISlavePointer) {
223
m_OldMasterDeviceID = pDevice->attachment;
225
m_OldMasterDeviceID = -1;
227
maxTouches = pTouchClass->num_touches;
235
AVG_TRACE(Logger::CONFIG, "Using multitouch input device " << m_sDeviceName
236
<< ", max touches: " << maxTouches);
238
throw Exception(AVG_ERR_MT_INIT,
239
"XInput 2.1 multitouch event source: No multitouch device found.");
241
XIFreeDeviceInfo(pDevices);
244
TouchEventPtr XInput21MTInputDevice::createEvent(int id, Event::Type type, IntPoint pos)
246
return TouchEventPtr(new TouchEvent(id, type, pos, Event::TOUCH));
249
int XInput21MTInputDevice::filterEvent(const SDL_Event * pEvent)
251
// This is a hook into libsdl event processing. Since libsdl doesn't know about
252
// XInput 2, it doesn't call XGetEventData either. By the time the event arrives
253
// in handleXIEvent(), other events may have arrived and XGetEventData can't be
254
// called anymore. Hence this function, which calls XGetEventData for each event
255
// that has a cookie.
256
if (pEvent->type == SDL_SYSWMEVENT) {
257
SDL_SysWMmsg* pMsg = pEvent->syswm.msg;
258
AVG_ASSERT(pMsg->subsystem == SDL_SYSWM_X11);
259
XEvent* pXEvent = &pMsg->event.xevent;
260
XGenericEventCookie* pCookie = (XGenericEventCookie*)&(pXEvent->xcookie);
261
// cerr << "---- filter xinput event: " << xEventTypeToName(pXEvent->type) << ", "
262
// << cookieTypeToName(pCookie->evtype) << endl;
263
XGetEventData(s_pDisplay, pCookie);
265
// cerr << "---- filter: " << int(pEvent->type) << endl;
270
// From xinput/test_xi2.c
271
const char* cookieTypeToName(int evtype)
275
case XI_DeviceChanged: name = "DeviceChanged"; break;
276
case XI_KeyPress: name = "KeyPress"; break;
277
case XI_KeyRelease: name = "KeyRelease"; break;
278
case XI_ButtonPress: name = "ButtonPress"; break;
279
case XI_ButtonRelease: name = "ButtonRelease"; break;
280
case XI_Motion: name = "Motion"; break;
281
case XI_Enter: name = "Enter"; break;
282
case XI_Leave: name = "Leave"; break;
283
case XI_FocusIn: name = "FocusIn"; break;
284
case XI_FocusOut: name = "FocusOut"; break;
285
case XI_HierarchyChanged: name = "HierarchyChanged"; break;
286
case XI_PropertyEvent: name = "PropertyEvent"; break;
287
case XI_RawKeyPress: name = "RawKeyPress"; break;
288
case XI_RawKeyRelease: name = "RawKeyRelease"; break;
289
case XI_RawButtonPress: name = "RawButtonPress"; break;
290
case XI_RawButtonRelease: name = "RawButtonRelease"; break;
291
case XI_RawMotion: name = "RawMotion"; break;
292
case XI_TouchBegin: name = "TouchBegin"; break;
293
case XI_TouchEnd: name = "TouchEnd"; break;
294
case XI_TouchUpdate: name = "TouchUpdate"; break;
295
case XI_TouchUpdateUnowned: name = "TouchUpdateUnowned"; break;
296
default: name = "unknown event type"; break;
301
string xEventTypeToName(int evtype)
309
return "ButtonPress";
311
return "ButtonRelease";
313
return "MotionNotify";
315
return "EnterNotify";
317
return "LeaveNotify";
323
return "KeymapNotify";
327
return "GraphicsExpose";
330
case VisibilityNotify:
331
return "VisibilityNotify";
333
return "CreateNotify";
335
return "DestroyNotify";
337
return "UnmapNotify";
343
return "ReparentNotify";
344
case ConfigureNotify:
345
return "ConfigureNotify";
346
case ConfigureRequest:
347
return "ConfigureRequest";
349
return "GravityNotify";
351
return "ResizeRequest";
352
case CirculateNotify:
353
return "CirculateNotify";
354
case CirculateRequest:
355
return "CirculateRequest";
357
return "PropertyNotify";
359
return "SelectionClear";
360
case SelectionRequest:
361
return "SelectionRequest";
362
case SelectionNotify:
363
return "SelectionNotify";
365
return "ColormapNotify";
367
return "ClientMessage";
369
return "MappingNotify";
371
return "GenericEvent";
373
return "Unknown event type";