Merge lp:~banw/compiz/compiz.a11y-shotcuts into lp:compiz

Proposed by Colomban Wendling on 2017-03-16
Status: Needs review
Proposed branch: lp:~banw/compiz/compiz.a11y-shotcuts
Merge into: lp:compiz
Diff against target: 1045 lines (+586/-41)
12 files modified
include/core/action.h (+3/-0)
include/core/screen.h (+1/-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 (+328/-15)
src/eventmanagement.h (+2/-0)
src/privateaction.h (+2/-0)
src/privatescreen.h (+11/-0)
src/screen.cpp (+136/-5)
src/window.cpp (+49/-0)
To merge this branch: bzr merge lp:~banw/compiz/compiz.a11y-shotcuts
Reviewer Review Type Date Requested Status
Andrea Azzarone 2017-03-16 Pending
Review via email: mp+320091@code.launchpad.net

Description of the Change

This is an attempt at solving https://bugs.launchpad.net/compiz/+bug/1653072

The current state is still WIP, but I'd like general opinion, and specific comments on how to fix some parts.

This uses XI2 Raw events to get mouse and keyboard events during active grabs (menus, etc.). Actions need be configured to use those, so it's opt-in.

I initially tried XI2 "normal" events, which include all possibly useful context, but simply listening to those prevent Core events from being dispatched on the window, so it would require using XI2 *only*, which sounds like a large change -- and even then, grabs might affect these too (on Master Devices?), I'm not 100% sure of how this works.

See the FIXMEs and alike in the code for the area that need attention and possibly a fix.

To post a comment you must log in.
Samuel thibault (samuel-thibault) wrote :

Yes, normal XI2 event are very probably affected by grabs, and so AIUI raw events are the only way.

I have commented along the patch. Overall the approach seems sound to me.

Samuel thibault (samuel-thibault) wrote :

Checking things again, it seems XI2 normal events are not affected by grabs, or
at least the ones I have tried: inside gtk3 menus and in screen saver

I can confirm that listening to XI2 normal events prevents core events from
being delivered, but that's only for compiz for the windows not created by
compiz, so it's not a problem for other applications. For compiz itself, I don't
think it is a problem that core events are not delivered any more: once XI2 is
enabled, I don't see a reason for doing anything with the core events (on the
contrary, that would lead to duplicates), so their processing could just be
disabled in that case. Was it only needed so as to detect whether grabbing was
enabled or not? That can be detected more easily with FocusIn/Out with mode
NotifyGrab/Ungrab.

I'll try to rework the solution with XI2 normal events only.

Samuel thibault (samuel-thibault) wrote :

I have finished reworking it, pushed to https://code.launchpad.net/~samuel-thibault/compiz/shortcuts

Unmerged revisions

4109. By Colomban Wendling on 2017-03-16

showmouse: Allow actions to be triggered during grabs

4108. By Colomban Wendling on 2017-03-16

neg: Allow actions to be triggered during grabs

4107. By Colomban Wendling on 2017-03-16

ezoom: Allow actions to be triggered during grabs

4106. By Colomban Wendling on 2017-03-16

Initial WIP working attempt at actions ignoring active grabs

This is useful for plugins that should still work e.g. inside menus, or
in the unlock screen. Typically, this includes accessibility-related
plugins.

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 2017-03-16 17:06:36 +0000
4@@ -193,6 +193,9 @@
5
6 bool active () const;
7
8+ bool ignoreGrabs () const;
9+ void setIgnoreGrabs (bool ignore);
10+
11 /* CompAction should be a pure virtual class so
12 * that we can pass the interface required to for setActionActiveState
13 * directly rather than using friends
14
15=== modified file 'include/core/screen.h'
16--- include/core/screen.h 2016-04-26 12:42:39 +0000
17+++ include/core/screen.h 2017-03-16 17:06:36 +0000
18@@ -388,6 +388,7 @@
19 virtual int outputDeviceForPoint (const CompPoint &point) = 0;
20 virtual int outputDeviceForPoint (int x, int y) = 0;
21 virtual int xkbEvent () = 0;
22+ virtual int xi2OpCode () const = 0;
23 virtual void warpPointer (int dx, int dy) = 0;
24 virtual void updateGrab (GrabHandle handle, Cursor cursor) = 0;
25 virtual int shapeEvent () = 0;
26
27=== modified file 'plugins/ezoom/src/ezoom.cpp'
28--- plugins/ezoom/src/ezoom.cpp 2016-07-01 15:37:19 +0000
29+++ plugins/ezoom/src/ezoom.cpp 2017-03-16 17:06:36 +0000
30@@ -2096,6 +2096,12 @@
31 optionSetEnsureVisibilityInitiate (boost::bind (
32 &EZoomScreen::ensureVisibilityAction, this,
33 _1, _2, _3));
34+
35+ foreach (CompOption &o, getOptions ())
36+ {
37+ if (o.isAction ())
38+ o.value ().action ().setIgnoreGrabs (true);
39+ }
40 }
41
42 EZoomScreen::~EZoomScreen ()
43
44=== modified file 'plugins/neg/src/neg.cpp'
45--- plugins/neg/src/neg.cpp 2016-11-03 09:09:57 +0000
46+++ plugins/neg/src/neg.cpp 2017-03-16 17:06:36 +0000
47@@ -454,6 +454,11 @@
48 optionSetNegDecorationsNotify (boost::bind (&NegScreen::optionChanged, this,
49 _1, _2));
50
51+ foreach (CompOption &o, getOptions ())
52+ {
53+ if (o.isAction ())
54+ o.value ().action ().setIgnoreGrabs (true);
55+ }
56 }
57
58
59
60=== modified file 'plugins/showmouse/src/showmouse.cpp'
61--- plugins/showmouse/src/showmouse.cpp 2016-05-31 17:30:42 +0000
62+++ plugins/showmouse/src/showmouse.cpp 2017-03-16 17:06:36 +0000
63@@ -687,6 +687,12 @@
64 this, _1, _2, _3));
65 optionSetInitiateEdgeTerminate (boost::bind (&ShowmouseScreen::terminate,
66 this, _1, _2, _3));
67+
68+ foreach (CompOption &o, getOptions ())
69+ {
70+ if (o.isAction ())
71+ o.value ().action ().setIgnoreGrabs (true);
72+ }
73 }
74
75 ShowmouseScreen::~ShowmouseScreen ()
76
77=== modified file 'src/action.cpp'
78--- src/action.cpp 2014-02-28 20:45:57 +0000
79+++ src/action.cpp 2017-03-16 17:06:36 +0000
80@@ -483,9 +483,10 @@
81 void
82 CompAction::copyState (const CompAction &action)
83 {
84- priv->initiate = action.priv->initiate;
85- priv->terminate = action.priv->terminate;
86- priv->state = action.priv->state;
87+ priv->initiate = action.priv->initiate;
88+ priv->terminate = action.priv->terminate;
89+ priv->state = action.priv->state;
90+ priv->ignoreGrabs = action.priv->ignoreGrabs;
91
92 memcpy (&priv->priv, &action.priv->priv, sizeof (CompPrivate));
93 }
94@@ -501,6 +502,7 @@
95 priv->button.button () != val.priv->button.button () ||
96 priv->bell != val.priv->bell ||
97 priv->edgeMask != val.priv->edgeMask ||
98+ priv->ignoreGrabs != val.priv->ignoreGrabs ||
99 memcmp (&priv->priv, &val.priv->priv, sizeof (CompPrivate)) != 0)
100 return false;
101
102@@ -651,6 +653,18 @@
103 return priv->active;
104 }
105
106+bool
107+CompAction::ignoreGrabs () const
108+{
109+ return priv->ignoreGrabs;
110+}
111+
112+void
113+CompAction::setIgnoreGrabs (bool ignore)
114+{
115+ priv->ignoreGrabs = ignore;
116+}
117+
118 void
119 PrivateAction::setActive (bool a)
120 {
121@@ -665,29 +679,31 @@
122 }
123
124 PrivateAction::PrivateAction () :
125- initiate (),
126- terminate (),
127- state (0),
128- type (0),
129- key (),
130- button (),
131- bell (false),
132- edgeMask (0),
133- active (false)
134+ initiate (),
135+ terminate (),
136+ state (0),
137+ type (0),
138+ key (),
139+ button (),
140+ bell (false),
141+ edgeMask (0),
142+ active (false),
143+ ignoreGrabs (false)
144 {
145 memset (&priv, 0, sizeof (CompPrivate));
146 }
147
148 PrivateAction::PrivateAction (const PrivateAction &a) :
149- initiate (a.initiate),
150- terminate (a.terminate),
151- state (a.state),
152- type (a.type),
153- key (a.key),
154- button (a.button),
155- bell (a.bell),
156- edgeMask (a.edgeMask),
157- active (a.active)
158+ initiate (a.initiate),
159+ terminate (a.terminate),
160+ state (a.state),
161+ type (a.type),
162+ key (a.key),
163+ button (a.button),
164+ bell (a.bell),
165+ edgeMask (a.edgeMask),
166+ active (a.active),
167+ ignoreGrabs (a.ignoreGrabs)
168 {
169 memcpy (&priv, &a.priv, sizeof (CompPrivate));
170 }
171
172=== modified file 'src/event.cpp'
173--- src/event.cpp 2016-05-17 02:52:07 +0000
174+++ src/event.cpp 2017-03-16 17:06:36 +0000
175@@ -35,6 +35,7 @@
176 #include <X11/extensions/shape.h>
177 #include <X11/extensions/Xrandr.h>
178 #include <X11/extensions/Xfixes.h>
179+#include <X11/extensions/XInput2.h>
180
181 #include <core/atoms.h>
182 #include <core/servergrab.h>
183@@ -266,6 +267,7 @@
184 ce::activateButtonPressOnWindowBindingOption (CompOption &option,
185 unsigned int eventButton,
186 unsigned int eventState,
187+ bool ignoreGrabs,
188 cps::EventManager &eventManager,
189 const ActionModsMatchesEventStateFunc &matchEventState,
190 ce::EventArguments &arguments)
191@@ -280,7 +282,8 @@
192 if (matchEventState (action->button ().modifiers (),
193 eventState))
194 {
195- if (eventManager.triggerPress (action, state, arguments))
196+ if (action->ignoreGrabs () != ignoreGrabs ||
197+ eventManager.triggerPress (action, state, arguments))
198 return true;
199 }
200 }
201@@ -294,6 +297,7 @@
202 unsigned int eventButton,
203 unsigned int eventState,
204 int edge,
205+ bool ignoreGrabs,
206 cps::EventManager &eventManager,
207 const ActionModsMatchesEventStateFunc &matchEventState,
208 ce::EventArguments &arguments)
209@@ -312,7 +316,8 @@
210 {
211 if (matchEventState (action->button ().modifiers (),
212 eventState))
213- if (action->initiate () (action, state,
214+ if (action->ignoreGrabs () != ignoreGrabs ||
215+ action->initiate () (action, state,
216 arguments))
217 return true;
218 }
219@@ -325,6 +330,7 @@
220 bool
221 PrivateScreen::triggerButtonPressBindings (CompOption::Vector &options,
222 XButtonEvent *event,
223+ bool ignoreGrabs,
224 CompOption::Vector &arguments)
225 {
226 int edge = -1;
227@@ -350,6 +356,7 @@
228 if (ce::activateButtonPressOnWindowBindingOption (option,
229 event->button,
230 event->state,
231+ ignoreGrabs,
232 eventManager,
233 matchEventState,
234 arguments))
235@@ -359,6 +366,7 @@
236 event->button,
237 event->state,
238 edge,
239+ ignoreGrabs,
240 eventManager,
241 matchEventState,
242 arguments))
243@@ -372,6 +380,7 @@
244 bool
245 PrivateScreen::triggerButtonReleaseBindings (CompOption::Vector &options,
246 XButtonEvent *event,
247+ bool ignoreGrabs,
248 CompOption::Vector &arguments)
249 {
250 CompAction::State state = CompAction::StateTermButton;
251@@ -385,7 +394,8 @@
252 {
253 if (action->button ().button () == (int) event->button)
254 {
255- if (eventManager.triggerRelease (action, state, arguments))
256+ if (action->ignoreGrabs () != ignoreGrabs ||
257+ eventManager.triggerRelease (action, state, arguments))
258 return true;
259 }
260 }
261@@ -439,6 +449,7 @@
262 PrivateScreen::triggerKeyPressBindings (CompOption::Vector &options,
263 CompAction::Vector &actions,
264 XKeyEvent *event,
265+ bool ignoreGrabs,
266 CompOption::Vector &arguments)
267 {
268 CompAction::State state = 0;
269@@ -476,17 +487,23 @@
270 CompAction *action;
271
272 if (isBound (option, CompAction::BindingTypeKey, state, &action) &&
273- shouldTriggerKeyPressAction (action, event) &&
274- eventManager.triggerPress (action, state, arguments))
275- return true;
276+ shouldTriggerKeyPressAction (action, event))
277+ {
278+ if (action->ignoreGrabs () != ignoreGrabs ||
279+ eventManager.triggerPress (action, state, arguments))
280+ return true;
281+ }
282 }
283
284 foreach (CompAction &action, actions)
285 {
286 if (isBound (action, CompAction::BindingTypeKey, state) &&
287- shouldTriggerKeyPressAction (&action, event) &&
288- eventManager.triggerPress (&action, state, arguments))
289- return true;
290+ shouldTriggerKeyPressAction (&action, event))
291+ {
292+ if (action.ignoreGrabs () != ignoreGrabs ||
293+ eventManager.triggerPress (&action, state, arguments))
294+ return true;
295+ }
296 }
297
298 return false;
299@@ -510,6 +527,7 @@
300 PrivateScreen::triggerKeyReleaseBindings (CompOption::Vector &options,
301 CompAction::Vector &actions,
302 XKeyEvent *event,
303+ bool ignoreGrabs,
304 CompOption::Vector &arguments)
305 {
306 CompAction::State state = CompAction::StateTermKey;
307@@ -525,14 +543,16 @@
308
309 if (isBound (option, CompAction::BindingTypeKey, state, &action) &&
310 shouldTriggerKeyReleaseAction (action, event))
311- handled |= eventManager.triggerRelease (action, state, arguments);
312+ handled |= (action->ignoreGrabs () != ignoreGrabs ||
313+ eventManager.triggerRelease (action, state, arguments));
314 }
315
316 foreach (CompAction &action, actions)
317 {
318 if (isBound (action, CompAction::BindingTypeKey, state) &&
319 shouldTriggerKeyReleaseAction (&action, event))
320- handled |= eventManager.triggerRelease (&action, state, arguments);
321+ handled |= (action.ignoreGrabs () != ignoreGrabs ||
322+ eventManager.triggerRelease (&action, state, arguments));
323 }
324
325 return handled;
326@@ -847,6 +867,34 @@
327 return false;
328 }
329
330+/* inspired from GDK */
331+unsigned int
332+PrivateScreen::translateXI2State (const XIDeviceEvent *event)
333+{
334+ unsigned int state = event->mods.effective;
335+
336+ /* We're only interested in the first 5 buttons as there is no higher Core mask */
337+ int len = MIN (5, event->buttons.mask_len * 8);
338+ for (int i = 1; i <= len; i++)
339+ {
340+ if (! XIMaskIsSet (event->buttons.mask, i))
341+ continue;
342+
343+ switch (i)
344+ {
345+ case 1: state |= Button1Mask; break;
346+ case 2: state |= Button2Mask; break;
347+ case 3: state |= Button3Mask; break;
348+ case 4: state |= Button4Mask; break;
349+ case 5: state |= Button5Mask; break;
350+ }
351+ }
352+
353+ state |= (event->group.effective) << 13;
354+
355+ return state;
356+}
357+
358 bool
359 PrivateScreen::handleActionEvent (XEvent *event)
360 {
361@@ -902,7 +950,7 @@
362 foreach (CompPlugin *p, CompPlugin::getPlugins ())
363 {
364 CompOption::Vector &options = p->vTable->getOptions ();
365- if (triggerButtonPressBindings (options, &event->xbutton, o))
366+ if (triggerButtonPressBindings (options, &event->xbutton, false, o))
367 return true;
368 }
369 break;
370@@ -923,7 +971,7 @@
371 foreach (CompPlugin *p, CompPlugin::getPlugins ())
372 {
373 CompOption::Vector &options = p->vTable->getOptions ();
374- if (triggerButtonReleaseBindings (options, &event->xbutton, o))
375+ if (triggerButtonReleaseBindings (options, &event->xbutton, false, o))
376 return true;
377 }
378 break;
379@@ -950,7 +998,7 @@
380 {
381 CompOption::Vector &options = p->vTable->getOptions ();
382 CompAction::Vector &actions = p->vTable->getActions ();
383- if (triggerKeyPressBindings (options, actions, &event->xkey, o))
384+ if (triggerKeyPressBindings (options, actions, &event->xkey, false, o))
385 return true;
386 }
387 break;
388@@ -977,6 +1025,81 @@
389 XEvent nev;
390 XPeekEvent (dpy, &nev);
391
392+/* FIXME: we have extra XI2 RawEvents that will likely break this detection,
393+ * but how to do this right? */
394+#if 0
395+#if 1
396+ //~ translateXI2Event (&nev);
397+ if (nev.type == GenericEvent && nev.xcookie.extension == xi2.get())
398+ {
399+ XGetEventData (dpy, &nev.xcookie);
400+
401+ XIDeviceEvent *xidevev = (XIDeviceEvent *) nev.xcookie.data;
402+ XIRawEvent *xiRawEv = (XIRawEvent *) nev.xcookie.data;
403+
404+ if ((xidevev->evtype == XI_KeyRelease ||
405+ xidevev->evtype == XI_KeyPress) &&
406+ xidevev->time == event->xkey.time &&
407+ (unsigned int) xidevev->detail == event->xkey.keycode)
408+ {
409+ if (xidevev->evtype == XI_KeyRelease)
410+ {
411+ /* it's the same as the Core event, we can safely drop
412+ * it and have a chance getting the next Core event */
413+ /* FIXME: actually no it's not safe, it'll eat XI2
414+ * events that might be the only triggers for an action */
415+ XFreeEventData (dpy, &nev.xcookie);
416+ XNextEvent (dpy, &nev);
417+ XPeekEvent (dpy, &nev);
418+ //~ XGetEventData (dpy, &nev.xcookie);
419+ }
420+ else
421+ nextKeyPressIsRepeated_ = true;
422+ }
423+ else if ((xiRawEv->evtype == XI_RawKeyRelease ||
424+ xiRawEv->evtype == XI_RawKeyPress) &&
425+ xiRawEv->time == event->xkey.time &&
426+ (unsigned int) xiRawEv->detail == event->xkey.keycode)
427+ {
428+ if (xiRawEv->evtype == XI_RawKeyRelease)
429+ {
430+ /* it's the same as the Core event, we can safely drop
431+ * it and have a chance getting the next Core event */
432+ /* FIXME: actually no it's not safe, it'll eat XI2
433+ * events that might be the only triggers for an action */
434+ XFreeEventData (dpy, &nev.xcookie);
435+ XNextEvent (dpy, &nev);
436+ XPeekEvent (dpy, &nev);
437+ //~ XGetEventData (dpy, &nev.xcookie);
438+ }
439+ else
440+ nextKeyPressIsRepeated_ = true;
441+ }
442+ }
443+#else
444+ /* we can safely ignore XI events here
445+ * -- well no, actually we can't, it'll eat XI2 events that might
446+ * be the only triggers for an action */
447+ bool hasEvents = true;
448+ if (nev.type == GenericEvent && nev.xcookie.extension == xi2.get())
449+ {
450+ XFreeEventData (dpy, &nev.xcookie);
451+ hasEvents = true;
452+ do
453+ {
454+ XNextEvent (dpy, &nev);
455+ if (! XEventsQueued (dpy, QueuedAfterReading))
456+ hasEvents = false;
457+ else
458+ XPeekEvent (dpy, &nev);
459+ }
460+ while (hasEvents && nev.type == GenericEvent &&
461+ nev.xcookie.extension == xi2.get());
462+ }
463+ if (hasEvents)
464+#endif
465+#endif
466+
467 if (nev.type == KeyPress && nev.xkey.time == event->xkey.time &&
468 nev.xkey.keycode == event->xkey.keycode)
469 {
470@@ -988,7 +1111,7 @@
471 {
472 CompOption::Vector &options = p->vTable->getOptions ();
473 CompAction::Vector &actions = p->vTable->getActions ();
474- handled |= triggerKeyReleaseBindings (options, actions, &event->xkey, o);
475+ handled |= triggerKeyReleaseBindings (options, actions, &event->xkey, false, o);
476 }
477
478 if (handled)
479@@ -1164,6 +1287,196 @@
480 xdndWindow = None;
481 }
482 break;
483+
484+ case GenericEvent:
485+ if (event->xcookie.extension == xi2.get())
486+ {
487+ XIRawEvent *xiRawEv = (XIRawEvent *) event->xcookie.data;
488+
489+ switch (xiRawEv->evtype)
490+ {
491+ case XI_RawButtonPress:
492+ {
493+ // Ignore Master Devices
494+ if (xiRawEv->deviceid != xiRawEv->sourceid)
495+ break;
496+
497+ XButtonEvent fakeEvent;
498+ fakeEvent.type = ButtonPress;
499+ fakeEvent.window = root; // FIXME:
500+ fakeEvent.state = pointerMods; // FIXME:
501+ fakeEvent.x_root = pointerX; // FIXME:
502+ fakeEvent.y_root = pointerY; // FIXME:
503+ fakeEvent.root = root; // FIXME
504+ fakeEvent.button = xiRawEv->detail;
505+ fakeEvent.time = xiRawEv->time;
506+
507+ /* We need to determine if we clicked on a parent frame
508+ * window, if so, pass the appropriate child window as
509+ * "window" and the frame as "event_window"
510+ */
511+
512+ xid = fakeEvent.window;
513+
514+ foreach (CompWindow *w, screen->windows ())
515+ {
516+ if (w->priv->frame == xid)
517+ xid = w->id ();
518+ }
519+
520+ o[0].value ().set ((int) fakeEvent.window);
521+ o[1].value ().set ((int) xid);
522+ o[2].value ().set ((int) fakeEvent.state);
523+ o[3].value ().set ((int) fakeEvent.x_root);
524+ o[4].value ().set ((int) fakeEvent.y_root);
525+ o[5].value ().set ((int) fakeEvent.root);
526+
527+ o[6].setName ("button", CompOption::TypeInt);
528+ o[7].setName ("time", CompOption::TypeInt);
529+
530+ o[6].value ().set ((int) fakeEvent.button);
531+ o[7].value ().set ((int) fakeEvent.time);
532+
533+ eventManager.resetPossibleTap();
534+ foreach (CompPlugin *p, CompPlugin::getPlugins ())
535+ {
536+ CompOption::Vector &options = p->vTable->getOptions ();
537+ if (triggerButtonPressBindings (options, &fakeEvent, true, o))
538+ return true;
539+ }
540+ break;
541+ }
542+ case XI_RawButtonRelease:
543+ {
544+ // Ignore Master Devices
545+ if (xiRawEv->deviceid != xiRawEv->sourceid)
546+ break;
547+
548+ XButtonEvent fakeEvent;
549+ fakeEvent.type = ButtonRelease;
550+ fakeEvent.window = root; // FIXME:
551+ fakeEvent.state = pointerMods; // FIXME:
552+ fakeEvent.x_root = pointerX; // FIXME:
553+ fakeEvent.y_root = pointerY; // FIXME:
554+ fakeEvent.root = root; // FIXME:
555+ fakeEvent.button = xiRawEv->detail;
556+ fakeEvent.time = xiRawEv->time;
557+
558+ o[0].value ().set ((int) fakeEvent.window);
559+ o[1].value ().set ((int) fakeEvent.window);
560+ o[2].value ().set ((int) fakeEvent.state);
561+ o[3].value ().set ((int) fakeEvent.x_root);
562+ o[4].value ().set ((int) fakeEvent.y_root);
563+ o[5].value ().set ((int) fakeEvent.root);
564+
565+ o[6].setName ("button", CompOption::TypeInt);
566+ o[7].setName ("time", CompOption::TypeInt);
567+
568+ o[6].value ().set ((int) fakeEvent.button);
569+ o[7].value ().set ((int) fakeEvent.time);
570+
571+ foreach (CompPlugin *p, CompPlugin::getPlugins ())
572+ {
573+ CompOption::Vector &options = p->vTable->getOptions ();
574+ if (triggerButtonReleaseBindings (options, &fakeEvent, true, o))
575+ return true;
576+ }
577+ break;
578+ }
579+ case XI_RawKeyPress:
580+ {
581+ // Ignore Master Devices
582+ if (xiRawEv->deviceid != xiRawEv->sourceid)
583+ break;
584+
585+ XKeyEvent fakeEvent;
586+ fakeEvent.type = KeyPress;
587+ fakeEvent.window = root; // FIXME:
588+ fakeEvent.state = pointerMods; // FIXME:
589+ fakeEvent.x_root = pointerX; // FIXME:
590+ fakeEvent.y_root = pointerY; // FIXME:
591+ fakeEvent.root = root; // FIXME:
592+ fakeEvent.keycode = xiRawEv->detail;
593+ fakeEvent.time = xiRawEv->time;
594+
595+ o[0].value ().set ((int) fakeEvent.window);
596+ o[1].value ().set ((int) orphanData.activeWindow);
597+ o[2].value ().set ((int) fakeEvent.state);
598+ o[3].value ().set ((int) fakeEvent.x_root);
599+ o[4].value ().set ((int) fakeEvent.y_root);
600+ o[5].value ().set ((int) fakeEvent.root);
601+
602+ o[6].setName ("keycode", CompOption::TypeInt);
603+ o[7].setName ("time", CompOption::TypeInt);
604+ o[8].setName ("is_repeated", CompOption::TypeBool);
605+
606+ o[6].value ().set ((int) fakeEvent.keycode);
607+ o[7].value ().set ((int) fakeEvent.time);
608+/*
609+ o[8].value ().set (nextKeyPressIsRepeated_);
610+ nextKeyPressIsRepeated_ = false;
611+*/
612+ // FIXME: is that really valid for RawKeyPress?
613+ o[8].value ().set ((xiRawEv->flags & XIKeyRepeat) != 0);
614+
615+ eventManager.resetPossibleTap();
616+ foreach (CompPlugin *p, CompPlugin::getPlugins ())
617+ {
618+ CompOption::Vector &options = p->vTable->getOptions ();
619+ CompAction::Vector &actions = p->vTable->getActions ();
620+ if (triggerKeyPressBindings (options, actions, &fakeEvent, true, o))
621+ return true;
622+ }
623+
624+ break;
625+ }
626+ case XI_RawKeyRelease:
627+ {
628+ // Ignore Master Devices
629+ if (xiRawEv->deviceid != xiRawEv->sourceid)
630+ break;
631+
632+ XKeyEvent fakeEvent;
633+ fakeEvent.type = KeyRelease;
634+ fakeEvent.window = root; // FIXME:
635+ fakeEvent.state = pointerMods; // FIXME:
636+ fakeEvent.x_root = pointerX; // FIXME:
637+ fakeEvent.y_root = pointerY; // FIXME:
638+ fakeEvent.root = root; // FIXME:
639+ fakeEvent.keycode = xiRawEv->detail;
640+ fakeEvent.time = xiRawEv->time;
641+
642+ o[0].value ().set ((int) fakeEvent.window);
643+ o[1].value ().set ((int) orphanData.activeWindow);
644+ o[2].value ().set ((int) fakeEvent.state);
645+ o[3].value ().set ((int) fakeEvent.x_root);
646+ o[4].value ().set ((int) fakeEvent.y_root);
647+ o[5].value ().set ((int) fakeEvent.root);
648+
649+ o[6].setName ("keycode", CompOption::TypeInt);
650+ o[7].setName ("time", CompOption::TypeInt);
651+
652+ o[6].value ().set ((int) fakeEvent.keycode);
653+ o[7].value ().set ((int) fakeEvent.time);
654+
655+ bool handled = false;
656+
657+ foreach (CompPlugin *p, CompPlugin::getPlugins ())
658+ {
659+ CompOption::Vector &options = p->vTable->getOptions ();
660+ CompAction::Vector &actions = p->vTable->getActions ();
661+ handled |= triggerKeyReleaseBindings (options, actions, &fakeEvent, true, o);
662+ }
663+
664+ if (handled)
665+ return true;
666+
667+ break;
668+ }
669+ }
670+ }
671+ break;
672+
673 default:
674 if (event->type == xkbEvent.get())
675 {
676
677=== modified file 'src/eventmanagement.h'
678--- src/eventmanagement.h 2012-10-11 10:06:30 +0000
679+++ src/eventmanagement.h 2017-03-16 17:06:36 +0000
680@@ -60,6 +60,7 @@
681 activateButtonPressOnWindowBindingOption (CompOption &option,
682 unsigned int eventButton,
683 unsigned int eventState,
684+ bool ignoreGrabs,
685 cps::EventManager &eventManager,
686 const ActionModsMatchesEventStateFunc &matchEventState,
687 EventArguments &arguments);
688@@ -69,6 +70,7 @@
689 unsigned int eventButton,
690 unsigned int eventState,
691 int edge,
692+ bool ignoreGrabs,
693 cps::EventManager &eventManager,
694 const ActionModsMatchesEventStateFunc &matchEventState,
695 EventArguments &arguments);
696
697=== modified file 'src/privateaction.h'
698--- src/privateaction.h 2012-10-02 10:28:01 +0000
699+++ src/privateaction.h 2017-03-16 17:06:36 +0000
700@@ -71,6 +71,8 @@
701
702 bool active;
703
704+ bool ignoreGrabs;
705+
706 CompPrivate priv;
707 };
708
709
710=== modified file 'src/privatescreen.h'
711--- src/privatescreen.h 2016-05-17 02:52:07 +0000
712+++ src/privatescreen.h 2017-03-16 17:06:36 +0000
713@@ -37,6 +37,8 @@
714 #include <time.h>
715 #include <boost/shared_ptr.hpp>
716
717+#include <X11/extensions/XInput2.h>
718+
719 #include <glibmm/main.h>
720
721 #include "privatetimeoutsource.h"
722@@ -628,10 +630,12 @@
723
724 bool triggerButtonPressBindings (CompOption::Vector &options,
725 XButtonEvent *event,
726+ bool ignoreGrabs,
727 CompOption::Vector &arguments);
728
729 bool triggerButtonReleaseBindings (CompOption::Vector &options,
730 XButtonEvent *event,
731+ bool ignoreGrabs,
732 CompOption::Vector &arguments);
733
734 bool shouldTriggerKeyPressAction (CompAction *action,
735@@ -649,11 +653,13 @@
736 bool triggerKeyPressBindings (CompOption::Vector &options,
737 CompAction::Vector &actions,
738 XKeyEvent *event,
739+ bool ignoreGrabs,
740 CompOption::Vector &arguments);
741
742 bool triggerKeyReleaseBindings (CompOption::Vector &options,
743 CompAction::Vector &actions,
744 XKeyEvent *event,
745+ bool ignoreGrabs,
746 CompOption::Vector &arguments);
747
748 bool triggerStateNotifyBindings (CompOption::Vector &options,
749@@ -667,6 +673,8 @@
750
751 void setAudibleBell (bool audible);
752
753+ static unsigned int translateXI2State (const XIDeviceEvent *event);
754+
755 bool handleActionEvent (XEvent *event);
756
757 void handleSelectionRequest (XEvent *event);
758@@ -766,6 +774,7 @@
759 compiz::private_screen::Extension xSync;
760 compiz::private_screen::Extension xRandr;
761 compiz::private_screen::Extension xShape;
762+ compiz::private_screen::Extension xi2;
763 compiz::private_screen::ViewPort viewPort;
764 compiz::private_screen::StartupSequenceImpl startupSequence;
765 compiz::private_screen::EventManager eventManager;
766@@ -951,6 +960,8 @@
767
768 int xkbEvent ();
769
770+ int xi2OpCode () const;
771+
772 XWindowAttributes attrib ();
773
774 int screenNum ();
775
776=== modified file 'src/screen.cpp'
777--- src/screen.cpp 2016-07-28 13:43:06 +0000
778+++ src/screen.cpp 2017-03-16 17:06:36 +0000
779@@ -788,6 +788,8 @@
780 XNextEvent (dpy, &peekEvent);
781 }
782 }
783+ if (ev.type == GenericEvent)
784+ XGetEventData (dpy, &ev.xcookie);
785
786 return true;
787 }
788@@ -880,15 +882,95 @@
789 &i, &i, &pointerMods);
790 }
791 break;
792+ case GenericEvent:
793+ if (event.xcookie.extension == xi2.get ())
794+ {
795+ XIDeviceEvent *xiDevEv = (XIDeviceEvent *) event.xcookie.data;
796+
797+ switch (xiDevEv->evtype)
798+ {
799+ case XI_ButtonPress:
800+ case XI_ButtonRelease:
801+ case XI_KeyPress:
802+ case XI_KeyRelease:
803+ case XI_Motion:
804+ /* as we use raw events, we need to track the X/Y location
805+ * as we don't get it with the event */
806+ // FIXME: does listening for XI_Motion prevent MotionNotify?
807+ // FIXME: does using XI2 motion render MousePoll moot?
808+ pointerX = (int) xiDevEv->root_x;
809+ pointerY = (int) xiDevEv->root_y;
810+ pointerMods = translateXI2State (xiDevEv);
811+ break;
812+ }
813+ }
814+ break;
815 default:
816 break;
817 }
818
819- sn_display_process_event (snDisplay, &event);
820-
821- inHandleEvent = true;
822- screen->alwaysHandleEvent (&event);
823- inHandleEvent = false;
824+ /* not to spam the event loop, ignore motion events */
825+ // FIXME: do we actually want to filter this?
826+ // filtering isn't based on any metrics, just intuition
827+ if (! (event.type == GenericEvent &&
828+ event.xcookie.extension == xi2.get () &&
829+ event.xcookie.evtype == XI_Motion))
830+ {
831+ sn_display_process_event (snDisplay, &event);
832+
833+ inHandleEvent = true;
834+ screen->alwaysHandleEvent (&event);
835+ inHandleEvent = false;
836+ }
837+
838+#if 0
839+ /* as we use raw events, we need to track their toggling to update
840+ * modifiers. It's kinda ugly, but what can we do? */
841+ if (event.type == GenericEvent && event.xcookie.extension == xi2.get ())
842+ {
843+ XIRawEvent *xiRawEv = (XIRawEvent *) event.xcookie.data;
844+
845+ switch (xiRawEv->evtype)
846+ {
847+ case XI_RawButtonPress:
848+ switch (xiRawEv->detail)
849+ {
850+ case 1: pointerMods |= Button1Mask; break;
851+ case 2: pointerMods |= Button2Mask; break;
852+ case 3: pointerMods |= Button3Mask; break;
853+ case 4: pointerMods |= Button4Mask; break;
854+ case 5: pointerMods |= Button5Mask; break;
855+ }
856+ break;
857+ case XI_RawButtonRelease:
858+ switch (xiRawEv->detail)
859+ {
860+ case 1: pointerMods &= ~Button1Mask; break;
861+ case 2: pointerMods &= ~Button2Mask; break;
862+ case 3: pointerMods &= ~Button3Mask; break;
863+ case 4: pointerMods &= ~Button4Mask; break;
864+ case 5: pointerMods &= ~Button5Mask; break;
865+ }
866+ break;
867+ case XI_RawKeyPress:
868+ pointerMods |= modHandler->keycodeToModifiers (xiRawEv->detail);
869+ break;
870+ case XI_RawKeyRelease:
871+ pointerMods &= ~modHandler->keycodeToModifiers (xiRawEv->detail);
872+ break;
873+ }
874+ }
875+#else
876+ /* XKB events are fired during grabs too, so that's good for us */
877+ if (event.type == xkbEvent.get() &&
878+ ((XkbAnyEvent *) &event)->xkb_type == XkbStateNotify)
879+ {
880+ pointerMods = ((XkbStateNotifyEvent *) &event)->mods;
881+ }
882+#endif
883+
884+ if (event.type == GenericEvent)
885+ XFreeEventData (dpy, &event.xcookie);
886
887 XFlush (dpy);
888
889@@ -4408,6 +4490,12 @@
890 return privateScreen.getXkbEvent ();
891 }
892
893+int
894+CompScreenImpl::xi2OpCode () const
895+{
896+ return privateScreen.xi2.get ();
897+}
898+
899 void
900 PrivateScreen::identifyEdgeWindow(Window id)
901 {
902@@ -4957,6 +5045,26 @@
903 }
904
905
906+namespace
907+{
908+static Bool XI2QueryExtension(Display *dpy, int *opcode, int *error)
909+{
910+ int event;
911+ if (!XQueryExtension(dpy, "XInputExtension", opcode, &event, error))
912+ return False;
913+
914+ /* we want 2.1 for RawEvent::sourceid */
915+ int major = 2, minor = 1;
916+ if (XIQueryVersion(dpy, &major, &minor) == BadRequest)
917+ return False;
918+ compLogMessage ("core", CompLogLevelDebug,
919+ "Got XI2 extension %d.%d", major, minor);
920+
921+ return True;
922+}
923+}
924+
925+
926 bool
927 PrivateScreen::initDisplay (const char *name, cps::History& history, unsigned int showingDesktopMask)
928 {
929@@ -5007,6 +5115,10 @@
930 "No XKB extension");
931 }
932
933+ xi2.init<XI2QueryExtension> (dpy);
934+ if (!xi2.isEnabled ())
935+ compLogMessage ("core", CompLogLevelWarn, "No XI2 extension");
936+
937 int xineramaError;
938 int xineramaEvent;
939 xineramaExtension = XineramaQueryExtension (dpy,
940@@ -5060,6 +5172,25 @@
941 SubstructureNotifyMask);
942 }
943
944+ if (xi2.isEnabled ())
945+ {
946+ XIEventMask eventmask;
947+ unsigned char mask[XIMaskLen (XI_LASTEVENT)] = { 0 };
948+
949+ eventmask.deviceid = XIAllDevices;
950+ eventmask.mask_len = sizeof (mask);
951+ eventmask.mask = mask;
952+
953+ XISetMask (mask, XI_RawKeyPress);
954+ XISetMask (mask, XI_RawKeyRelease);
955+ XISetMask (mask, XI_RawButtonPress);
956+ XISetMask (mask, XI_RawButtonRelease);
957+ // for tracking the coordinates of raw events
958+ XISetMask (mask, XI_Motion);
959+
960+ XISelectEvents (dpy, root_tmp, &eventmask, 1);
961+ }
962+
963 for (int i = 0; i < SCREEN_EDGE_NUM; i++)
964 {
965 screenEdge[i].id = None;
966
967=== modified file 'src/window.cpp'
968--- src/window.cpp 2016-05-17 02:52:25 +0000
969+++ src/window.cpp 2017-03-16 17:06:36 +0000
970@@ -27,6 +27,7 @@
971 #include <X11/Xatom.h>
972 #include <X11/Xproto.h>
973 #include <X11/extensions/shape.h>
974+#include <X11/extensions/XInput2.h>
975
976 #include <stdio.h>
977 #include <string.h>
978@@ -6946,6 +6947,18 @@
979
980 /* Do not get any events from here on */
981 XSelectInput (dpy, screen->root (), NoEventMask);
982+ // FIXME: do we really need to ignore XI2 events too?
983+ if (screen->xi2OpCode () != -1)
984+ {
985+ XIEventMask eventmask;
986+ unsigned char mask[1] = { 0 };
987+
988+ eventmask.deviceid = XIAllDevices;
989+ eventmask.mask_len = sizeof (mask);
990+ eventmask.mask = mask;
991+
992+ XISelectEvents (dpy, screen->root (), &eventmask, 1);
993+ }
994
995 /* If we have some frame extents, we should apply them here and
996 * set lastFrameExtents */
997@@ -7050,6 +7063,24 @@
998 FocusChangeMask |
999 ExposureMask);
1000
1001+ if (screen->xi2OpCode () != -1)
1002+ {
1003+ XIEventMask eventmask;
1004+ unsigned char mask[XIMaskLen (XI_LASTEVENT)] = { 0 };
1005+
1006+ eventmask.deviceid = XIAllDevices;
1007+ eventmask.mask_len = sizeof (mask);
1008+ eventmask.mask = mask;
1009+
1010+ XISetMask (mask, XI_RawKeyPress);
1011+ XISetMask (mask, XI_RawKeyRelease);
1012+ XISetMask (mask, XI_RawButtonPress);
1013+ XISetMask (mask, XI_RawButtonRelease);
1014+ XISetMask (mask, XI_Motion);
1015+
1016+ XISelectEvents (dpy, screen->root (), &eventmask, 1);
1017+ }
1018+
1019 XUngrabServer (dpy);
1020 XSync (dpy, false);
1021
1022@@ -7179,6 +7210,24 @@
1023 FocusChangeMask |
1024 ExposureMask);
1025
1026+ if (screen->xi2OpCode () != -1)
1027+ {
1028+ XIEventMask eventmask;
1029+ unsigned char mask[XIMaskLen (XI_LASTEVENT)] = { 0 };
1030+
1031+ eventmask.deviceid = XIAllDevices;
1032+ eventmask.mask_len = sizeof (mask);
1033+ eventmask.mask = mask;
1034+
1035+ XISetMask (mask, XI_RawKeyPress);
1036+ XISetMask (mask, XI_RawKeyRelease);
1037+ XISetMask (mask, XI_RawButtonPress);
1038+ XISetMask (mask, XI_RawButtonRelease);
1039+ XISetMask (mask, XI_Motion);
1040+
1041+ XISelectEvents (dpy, screen->root (), &eventmask, 1);
1042+ }
1043+
1044 XUngrabServer (dpy);
1045 XSync (dpy, false);
1046

Subscribers

People subscribed via source and target branches