Merge lp:~ksamak/compiz/ezoom_focus_tracking into lp:compiz/0.9.13

Proposed by ksamak
Status: Needs review
Proposed branch: lp:~ksamak/compiz/ezoom_focus_tracking
Merge into: lp:compiz/0.9.13
Diff against target: 1617 lines (+1252/-39)
17 files modified
debian/compiz-dev.install (+1/-0)
debian/compiz-plugins-default.install (+2/-0)
debian/control (+1/-0)
plugins/ezoom/CMakeLists.txt (+1/-1)
plugins/ezoom/ezoom.xml.in (+35/-8)
plugins/ezoom/src/ezoom.cpp (+123/-28)
plugins/ezoom/src/ezoom.h (+22/-2)
plugins/focuspoll/CMakeLists.txt (+9/-0)
plugins/focuspoll/compiz-focuspoll.pc.in (+12/-0)
plugins/focuspoll/focuspoll.xml.in (+34/-0)
plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h (+82/-0)
plugins/focuspoll/include/accessibilitywatcher/focusinfo.h (+76/-0)
plugins/focuspoll/include/focuspoll/focuspoll.h (+50/-0)
plugins/focuspoll/src/accessibilitywatcher.cpp (+506/-0)
plugins/focuspoll/src/focuspoll.cpp (+220/-0)
plugins/focuspoll/src/private.h (+75/-0)
plugins/showmouse/showmouse.xml.in (+3/-0)
To merge this branch: bzr merge lp:~ksamak/compiz/ezoom_focus_tracking
Reviewer Review Type Date Requested Status
ksamak (community) Needs Resubmitting
Andrea Azzarone Pending
Marco Trevisan (TreviƱo) Pending
Sam Spilsbury code, style Pending
Review via email: mp+321643@code.launchpad.net

This proposal supersedes a proposal from 2017-02-24.

Description of the change

ezoom: added focus tracking based on at-spi a11y events.

To post a comment you must log in.
Revision history for this message
Andrea Azzarone (azzar1) wrote : Posted in a previous version of this proposal

Can you please link a bug to the MP?

Revision history for this message
Andrea Azzarone (azzar1) wrote : Posted in a previous version of this proposal

Ok this is the first part of the review. I'll continue later.

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

Thanks for all the hard work in implementing this. There's a fair bit of work to be done before this can be integrated. In particular, this cannot be merged whilst it opens a separate display connection - that is a recipe for deadlocks.

review: Needs Fixing (code, style)
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal
Revision history for this message
ksamak (ksamak) wrote : Posted in a previous version of this proposal

well, this plugin would indeed have been helpful.

Sam Spilsbury (smspillaz) wrote 18 hours ago:
I like the fact that this is a separate library, but this absolutely doesn't belong in the mousepoll plugin. The mouse poll plugin is supposed to do one thing and one thing only - update the position of the mouse every N milliseconds. Move all this into either a separate plugin or library.

I originally made a copy of the mousePoll plugin (called focusPoll), but for some unknown reason it... deadlocked, or froze at runtime. I couldn't find why, and no one had time to look at it, so I merged both logics in mousepoll, cause then, with sensibly the same methods, it didn't freeze.

I agree an additional plugin would serve better, so I'd appreciate some input on that problem.
there's a readyish branch on http://git.hypra.fr/hypra/compiz.git named focuspoll. Maybe you would have some insights as to what is blocking here. It might be, as you pointed, the call to open the display.
question: how would you circumvent the problem of the call to XOpenDisplay, if the whole a11ywatcher was a library? Is there a better way to get the screen info? that is without causing all those problems.

I will meditate on these ideas, and weight the pros and cons of the different solutions, and i'll then continue the work.
thank you for all the advice and corrections, including those pending below. I am available to discuss these matters further, or if you have more insight.

review: Needs Fixing
Revision history for this message
Sam Spilsbury (smspillaz) wrote : Posted in a previous version of this proposal

> Maybe you would have some insights as to what is blocking here. It
> might be, as you pointed, the call to open the display.
> question: how would you circumvent the problem of the call to XOpenDisplay, if
> the whole a11ywatcher was a library? Is there a better way to get the screen
> info? that is without causing all those problems.

Yes, there's a global variable called 'screen' and you can access the current connection from there (screen->dpy()). If your library needs a reference to it, I'd suggest having the caller pass it in.

Revision history for this message
ksamak (ksamak) wrote : Posted in a previous version of this proposal

I've answered some of the following, and will superseed the merge request with the new version from the branch.
i thank you for your time, advice, and corrections.

Revision history for this message
ksamak (ksamak) : Posted in a previous version of this proposal
Revision history for this message
ksamak (ksamak) wrote : Posted in a previous version of this proposal

call for help on the matter of binding C callback to c++ member function.
thx

Revision history for this message
ksamak (ksamak) wrote : Posted in a previous version of this proposal

just a ping, i changed coding style again, on request from 3v1n0.

Revision history for this message
Andrea Azzarone (azzar1) wrote : Posted in a previous version of this proposal

I reviews part of the code. Fix it before I going on.

review: Needs Fixing
Revision history for this message
ksamak (ksamak) wrote : Posted in a previous version of this proposal

I fixed the code according to your specifications, i changed the whole coding style of my editor, thanks to the few lines i (finally) found here:
http://wiki.compiz.org/Development/CodingStyle
Sorry again for my misunderstanding tabs and coding style, i'll try my best to respect that from initial coding on, event though i find it less readable.
I thank you for your patience.

review: Needs Resubmitting
4122. By ksamak <ksamak@ksalaptop>

added precedence relation to start showmouse before ezoom

4123. By ksamak <ksamak@ksalaptop>

coding style

4124. By ksamak <ksamak@ksalaptop>

added rebranding of icedove to thunderbird support

Revision history for this message
ksamak (ksamak) wrote :

created lp:~ksamak/compiz/correct_mousetheme_targetting_for_ezoom, although i think it wasn't necessary. I'll delete it.
I add the update for the rebranding of icedove into thunderbird.
thank you.

review: Needs Resubmitting
Revision history for this message
Samuel thibault (samuel-thibault) wrote :

Hello,

I have gone through the previous review from azzar1, and commented further.

Samuel

Revision history for this message
Samuel thibault (samuel-thibault) wrote : Posted in a previous version of this proposal

I have commented on the callback issue, to avoid the singleton.

Unmerged revisions

4124. By ksamak <ksamak@ksalaptop>

added rebranding of icedove to thunderbird support

4123. By ksamak <ksamak@ksalaptop>

coding style

4122. By ksamak <ksamak@ksalaptop>

added precedence relation to start showmouse before ezoom

4121. By ksamak <ksamak@ksalaptop>

modifications of code according to azzar's request

4120. By ksamak <ksamak@ksalaptop>

coding style modifs. changed if format on 3v1n0's request

4119. By ksamak <ksamak@ksalaptop>

coding style modifs

4118. By ksamak <ksamak@ksalaptop>

coding style modifs

4117. By ksamak <ksamak@ksalaptop>

minors code modifs

4116. By ksamak <ksamak@ksalaptop>

minors code modifs

4115. By ksamak <ksamak@ksalaptop>

corrected segfault, minors code modifs

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'debian/compiz-dev.install'
--- debian/compiz-dev.install 2016-07-14 15:21:59 +0000
+++ debian/compiz-dev.install 2017-04-28 09:59:04 +0000
@@ -7,6 +7,7 @@
7debian/tmp/usr/lib/*/pkgconfig/compiz-composite.pc7debian/tmp/usr/lib/*/pkgconfig/compiz-composite.pc
8debian/tmp/usr/lib/*/pkgconfig/compiz-cube.pc8debian/tmp/usr/lib/*/pkgconfig/compiz-cube.pc
9debian/tmp/usr/lib/*/pkgconfig/compiz-mousepoll.pc9debian/tmp/usr/lib/*/pkgconfig/compiz-mousepoll.pc
10debian/tmp/usr/lib/*/pkgconfig/compiz-focuspoll.pc
10debian/tmp/usr/lib/*/pkgconfig/compiz-opengl.pc11debian/tmp/usr/lib/*/pkgconfig/compiz-opengl.pc
11debian/tmp/usr/lib/*/pkgconfig/compiz.pc12debian/tmp/usr/lib/*/pkgconfig/compiz.pc
12debian/tmp/usr/lib/*/pkgconfig/compiz-scale.pc13debian/tmp/usr/lib/*/pkgconfig/compiz-scale.pc
1314
=== modified file 'debian/compiz-plugins-default.install'
--- debian/compiz-plugins-default.install 2015-07-24 16:28:37 +0000
+++ debian/compiz-plugins-default.install 2017-04-28 09:59:04 +0000
@@ -26,6 +26,8 @@
26usr/share/compiz/*matecompat.*26usr/share/compiz/*matecompat.*
27usr/lib/*/compiz/*mousepoll.*27usr/lib/*/compiz/*mousepoll.*
28usr/share/compiz/*mousepoll.*28usr/share/compiz/*mousepoll.*
29usr/lib/*/compiz/*focuspoll.*
30usr/share/compiz/*focuspoll.*
29usr/lib/*/compiz/*move.*31usr/lib/*/compiz/*move.*
30usr/share/compiz/*move.*32usr/share/compiz/*move.*
31usr/lib/*/compiz/*opengl.*33usr/lib/*/compiz/*opengl.*
3234
=== modified file 'debian/control'
--- debian/control 2016-11-03 18:29:41 +0000
+++ debian/control 2017-04-28 09:59:04 +0000
@@ -14,6 +14,7 @@
14 quilt (>= 0.40),14 quilt (>= 0.40),
15 libcairo2-dev,15 libcairo2-dev,
16 libdbus-glib-1-dev,16 libdbus-glib-1-dev,
17 libatspi2.0-dev,
17 libgl1-mesa-dev (>= 6.5.1) [!armhf !armel] | libgl-dev [!armhf !armel],18 libgl1-mesa-dev (>= 6.5.1) [!armhf !armel] | libgl-dev [!armhf !armel],
18 libegl1-mesa-dev [armhf armel], libgles2-mesa-dev [armhf armel],19 libegl1-mesa-dev [armhf armel], libgles2-mesa-dev [armhf armel],
19 libboost-dev,20 libboost-dev,
2021
=== modified file 'plugins/ezoom/CMakeLists.txt'
--- plugins/ezoom/CMakeLists.txt 2012-05-16 17:40:27 +0000
+++ plugins/ezoom/CMakeLists.txt 2017-04-28 09:59:04 +0000
@@ -2,4 +2,4 @@
22
3include (CompizPlugin)3include (CompizPlugin)
44
5compiz_plugin (ezoom PLUGINDEPS composite opengl mousepoll)5compiz_plugin (ezoom PLUGINDEPS composite opengl mousepoll focuspoll)
66
=== modified file 'plugins/ezoom/ezoom.xml.in'
--- plugins/ezoom/ezoom.xml.in 2016-07-01 15:37:19 +0000
+++ plugins/ezoom/ezoom.xml.in 2017-04-28 09:59:04 +0000
@@ -7,6 +7,7 @@
7 <plugin>expo</plugin>7 <plugin>expo</plugin>
8 <plugin>decor</plugin>8 <plugin>decor</plugin>
9 <plugin>mousepoll</plugin>9 <plugin>mousepoll</plugin>
10 <plugin>focuspoll</plugin>
10 </relation>11 </relation>
11 <relation type="before">12 <relation type="before">
12 <plugin>staticswitcher</plugin>13 <plugin>staticswitcher</plugin>
@@ -15,6 +16,7 @@
15 <requirement>16 <requirement>
16 <plugin>opengl</plugin>17 <plugin>opengl</plugin>
17 <plugin>mousepoll</plugin>18 <plugin>mousepoll</plugin>
19 <plugin>focuspoll</plugin>
18 </requirement>20 </requirement>
19 </deps>21 </deps>
20 <_short>Enhanced Zoom Desktop</_short>22 <_short>Enhanced Zoom Desktop</_short>
@@ -425,12 +427,17 @@
425 <_short>Focus Tracking</_short>427 <_short>Focus Tracking</_short>
426 <option type="bool" name="follow_focus">428 <option type="bool" name="follow_focus">
427 <_short>Enable focus tracking</_short>429 <_short>Enable focus tracking</_short>
428 <_long>Move the zoom area when focus changes.</_long>430 <_long>Move the zoom area when focus changes. You may see the focuspoll module options for more</_long>
429 <default>true</default>431 <default>true</default>
430 </option>432 </option>
433 <option type="bool" name="follow_window_focus">
434 <_short>Enable focus tracking when switching windows</_short>
435 <_long>Move the zoom area to the center of a newly switched window.</_long>
436 <default>false</default>
437 </option>
431 <option type="bool" name="focus_fit_window">438 <option type="bool" name="focus_fit_window">
432 <_short>Fit zoom level to window on focus change</_short>439 <_short>Fit zoom level to newly switched window</_short>
433 <_long>Fit the zoomed area to the window when the zoomed area moves as a result of focus tracking.</_long>440 <_long>Fit the zoomed area to the window when it switches.</_long>
434 <default>false</default>441 <default>false</default>
435 </option>442 </option>
436 <option type="float" name="autoscale_min">443 <option type="float" name="autoscale_min">
@@ -443,16 +450,31 @@
443 </option>450 </option>
444 <option type="bool" name="always_focus_fit_window">451 <option type="bool" name="always_focus_fit_window">
445 <_short>Always fit to window on focus track</_short>452 <_short>Always fit to window on focus track</_short>
446 <_long>Fit the zoomed area to the window when the zoomed area moves as a result of focus tracking. Even when not initially zoomed in. </_long>453 <_long>Fit the zoomed area to the newly switched window, even when not initially zoomed in. </_long>
447 <default>false</default>454 <default>false</default>
448 </option>455 </option>
449 <option type="int" name="follow_focus_delay">456 <option type="int" name="follow_focus_delay">
450 <_short>Follow Focus Delay</_short>457 <_short>tracking delay between mouse/keyboard modes</_short>
451 <_long>Only attempt to center newly focused windows if the mouse hasn't moved in this amount of seconds. Use this to avoid jumping when using sloppy focus.</_long>458 <_long>Only attempt to follow focus if the mouse hasn't moved in this amount of seconds. Use this to avoid jumping everywhere on the screen.</_long>
452 <default>0</default>459 <default>1</default>
453 <min>0</min>460 <min>0</min>
454 <max>15</max>461 <max>15</max>
455 </option>462 </option>
463 <option type="bool" name="always_center_mouse">
464 <_short>Keep Mouse centered inside zoom area</_short>
465 <_long>If activated, the mouse will always be centered when in mouse sync mode, if not, it will move around the zoom area</_long>
466 <default>true</default>
467 </option>
468 <option type="bool" name="restrain_zoom_to_screen">
469 <_short>Restrain the zoom area inside the screen</_short>
470 <_long>if enabled, zoom area will stay inside the screen, if not, zoom area can go over screen borders (it will show empty black space)</_long>
471 <default>true</default>
472 </option>
473 <option type="bool" name="warp_mouse_to_focus">
474 <_short>when switching focus back to mouse, warp mouse inside the zoomed area</_short>
475 <_long>when following a keyboard focus, one can want the mouse to come back into the field of vision before moving it. avoids jumping to another part of the screen when switching mouse/keyboard focus.</_long>
476 <default>true</default>
477 </option>
456 </group>478 </group>
457 <group>479 <group>
458 <_short>Animation</_short>480 <_short>Animation</_short>
@@ -461,7 +483,7 @@
461 <_long>Zoom Speed</_long>483 <_long>Zoom Speed</_long>
462 <default>25</default>484 <default>25</default>
463 <min>0.1</min>485 <min>0.1</min>
464 <max>100</max>486 <max>500</max>
465 <precision>0.1</precision>487 <precision>0.1</precision>
466 </option>488 </option>
467 <option type="float" name="timestep">489 <option type="float" name="timestep">
@@ -472,6 +494,11 @@
472 <max>50</max>494 <max>50</max>
473 <precision>0.1</precision>495 <precision>0.1</precision>
474 </option>496 </option>
497 <option type="bool" name="instant_pan">
498 <_short>move the zoom area instantly</_short>
499 <_long>when enabled, zoom area is warped instantly to focus position. When disabled, zoom area makes a smooth move to the focus position</_long>
500 <default>false</default>
501 </option>
475 </group>502 </group>
476 </options>503 </options>
477 </plugin>504 </plugin>
478505
=== modified file 'plugins/ezoom/src/ezoom.cpp'
--- plugins/ezoom/src/ezoom.cpp 2016-07-01 15:37:19 +0000
+++ plugins/ezoom/src/ezoom.cpp 2017-04-28 09:59:04 +0000
@@ -76,7 +76,6 @@
7676
77COMPIZ_PLUGIN_20090315 (ezoom, ZoomPluginVTable)77COMPIZ_PLUGIN_20090315 (ezoom, ZoomPluginVTable)
7878
79
80/*79/*
81 * This toggles paint functions. We don't need to continually run code when we80 * This toggles paint functions. We don't need to continually run code when we
82 * are not doing anything.81 * are not doing anything.
@@ -573,18 +572,45 @@
573 int y,572 int y,
574 bool instant)573 bool instant)
575{574{
575 setCenter (x, y, instant, false);
576}
577
578void
579EZoomScreen::setCenter (int x,
580 int y,
581 bool instant,
582 bool center)
583{
576 int out = screen->outputDeviceForPoint (x, y);584 int out = screen->outputDeviceForPoint (x, y);
577 CompOutput *o = &screen->outputDevs ().at (out);585 CompOutput *o = &screen->outputDevs ().at (out);
578586
579 if (zooms.at (out).locked)587 if (zooms.at (out).locked)
580 return;588 return;
581589
582 zooms.at (out).xTranslate = (float)590 // this correction allows constant centering of the mouse
583 ((x - o->x1 ()) - o->width () / 2) / (o->width ());591 float zoomCorrection = std::max(1.0f - zooms.at (out).newZoom, 0.1f);
584 zooms.at (out).yTranslate = (float)592 if (!center) {
585 ((y - o->y1 ()) - o->height () / 2) / (o->height ());593 zoomCorrection = EZOOM_SCREEN_BORDER_OFFSET;
586594 }
587 if (instant)595
596 float zoom_x_inside = (float) (x - o->x1 () - o->width () / 2);
597 // We need the zoom correction to center the mouse in zoomarea
598 zoom_x_inside /= o->width () * zoomCorrection;
599 float zoom_y_inside = (float) (y - o->y1 () - o->height () / 2);
600 zoom_y_inside /= o->height () * zoomCorrection;
601
602 if (optionGetRestrainZoomToScreen ()) {
603 if (zoom_x_inside < -0.5f) { zoom_x_inside = -0.5f;}
604 if (zoom_x_inside > 0.5f) { zoom_x_inside = 0.5f;}
605 if (zoom_y_inside < -0.5f) { zoom_y_inside = -0.5f;}
606 if (zoom_y_inside > 0.5f) { zoom_y_inside = 0.5f;}
607 }
608
609 zooms.at (out).xTranslate = zoom_x_inside;
610 zooms.at (out).yTranslate = zoom_y_inside;
611
612
613 if (instant || optionGetInstantPan ())
588 {614 {
589 zooms.at (out).realXTranslate = zooms.at (out).xTranslate;615 zooms.at (out).realXTranslate = zooms.at (out).xTranslate;
590 zooms.at (out).realYTranslate = zooms.at (out).yTranslate;616 zooms.at (out).realYTranslate = zooms.at (out).yTranslate;
@@ -671,11 +697,23 @@
671void697void
672EZoomScreen::enableMousePolling ()698EZoomScreen::enableMousePolling ()
673{699{
674 pollHandle.start ();700 pollMouseHandle.start ();
675 lastChange = time(NULL);701 lastMouseChange = time (NULL);
676 mouse = MousePoller::getCurrentPosition ();702 mouse = MousePoller::getCurrentPosition ();
677}703}
678704
705/* Enables polling of focus position */
706void
707EZoomScreen::enableFocusPolling ()
708{
709 if (!optionGetFollowFocus ())
710 {
711 return;
712 }
713 pollFocusHandle.start ();
714 lastFocusChange = time (NULL);
715}
716
679/* Sets the zoom (or scale) level.717/* Sets the zoom (or scale) level.
680 * Cleans up if we are suddenly zoomed out.718 * Cleans up if we are suddenly zoomed out.
681 */719 */
@@ -690,9 +728,12 @@
690 value = 1.0f;728 value = 1.0f;
691 else729 else
692 {730 {
693 if (!pollHandle.active ())731 if (!pollMouseHandle.active ())
694 enableMousePolling ();732 enableMousePolling ();
695733
734 if (!pollFocusHandle.active ())
735 enableFocusPolling ();
736
696 grabbed |= (1 << zooms.at (out).output);737 grabbed |= (1 << zooms.at (out).output);
697 cursorZoomActive (out);738 cursorZoomActive (out);
698 }739 }
@@ -738,7 +779,7 @@
738{779{
739 int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());780 int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());
740781
741 if (!isInMovement (out))782 if (!isInMovement (out) || nonMouseFocusTracking)
742 return;783 return;
743784
744 CompOutput *o = &screen->outputDevs ().at (out);785 CompOutput *o = &screen->outputDevs ().at (out);
@@ -1026,7 +1067,7 @@
10261067
1027 if (zooms.at (out).currentZoom == 1.0f)1068 if (zooms.at (out).currentZoom == 1.0f)
1028 {1069 {
1029 lastChange = time(NULL);1070 lastMouseChange = time (NULL);
1030 mouse = MousePoller::getCurrentPosition ();1071 mouse = MousePoller::getCurrentPosition ();
1031 }1072 }
10321073
@@ -1097,20 +1138,57 @@
1097void1138void
1098EZoomScreen::updateMousePosition (const CompPoint &p)1139EZoomScreen::updateMousePosition (const CompPoint &p)
1099{1140{
1141 auto localTime = time (NULL);
1100 mouse.setX (p.x ());1142 mouse.setX (p.x ());
1101 mouse.setY (p.y ());1143 mouse.setY (p.y ());
11021144
1103 int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());1145 int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());
1104 lastChange = time(NULL);1146
11051147 if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse && !isInMovement (out))
1106 if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse &&1148 {
1107 !isInMovement (out))1149 if (optionGetWarpMouseToFocus ()) {
1108 setCenter (mouse.x (), mouse.y (), true);1150 // mouse taking back control of focus
11091151 if (lastMouseChange <= lastFocusChange) {
1152 nonMouseFocusTracking = false;
1153 screen->warpPointer (mouse.x (), mouse.y ());
1154 restrainCursor (out);
1155 } else {
1156 setCenter (mouse.x (), mouse.y (), true, optionGetAlwaysCenterMouse ());
1157 }
1158 lastMouseChange = localTime;
1159 // respect a timing in case user has merely grazed his mouse
1160 } else if (localTime - lastFocusChange > optionGetFollowFocusDelay ()) {
1161 setCenter (mouse.x (), mouse.y (), true, optionGetAlwaysCenterMouse ());
1162 lastMouseChange = localTime;
1163 }
1164 }
1110 cursorMoved ();1165 cursorMoved ();
1111 cScreen->damageScreen ();1166 cScreen->damageScreen ();
1112}1167}
11131168
1169void
1170EZoomScreen::updateFocusPosition (const CompRect &rect)
1171{
1172 auto localTime = time (NULL);
1173 int out = screen->outputDeviceForPoint (rect.x (), rect.y ());
1174 if (localTime - lastMouseChange > optionGetFollowFocusDelay ()) {
1175 int zoom_area_width = screen->outputDevs ().at (out).width () * zooms.at (out).currentZoom;
1176 int zoom_area_height = screen->outputDevs ().at (out).height () * zooms.at (out).currentZoom;
1177 int pos_x = rect.x ()+ rect.width () / 2;
1178 int pos_y = rect.y () + rect.height () / 2;
1179 if (rect.width () > zoom_area_width) { // corrects a too big target, aims at top-left corner
1180 pos_x -= (rect.width () - zoom_area_width) / 2;
1181 }
1182 if (rect.height () > zoom_area_height) {
1183 pos_y -= (rect.height () - zoom_area_height) / 2;
1184 }
1185 setCenter (pos_x, pos_y, false, true);
1186 nonMouseFocusTracking = true;
1187 lastFocusChange = localTime;
1188 }
1189 cScreen->damageScreen ();
1190}
1191
1114/* Timeout handler to poll the mouse. Returns false (and thereby does not1192/* Timeout handler to poll the mouse. Returns false (and thereby does not
1115 * get re-added to the queue) when zoom is not active. */1193 * get re-added to the queue) when zoom is not active. */
1116void1194void
@@ -1122,11 +1200,21 @@
1122 {1200 {
1123 cursorMoved ();1201 cursorMoved ();
11241202
1125 if (pollHandle.active ())1203 if (pollMouseHandle.active ())
1126 pollHandle.stop ();1204 pollMouseHandle.stop ();
1127 }1205 }
1128}1206}
11291207
1208/* Timeout handler to focusPoll. */
1209void
1210EZoomScreen::updateFocusInterval (const CompRect &rect)
1211{
1212 updateFocusPosition (rect);
1213
1214 if (!grabbed && pollFocusHandle.active ())
1215 pollFocusHandle.stop ();
1216}
1217
1130/* Free a cursor */1218/* Free a cursor */
1131void1219void
1132EZoomScreen::freeCursor (CursorTexture *cursor)1220EZoomScreen::freeCursor (CursorTexture *cursor)
@@ -1878,8 +1966,8 @@
18781966
1879 if (w == NULL ||1967 if (w == NULL ||
1880 w->id () == screen->activeWindow () ||1968 w->id () == screen->activeWindow () ||
1881 time(NULL) - lastChange < optionGetFollowFocusDelay () ||1969 time (NULL) - lastMouseChange < optionGetFollowFocusDelay () ||
1882 !optionGetFollowFocus ())1970 !optionGetFollowWindowFocus ())
1883 return;1971 return;
18841972
1885 int out = screen->outputDeviceForGeometry (w->geometry ());1973 int out = screen->outputDeviceForGeometry (w->geometry ());
@@ -1972,7 +2060,9 @@
1972 gScreen (GLScreen::get (screen)),2060 gScreen (GLScreen::get (screen)),
1973 grabbed (0),2061 grabbed (0),
1974 grabIndex (0),2062 grabIndex (0),
1975 lastChange (0),2063 lastMouseChange (0),
2064 lastFocusChange (0),
2065 nonMouseFocusTracking (false),
1976 cursorInfoSelected (false),2066 cursorInfoSelected (false),
1977 cursorHidden (false)2067 cursorHidden (false)
1978{2068{
@@ -2006,8 +2096,10 @@
2006 zooms.push_back (za);2096 zooms.push_back (za);
2007 }2097 }
20082098
2009 pollHandle.setCallback (boost::bind (2099 pollMouseHandle.setCallback (boost::bind(&EZoomScreen::updateMouseInterval,
2010 &EZoomScreen::updateMouseInterval, this, _1));2100 this, _1));
2101 pollFocusHandle.setCallback (boost::bind(&EZoomScreen::updateFocusInterval,
2102 this, _1));
20112103
2012 optionSetZoomInButtonInitiate (boost::bind (&EZoomScreen::zoomIn, this, _1,2104 optionSetZoomInButtonInitiate (boost::bind (&EZoomScreen::zoomIn, this, _1,
2013 _2, _3));2105 _2, _3));
@@ -2100,8 +2192,10 @@
21002192
2101EZoomScreen::~EZoomScreen ()2193EZoomScreen::~EZoomScreen ()
2102{2194{
2103 if (pollHandle.active ())2195 if (pollMouseHandle.active ())
2104 pollHandle.stop ();2196 pollMouseHandle.stop ();
2197 if (pollFocusHandle.active ())
2198 pollFocusHandle.stop ();
21052199
2106 if (zooms.size ())2200 if (zooms.size ())
2107 zooms.clear ();2201 zooms.clear ();
@@ -2116,7 +2210,8 @@
2116 if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) &&2210 if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) &&
2117 CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) &&2211 CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) &&
2118 CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI) &&2212 CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI) &&
2119 CompPlugin::checkPluginABI ("mousepoll", COMPIZ_MOUSEPOLL_ABI))2213 CompPlugin::checkPluginABI ("mousepoll", COMPIZ_MOUSEPOLL_ABI) &&
2214 CompPlugin::checkPluginABI ("focuspoll", COMPIZ_FOCUSPOLL_ABI))
2120 return true;2215 return true;
21212216
2122 return false;2217 return false;
21232218
=== modified file 'plugins/ezoom/src/ezoom.h'
--- plugins/ezoom/src/ezoom.h 2016-07-01 15:37:19 +0000
+++ plugins/ezoom/src/ezoom.h 2017-04-28 09:59:04 +0000
@@ -43,12 +43,15 @@
43#include <composite/composite.h>43#include <composite/composite.h>
44#include <opengl/opengl.h>44#include <opengl/opengl.h>
45#include <mousepoll/mousepoll.h>45#include <mousepoll/mousepoll.h>
46#include <focuspoll/focuspoll.h>
4647
4748
48#include "ezoom_options.h"49#include "ezoom_options.h"
4950
50#include <cmath>51#include <cmath>
5152
53#define EZOOM_SCREEN_BORDER_OFFSET 0.92
54
52enum SpecificZoomTarget55enum SpecificZoomTarget
53{56{
54 ZoomTarget1 = 0,57 ZoomTarget1 = 0,
@@ -169,16 +172,19 @@
169 CompPoint mouse; // we get this from mousepoll172 CompPoint mouse; // we get this from mousepoll
170 unsigned long int grabbed;173 unsigned long int grabbed;
171 CompScreen::GrabHandle grabIndex; // for zoomBox174 CompScreen::GrabHandle grabIndex; // for zoomBox
172 time_t lastChange;175 time_t lastMouseChange;
176 time_t lastFocusChange;
173 CursorTexture cursor; // the texture for the faux-cursor177 CursorTexture cursor; // the texture for the faux-cursor
174 // we paint to do fake input178 // we paint to do fake input
175 // handling179 // handling
180 bool nonMouseFocusTracking;
176 bool cursorInfoSelected;181 bool cursorInfoSelected;
177 bool cursorHidden;182 bool cursorHidden;
178 CompRect box;183 CompRect box;
179 CompPoint clickPos;184 CompPoint clickPos;
180185
181 MousePoller pollHandle; // mouse poller object186 MousePoller pollMouseHandle; // mouse poller object
187 FocusPoller pollFocusHandle; // focus poller object
182188
183 private:189 private:
184190
@@ -231,6 +237,11 @@
231 setCenter (int x,237 setCenter (int x,
232 int y,238 int y,
233 bool instant);239 bool instant);
240 void
241 setCenter (int x,
242 int y,
243 bool instant,
244 bool center);
234245
235 void246 void
236 setZoomArea (int x,247 setZoomArea (int x,
@@ -250,6 +261,9 @@
250 enableMousePolling ();261 enableMousePolling ();
251262
252 void263 void
264 enableFocusPolling ();
265
266 void
253 setScale (int out,267 setScale (int out,
254 float value);268 float value);
255269
@@ -295,6 +309,12 @@
295 void309 void
296 updateMouseInterval (const CompPoint &p);310 updateMouseInterval (const CompPoint &p);
297311
312 void
313 updateFocusPosition (const CompRect &retc);
314
315 void
316 updateFocusInterval (const CompRect &rect);
317
298 /* Make dtor */318 /* Make dtor */
299 void319 void
300 freeCursor (CursorTexture * cursor);320 freeCursor (CursorTexture * cursor);
301321
=== added directory 'plugins/focuspoll'
=== added file 'plugins/focuspoll/CMakeLists.txt'
--- plugins/focuspoll/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/CMakeLists.txt 2017-04-28 09:59:04 +0000
@@ -0,0 +1,9 @@
1find_package (Compiz REQUIRED)
2
3include (CompizPlugin)
4include_directories (include/accessibilitywatcher)
5
6pkg_search_module( ATSPI REQUIRED atspi-2 )
7
8compiz_plugin (focuspoll PKGDEPS atspi-2 )
9
010
=== added file 'plugins/focuspoll/compiz-focuspoll.pc.in'
--- plugins/focuspoll/compiz-focuspoll.pc.in 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/compiz-focuspoll.pc.in 2017-04-28 09:59:04 +0000
@@ -0,0 +1,12 @@
1prefix=@prefix@
2exec_prefix=@exec_prefix@
3libdir=@libdir@
4includedir=@includedir@
5
6Name: compiz-focuspoll
7Description: Focuspoll plugin for compiz
8Version: @VERSION@
9
10Requires: compiz
11Libs: -L${libdir}/compiz -lfocuspoll
12Cflags: @COMPIZ_CFLAGS@ -I${includedir}/compiz
013
=== added file 'plugins/focuspoll/focuspoll.xml.in'
--- plugins/focuspoll/focuspoll.xml.in 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/focuspoll.xml.in 2017-04-28 09:59:04 +0000
@@ -0,0 +1,34 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<compiz>
3 <plugin name="focuspoll" useBcop="true">
4 <_short>Focus position polling</_short>
5 <_long>Updates the focus pointer position from the accessibility library AT-SPI through dbus</_long>
6 <category>Utility</category>
7 <options>
8 <_short>Misc</_short>
9 <option type="bool" name="track_components">
10 <_short>track movement of components (like a menu, label, or button)</_short>
11 <_long>if enabled, focuspoll will track the movements of every component able to provide focus.</_long>
12 <default>true</default>
13 </option>
14 <option type="bool" name="track_caret">
15 <_short>track movement of the caret (the cursor in text)</_short>
16 <_long>if enabled, focuspoll will track the movements of the caret.</_long>
17 <default>true</default>
18 </option>
19 <option type="bool" name="ignore_links">
20 <_short>ignores link focuses</_short>
21 <_long>if enabled, focuspoll will not track the focus events of link items</_long>
22 <default>true</default>
23 </option>
24 <option type="int" name="focus_poll_interval">
25 <_short>Focus Poll Interval</_short>
26 <_long>How often to poll DBus for focus changes, in milliseconds. Reduce this to reduce choppy behavior.</_long>
27 <default>10</default>
28 <min>1</min>
29 <max>500</max>
30 </option>
31 </options>
32 </plugin>
33</compiz>
34
035
=== added directory 'plugins/focuspoll/include'
=== added directory 'plugins/focuspoll/include/accessibilitywatcher'
=== added file 'plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h'
--- plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h 2017-04-28 09:59:04 +0000
@@ -0,0 +1,82 @@
1/*
2 * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net>
3 *
4 * This file is part of compiz.
5 *
6 * this program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef ACCESSIBILITY_WATCHER_H
21#define ACCESSIBILITY_WATCHER_H
22
23#include <deque>
24#include <vector>
25
26#include "focusinfo.h"
27
28#include <atspi/atspi.h>
29
30class AccessibilityWatcher {
31 public:
32 ~AccessibilityWatcher();
33 static AccessibilityWatcher* getInstance();
34
35 void init();
36 void quit();
37
38 void setIgnoreLinks(bool);
39 void setScreenLimits(int, int);
40 int getScreenWidth();
41 int getScreenHeight();
42
43 std::deque<FocusInfo> getFocusQueue();
44 void resetFocusQueue();
45 bool returnToPrevMenu();
46
47 private:
48 AccessibilityWatcher();
49 static AccessibilityWatcher* instance;
50
51 bool initialized;
52 int screenWidth;
53 int screenHeight;
54 static bool ignoreLinks;
55 std::deque<FocusInfo> focusList;
56 std::vector<FocusInfo> previouslyActiveMenus;
57
58 AtspiEventListener *focusListener;
59 AtspiEventListener *caretMoveListener;
60 AtspiEventListener *selectedListener;
61 AtspiEventListener *descendantChangedListener;
62
63 void addWatches();
64 void removeWatches();
65
66 static std::string getLabel(AtspiAccessible *accessible);
67 const static gchar* atspiStateGetName (gint state);
68 static std::string getStateSet (AtspiAccessible *accessible);
69
70 static void registerEvent(const AtspiEvent *event, const std::string& type);
71 static bool appSpecificFilter(FocusInfo& focusInfo, const AtspiEvent* event);
72 static bool filterBadEvents(const FocusInfo& event);
73 static void getAlternativeCaret(FocusInfo& focus, const AtspiEvent* event);
74
75 static void onFocus(const AtspiEvent *event, void *data);
76 static void onSelectedChange(const AtspiEvent *event, void *data);
77 static void onCaretMove(const AtspiEvent *event, void *data);
78 static void onDescendantChanged(const AtspiEvent *event, void *data);
79
80};
81
82#endif
083
=== added file 'plugins/focuspoll/include/accessibilitywatcher/focusinfo.h'
--- plugins/focuspoll/include/accessibilitywatcher/focusinfo.h 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/include/accessibilitywatcher/focusinfo.h 2017-04-28 09:59:04 +0000
@@ -0,0 +1,76 @@
1/*
2 * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net>
3 *
4 * This file is part of compiz.
5 *
6 * this program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef FOCUS_INFO_H
21#define FOCUS_INFO_H
22
23#include <tuple>
24#include <string>
25#include <sstream>
26
27
28class FocusInfo {
29 public:
30
31 FocusInfo(const std::string& type,
32 const std::string& name,
33 const std::string& label,
34 const std::string& role,
35 const std::string& application,
36 int x,
37 int y,
38 int width,
39 int height);
40
41 FocusInfo(const std::string& type,
42 const std::string& name,
43 const std::string& label,
44 const std::string& role,
45 const std::string& application);
46
47 FocusInfo();
48
49 int x, y, w, h;
50 int xAlt, yAlt, wAlt, hAlt;
51 std::string type;
52 std::string name;
53 std::string label;
54 std::string role;
55 std::string application;
56
57 // AT-SPI events that are interesting to know about the event
58 bool enabled;
59 bool active;
60 bool focusable;
61 bool focused;
62 bool selected;
63 bool showing;
64 bool visible;
65
66 std::string getType();
67 std::string summary();
68 std::tuple<int, int> getPosition();
69 std::tuple<int, int> getSize();
70 std::tuple<int, int, int, int> getBBox();
71
72 bool operator== (const FocusInfo& other) const;
73 bool operator!= (const FocusInfo& other) const;
74};
75
76#endif
077
=== added directory 'plugins/focuspoll/include/focuspoll'
=== added file 'plugins/focuspoll/include/focuspoll/focuspoll.h'
--- plugins/focuspoll/include/focuspoll/focuspoll.h 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/include/focuspoll/focuspoll.h 2017-04-28 09:59:04 +0000
@@ -0,0 +1,50 @@
1/*
2 * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net>
3 *
4 * This file is part of compiz.
5 *
6 * this program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifndef _COMPIZ_FOCUSPOLL_H
21#define _COMPIZ_FOCUSPOLL_H
22
23#define COMPIZ_FOCUSPOLL_ABI 1
24
25#include <boost/function.hpp>
26#include <core/rect.h>
27#include <accessibilitywatcher/focusinfo.h>
28
29class FocusPoller
30{
31 public:
32 typedef boost::function<void(const CompRect &)> CallBack;
33
34 FocusPoller ();
35 ~FocusPoller ();
36
37 void setCallback (CallBack callback);
38 void start ();
39 void stop ();
40 bool active ();
41
42 private:
43 bool mActive;
44 CallBack mCallback;
45 CompRect focusRect;
46
47 friend class FocuspollScreen;
48};
49
50#endif
051
=== added directory 'plugins/focuspoll/src'
=== added file 'plugins/focuspoll/src/accessibilitywatcher.cpp'
--- plugins/focuspoll/src/accessibilitywatcher.cpp 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/src/accessibilitywatcher.cpp 2017-04-28 09:59:04 +0000
@@ -0,0 +1,506 @@
1/*
2 * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net>
3 *
4 * This file is part of compiz.
5 *
6 * this program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <iostream>
21#include <functional>
22
23#include <boost/mem_fn.hpp>
24#include <boost/bind.hpp>
25#include <boost/function.hpp>
26
27#include <string.h>
28#include <stdlib.h>
29#include <unistd.h>
30
31#include "accessibilitywatcher.h"
32
33namespace {
34const int A11YWATCHER_MAX_CARET_WIDTH = 50;
35const int A11YWATCHER_MAX_CARET_HEIGHT = 70;
36}
37
38AccessibilityWatcher* AccessibilityWatcher::instance = NULL;
39bool AccessibilityWatcher::ignoreLinks = false;
40
41AccessibilityWatcher::AccessibilityWatcher() :
42 initialized(false),
43 screenWidth(0),
44 screenHeight(0)
45{
46}
47
48AccessibilityWatcher::~AccessibilityWatcher() {
49 quit();
50};
51
52AccessibilityWatcher *AccessibilityWatcher::getInstance () {
53 if (instance == NULL)
54 {
55 instance = new AccessibilityWatcher();
56 }
57 return instance;
58}
59
60std::string AccessibilityWatcher::getLabel(AtspiAccessible *accessible) {
61 GArray *relations = atspi_accessible_get_relation_set (accessible, NULL);
62 if (relations == NULL)
63 {
64 return "";
65 }
66
67 AtspiRelation *relation;
68 for (unsigned i = 0; i < relations->len; ++i) {
69 relation = g_array_index (relations, AtspiRelation*, i);
70 if (atspi_relation_get_relation_type (relation) == ATSPI_RELATION_LABELLED_BY)
71 {
72 g_array_free (relations, true);
73 g_object_unref(relations);
74 auto res_label = atspi_accessible_get_name(atspi_relation_get_target(relation, 0), NULL);
75 g_object_unref(relation);
76 return (res_label == NULL) ? "" : res_label;
77 }
78 }
79 if (relations->len > 0)
80 {
81 g_object_unref(relations);
82 }
83 return "";
84}
85
86void AccessibilityWatcher::setIgnoreLinks(bool val) {
87 ignoreLinks = val;
88}
89
90void AccessibilityWatcher::setScreenLimits(int x, int y) {
91 screenWidth = x;
92 screenHeight = y;
93}
94
95int AccessibilityWatcher::getScreenWidth () {
96 return screenWidth;
97}
98
99int AccessibilityWatcher::getScreenHeight () {
100 return screenHeight;
101}
102
103void AccessibilityWatcher::registerEvent(const AtspiEvent *event, const std::string &type) {
104 // type is registered from filter on calling event
105 FocusInfo res(type,
106 atspi_accessible_get_name (event->source, NULL),
107 getLabel (event->source),
108 atspi_accessible_get_role_name (event->source, NULL),
109 atspi_accessible_get_name(atspi_accessible_get_application (event->source, NULL), NULL));
110
111 if (!res.active)
112 {
113 // prevents skipping events that are not designated as active. we check the parent.
114 auto parent = atspi_accessible_get_parent(event->source, NULL);
115 while (!res.active && parent) {
116 auto stateSet = atspi_accessible_get_state_set (child);
117 if (atspi_state_set_contains(stateSet, ATSPI_STATE_ACTIVE))
118 {
119 res.active = true;
120 }
121 g_object_unref(stateSet);
122 g_object_unref(parent);
123 child = atspi_accessible_get_parent(child, NULL);
124 }
125 g_object_unref(parent);
126 }
127
128 AtspiComponent* component;
129 if (res.type == "active-descendant-changed")
130 {
131 auto accessible = atspi_accessible_get_child_at_index(event->source, event->detail1, NULL);
132 component = atspi_accessible_get_component(accessible);
133 g_object_unref(accessible);
134 } else {
135 component = atspi_accessible_get_component(event->source);
136 }
137
138 if (component)
139 {
140 AtspiRect* size = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL);
141 res.x = size->x;
142 res.y = size->y;
143 res.w = size->width;
144 res.h = size->height;
145 g_object_unref(component);
146 }
147 // let's get the caret offset, and then its position for a caret event
148 if (type == "caret")
149 {
150 auto text = atspi_accessible_get_text(event->source);
151 if (!text)
152 {
153 return;
154 }
155 auto offset = atspi_text_get_caret_offset(text, NULL);
156 // if we're at the beginning of text, let the widget pos decide
157 if (event->detail1)
158 {
159 AtspiRect *size = atspi_text_get_character_extents(text, offset, ATSPI_COORD_TYPE_SCREEN, NULL);
160 res.x = size->x;
161 res.y = size->y;
162 res.w = size->width;
163 res.h = size->height;
164 }
165 // correcting a missing offset to a caret move event in firefox
166 if (res.x == 0 && res.y == 0 && offset > 0)
167 {
168 AtspiRect *size = atspi_text_get_character_extents(text, offset-1, ATSPI_COORD_TYPE_SCREEN, NULL);
169 res.x = size->x;
170 res.y = size->y;
171 res.w = size->width;
172 res.h = size->height;
173 }
174 // when result is obviously not a caret size
175 if (strcmp(event->type, "object:text-caret-moved") == 0 && (res.w > A11YWATCHER_MAX_CARET_WIDTH || res.h > A11YWATCHER_MAX_CARET_HEIGHT))
176 {
177 AtspiRect *size = atspi_text_get_character_extents(text, offset, ATSPI_COORD_TYPE_SCREEN, NULL);
178 res.x = size->x;
179 res.y = size->y;
180 res.w = size->width;
181 res.h = size->height;
182 if (type == "caret" && strcmp(event->type, "object:text-caret-moved") == 0 && (res.w > A11YWATCHER_MAX_CARET_WIDTH || res.h > A11YWATCHER_MAX_CARET_HEIGHT))
183 {
184 res.x = 0;
185 res.y = 0;
186 }
187 }
188 g_object_unref(text);
189
190 // still no offset, it's probably a newline and we're at bugzilla #1319273 (with new paragraph obj)
191 if (res.x == 0 && res.y == 0 &&
192 (strcmp(event->type, "object:text-changed:insert") == 0 ||
193 strcmp(event->type, "object:text-changed:removed") == 0 ||
194 strcmp(event->type, "object:text-caret-moved") == 0)) {
195 res.x = res.xAlt;
196 res.y = res.yAlt;
197 res.w = res.wAlt;
198 res.h = res.hAlt;
199 }
200 }
201
202 // getting the states on event
203 auto stateSet = atspi_accessible_get_state_set (event->source);
204 if (atspi_state_set_contains(stateSet, ATSPI_STATE_FOCUSED))
205 {
206 res.focused = true;
207 AccessibilityWatcher::getInstance()->previouslyActiveMenus.clear(); // reset potential menu stack
208 }
209 if (atspi_state_set_contains(stateSet, ATSPI_STATE_SELECTED))
210 { // never works, GTK Bad implem?
211 res.selected = true;
212 }
213 if (res.type == "state-changed:selected" && event->detail1 == 1)
214 {
215 res.selected = true;
216 AccessibilityWatcher::getInstance()->previouslyActiveMenus.push_back(res); // add to stack of menus
217 }
218
219 if (appSpecificFilter(res, event))
220 {
221 return;
222 }
223 if (filterBadEvents(res))
224 {
225 return;
226 }
227 while (AccessibilityWatcher::getInstance()->focusList.size() >= 5) { // don't keep the whole history
228 AccessibilityWatcher::getInstance()->focusList.erase(AccessibilityWatcher::getInstance()->focusList.begin());
229 }
230 AccessibilityWatcher::getInstance()->focusList.push_back(res);
231}
232
233bool AccessibilityWatcher::appSpecificFilter(FocusInfo& focus, const AtspiEvent* event)
234{
235 if (focus.type == "state-changed:selected" && // emulates on-change:selected false behaviour. eg: for menus
236 (focus.role == "menu item" ||
237 focus.role == "menu" ||
238 focus.role == "check menu item" ||
239 focus.role == "radio menu item") &&
240 focus.application != "mate-panel")
241 {
242 if (!focus.selected && AccessibilityWatcher::getInstance()->returnToPrevMenu())
243 {
244 return true;
245 }
246 focus.active = true;
247 }
248 if (focus.application == "soffice" && focus.role == "paragraph")
249 { // LO-calc: avoid spam event from main edit line
250 auto parent = atspi_accessible_get_parent(event->source, NULL);
251 std::string parentLabel = atspi_accessible_get_name(parent, NULL);
252 if (parentLabel == "Input line" ||
253 parentLabel == "Ligne de saisie") {
254 return true;
255 }
256 }
257 if (focus.application == "Icedove" || focus.application == "Thunderbird")
258 {
259 if (focus.type == "caret")
260 {
261 auto text = atspi_accessible_get_text(event->source); // next if deals with a special newline char, that remained buggy. hypra issue #430
262 auto offset = atspi_text_get_caret_offset(text, NULL);
263 auto string = std::string(atspi_text_get_text_at_offset(text, offset, ATSPI_TEXT_BOUNDARY_CHAR, NULL)->content);
264 auto stringM1 = std::string(atspi_text_get_text_at_offset(text, offset -1, ATSPI_TEXT_BOUNDARY_CHAR, NULL)->content);
265 if (offset == atspi_text_get_character_count(text, NULL) && string == "\0" && (stringM1 == "\n" || int(stringM1.c_str()[0]) == -17))
266 {
267 getAlternativeCaret(focus, event);
268 focus.x = focus.xAlt;
269 focus.y = focus.yAlt + focus.hAlt;
270 focus.w = focus.wAlt;
271 focus.h = focus.hAlt;
272 }
273 if (!(focus.x == 0 && focus.y == 0))
274 { // prevents compose window loss of tracking in HTML mode (active flag ok, but no focused flag)
275 AccessibilityWatcher::getInstance()->focusList.push_back(focus);
276 return true;
277 }
278 auto component = atspi_accessible_get_component(event->source);
279 if (component)
280 {
281 AtspiRect* size = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL);
282 focus.x = size->x;
283 focus.y = size->y;
284 focus.w = 7;
285 focus.h = size->height;
286 AccessibilityWatcher::getInstance()->focusList.push_back(focus);
287 return true;
288 }
289 }
290 }
291 if (focus.application == "Firefox")
292 {
293 if (ignoreLinks && focus.type != "caret" && focus.role == "link")
294 {
295 return true;
296 }
297 if (focus.type == "caret" &&
298 (static_cast<std::string>(event->type) == "object:text-changed:insert:system" ||
299 static_cast<std::string>(event->type) == "object:text-changed:delete:system")) { // prevents status bar focus in firefox
300 return true;
301 }
302 if (focus.type == "focus" && focus.role == "document frame")
303 { // general page parasite event
304 return true;
305 }
306 if (focus.type == "caret" && !(focus.x == 0 && focus.y == 0))
307 {
308 AccessibilityWatcher::getInstance()->focusList.push_back(focus);
309 return true;
310 }
311 getAlternativeCaret(focus, event);
312 if (focus.type == "caret" && !(focus.xAlt == 0 && focus.yAlt == 0))
313 {
314 focus.x = focus.xAlt;
315 focus.y = focus.yAlt + focus.hAlt;
316 focus.w = focus.wAlt;
317 focus.h = focus.hAlt;
318 AccessibilityWatcher::getInstance()->focusList.push_back(focus);
319 return true;
320 }
321 }
322 if (focus.application == "evince" && focus.type == "state-changed:selected" && focus.role == "icon")
323 { // LO-calc: avoid spam event from main edit line
324 return true; // ignores the parasite event from evince icon
325 }
326 return false;
327}
328
329bool AccessibilityWatcher::filterBadEvents(const FocusInfo& event) {
330 if (event.type == "caret" && event.x ==0 && event.y == 0)
331 {
332 return true;
333 }
334 if (!event.active)
335 {
336 return true;
337 }
338 if (!event.focused && !event.selected)
339 {
340 return true;
341 }
342 if (AccessibilityWatcher::getInstance()->getScreenWidth() != 0 && AccessibilityWatcher::getInstance()->getScreenHeight() !=0 &&
343 (event.x > AccessibilityWatcher::getInstance()->getScreenWidth() ||
344 event.y > AccessibilityWatcher::getInstance()->getScreenHeight() || // TODO remove when non-singleton
345 event.x < 0 ||
346 event.y < 0 )) {
347 return true;
348 }
349 return false;
350}
351
352/*
353 * this is meant to emulate the missing "selected" argument in menu hierarchy.
354 * no idea whether this is a fail in GTK, or in ATSPI
355 */
356bool AccessibilityWatcher::returnToPrevMenu() {
357 if (previouslyActiveMenus.size() > 1)
358 {
359 previouslyActiveMenus.pop_back();
360 focusList.push_back(previouslyActiveMenus.back());
361 return true;
362 }
363 return false;
364}
365
366/*
367 * Tries to extrapolate a missing caret position from other text characters.
368 * is used as last resort when application doesn't respect at-spi standarts,
369 * or at-spi bugs.
370 */
371void AccessibilityWatcher::getAlternativeCaret(FocusInfo& focus, const AtspiEvent* event) {
372 auto text = atspi_accessible_get_text(event->source);
373 if (!text) { return; }
374 auto offset = atspi_text_get_caret_offset(text, NULL);
375 auto caretChar = std::string(atspi_text_get_string_at_offset(text, offset, ATSPI_TEXT_GRANULARITY_CHAR, NULL)->content);
376 bool hasSeenDeviceControl1 = false;
377
378 // if we're at a newline, sometimes at-spi isn't giving us a caret position. unknown bug in some apps.
379 if (caretChar == "\n" || caretChar == "\0")
380 {
381 // gives the last empty line the right focus.
382 int lines = atspi_text_get_character_count(text, NULL) == offset ? 1 : 0;
383 int charIndex = 1;
384 bool charExtentsFound = false;
385
386 AtspiRect *size = atspi_text_get_character_extents(text, offset, ATSPI_COORD_TYPE_SCREEN, NULL);
387 // try and find the character on upper line to extrapolate position from. no more that 300 char, we risk lag.
388 while (!charExtentsFound && charIndex <= offset && charIndex < 300) {
389 size = atspi_text_get_character_extents(text, offset - charIndex, ATSPI_COORD_TYPE_SCREEN, NULL);
390 caretChar = atspi_text_get_string_at_offset(text, offset - charIndex, ATSPI_TEXT_GRANULARITY_CHAR, NULL)->content;
391 // if we found a caret, check we're at beginning of line (or of text) to extrapolate position
392 if ((size->x != 0 || size->y != 0) && int(caretChar[0]) != -17)
393 {
394 if (offset - charIndex -1 >= 0 && std::string(atspi_text_get_string_at_offset(text, offset - charIndex -1, ATSPI_TEXT_GRANULARITY_CHAR, NULL)->content) == "\n")
395 {
396 charExtentsFound = true; // first character of upper line has been found
397 }
398 else if (offset - charIndex -1 == 0)
399 {
400 size = atspi_text_get_character_extents(text, 0, ATSPI_COORD_TYPE_SCREEN, NULL);
401 // first character of upper line has been found
402 charExtentsFound = true;
403 }
404 }
405 else if (caretChar == "\n" || int(caretChar[0]) == -17)
406 {
407 ++lines;
408 }
409 if (!hasSeenDeviceControl1 && int(caretChar[0]) == -17 )
410 {
411 hasSeenDeviceControl1 = true;
412 }
413 ++charIndex;
414 }
415 if (!hasSeenDeviceControl1)
416 {
417 lines--;
418 }
419 focus.xAlt = size->x;
420 focus.yAlt = size->y + (lines-1) * size->height;
421 focus.wAlt = size->width;
422 focus.hAlt = size->height;
423 }
424}
425
426
427void AccessibilityWatcher::onCaretMove (const AtspiEvent *event, void *data) {
428 registerEvent(event, "caret");
429}
430
431void AccessibilityWatcher::onSelectedChange (const AtspiEvent *event, void *data) {
432 registerEvent(event, "state-changed:selected");
433}
434
435void AccessibilityWatcher::onFocus (const AtspiEvent *event, void *data) {
436 /* We only care about focus/selection gain
437 * there's no detail1 on focus loss in AT-SPI specs */
438 if (!event->detail1) {return;}
439
440 registerEvent(event, "focus");
441}
442
443void AccessibilityWatcher::onDescendantChanged (const AtspiEvent *event, void *data) {
444 registerEvent(event, "active-descendant-changed");
445}
446
447/* Register to events */
448void AccessibilityWatcher::addWatches() {
449
450 //auto callback = boost::bind (&AccessibilityWatcher::onFocus, this, _1, _2);
451 //auto bound_fn = boost::bind<void>( boost::mem_fn(&AccessibilityWatcher::onFocus), this, _1, _2);
452 //auto bound_fn = boost::bind( &AccessibilityWatcher::onFocus, this, _1, _2);
453 //boost::function<void (const AtspiEvent *event, void *data)> callback(functor, this);
454
455
456 //std::function<void (AtspiEvent*, void*)> bound_fn = std::bind(&AccessibilityWatcher::onFocus, this, std::placeholders::_1, std::placeholders::_2);
457 //using sig = void (AtspiEvent*, void*);
458 //using tamere = std::function<sig>;
459 //AtspiEventListenerCB bound_fn = std::bind(&AccessibilityWatcher::onFocus, this, std::placeholders::_1, std::placeholders::_2);
460 //focusListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(bound_fn), NULL, NULL);
461
462 focusListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(onFocus), NULL, NULL);
463 caretMoveListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(onCaretMove), NULL, NULL);
464 selectedListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(onSelectedChange), NULL, NULL);
465 descendantChangedListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(onDescendantChanged), NULL, NULL);
466
467 atspi_event_listener_register (focusListener, "object:state-changed:focused", NULL);
468 atspi_event_listener_register (caretMoveListener, "object:text-caret-moved", NULL);
469 atspi_event_listener_register (caretMoveListener, "object:text-changed:inserted", NULL);
470 atspi_event_listener_register (caretMoveListener, "object:text-changed:removed", NULL);
471 atspi_event_listener_register (selectedListener, "object:state-changed:selected", NULL);
472 atspi_event_listener_register (descendantChangedListener, "object:active-descendant-changed", NULL);
473}
474
475void AccessibilityWatcher::removeWatches() {
476 atspi_event_listener_deregister (focusListener, "object:state-changed:focused", NULL);
477 atspi_event_listener_deregister (caretMoveListener, "object:text-caret-moved", NULL);
478 atspi_event_listener_deregister (caretMoveListener, "object:text-changed:inserted", NULL);
479 atspi_event_listener_deregister (caretMoveListener, "object:text-changed:removed", NULL);
480 atspi_event_listener_deregister (selectedListener, "object:state-changed:selected", NULL);
481 atspi_event_listener_deregister (descendantChangedListener, "object:active-descendant-changed", NULL);
482}
483
484void AccessibilityWatcher::init() {
485 if (initialized) { return; }
486
487 atspi_init ();
488 atspi_set_main_context(g_main_context_default());
489 addWatches();
490
491 initialized = true;
492}
493
494void AccessibilityWatcher::quit() {
495 removeWatches();
496 initialized = false;
497}
498
499std::deque<FocusInfo> AccessibilityWatcher::getFocusQueue() {
500 return focusList;
501}
502
503void AccessibilityWatcher::resetFocusQueue() {
504 focusList.clear();
505}
506
0507
=== added file 'plugins/focuspoll/src/focuspoll.cpp'
--- plugins/focuspoll/src/focuspoll.cpp 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/src/focuspoll.cpp 2017-04-28 09:59:04 +0000
@@ -0,0 +1,220 @@
1/*
2 * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net>
3 *
4 * This file is part of compiz.
5 *
6 * this program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "private.h"
21#include <iostream>
22
23COMPIZ_PLUGIN_20090315 (focuspoll, FocuspollPluginVTable);
24
25bool
26FocuspollScreen::updatePosition ()
27{
28 if (!a11ywatcher->getFocusQueue().empty()) {
29 int focusX, focusY, focusW, focusH;
30 std::tie(focusX, focusY, focusW, focusH) = a11ywatcher->getFocusQueue().back().getBBox();
31 focusRect = CompRect(focusX, focusY, focusW, focusH);
32 // for (auto poller : pollers) segfaults at focuspoller.stop, reverting TODO
33 for (std::list<FocusPoller *>::iterator it = pollers.begin (); it != pollers.end ();) {
34 FocusPoller *poller = *it;
35 ++it;
36
37 poller->mCallback(focusRect);
38 }
39 }
40 a11ywatcher->resetFocusQueue();
41 return true;
42}
43
44bool
45FocuspollScreen::addTimer (FocusPoller *poller)
46{
47 bool start = pollers.empty ();
48
49 std::list<FocusPoller *>::iterator it =
50 std::find (pollers.begin (), pollers.end (), poller);
51
52 if (it != pollers.end ())
53 return false;
54
55 pollers.insert (it, poller);
56
57 if (start)
58 {
59 //focusHasMoved();
60 timer.start();
61 }
62
63 return true;
64}
65
66void
67FocuspollScreen::removeTimer (FocusPoller *poller)
68{
69 std::list<FocusPoller *>::iterator it =
70 std::find (pollers.begin(), pollers.end (), poller);
71
72 if (it == pollers.end ())
73 return;
74
75 pollers.erase (it);
76
77 if (pollers.empty ()) {
78 timer.stop ();
79 }
80}
81
82void
83FocusPoller::setCallback (FocusPoller::CallBack callback)
84{
85 bool wasActive = mActive;
86
87 if (mActive)
88 stop ();
89
90 mCallback = callback;
91
92 if (wasActive)
93 start ();
94}
95
96void
97FocusPoller::start ()
98{
99 FOCUSPOLL_SCREEN (screen);
100
101 if (!fs)
102 {
103 compLogMessage ("focuspoll", CompLogLevelWarn,
104 "Plugin version mismatch, can't start focus poller.");
105
106 return;
107 }
108
109 if (mCallback.empty ())
110 {
111 compLogMessage ("focuspoll", CompLogLevelWarn,
112 "Can't start focus poller without callback.");
113 return;
114 }
115
116 fs->addTimer (this);
117
118 mActive = true;
119}
120
121void
122FocusPoller::stop ()
123{
124 FOCUSPOLL_SCREEN (screen);
125
126 /* Prevent broken plugins from calling stop () twice */
127
128 if (!mActive)
129 return;
130
131 if (!fs)
132 {
133 compLogMessage ("focuspoll",
134 CompLogLevelWarn,
135 "Plugin version mismatch, can't stop focus poller.");
136 return;
137 }
138
139 mActive = false;
140 fs->removeTimer (this);
141}
142
143bool
144FocusPoller::active ()
145{
146 return mActive;
147}
148
149FocusPoller::FocusPoller () :
150 mActive (false),
151 mCallback (NULL) {
152 }
153
154std::tuple<int, int> FocuspollScreen::getScreenLimits() {
155 int x =0, y = 0;
156 for (auto dev : screen->outputDevs()) {
157 x = std::max(x, dev.x() + dev.width());
158 y = std::max(y, dev.y() + dev.height());
159 }
160 return std::make_tuple(x, y);
161}
162
163void
164FocuspollScreen::updateTimer ()
165{
166 float timeout = optionGetFocusPollInterval ();
167 timer.setTimes (timeout, timeout * 1.5);
168}
169
170FocusPoller::~FocusPoller ()
171{
172 if (mActive) {
173 stop();
174 }
175}
176
177void
178FocuspollScreen::setOptions()
179{
180 a11ywatcher->setIgnoreLinks(optionGetIgnoreLinks());
181}
182
183template class PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI>;
184
185FocuspollScreen::FocuspollScreen (CompScreen *screen) :
186 PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI> (screen)
187{
188 a11ywatcher = AccessibilityWatcher::getInstance();
189 a11ywatcher->init();
190
191 int screenLimitX, screenLimitY;
192 std::tie(screenLimitX, screenLimitY) = getScreenLimits();
193 a11ywatcher->setScreenLimits(screenLimitX, screenLimitY);
194
195 updateTimer ();
196 timer.setCallback (boost::bind (&FocuspollScreen::updatePosition, this));
197 optionSetFocusPollIntervalNotify (boost::bind (&FocuspollScreen::updateTimer, this));
198
199 optionSetIgnoreLinksNotify (boost::bind (&FocuspollScreen::setOptions, this));
200
201}
202
203bool
204FocuspollPluginVTable::init ()
205{
206 if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))
207 {
208 CompPrivate p;
209 p.uval = COMPIZ_FOCUSPOLL_ABI;
210 screen->storeValue ("focuspoll_ABI", p);
211 return true;
212 }
213 return false;
214}
215
216void
217FocuspollPluginVTable::fini ()
218{
219 screen->eraseValue ("focuspoll_ABI");
220}
0221
=== added file 'plugins/focuspoll/src/private.h'
--- plugins/focuspoll/src/private.h 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/src/private.h 2017-04-28 09:59:04 +0000
@@ -0,0 +1,75 @@
1/*
2 * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net>
3 *
4 * This file is part of compiz.
5 *
6 * this program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option) any
9 * later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 * details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <tuple>
21#include <core/core.h>
22#include <core/pluginclasshandler.h>
23#include <core/timer.h>
24
25#include <focuspoll/focuspoll.h>
26#include <accessibilitywatcher/accessibilitywatcher.h>
27
28#include "focuspoll_options.h"
29
30typedef enum _FocuspollOptions
31{
32 MP_DISPLAY_OPTION_FOCUS_POLL_INTERVAL,
33 MP_DISPLAY_OPTION_NUM
34} FocuspollDisplayOptions;
35
36class FocuspollScreen;
37extern template class PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI>;
38
39class FocuspollScreen :
40 public PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI>,
41 public FocuspollOptions
42{
43 public:
44 FocuspollScreen (CompScreen *screen);
45
46 std::list<FocusPoller *> pollers;
47 CompTimer timer;
48 CompTimer settingsTimer;
49 CompRect focusRect;
50
51 bool updatePosition ();
52 bool addTimer (FocusPoller *poller);
53 void removeTimer (FocusPoller *poller);
54 void updateTimer ();
55
56 private:
57 AccessibilityWatcher* a11ywatcher;
58
59 std::tuple<int, int> getScreenLimits();
60 void setOptions();
61
62};
63
64#define FOCUSPOLL_SCREEN(s) \
65 FocuspollScreen *fs = FocuspollScreen::get (s)
66
67#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption))
68
69class FocuspollPluginVTable :
70 public CompPlugin::VTableForScreen<FocuspollScreen, COMPIZ_FOCUSPOLL_ABI>
71{
72 public:
73 bool init ();
74 void fini ();
75};
076
=== modified file 'plugins/showmouse/showmouse.xml.in'
--- plugins/showmouse/showmouse.xml.in 2016-05-31 17:30:42 +0000
+++ plugins/showmouse/showmouse.xml.in 2017-04-28 09:59:04 +0000
@@ -11,6 +11,9 @@
11 <plugin>cube</plugin>11 <plugin>cube</plugin>
12 <plugin>decor</plugin>12 <plugin>decor</plugin>
13 </relation>13 </relation>
14 <relation type="before">
15 <plugin>ezoom</plugin>
16 </relation>
14 <requirement>17 <requirement>
15 <plugin>opengl</plugin>18 <plugin>opengl</plugin>
16 <plugin>mousepoll</plugin>19 <plugin>mousepoll</plugin>

Subscribers

People subscribed via source and target branches