Send Integration KeyEvents to Core
[platform/core/uifw/dali-adaptor.git] / adaptors / x11 / x-events / x-input2.cpp
1 // CLASS HEADER
2 #include "x-input2.h"
3
4 // EXTERNAL INCLUDES
5 #include <X11/XKBlib.h>
6 #include <dali/integration-api/debug.h>
7
8 // INTERNAL INCLUDES
9 #include "debug/x-input2-debug.h"
10
11
12 namespace Dali
13 {
14
15 namespace Internal
16 {
17
18 namespace Adaptor
19 {
20
21 namespace
22 {
23 // For multi-touch we need XI2 version 2.2
24 int XI2MinorVersionRequired = 2;
25 int XI2MajorVersionRequired = 2;
26 }
27
28 XInput2::XInput2( XID window, Display* display, WindowEventInterface* eventInterface )
29 : mEventInterface( eventInterface ),
30   mDisplay( display ),
31   mWindow( window ),
32   mXI2ExtensionId(-1),
33   mMultiTouchSupport( false )
34 {
35
36 }
37 XInput2::~XInput2()
38 {
39 }
40
41 void XInput2::Initialize()
42 {
43   // Check if X supports the multi-touch protocol
44   QueryMultiTouchSupport();
45
46   // Query what input devices are available on the system.
47   QueryDevices();
48
49   // Select the input events we want to receive from the input devices available
50   SelectInputEvents();
51
52 }
53
54 int XInput2::GetExtensionId() const
55 {
56   return mXI2ExtensionId;
57 }
58
59 bool XInput2::FilteredDevice( int deviceId ) const
60 {
61   for( VectorBase::SizeType i = 0; i < mInputDeviceInfo.Count(); ++i )
62   {
63     if( mInputDeviceInfo[i].deviceId == deviceId )
64     {
65       return true;
66     }
67   }
68   return false;
69 }
70
71 bool XInput2::PreProcessEvent( XIDeviceEvent *deviceEvent ) const
72 {
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 )
76   {
77     return false;
78   }
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 ) )
82   {
83     return false;
84   }
85
86   if( !FilteredDevice( deviceEvent->deviceid ))
87   {
88     return false;
89   }
90   return true;
91 }
92
93 void XInput2::CreateKeyEvent( const XIDeviceEvent* deviceEvent, KeyEvent& keyEvent ) const
94 {
95   // get the physical key code ( range 8..255)
96   KeyCode keycode = deviceEvent->detail;
97
98   keyEvent.keyCode = keycode;
99   keyEvent.state = KeyEvent::Down;
100   keyEvent.keyModifier = deviceEvent->mods.effective;
101
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 );
108
109   keyEvent.keyPressedName = keyname;
110   keyEvent.time = deviceEvent->time;
111
112 }
113
114 // prototyping implementation
115 void XInput2::ProcessKeyEvent( XKeyEvent* xEvent )
116 {
117   KeyEvent keyEvent;
118   keyEvent.keyCode = xEvent->keycode;
119   keyEvent.state = KeyEvent::Down;
120
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;
125
126   Integration::KeyEvent convertedEvent( keyEvent );
127
128   mEventInterface->KeyEvent( convertedEvent );
129 }
130 void XInput2::ProcessClientMessage( XEvent* event )
131 {
132   // Format for client message for key event
133   // XEvent xev;
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 */
144
145   Atom input_atom = XInternAtom( mDisplay, "VDINPUT_KEYEVENT", false);
146   KeyEvent keyEvent;
147
148   keyEvent.state = KeyEvent::Down;
149
150   // if atom is "VDINPUT_KEYEVENT"
151   if( input_atom == event->xclient.message_type )
152   {
153
154     keyEvent.keyModifier = event->xclient.data.l[1];
155     keyEvent.keyCode = event->xclient.data.l[2];
156
157     // only transmit keydown events
158     if( event->xclient.data.l[3] != 2)
159     {
160       return; // 2 = key down, 3 = key release
161     }
162
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];
167
168     Integration::KeyEvent convertedEvent( keyEvent );
169
170     mEventInterface->KeyEvent( convertedEvent );
171   }
172 }
173
174 void XInput2::ProcessGenericEvent( XGenericEventCookie* cookie )
175 {
176   XIDeviceEvent* deviceEvent = static_cast< XIDeviceEvent* >(cookie->data);
177
178   X11Debug::LogXI2Event( cookie );
179
180   bool requiresProcessing  = PreProcessEvent( deviceEvent );
181
182   if( ! requiresProcessing )
183   {
184     return;
185   }
186
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 )
191
192   switch( cookie->evtype)
193   {
194     case XI_TouchUpdate:
195     case XI_Motion:
196     {
197       point.SetState( PointState::MOTION );
198       mEventInterface->TouchEvent( point, time );
199       break;
200     }
201     case XI_TouchBegin:
202     case XI_ButtonPress:
203     {
204       point.SetState( PointState::DOWN );
205       mEventInterface->TouchEvent( point, time );
206       break;
207     }
208     case XI_TouchEnd:
209     case XI_ButtonRelease:
210     {
211       point.SetState( PointState::UP );
212       mEventInterface->TouchEvent( point, time );
213       break;
214     }
215     case XI_FocusIn:
216     {
217       mEventInterface->WindowFocusIn();
218       break;
219     }
220     case XI_FocusOut:
221     {
222       mEventInterface->WindowFocusOut();
223       break;
224     }
225     case XI_KeyPress:
226     {
227       KeyEvent keyEvent;
228       CreateKeyEvent( deviceEvent, keyEvent );
229       Integration::KeyEvent convertedEvent( keyEvent );
230       mEventInterface->KeyEvent( convertedEvent );
231       break;
232     }
233     default:
234     {
235       break;
236     }
237   }
238 }
239
240 void XInput2::QueryMultiTouchSupport()
241 {
242   int minor = XI2MinorVersionRequired;
243   int major = XI2MajorVersionRequired;
244   int firstEventCode, firstErrorCode;
245
246   // Check if the extension is available and get the extension id
247   if( !XQueryExtension(mDisplay, "XInputExtension",  &mXI2ExtensionId, &firstEventCode, &firstErrorCode) )
248   {
249     DALI_LOG_ERROR(" XInputExtension not available \n");
250     return;
251   }
252
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 )
257   {
258     DALI_LOG_ERROR(" XIQueryVersion %d,%d failed \n", major, minor );
259     return;
260   }
261
262   // check the version is supports multi-touch
263   if( ( major * 1000 + minor ) >= ( XI2MajorVersionRequired * 1000 + XI2MinorVersionRequired ) )
264   {
265       mMultiTouchSupport = true;
266   }
267   else
268   {
269     DALI_LOG_ERROR( "XInput 2.2 or greater required for multi-touch\n");
270   }
271
272 }
273 void XInput2::QueryDevices()
274  {
275    int numberOfDevices( 0 );
276
277    // QueryDevice returns information about one or more input devices
278    XIDeviceInfo* deviceInfoArray = XIQueryDevice( mDisplay, XIAllDevices, &numberOfDevices);
279    XIDeviceInfo* device = deviceInfoArray;
280
281    X11Debug::LogInputDeviceInfo( deviceInfoArray, numberOfDevices );
282
283    mInputDeviceInfo.Resize( numberOfDevices );
284
285    for( int i = 0; i < numberOfDevices; ++i, ++device )
286    {
287      XInput2Device info;
288
289      info.AssignDeviceInfo( device );
290
291      mInputDeviceInfo.PushBack( info );
292    }
293
294    XIFreeDeviceInfo( deviceInfoArray );
295  }
296
297 void XInput2::SelectEvents( int deviceId, const Dali::Vector< unsigned int >& filter )
298 {
299   if( filter.Size() ==  0)
300   {
301     return;
302   }
303
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;
308
309   eventMask.deviceid = deviceId;
310   eventMask.mask_len = sizeof( mask);
311   eventMask.mask = mask;
312
313   for( VectorBase::SizeType i = 0; i< filter.Count(); ++i )
314   {
315       XISetMask( mask, filter[i] );
316   }
317
318   XISelectEvents( mDisplay, mWindow, &eventMask, 1);
319
320 }
321 void XInput2::SelectInputEvents()
322 {
323   /*
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
330    *
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.
336    */
337   Dali::Vector< unsigned int > eventFilter;
338   eventFilter.Reserve( 6 );    // typically filter no more than 6 events
339
340   for( VectorBase::SizeType i = 0; i < mInputDeviceInfo.Count(); ++i )
341   {
342     const XInput2Device& device( mInputDeviceInfo[ i ] );
343
344     eventFilter.Clear();
345
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 ) )
348     {
349       if( device.buttonClass )
350       {
351         eventFilter.PushBack( XI_ButtonPress );
352         eventFilter.PushBack( XI_ButtonRelease );
353         eventFilter.PushBack( XI_Motion );
354       }
355       if( device.touchClass )
356       {
357         eventFilter.PushBack( XI_TouchUpdate );
358         eventFilter.PushBack( XI_TouchBegin );
359         eventFilter.PushBack( XI_TouchEnd );
360       }
361       if( device.keyClass )
362       {
363         eventFilter.PushBack( XI_KeyPress );
364         eventFilter.PushBack( XI_KeyRelease );
365       }
366     }
367
368     if( eventFilter.Count() > 0 )
369     {
370       SelectEvents( device.deviceId, eventFilter );
371     }
372   }
373 }
374 } // namespace internal
375 } // namespace adaptor
376 } // namespace dali