Merge branch 'devel/master(1.1.39)' into tizen
[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   mEventInterface->KeyEvent( keyEvent );
127 }
128 void XInput2::ProcessClientMessage( XEvent* event )
129 {
130   // Format for client message for key event
131   // XEvent xev;
132   // xev.xclient.type = ClientMessage;
133   // xev.xclient.display = keyrouter.disp;
134   // xev.xclient.window = window;
135   // xev.xclient.format = 32;
136   // xev.xclient.message_type = ecore_x_atom_get("VDINPUT_KEYEVENT");
137   // xev.xclient.data.l[0] = ev->time; /* time */
138   // xev.xclient.data.l[1] = ev->state; /* modifier */
139   // xev.xclient.data.l[2] = ev->code; /* keycode */
140   // xev.xclient.data.l[3] = ev->value; /* press/release */
141   // xev.xclient.data.l[4] = ev->device_id; /* deviceId */
142
143   Atom input_atom = XInternAtom( mDisplay, "VDINPUT_KEYEVENT", false);
144   KeyEvent keyEvent;
145
146   keyEvent.state = KeyEvent::Down;
147
148   // if atom is "VDINPUT_KEYEVENT"
149   if( input_atom == event->xclient.message_type )
150   {
151
152     keyEvent.keyModifier = event->xclient.data.l[1];
153     keyEvent.keyCode = event->xclient.data.l[2];
154
155     // only transmit keydown events
156     if( event->xclient.data.l[3] != 2)
157     {
158       return; // 2 = key down, 3 = key release
159     }
160
161     KeySym sym = XkbKeycodeToKeysym( mDisplay,  keyEvent.keyCode, 0 /* group */ , keyEvent.IsShiftModifier() );
162     char* keyname = XKeysymToString( sym );
163     keyEvent.keyPressedName = keyname;
164     keyEvent.time = event->xclient.data.l[0];
165
166     mEventInterface->KeyEvent( keyEvent );
167   }
168 }
169
170 void XInput2::ProcessGenericEvent( XGenericEventCookie* cookie )
171 {
172   XIDeviceEvent* deviceEvent = static_cast< XIDeviceEvent* >(cookie->data);
173
174   X11Debug::LogXI2Event( cookie );
175
176   bool requiresProcessing  = PreProcessEvent( deviceEvent );
177
178   if( ! requiresProcessing )
179   {
180     return;
181   }
182
183   Integration::Point point;
184   point.SetDeviceId( deviceEvent->deviceid );
185   point.SetScreenPosition( Vector2( deviceEvent->event_x, deviceEvent->event_y ) );
186   Time time( deviceEvent->time ); // X is using uint32 for time field ( see XI2proto.h )
187
188   switch( cookie->evtype)
189   {
190     case XI_TouchUpdate:
191     case XI_Motion:
192     {
193       point.SetState( PointState::MOTION );
194       mEventInterface->TouchEvent( point, time );
195       break;
196     }
197     case XI_TouchBegin:
198     case XI_ButtonPress:
199     {
200       point.SetState( PointState::DOWN );
201       mEventInterface->TouchEvent( point, time );
202       break;
203     }
204     case XI_TouchEnd:
205     case XI_ButtonRelease:
206     {
207       point.SetState( PointState::UP );
208       mEventInterface->TouchEvent( point, time );
209       break;
210     }
211     case XI_FocusIn:
212     {
213       mEventInterface->WindowFocusIn();
214       break;
215     }
216     case XI_FocusOut:
217     {
218       mEventInterface->WindowFocusOut();
219       break;
220     }
221     case XI_KeyPress:
222     {
223       KeyEvent keyEvent;
224       CreateKeyEvent( deviceEvent, keyEvent );
225       mEventInterface->KeyEvent( keyEvent );
226       break;
227     }
228     default:
229     {
230       break;
231     }
232   }
233 }
234
235 void XInput2::QueryMultiTouchSupport()
236 {
237   int minor = XI2MinorVersionRequired;
238   int major = XI2MajorVersionRequired;
239   int firstEventCode, firstErrorCode;
240
241   // Check if the extension is available and get the extension id
242   if( !XQueryExtension(mDisplay, "XInputExtension",  &mXI2ExtensionId, &firstEventCode, &firstErrorCode) )
243   {
244     DALI_LOG_ERROR(" XInputExtension not available \n");
245     return;
246   }
247
248   // inform X that DALi ( the client ) supports XI2 version 2.2
249   // it will assign the X servers supported version to the parameters
250   int ret = XIQueryVersion( mDisplay, &major, &minor);
251   if( ret == BadValue )
252   {
253     DALI_LOG_ERROR(" XIQueryVersion %d,%d failed \n", major, minor );
254     return;
255   }
256
257   // check the version is supports multi-touch
258   if( ( major * 1000 + minor ) >= ( XI2MajorVersionRequired * 1000 + XI2MinorVersionRequired ) )
259   {
260       mMultiTouchSupport = true;
261   }
262   else
263   {
264     DALI_LOG_ERROR( "XInput 2.2 or greater required for multi-touch\n");
265   }
266
267 }
268 void XInput2::QueryDevices()
269  {
270    int numberOfDevices( 0 );
271
272    // QueryDevice returns information about one or more input devices
273    XIDeviceInfo* deviceInfoArray = XIQueryDevice( mDisplay, XIAllDevices, &numberOfDevices);
274    XIDeviceInfo* device = deviceInfoArray;
275
276    X11Debug::LogInputDeviceInfo( deviceInfoArray, numberOfDevices );
277
278    mInputDeviceInfo.Resize( numberOfDevices );
279
280    for( int i = 0; i < numberOfDevices; ++i, ++device )
281    {
282      XInput2Device info;
283
284      info.AssignDeviceInfo( device );
285
286      mInputDeviceInfo.PushBack( info );
287    }
288
289    XIFreeDeviceInfo( deviceInfoArray );
290  }
291
292 void XInput2::SelectEvents( int deviceId, const Dali::Vector< unsigned int >& filter )
293 {
294   if( filter.Size() ==  0)
295   {
296     return;
297   }
298
299   // each event like XI_ButtonPress is stored as unique bit, so if there's 32 events we need 4 bytes
300   // the XIMaskLen macro provides the length for us at compile time.
301   unsigned char mask[ XIMaskLen( XI_LASTEVENT ) ] = {};
302   XIEventMask eventMask;
303
304   eventMask.deviceid = deviceId;
305   eventMask.mask_len = sizeof( mask);
306   eventMask.mask = mask;
307
308   for( VectorBase::SizeType i = 0; i< filter.Count(); ++i )
309   {
310       XISetMask( mask, filter[i] );
311   }
312
313   XISelectEvents( mDisplay, mWindow, &eventMask, 1);
314
315 }
316 void XInput2::SelectInputEvents()
317 {
318   /*
319    * From the X documentation:
320    * "A master pointer is a virtual pointer device that does not represent a physical device.
321    * If a slave device generates an event, the event is also generated by the respective master device.
322    * Multiple slave devices can be attached to a single master device."
323    * master = cursor / keyboard focus,
324    * slave = physical device
325    *
326    * For motion events, we currently just listen to the slave devices. This allows us the ability to
327    * perform a XIGrabDevice on the slave if we need to, which will temporarily detach it from the master.
328    * In DALi we currently don't perform a grab as typically we just have a single x-window displayed.
329    * Where as other toolkits may have a window for a popup and want do know when the mouse is clicked outside
330    * of the popup, to close it.
331    */
332   Dali::Vector< unsigned int > eventFilter;
333   eventFilter.Reserve( 6 );    // typically filter no more than 6 events
334
335   for( VectorBase::SizeType i = 0; i < mInputDeviceInfo.Count(); ++i )
336   {
337     const XInput2Device& device( mInputDeviceInfo[ i ] );
338
339     eventFilter.Clear();
340
341     // Floating slave devices also can generate key events. For example, TV remote controllers.
342     if( ( device.use == XIFloatingSlave ) || ( device.use == XISlavePointer ) || ( device.use == XISlaveKeyboard ) )
343     {
344       if( device.buttonClass )
345       {
346         eventFilter.PushBack( XI_ButtonPress );
347         eventFilter.PushBack( XI_ButtonRelease );
348         eventFilter.PushBack( XI_Motion );
349       }
350       if( device.touchClass )
351       {
352         eventFilter.PushBack( XI_TouchUpdate );
353         eventFilter.PushBack( XI_TouchBegin );
354         eventFilter.PushBack( XI_TouchEnd );
355       }
356       if( device.keyClass )
357       {
358         eventFilter.PushBack( XI_KeyPress );
359         eventFilter.PushBack( XI_KeyRelease );
360       }
361     }
362
363     if( eventFilter.Count() > 0 )
364     {
365       SelectEvents( device.deviceId, eventFilter );
366     }
367   }
368 }
369 } // namespace internal
370 } // namespace adaptor
371 } // namespace dali