5 #include <X11/XKBlib.h>
6 #include <dali/integration-api/debug.h>
9 #include "debug/x-input2-debug.h"
23 // For multi-touch we need XI2 version 2.2
24 int XI2MinorVersionRequired = 2;
25 int XI2MajorVersionRequired = 2;
28 XInput2::XInput2( XID window, Display* display, WindowEventInterface* eventInterface )
29 : mEventInterface( eventInterface ),
33 mMultiTouchSupport( false )
41 void XInput2::Initialize()
43 // Check if X supports the multi-touch protocol
44 QueryMultiTouchSupport();
46 // Query what input devices are available on the system.
49 // Select the input events we want to receive from the input devices available
54 int XInput2::GetExtensionId() const
56 return mXI2ExtensionId;
59 bool XInput2::FilteredDevice( int deviceId ) const
61 for( VectorBase::SizeType i = 0; i < mInputDeviceInfo.Count(); ++i )
63 if( mInputDeviceInfo[i].deviceId == deviceId )
71 bool XInput2::PreProcessEvent( XIDeviceEvent *deviceEvent ) const
73 // @todo need to do some testing to see if this check is actually required
74 // E.g. if IME window is sending events, this check may fail
75 if( deviceEvent->event != mWindow )
79 // emulated flags means that the event has been emulated from another XI 2.x event for legacy client support
80 // We don't call XISelectEvents on these events so hopefully shouldn't get them.
81 if( ( deviceEvent->flags & XIPointerEmulated ) || ( deviceEvent->flags & XITouchEmulatingPointer ) )
86 if( !FilteredDevice( deviceEvent->deviceid ))
93 void XInput2::CreateKeyEvent( const XIDeviceEvent* deviceEvent, KeyEvent& keyEvent ) const
95 // get the physical key code ( range 8..255)
96 KeyCode keycode = deviceEvent->detail;
98 keyEvent.keyCode = keycode;
99 keyEvent.state = KeyEvent::Down;
100 keyEvent.keyModifier = deviceEvent->mods.effective;
102 // extract key symbol. The symbol is typically the name visible on the key
103 // e.g. key code 201 might = Brightness increase, or a Korean character depending on the keyboard mapping.
104 // @todo For XKbKeycodeToKeysym to work correctly we need the group and level.
105 // investigate using XkbGetState to get initial state then start monitoring for XkbStateNotify events
106 KeySym sym = XkbKeycodeToKeysym( mDisplay, keycode, 0 /* group */ , keyEvent.IsShiftModifier() );
107 char* keyname = XKeysymToString( sym );
109 keyEvent.keyPressedName = keyname;
110 keyEvent.time = deviceEvent->time;
114 // prototyping implementation
115 void XInput2::ProcessKeyEvent( XKeyEvent* xEvent )
118 keyEvent.keyCode = xEvent->keycode;
119 keyEvent.state = KeyEvent::Down;
121 KeySym sym = XkbKeycodeToKeysym( mDisplay, xEvent->keycode, 0 /* group */ , keyEvent.IsShiftModifier() );
122 char* keyname = XKeysymToString( sym );
123 keyEvent.keyPressedName = keyname;
124 keyEvent.time = xEvent->time;
126 Integration::KeyEvent convertedEvent( keyEvent );
128 mEventInterface->KeyEvent( convertedEvent );
130 void XInput2::ProcessClientMessage( XEvent* event )
132 // Format for client message for key event
134 // xev.xclient.type = ClientMessage;
135 // xev.xclient.display = keyrouter.disp;
136 // xev.xclient.window = window;
137 // xev.xclient.format = 32;
138 // xev.xclient.message_type = ecore_x_atom_get("VDINPUT_KEYEVENT");
139 // xev.xclient.data.l[0] = ev->time; /* time */
140 // xev.xclient.data.l[1] = ev->state; /* modifier */
141 // xev.xclient.data.l[2] = ev->code; /* keycode */
142 // xev.xclient.data.l[3] = ev->value; /* press/release */
143 // xev.xclient.data.l[4] = ev->device_id; /* deviceId */
145 Atom input_atom = XInternAtom( mDisplay, "VDINPUT_KEYEVENT", false);
148 keyEvent.state = KeyEvent::Down;
150 // if atom is "VDINPUT_KEYEVENT"
151 if( input_atom == event->xclient.message_type )
154 keyEvent.keyModifier = event->xclient.data.l[1];
155 keyEvent.keyCode = event->xclient.data.l[2];
157 // only transmit keydown events
158 if( event->xclient.data.l[3] != 2)
160 return; // 2 = key down, 3 = key release
163 KeySym sym = XkbKeycodeToKeysym( mDisplay, keyEvent.keyCode, 0 /* group */ , keyEvent.IsShiftModifier() );
164 char* keyname = XKeysymToString( sym );
165 keyEvent.keyPressedName = keyname;
166 keyEvent.time = event->xclient.data.l[0];
168 Integration::KeyEvent convertedEvent( keyEvent );
170 mEventInterface->KeyEvent( convertedEvent );
174 void XInput2::ProcessGenericEvent( XGenericEventCookie* cookie )
176 XIDeviceEvent* deviceEvent = static_cast< XIDeviceEvent* >(cookie->data);
178 X11Debug::LogXI2Event( cookie );
180 bool requiresProcessing = PreProcessEvent( deviceEvent );
182 if( ! requiresProcessing )
187 Integration::Point point;
188 point.SetDeviceId( deviceEvent->deviceid );
189 point.SetScreenPosition( Vector2( deviceEvent->event_x, deviceEvent->event_y ) );
190 Time time( deviceEvent->time ); // X is using uint32 for time field ( see XI2proto.h )
192 switch( cookie->evtype)
197 point.SetState( PointState::MOTION );
198 mEventInterface->TouchEvent( point, time );
204 point.SetState( PointState::DOWN );
205 mEventInterface->TouchEvent( point, time );
209 case XI_ButtonRelease:
211 point.SetState( PointState::UP );
212 mEventInterface->TouchEvent( point, time );
217 mEventInterface->WindowFocusIn();
222 mEventInterface->WindowFocusOut();
228 CreateKeyEvent( deviceEvent, keyEvent );
229 Integration::KeyEvent convertedEvent( keyEvent );
230 mEventInterface->KeyEvent( convertedEvent );
240 void XInput2::QueryMultiTouchSupport()
242 int minor = XI2MinorVersionRequired;
243 int major = XI2MajorVersionRequired;
244 int firstEventCode, firstErrorCode;
246 // Check if the extension is available and get the extension id
247 if( !XQueryExtension(mDisplay, "XInputExtension", &mXI2ExtensionId, &firstEventCode, &firstErrorCode) )
249 DALI_LOG_ERROR(" XInputExtension not available \n");
253 // inform X that DALi ( the client ) supports XI2 version 2.2
254 // it will assign the X servers supported version to the parameters
255 int ret = XIQueryVersion( mDisplay, &major, &minor);
256 if( ret == BadValue )
258 DALI_LOG_ERROR(" XIQueryVersion %d,%d failed \n", major, minor );
262 // check the version is supports multi-touch
263 if( ( major * 1000 + minor ) >= ( XI2MajorVersionRequired * 1000 + XI2MinorVersionRequired ) )
265 mMultiTouchSupport = true;
269 DALI_LOG_ERROR( "XInput 2.2 or greater required for multi-touch\n");
273 void XInput2::QueryDevices()
275 int numberOfDevices( 0 );
277 // QueryDevice returns information about one or more input devices
278 XIDeviceInfo* deviceInfoArray = XIQueryDevice( mDisplay, XIAllDevices, &numberOfDevices);
279 XIDeviceInfo* device = deviceInfoArray;
281 X11Debug::LogInputDeviceInfo( deviceInfoArray, numberOfDevices );
283 mInputDeviceInfo.Resize( numberOfDevices );
285 for( int i = 0; i < numberOfDevices; ++i, ++device )
289 info.AssignDeviceInfo( device );
291 mInputDeviceInfo.PushBack( info );
294 XIFreeDeviceInfo( deviceInfoArray );
297 void XInput2::SelectEvents( int deviceId, const Dali::Vector< unsigned int >& filter )
299 if( filter.Size() == 0)
304 // each event like XI_ButtonPress is stored as unique bit, so if there's 32 events we need 4 bytes
305 // the XIMaskLen macro provides the length for us at compile time.
306 unsigned char mask[ XIMaskLen( XI_LASTEVENT ) ] = {};
307 XIEventMask eventMask;
309 eventMask.deviceid = deviceId;
310 eventMask.mask_len = sizeof( mask);
311 eventMask.mask = mask;
313 for( VectorBase::SizeType i = 0; i< filter.Count(); ++i )
315 XISetMask( mask, filter[i] );
318 XISelectEvents( mDisplay, mWindow, &eventMask, 1);
321 void XInput2::SelectInputEvents()
324 * From the X documentation:
325 * "A master pointer is a virtual pointer device that does not represent a physical device.
326 * If a slave device generates an event, the event is also generated by the respective master device.
327 * Multiple slave devices can be attached to a single master device."
328 * master = cursor / keyboard focus,
329 * slave = physical device
331 * For motion events, we currently just listen to the slave devices. This allows us the ability to
332 * perform a XIGrabDevice on the slave if we need to, which will temporarily detach it from the master.
333 * In DALi we currently don't perform a grab as typically we just have a single x-window displayed.
334 * Where as other toolkits may have a window for a popup and want do know when the mouse is clicked outside
335 * of the popup, to close it.
337 Dali::Vector< unsigned int > eventFilter;
338 eventFilter.Reserve( 6 ); // typically filter no more than 6 events
340 for( VectorBase::SizeType i = 0; i < mInputDeviceInfo.Count(); ++i )
342 const XInput2Device& device( mInputDeviceInfo[ i ] );
346 // Floating slave devices also can generate key events. For example, TV remote controllers.
347 if( ( device.use == XIFloatingSlave ) || ( device.use == XISlavePointer ) || ( device.use == XISlaveKeyboard ) )
349 if( device.buttonClass )
351 eventFilter.PushBack( XI_ButtonPress );
352 eventFilter.PushBack( XI_ButtonRelease );
353 eventFilter.PushBack( XI_Motion );
355 if( device.touchClass )
357 eventFilter.PushBack( XI_TouchUpdate );
358 eventFilter.PushBack( XI_TouchBegin );
359 eventFilter.PushBack( XI_TouchEnd );
361 if( device.keyClass )
363 eventFilter.PushBack( XI_KeyPress );
364 eventFilter.PushBack( XI_KeyRelease );
368 if( eventFilter.Count() > 0 )
370 SelectEvents( device.deviceId, eventFilter );
374 } // namespace internal
375 } // namespace adaptor