Merge lp:~samuel-thibault/compiz/shortcuts into lp:compiz/0.9.13

Proposed by Samuel thibault on 2017-11-07
Status: Needs review
Proposed branch: lp:~samuel-thibault/compiz/shortcuts
Merge into: lp:compiz/0.9.13
Diff against target: 830 lines (+434/-47)
13 files modified
include/core/action.h (+8/-0)
include/core/screen.h (+4/-0)
include/core/window.h (+1/-0)
plugins/colorfilter/src/colorfilter.cpp (+6/-0)
plugins/ezoom/src/ezoom.cpp (+6/-0)
plugins/neg/src/neg.cpp (+5/-0)
plugins/showmouse/src/showmouse.cpp (+6/-0)
src/action.cpp (+37/-21)
src/event.cpp (+56/-11)
src/privateaction.h (+2/-0)
src/privatescreen.h (+14/-0)
src/screen.cpp (+279/-7)
src/window.cpp (+10/-8)
To merge this branch: bzr merge lp:~samuel-thibault/compiz/shortcuts
Reviewer Review Type Date Requested Status
Marco Trevisan (Treviño) 2017-11-07 Pending
Review via email: mp+333330@code.launchpad.net

Description of the change

This is a rework of the previous work of Colomban Wendling (https://code.launchpad.net/~banw/compiz/compiz.a11y-shotcuts). This version uses normal XI2 events, which poses much less problems and still allows to ignore grabs. In the end I changed the approach into converting XI2 events right from the xevent loop before handing them to the rest of compiz.

This depends on my branch https://code.launchpad.net/~samuel-thibault/compiz/motion-dup-fix

To post a comment you must log in.
lp:~samuel-thibault/compiz/shortcuts updated on 2017-11-14
4137. By Samuel thibault on 2017-11-14

Fix getting events for our own grabs

If a grab was taken by one of our own plugins, we have to ignore the X grab.

4138. By Samuel thibault on 2017-11-14

screen: Fix uninitialized variable

Marco Trevisan (Treviño) (3v1n0) wrote :

Thanks for your work, it's really appreciated and something I really had in mind since long time...

However, a part few cleanups (and a bit more of C++11 features usage), we've to think as this will deal with Unity, since there we're using XI2 already for some input monitor operations (mostly for edge events and for monitoring the mouse position when menus are opened, plus few other things), and the fact is that we disable such events when we don't need them.

Now, in theory we could move all that to compiz, introducing new actions types, but the problem now is that we can't really deal with all this work now that we're focused in GNOME

Another possibility is just to make unity to save the state on load with XIGetSelectedEvents and resume that state when we disable it, but in general I'd prefer the events not to be selected unless there's anything else that need them.

Another possibility is just make unity to keep them always enabled, and never toggle them.

In any case, it would be cool if you could test it with your changes, and in case propose a fix for it too. As without that change we can't land compiz in ubuntu (and thus upstream) easily as it could be prone to regressions.

lp:~samuel-thibault/compiz/shortcuts updated on 2017-11-24
4139. By Samuel thibault on 2017-11-16

screen: factorize XI2 to core event translation

4140. By Samuel thibault on 2017-11-24

Factorize XI2 SelectEvents code into privatescreen

Samuel thibault (samuel-thibault) wrote :

Hello,

I agree that selecting XI2 events only when needed would be ideally ideal, but
the obtained optimization (avoiding to trivially convert from XI2 events to core
events) does not seem to me worth the complexity cost.

Assuming that we avoid that complexity and just make compiz use XI2 whenever
available (Unity requires it anyway), things would be much simpler:

- Unity can use XIGetSelectedEvents() to determine what Compiz configured, and
merge it before XISelectEvents() and resume it, as you suggested.

- Compiz only disables XI2 temporarily within PrivateWindow::reparent(), so it
can also use XIGetSelectedEvents() to restore it as it was, either the original
compiz set or the set as merged by Unity.

That way, both sides are easily careful about what was set by the other side.

What do you think?

Samuel

Marco Trevisan (Treviño) (3v1n0) wrote :

Ok, that would look reasonable.

Could you possibly do that unity side of work too?
As otherwise this MP might stuck until I've time for that (and it might take a while).

Cheers

Samuel thibault (samuel-thibault) wrote :

Sure, I'll do the unity side :)
(probably on Tuesday)

lp:~samuel-thibault/compiz/shortcuts updated on 2017-12-12
4141. By Samuel thibault on 2017-12-12

xi2: save/restore masks instead of disabling/enabling compiz mask

Unity also uses xi2, so we need to restore its mask, and not the mask that
Compiz uses. This changeset completely replaces xi2SelectNoInput with
xi2DisableInput, which returns the mask which was active, so that xi2EnableInput
can reenable that same mask.

Samuel thibault (samuel-thibault) wrote :

Sorry I got preempted by other issues. I have pushed the compiz side, and will work on the unity side.

Samuel thibault (samuel-thibault) wrote :

Hello,

I have pushed the unity side on https://code.launchpad.net/~samuel-thibault/unity/shortcuts

Samuel

lp:~samuel-thibault/compiz/shortcuts updated on 2018-09-25
4142. By Samuel thibault on 2018-06-29

Enable grab ignore on colorfilter plugin too

4143. By Samuel thibault on 2018-09-25

Fix ButtonRelease/XI_ButtonRelease KeyRelease/XI_KeyRelease confusion

4144. By Samuel thibault on 2018-09-25

fix memleak

Unmerged revisions

4144. By Samuel thibault on 2018-09-25

fix memleak

4143. By Samuel thibault on 2018-09-25

Fix ButtonRelease/XI_ButtonRelease KeyRelease/XI_KeyRelease confusion

4142. By Samuel thibault on 2018-06-29

Enable grab ignore on colorfilter plugin too

4141. By Samuel thibault on 2017-12-12

xi2: save/restore masks instead of disabling/enabling compiz mask

Unity also uses xi2, so we need to restore its mask, and not the mask that
Compiz uses. This changeset completely replaces xi2SelectNoInput with
xi2DisableInput, which returns the mask which was active, so that xi2EnableInput
can reenable that same mask.

4140. By Samuel thibault on 2017-11-24

Factorize XI2 SelectEvents code into privatescreen

4139. By Samuel thibault on 2017-11-16

screen: factorize XI2 to core event translation

4138. By Samuel thibault on 2017-11-14

screen: Fix uninitialized variable

4137. By Samuel thibault on 2017-11-14

Fix getting events for our own grabs

If a grab was taken by one of our own plugins, we have to ignore the X grab.

4136. By Samuel thibault on 2017-11-07

Allow plugins to ignore keyboard grabs

by using XInput2 events when available, converting them into core events before handing them to plugins.

Based on the work of Colomban Wendling

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/core/action.h'
2--- include/core/action.h 2014-01-21 19:46:32 +0000
3+++ include/core/action.h 2018-09-25 16:05:25 +0000
4@@ -193,6 +193,14 @@
5
6 bool active () const;
7
8+ /* When enabled, key events will be reported even while a keyboard grab
9+ * is ongoing. This allows shortcuts to work while a menu is open, but
10+ * also while xlock is running, so this should not be set lightly, and
11+ * notably not in a plugin which could commands and thus override xlock.
12+ */
13+ bool ignoreGrabs () const;
14+ void setIgnoreGrabs (bool ignore);
15+
16 /* CompAction should be a pure virtual class so
17 * that we can pass the interface required to for setActionActiveState
18 * directly rather than using friends
19
20=== modified file 'include/core/screen.h'
21--- include/core/screen.h 2017-06-29 17:19:00 +0000
22+++ include/core/screen.h 2018-09-25 16:05:25 +0000
23@@ -392,6 +392,10 @@
24 virtual int outputDeviceForPoint (const CompPoint &point) = 0;
25 virtual int outputDeviceForPoint (int x, int y) = 0;
26 virtual int xkbEvent () = 0;
27+ virtual long xi2CoreMask () = 0;
28+ virtual void xi2SelectInput () = 0;
29+ virtual XIEventMask * xi2DisableInput (int *num_masks_return) = 0;
30+ virtual void xi2EnableInput (XIEventMask *masks, int num_masks) = 0;
31 virtual void warpPointer (int dx, int dy) = 0;
32 virtual void updateGrab (GrabHandle handle, Cursor cursor) = 0;
33 virtual int shapeEvent () = 0;
34
35=== modified file 'include/core/window.h'
36--- include/core/window.h 2018-02-23 16:53:18 +0000
37+++ include/core/window.h 2018-09-25 16:05:25 +0000
38@@ -34,6 +34,7 @@
39 #include <X11/Xutil.h>
40 #include <X11/Xregion.h>
41 #include <X11/extensions/Xdamage.h>
42+#include <X11/extensions/XInput2.h>
43 #include <X11/extensions/sync.h>
44
45 #include <core/action.h>
46
47=== modified file 'plugins/colorfilter/src/colorfilter.cpp'
48--- plugins/colorfilter/src/colorfilter.cpp 2017-03-28 16:39:34 +0000
49+++ plugins/colorfilter/src/colorfilter.cpp 2018-09-25 16:05:25 +0000
50@@ -450,6 +450,12 @@
51 &ColorfilterScreen::damageDecorations, this, _1,
52 _2));
53
54+ foreach (CompOption &o, getOptions ())
55+ {
56+ if (o.isAction ())
57+ o.value ().action ().setIgnoreGrabs (true);
58+ }
59+
60 loadFilters ();
61 };
62
63
64=== modified file 'plugins/ezoom/src/ezoom.cpp'
65--- plugins/ezoom/src/ezoom.cpp 2017-12-05 14:45:06 +0000
66+++ plugins/ezoom/src/ezoom.cpp 2018-09-25 16:05:25 +0000
67@@ -2132,6 +2132,12 @@
68 optionSetEnsureVisibilityInitiate (boost::bind (
69 &EZoomScreen::ensureVisibilityAction, this,
70 _1, _2, _3));
71+
72+ foreach (CompOption &o, getOptions ())
73+ {
74+ if (o.isAction ())
75+ o.value ().action ().setIgnoreGrabs (true);
76+ }
77 }
78
79 EZoomScreen::~EZoomScreen ()
80
81=== modified file 'plugins/neg/src/neg.cpp'
82--- plugins/neg/src/neg.cpp 2017-08-24 13:54:22 +0000
83+++ plugins/neg/src/neg.cpp 2018-09-25 16:05:25 +0000
84@@ -466,6 +466,11 @@
85 optionSetActivateAtStartupNotify (boost::bind (&NegScreen::optionChanged, this,
86 _1, _2));
87
88+ foreach (CompOption &o, getOptions ())
89+ {
90+ if (o.isAction ())
91+ o.value ().action ().setIgnoreGrabs (true);
92+ }
93 }
94
95
96
97=== modified file 'plugins/showmouse/src/showmouse.cpp'
98--- plugins/showmouse/src/showmouse.cpp 2017-04-20 13:04:22 +0000
99+++ plugins/showmouse/src/showmouse.cpp 2018-09-25 16:05:25 +0000
100@@ -700,6 +700,12 @@
101 this, _1, _2, _3));
102
103 optionSetActivateAtStartupNotify (boost::bind (&ShowmouseScreen::startupToggle, this));
104+
105+ foreach (CompOption &o, getOptions ())
106+ {
107+ if (o.isAction ())
108+ o.value ().action ().setIgnoreGrabs (true);
109+ }
110 }
111
112 ShowmouseScreen::~ShowmouseScreen ()
113
114=== modified file 'src/action.cpp'
115--- src/action.cpp 2014-02-28 20:45:57 +0000
116+++ src/action.cpp 2018-09-25 16:05:25 +0000
117@@ -483,9 +483,10 @@
118 void
119 CompAction::copyState (const CompAction &action)
120 {
121- priv->initiate = action.priv->initiate;
122- priv->terminate = action.priv->terminate;
123- priv->state = action.priv->state;
124+ priv->initiate = action.priv->initiate;
125+ priv->terminate = action.priv->terminate;
126+ priv->state = action.priv->state;
127+ priv->ignoreGrabs = action.priv->ignoreGrabs;
128
129 memcpy (&priv->priv, &action.priv->priv, sizeof (CompPrivate));
130 }
131@@ -501,6 +502,7 @@
132 priv->button.button () != val.priv->button.button () ||
133 priv->bell != val.priv->bell ||
134 priv->edgeMask != val.priv->edgeMask ||
135+ priv->ignoreGrabs != val.priv->ignoreGrabs ||
136 memcmp (&priv->priv, &val.priv->priv, sizeof (CompPrivate)) != 0)
137 return false;
138
139@@ -651,6 +653,18 @@
140 return priv->active;
141 }
142
143+bool
144+CompAction::ignoreGrabs () const
145+{
146+ return priv->ignoreGrabs;
147+}
148+
149+void
150+CompAction::setIgnoreGrabs (bool ignore)
151+{
152+ priv->ignoreGrabs = ignore;
153+}
154+
155 void
156 PrivateAction::setActive (bool a)
157 {
158@@ -665,29 +679,31 @@
159 }
160
161 PrivateAction::PrivateAction () :
162- initiate (),
163- terminate (),
164- state (0),
165- type (0),
166- key (),
167- button (),
168- bell (false),
169- edgeMask (0),
170- active (false)
171+ initiate (),
172+ terminate (),
173+ state (0),
174+ type (0),
175+ key (),
176+ button (),
177+ bell (false),
178+ edgeMask (0),
179+ active (false),
180+ ignoreGrabs (false)
181 {
182 memset (&priv, 0, sizeof (CompPrivate));
183 }
184
185 PrivateAction::PrivateAction (const PrivateAction &a) :
186- initiate (a.initiate),
187- terminate (a.terminate),
188- state (a.state),
189- type (a.type),
190- key (a.key),
191- button (a.button),
192- bell (a.bell),
193- edgeMask (a.edgeMask),
194- active (a.active)
195+ initiate (a.initiate),
196+ terminate (a.terminate),
197+ state (a.state),
198+ type (a.type),
199+ key (a.key),
200+ button (a.button),
201+ bell (a.bell),
202+ edgeMask (a.edgeMask),
203+ active (a.active),
204+ ignoreGrabs (a.ignoreGrabs)
205 {
206 memcpy (&priv, &a.priv, sizeof (CompPrivate));
207 }
208
209=== modified file 'src/event.cpp'
210--- src/event.cpp 2018-03-02 16:56:34 +0000
211+++ src/event.cpp 2018-09-25 16:05:25 +0000
212@@ -35,6 +35,7 @@
213 #include <X11/extensions/shape.h>
214 #include <X11/extensions/Xrandr.h>
215 #include <X11/extensions/Xfixes.h>
216+#include <X11/extensions/XInput2.h>
217
218 #include <core/atoms.h>
219 #include <core/servergrab.h>
220@@ -164,6 +165,25 @@
221 return true;
222 }
223
224+static bool
225+shouldTriggerAction (CompScreen *screen, CompAction *action)
226+{
227+ if (!screen->grabbed ())
228+ /* No grab, no problem */
229+ return true;
230+
231+ if (screen->otherGrabExist (NULL))
232+ /* Our own grab */
233+ return true;
234+
235+ if (action->ignoreGrabs ())
236+ /* Action should ignore grabs anyway */
237+ return true;
238+
239+ /* Not our grab, respect it */
240+ return false;
241+}
242+
243 bool
244 cps::EventManager::triggerPress (CompAction *action,
245 CompAction::State state,
246@@ -280,8 +300,11 @@
247 if (matchEventState (action->button ().modifiers (),
248 eventState))
249 {
250- if (eventManager.triggerPress (action, state, arguments))
251- return true;
252+ if (shouldTriggerAction (screen, action))
253+ {
254+ if (eventManager.triggerPress (action, state, arguments))
255+ return true;
256+ }
257 }
258 }
259 }
260@@ -312,9 +335,14 @@
261 {
262 if (matchEventState (action->button ().modifiers (),
263 eventState))
264- if (action->initiate () (action, state,
265- arguments))
266- return true;
267+ {
268+ if (shouldTriggerAction (screen, action))
269+ {
270+ if (action->initiate () (action, state,
271+ arguments))
272+ return true;
273+ }
274+ }
275 }
276 }
277 }
278@@ -385,8 +413,11 @@
279 {
280 if (action->button ().button () == (int) event->button)
281 {
282- if (eventManager.triggerRelease (action, state, arguments))
283- return true;
284+ if (shouldTriggerAction (screen, action))
285+ {
286+ if (eventManager.triggerRelease (action, state, arguments))
287+ return true;
288+ }
289 }
290 }
291 }
292@@ -477,6 +508,7 @@
293
294 if (isBound (option, CompAction::BindingTypeKey, state, &action) &&
295 shouldTriggerKeyPressAction (action, event) &&
296+ shouldTriggerAction (screen, action) &&
297 eventManager.triggerPress (action, state, arguments))
298 return true;
299 }
300@@ -485,6 +517,7 @@
301 {
302 if (isBound (action, CompAction::BindingTypeKey, state) &&
303 shouldTriggerKeyPressAction (&action, event) &&
304+ shouldTriggerAction (screen, &action) &&
305 eventManager.triggerPress (&action, state, arguments))
306 return true;
307 }
308@@ -524,14 +557,16 @@
309 CompAction *action;
310
311 if (isBound (option, CompAction::BindingTypeKey, state, &action) &&
312- shouldTriggerKeyReleaseAction (action, event))
313+ shouldTriggerKeyReleaseAction (action, event) &&
314+ shouldTriggerAction (screen, action))
315 handled |= eventManager.triggerRelease (action, state, arguments);
316 }
317
318 foreach (CompAction &action, actions)
319 {
320 if (isBound (action, CompAction::BindingTypeKey, state) &&
321- shouldTriggerKeyReleaseAction (&action, event))
322+ shouldTriggerKeyReleaseAction (&action, event) &&
323+ shouldTriggerAction (screen, &action))
324 handled |= eventManager.triggerRelease (&action, state, arguments);
325 }
326
327@@ -976,12 +1011,22 @@
328 {
329 XEvent nev;
330 XPeekEvent (dpy, &nev);
331+ if (nev.type == GenericEvent)
332+ XGetEventData (dpy, &nev.xcookie);
333+ XIDeviceEvent *nevdev = (XIDeviceEvent *) nev.xcookie.data;
334
335- if (nev.type == KeyPress && nev.xkey.time == event->xkey.time &&
336- nev.xkey.keycode == event->xkey.keycode)
337+ if ((nev.type == KeyPress && nev.xkey.time == event->xkey.time &&
338+ nev.xkey.keycode == event->xkey.keycode) ||
339+ (nev.type == GenericEvent &&
340+ nev.xcookie.evtype == XI_KeyPress &&
341+ nevdev->time == event->xkey.time &&
342+ (unsigned int) nevdev->detail == event->xkey.keycode))
343 {
344 nextKeyPressIsRepeated_ = true;
345 }
346+
347+ if (nev.type == GenericEvent)
348+ XFreeEventData (dpy, &nev.xcookie);
349 }
350
351 foreach (CompPlugin *p, CompPlugin::getPlugins ())
352
353=== modified file 'src/privateaction.h'
354--- src/privateaction.h 2012-10-02 10:28:01 +0000
355+++ src/privateaction.h 2018-09-25 16:05:25 +0000
356@@ -71,6 +71,8 @@
357
358 bool active;
359
360+ bool ignoreGrabs;
361+
362 CompPrivate priv;
363 };
364
365
366=== modified file 'src/privatescreen.h'
367--- src/privatescreen.h 2017-06-29 17:19:00 +0000
368+++ src/privatescreen.h 2018-09-25 16:05:25 +0000
369@@ -37,6 +37,8 @@
370 #include <time.h>
371 #include <boost/shared_ptr.hpp>
372
373+#include <X11/extensions/XInput2.h>
374+
375 #include <glibmm/main.h>
376
377 #include "privatetimeoutsource.h"
378@@ -667,6 +669,12 @@
379
380 void setAudibleBell (bool audible);
381
382+ int xi2OpCode () const;
383+ void xi2SelectInput (Window id);
384+ XIEventMask * xi2DisableInput (Window id, int *num_masks_return);
385+ void xi2EnableInput (Window id, XIEventMask *masks, int num_masks);
386+ long xi2CoreMask ();
387+
388 bool handleActionEvent (XEvent *event);
389
390 void handleSelectionRequest (XEvent *event);
391@@ -768,6 +776,7 @@
392 compiz::private_screen::Extension xSync;
393 compiz::private_screen::Extension xRandr;
394 compiz::private_screen::Extension xShape;
395+ compiz::private_screen::Extension xi2;
396 compiz::private_screen::ViewPort viewPort;
397 compiz::private_screen::StartupSequenceImpl startupSequence;
398 compiz::private_screen::EventManager eventManager;
399@@ -955,6 +964,11 @@
400
401 int xkbEvent ();
402
403+ long xi2CoreMask ();
404+ void xi2SelectInput ();
405+ XIEventMask * xi2DisableInput (int *num_masks_return);
406+ void xi2EnableInput (XIEventMask *masks, int num_masks);
407+
408 XWindowAttributes attrib ();
409
410 int screenNum ();
411
412=== modified file 'src/screen.cpp'
413--- src/screen.cpp 2017-10-26 17:02:38 +0000
414+++ src/screen.cpp 2018-09-25 16:05:25 +0000
415@@ -773,20 +773,48 @@
416 return false;
417 XNextEvent (dpy, &ev);
418
419+ if (ev.type == GenericEvent)
420+ XGetEventData (dpy, &ev.xcookie);
421+
422 /* Skip to the last MotionNotify
423 * event in this sequence */
424- if (ev.type == MotionNotify)
425+ if (ev.type == MotionNotify ||
426+ (ev.type == GenericEvent &&
427+ ev.xcookie.extension == xi2.get () &&
428+ ev.xcookie.evtype == XI_Motion))
429 {
430 XEvent peekEvent;
431 while (XPending (dpy))
432 {
433 XPeekEvent (dpy, &peekEvent);
434
435- if (peekEvent.type != MotionNotify)
436+ if (peekEvent.type != MotionNotify &&
437+ ! (peekEvent.type == GenericEvent &&
438+ peekEvent.xcookie.extension == xi2.get () &&
439+ peekEvent.xcookie.evtype == XI_Motion))
440 break;
441
442+ if (peekEvent.type == GenericEvent)
443+ XGetEventData (dpy, &peekEvent.xcookie);
444+ XIDeviceEvent *xiDevEv = (XIDeviceEvent *) peekEvent.xcookie.data;
445+
446+ if (peekEvent.type == GenericEvent &&
447+ peekEvent.xcookie.extension == xi2.get () &&
448+ peekEvent.xcookie.evtype == XI_Motion &&
449+ xiDevEv->deviceid != xiDevEv->sourceid)
450+ {
451+ // Ignore Master Devices
452+ XFreeEventData (dpy, &peekEvent.xcookie);
453+ XNextEvent (dpy, &peekEvent);
454+ continue;
455+ }
456+ XFreeEventData (dpy, &peekEvent.xcookie);
457+
458 /* Overwrite previous MotionNotify event */
459 XNextEvent (dpy, &ev);
460+
461+ if (ev.type == GenericEvent)
462+ XGetEventData (dpy, &ev.xcookie);
463 }
464 }
465
466@@ -806,6 +834,56 @@
467 return getNextXEvent (ev);
468 }
469
470+static unsigned int
471+translateXI2State (const XIDeviceEvent *event)
472+{
473+ unsigned int state;
474+
475+ /* Keyboard Modifiers, bits 0 to 7 */
476+ state = event->mods.effective & ((1 << 8) - 1);
477+
478+ /* We are only interested in the buttons numbered 1 to 5, which correspond
479+ * to the core buttons, bits 8 to 12 */
480+ int maxbits = MIN (5, event->buttons.mask_len - 1);
481+ for (int i = 1; i <= maxbits; i++)
482+ {
483+ if (! XIMaskIsSet (event->buttons.mask, i))
484+ continue;
485+
486+ switch (i)
487+ {
488+ case 1: state |= Button1Mask; break;
489+ case 2: state |= Button2Mask; break;
490+ case 3: state |= Button3Mask; break;
491+ case 4: state |= Button4Mask; break;
492+ case 5: state |= Button5Mask; break;
493+ }
494+ }
495+
496+ /* Keyboard groups, bits 13 to 15 */
497+ state |= (event->group.effective & ((1 << 3) - 1)) << 13;
498+
499+ return state;
500+}
501+
502+template <typename EVENT>
503+void fakeEventFromXiEvent(EVENT *fakeEvent, XEvent *event, XIDeviceEvent *xiDevEv)
504+{
505+ fakeEvent->serial = event->xcookie.serial;
506+ fakeEvent->send_event = event->xcookie.send_event;
507+ fakeEvent->display = event->xcookie.display;
508+ fakeEvent->window = xiDevEv->event;
509+ fakeEvent->root = xiDevEv->root;
510+ fakeEvent->subwindow = xiDevEv->child;
511+ fakeEvent->time = xiDevEv->time;
512+ fakeEvent->x = xiDevEv->event_x;
513+ fakeEvent->y = xiDevEv->event_y;
514+ fakeEvent->x_root = xiDevEv->root_x;
515+ fakeEvent->y_root = xiDevEv->root_y;
516+ fakeEvent->state = translateXI2State(xiDevEv);
517+ fakeEvent->same_screen = True;
518+}
519+
520 void
521 PrivateScreen::processEvents ()
522 {
523@@ -832,6 +910,77 @@
524
525 while (getNextEvent (event))
526 {
527+ if (xi2.isEnabled ()) {
528+ /* Using XI2 events, ignore any duplicate core events */
529+ switch (event.type) {
530+ case KeyPress:
531+ case KeyRelease:
532+ XFlush (dpy);
533+ continue;
534+ }
535+ }
536+
537+ if (event.type == GenericEvent
538+ && event.xcookie.extension == xi2.get ()) {
539+ /* Convert XI2 events to core events */
540+
541+ XIDeviceEvent *xiDevEv = (XIDeviceEvent *) event.xcookie.data;
542+
543+ if (xiDevEv->deviceid != xiDevEv->sourceid) {
544+ // Ignore Master Devices
545+ XFreeEventData (dpy, &event.xcookie);
546+ continue;
547+ }
548+
549+ switch (event.xcookie.evtype) {
550+ case XI_ButtonPress:
551+ case XI_ButtonRelease:
552+ {
553+ XButtonEvent fakeEvent;
554+ fakeEventFromXiEvent(&fakeEvent, &event, xiDevEv);
555+ fakeEvent.type = event.xcookie.evtype == XI_ButtonPress ?
556+ ButtonPress : ButtonRelease;
557+ fakeEvent.button = xiDevEv->detail;
558+ XFreeEventData (dpy, &event.xcookie);
559+ event.xbutton = fakeEvent;
560+ break;
561+ }
562+
563+ case XI_KeyPress:
564+ case XI_KeyRelease:
565+ {
566+ XKeyEvent fakeEvent;
567+ fakeEventFromXiEvent(&fakeEvent, &event, xiDevEv);
568+ fakeEvent.type = event.xcookie.evtype == XI_KeyPress ?
569+ KeyPress : KeyRelease;
570+ fakeEvent.keycode = xiDevEv->detail;
571+ XFreeEventData (dpy, &event.xcookie);
572+ event.xkey = fakeEvent;
573+ break;
574+ }
575+
576+ case XI_Motion:
577+ {
578+ /* TODO: mousepoll is probably useless with XI2. */
579+ XMotionEvent fakeEvent;
580+ fakeEventFromXiEvent(&fakeEvent, &event, xiDevEv);
581+ fakeEvent.type = MotionNotify;
582+ fakeEvent.is_hint = NotifyNormal;
583+ XFreeEventData (dpy, &event.xcookie);
584+ event.xmotion = fakeEvent;
585+ break;
586+ }
587+
588+ default:
589+ /* Ignore other XI2 events */
590+ XFreeEventData (dpy, &event.xcookie);
591+ XFlush (dpy);
592+ continue;
593+ }
594+ }
595+ else if (event.type == GenericEvent)
596+ XFreeEventData (dpy, &event.xcookie);
597+
598 switch (event.type) {
599 case ButtonPress:
600 case ButtonRelease:
601@@ -4532,6 +4681,104 @@
602 return privateScreen.getXkbEvent ();
603 }
604
605+int
606+PrivateScreen::xi2OpCode () const
607+{
608+ return xi2.get ();
609+}
610+
611+void
612+PrivateScreen::xi2SelectInput (Window id)
613+{
614+ if (xi2OpCode () == -1)
615+ return;
616+
617+ XIEventMask eventmask;
618+ unsigned char mask[XIMaskLen (XI_LASTEVENT)] = { 0 };
619+
620+ eventmask.deviceid = XIAllDevices;
621+ eventmask.mask_len = sizeof (mask);
622+ eventmask.mask = mask;
623+
624+ XISetMask (mask, XI_KeyPress);
625+ XISetMask (mask, XI_KeyRelease);
626+ XISetMask (mask, XI_ButtonPress);
627+ XISetMask (mask, XI_ButtonRelease);
628+ XISetMask (mask, XI_Motion);
629+
630+ XISelectEvents (dpy, id, &eventmask, 1);
631+}
632+
633+void
634+CompScreenImpl::xi2SelectInput (void)
635+{
636+ privateScreen.xi2SelectInput (privateScreen.rootWindow ());
637+}
638+
639+XIEventMask *
640+PrivateScreen::xi2DisableInput (Window id, int *num_masks_return)
641+{
642+ if (xi2OpCode () == -1)
643+ return NULL;
644+
645+ /* First get previous mask to be restored in xi2EnableInput */
646+ XIEventMask *prevmask;
647+ prevmask = XIGetSelectedEvents (dpy, id, num_masks_return);
648+
649+ /* Then disable everything */
650+ XIEventMask eventmask;
651+ unsigned char mask[1] = { 0 };
652+
653+ eventmask.deviceid = XIAllDevices;
654+ eventmask.mask_len = sizeof (mask);
655+ eventmask.mask = mask;
656+
657+ XISelectEvents (dpy, id, &eventmask, 1);
658+
659+ return prevmask;
660+}
661+
662+XIEventMask *
663+CompScreenImpl::xi2DisableInput (int *num_masks_return)
664+{
665+ return privateScreen.xi2DisableInput (privateScreen.rootWindow (), num_masks_return);
666+}
667+
668+void
669+PrivateScreen::xi2EnableInput (Window id, XIEventMask *masks, int num_masks)
670+{
671+ if (xi2OpCode () == -1)
672+ return;
673+
674+ XISelectEvents (dpy, id, masks, num_masks);
675+ XFree (masks);
676+}
677+
678+void
679+CompScreenImpl::xi2EnableInput (XIEventMask *mask, int num_masks)
680+{
681+ return privateScreen.xi2EnableInput (privateScreen.rootWindow (), mask, num_masks);
682+}
683+
684+long
685+PrivateScreen::xi2CoreMask (void)
686+{
687+ if (xi2OpCode () != -1)
688+ return 0;
689+
690+ return KeyPressMask |
691+ KeyReleaseMask |
692+ ButtonPressMask |
693+ ButtonReleaseMask;
694+}
695+
696+long
697+CompScreenImpl::xi2CoreMask (void)
698+{
699+ return privateScreen.xi2CoreMask ();
700+}
701+
702+
703 void
704 PrivateScreen::identifyEdgeWindow(Window id)
705 {
706@@ -5087,6 +5334,28 @@
707 }
708
709
710+namespace
711+{
712+static Bool XI2QueryExtension(Display *dpy, int *opcode, int *error)
713+{
714+ int event;
715+
716+ if (!XQueryExtension (dpy, "XInputExtension", opcode, &event, error))
717+ return False;
718+
719+ /* we want 2.1 for DeviceEvent::sourceid */
720+ int major = 2, minor = 1;
721+
722+ if (XIQueryVersion (dpy, &major, &minor) == BadRequest)
723+ return False;
724+ compLogMessage ("core", CompLogLevelDebug,
725+ "Got XI2 extension %d.%d", major, minor);
726+
727+ return True;
728+}
729+}
730+
731+
732 bool
733 PrivateScreen::initDisplay (const char *name, cps::History& history, unsigned int showingDesktopMask)
734 {
735@@ -5137,6 +5406,10 @@
736 "No XKB extension");
737 }
738
739+ xi2.init<XI2QueryExtension> (dpy);
740+ if (!xi2.isEnabled ())
741+ compLogMessage ("core", CompLogLevelWarn, "No XI2 extension");
742+
743 int xineramaError;
744 int xineramaEvent;
745 xineramaExtension = XineramaQueryExtension (dpy,
746@@ -5152,7 +5425,6 @@
747
748 Window root_tmp = XRootWindow (dpy, DefaultScreen (dpy));
749
750-
751 /* Don't select for SubstructureRedirectMask or
752 * SubstructureNotifyMask yet since we need to
753 * act in complete synchronization here when
754@@ -5165,10 +5437,7 @@
755 PropertyChangeMask |
756 LeaveWindowMask |
757 EnterWindowMask |
758- KeyPressMask |
759- KeyReleaseMask |
760- ButtonPressMask |
761- ButtonReleaseMask |
762+ xi2CoreMask () |
763 FocusChangeMask |
764 ExposureMask);
765
766@@ -5190,6 +5459,8 @@
767 SubstructureNotifyMask);
768 }
769
770+ xi2SelectInput (root_tmp);
771+
772 for (int i = 0; i < SCREEN_EDGE_NUM; i++)
773 {
774 screenEdge[i].id = None;
775@@ -5635,6 +5906,7 @@
776 lastFileWatchHandle (1),
777 watchFds (0),
778 lastWatchFdHandle (1),
779+ grabbed (false),
780 grabWindow (None)
781 {
782 TimeoutHandler *dTimeoutHandler = new TimeoutHandler ();
783
784=== modified file 'src/window.cpp'
785--- src/window.cpp 2018-03-02 17:02:22 +0000
786+++ src/window.cpp 2018-09-25 16:05:25 +0000
787@@ -6950,6 +6950,10 @@
788 /* Do not get any events from here on */
789 XSelectInput (dpy, screen->root (), NoEventMask);
790
791+ XIEventMask *xi2Masks;
792+ int num_masks;
793+ xi2Masks = screen->xi2DisableInput (&num_masks);
794+
795 /* If we have some frame extents, we should apply them here and
796 * set lastFrameExtents */
797 wrapper = XCreateWindow (dpy, serverFrame,
798@@ -7046,13 +7050,12 @@
799 PropertyChangeMask |
800 LeaveWindowMask |
801 EnterWindowMask |
802- KeyPressMask |
803- KeyReleaseMask |
804- ButtonPressMask |
805- ButtonReleaseMask |
806+ screen->xi2CoreMask () |
807 FocusChangeMask |
808 ExposureMask);
809
810+ screen->xi2EnableInput (xi2Masks, num_masks);
811+
812 XUngrabServer (dpy);
813 XSync (dpy, false);
814
815@@ -7175,13 +7178,12 @@
816 PropertyChangeMask |
817 LeaveWindowMask |
818 EnterWindowMask |
819- KeyPressMask |
820- KeyReleaseMask |
821- ButtonPressMask |
822- ButtonReleaseMask |
823+ screen->xi2CoreMask () |
824 FocusChangeMask |
825 ExposureMask);
826
827+ screen->xi2SelectInput ();
828+
829 XUngrabServer (dpy);
830 XSync (dpy, false);
831

Subscribers

People subscribed via source and target branches