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

Proposed by Samuel thibault
Status: Needs review
Proposed branch: lp:~samuel-thibault/compiz/ezoom_focus_tracking
Merge into: lp:compiz/0.9.13
Diff against target: 2188 lines (+1695/-52)
21 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 (+48/-11)
plugins/ezoom/src/ezoom.cpp (+196/-37)
plugins/ezoom/src/ezoom.h (+31/-3)
plugins/focuspoll/CMakeLists.txt (+9/-0)
plugins/focuspoll/compiz-focuspoll.pc.in (+12/-0)
plugins/focuspoll/focuspoll.xml.in (+29/-0)
plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h (+70/-0)
plugins/focuspoll/include/accessibilitywatcher/focusinfo.h (+71/-0)
plugins/focuspoll/include/focuspoll/focuspoll.h (+50/-0)
plugins/focuspoll/src/accessibilitywatcher.cpp (+732/-0)
plugins/focuspoll/src/focusinfo.cpp (+123/-0)
plugins/focuspoll/src/focuspoll.cpp (+225/-0)
plugins/focuspoll/src/private.h (+75/-0)
plugins/mousepoll/src/mousepoll.cpp (+14/-0)
plugins/mousepoll/src/private.h (+1/-0)
plugins/showmouse/showmouse.xml.in (+3/-0)
po/POTFILES.in (+1/-0)
To merge this branch: bzr merge lp:~samuel-thibault/compiz/ezoom_focus_tracking
Reviewer Review Type Date Requested Status
Compiz Maintainers Pending
Review via email: mp+345941@code.launchpad.net

Commit message

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

This adds a focuspoll module which tracks focus events from a11y, used by ezoom concurrently to mouse events so the zoom moves nicely while working with the keyboard only.

Description of the change

Hello,

This is a resubmission of ksamak's work in https://code.launchpad.net/~ksamak/compiz/ezoom_focus_tracking, with (I believe) all raised comments addressed, notably:

- avoid the singleton technique
- avoid useless copies
- using smart pointers to avoid any memory leak
- better document at-spi workarounds

Samuel

To post a comment you must log in.
4174. By Samuel thibault

Fix corrupted iterator and explain why

4175. By Samuel thibault

merge from trunk

4176. By Samuel thibault

rework the translation correction to integrate it better and thus fix various translation oddities

4177. By Samuel thibault

Only warp mouse if it is out of zoomed screen

4178. By Samuel thibault

fix bogus unique method

4179. By Samuel thibault

Make event coordinates incorrect by default so they get ignored by default

4180. By Samuel thibault

Be more defensive against bogus event coordinates

4181. By Samuel thibault

Also make alternate coordinates bogus by default

4182. By Samuel thibault

Simplify source code

4183. By Samuel thibault

Avoid crashes in case the child went away in between

4184. By Samuel thibault

Automatically look at the first element of a table instead of the table

4185. By Samuel thibault

Also consider mouse clicks for its tracking delays

Otherwise when using both mouse and focus tracking tracking in ezoom, if one
clicks after a one-second wait and that produces a focus tracking event, the
mouse wraps there. The one-second delay should also apply here, which is a mere
matter of making mouse clicks be reported by mousepoll.

Revision history for this message
Alberts Muktupāvels (muktupavels) wrote :

You have unresolved conflict pushed. See POTFILES.in.

"This adds a focuspoll module which tracks focus events from a11y"
Are all changes here related to this? For example I see there are max value change that seems unrelated. Same with some description changes...

If these changes does not depend on focuspoll or are unreleated to adding focuspoll I would like to see these changes to be split out inside separate merge requests. Smaller changes are easier to review and could be merged without waiting for full review on this.

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

The conflict is there simply since the very recent merging of my other i18n branch.

Yes, all changes are related to this, I moved what I could find out into other branches (the i18n branch is one of them).

The max value is actually related: with focus tracking, the current maximum zoom movement speed is not enough for some users which want to get the new position very quickly. The description changes are also due to the new possibility of the combination of mouse and focus tracking.

Really I believe I moved what I could: this contains only the addition of the focuspoll plugin (which is mere addition of code) and its integration in ezoom with the corresponding changes: option tuning, zoom computation etc.

4186. By Samuel thibault

merge from trunk

4187. By Samuel thibault

Add change lost during merge (?!)

4188. By Samuel thibault

Drop unused option

4189. By Samuel thibault

Add missing deletes

4190. By Samuel thibault

fix build

4191. By Samuel thibault

Avoid tracking non-expanded menu items

4192. By Samuel thibault

Ignore zeroed component areas

4193. By Samuel thibault

fix memleak of focus info

4194. By Samuel thibault

Extend workaround for missing coords at end of text

4195. By Samuel thibault

Fix crash in menus

4196. By Samuel thibault

Add missing piece of backport

4197. By Samuel thibault

fix build

4198. By Samuel thibault

Add missing delete

4199. By Samuel thibault

Add missing frees and dups

4200. By Samuel thibault

Do not duplicate constant strings

4201. By Samuel thibault

Better handle table focus

4202. By Samuel thibault

Guard against AT-SPI parent loops

4203. By Samuel thibault

Fix some missing references

Revision history for this message
Dmitry Shachnev (mitya57) wrote :

We have recently switched Compiz to Git. Can you please re-submit this (and other proposals if they are still actual) against https://git.launchpad.net/compiz ?

I don’t have much knowledge of the Compiz code yet, but I will do my best to review them.

Unmerged revisions

4203. By Samuel thibault

Fix some missing references

4202. By Samuel thibault

Guard against AT-SPI parent loops

4201. By Samuel thibault

Better handle table focus

4200. By Samuel thibault

Do not duplicate constant strings

4199. By Samuel thibault

Add missing frees and dups

4198. By Samuel thibault

Add missing delete

4197. By Samuel thibault

fix build

4196. By Samuel thibault

Add missing piece of backport

4195. By Samuel thibault

Fix crash in menus

4194. By Samuel thibault

Extend workaround for missing coords at end of text

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 2018-10-23 08:53:35 +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 2017-04-26 08:18:38 +0000
+++ debian/compiz-plugins-default.install 2018-10-23 08:53:35 +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/*colorfilter.*31usr/lib/*/compiz/*colorfilter.*
30usr/share/compiz/*colorfilter.*32usr/share/compiz/*colorfilter.*
31usr/share/compiz/colorfilter33usr/share/compiz/colorfilter
3234
=== modified file 'debian/control'
--- debian/control 2018-07-24 10:32:11 +0000
+++ debian/control 2018-10-23 08:53:35 +0000
@@ -15,6 +15,7 @@
15 quilt (>= 0.40),15 quilt (>= 0.40),
16 libcairo2-dev,16 libcairo2-dev,
17 libdbus-glib-1-dev,17 libdbus-glib-1-dev,
18 libatspi2.0-dev,
18 libgl1-mesa-dev (>= 6.5.1) [!armhf !armel] | libgl-dev [!armhf !armel],19 libgl1-mesa-dev (>= 6.5.1) [!armhf !armel] | libgl-dev [!armhf !armel],
19 libegl1-mesa-dev [armhf armel], libgles2-mesa-dev [armhf armel],20 libegl1-mesa-dev [armhf armel], libgles2-mesa-dev [armhf armel],
20 libboost-dev,21 libboost-dev,
2122
=== modified file 'plugins/ezoom/CMakeLists.txt'
--- plugins/ezoom/CMakeLists.txt 2012-05-16 17:40:27 +0000
+++ plugins/ezoom/CMakeLists.txt 2018-10-23 08:53:35 +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 2018-07-11 15:30:30 +0000
+++ plugins/ezoom/ezoom.xml.in 2018-10-23 08:53:35 +0000
@@ -7,6 +7,8 @@
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>
11 <plugin>showmouse</plugin>
10 </relation>12 </relation>
11 <relation type="before">13 <relation type="before">
12 <plugin>staticswitcher</plugin>14 <plugin>staticswitcher</plugin>
@@ -15,6 +17,7 @@
15 <requirement>17 <requirement>
16 <plugin>opengl</plugin>18 <plugin>opengl</plugin>
17 <plugin>mousepoll</plugin>19 <plugin>mousepoll</plugin>
20 <plugin>focuspoll</plugin>
18 </requirement>21 </requirement>
19 </deps>22 </deps>
20 <_short>Enhanced Zoom Desktop</_short>23 <_short>Enhanced Zoom Desktop</_short>
@@ -440,12 +443,17 @@
440 <_short>Focus Tracking</_short>443 <_short>Focus Tracking</_short>
441 <option type="bool" name="follow_focus">444 <option type="bool" name="follow_focus">
442 <_short>Enable focus tracking</_short>445 <_short>Enable focus tracking</_short>
443 <_long>Move the zoom area when focus changes.</_long>446 <_long>Move the zoom area when focus changes. You may see the mousepoll and focuspoll modules options for more</_long>
444 <default>true</default>447 <default>true</default>
445 </option>448 </option>
449 <option type="bool" name="follow_window_focus">
450 <_short>Enable focus tracking when switching windows</_short>
451 <_long>Move the zoom area to the center of a newly switched window.</_long>
452 <default>false</default>
453 </option>
446 <option type="bool" name="focus_fit_window">454 <option type="bool" name="focus_fit_window">
447 <_short>Fit zoom level to window on focus change</_short>455 <_short>Fit zoom level to newly switched window</_short>
448 <_long>Fit the zoomed area to the window when the zoomed area moves as a result of focus tracking.</_long>456 <_long>Fit the zoomed area to the window when it switches.</_long>
449 <default>false</default>457 <default>false</default>
450 </option>458 </option>
451 <option type="float" name="autoscale_min">459 <option type="float" name="autoscale_min">
@@ -458,15 +466,39 @@
458 </option>466 </option>
459 <option type="bool" name="always_focus_fit_window">467 <option type="bool" name="always_focus_fit_window">
460 <_short>Always fit to window on focus track</_short>468 <_short>Always fit to window on focus track</_short>
461 <_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>469 <_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>
462 <default>false</default>470 <default>false</default>
463 </option>471 </option>
464 <option type="int" name="follow_focus_delay">472 <option type="float" name="follow_focus_delay">
465 <_short>Follow Focus Delay</_short>473 <_short>Tracking delay from mouse to focus mode</_short>
466 <_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>474 <_long>Only attempt to follow focus if the mouse hasn't moved in this amount of seconds. Use this to avoid zoom jumping everywhere on the screen due to external events while trying to use the mouse.</_long>
467 <default>0</default>475 <default>1</default>
468 <min>0</min>476 <min>0</min>
469 <max>15</max>477 <max>5</max>
478 <precision>0.1</precision>
479 </option>
480 <option type="float" name="follow_mouse_delay">
481 <_short>Tracking delay from focus to mouse mode</_short>
482 <_long>Only attempt to follow mouse if the focus hasn't moved in this amount of seconds. Use this to avoid jumping only due to small accidental mouse movements. This does not have effect if the mouse warp option is enabled.</_long>
483 <default>0.1</default>
484 <min>0</min>
485 <max>5</max>
486 <precision>0.1</precision>
487 </option>
488 <option type="bool" name="always_center_mouse">
489 <_short>Keep Mouse centered inside zoom area</_short>
490 <_long>If activated, the mouse will always be centered when in mouse sync mode, if not, it will move around the zoom area</_long>
491 <default>true</default>
492 </option>
493 <option type="bool" name="restrain_zoom_to_screen">
494 <_short>Restrain the zoom area inside the screen</_short>
495 <_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>
496 <default>true</default>
497 </option>
498 <option type="bool" name="warp_mouse_to_focus">
499 <_short>When switching focus back to mouse, warp mouse inside the zoomed area</_short>
500 <_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>
501 <default>true</default>
470 </option>502 </option>
471 </group>503 </group>
472 <group>504 <group>
@@ -476,7 +508,7 @@
476 <_long>Zoom Change Speed</_long>508 <_long>Zoom Change Speed</_long>
477 <default>25</default>509 <default>25</default>
478 <min>0.1</min>510 <min>0.1</min>
479 <max>100</max>511 <max>500</max>
480 <precision>0.1</precision>512 <precision>0.1</precision>
481 </option>513 </option>
482 <option type="float" name="timestep">514 <option type="float" name="timestep">
@@ -487,6 +519,11 @@
487 <max>50</max>519 <max>50</max>
488 <precision>0.1</precision>520 <precision>0.1</precision>
489 </option>521 </option>
522 <option type="bool" name="instant_pan">
523 <_short>Move the zoom area instantly</_short>
524 <_long>When enabled, zoom area is warped instantly to focus position. When disabled, zoom area makes a smooth move to the focus position</_long>
525 <default>false</default>
526 </option>
490 </group>527 </group>
491 </options>528 </options>
492 </plugin>529 </plugin>
493530
=== modified file 'plugins/ezoom/src/ezoom.cpp'
--- plugins/ezoom/src/ezoom.cpp 2017-12-05 14:45:06 +0000
+++ plugins/ezoom/src/ezoom.cpp 2018-10-23 08:53:35 +0000
@@ -168,6 +168,39 @@
168 zs->gScreen->setTextureFilter (oldFilter);168 zs->gScreen->setTextureFilter (oldFilter);
169}169}
170170
171/* Return current time in seconds */
172static inline double
173getTime (void)
174{
175 struct timespec tp;
176 clock_gettime (CLOCK_MONOTONIC, &tp);
177 return tp.tv_sec + tp.tv_nsec / 1e9;
178}
179
180/* This returns the proper translation correction depending on the desired
181 * centering (exact center, small overflow, or exact screen fit) */
182float
183EZoomScreen::getTranslateCorrection (float zoom)
184{
185 if (optionGetAlwaysCenterMouse ())
186 {
187 // Bring to X,Y position the exact zoom center instead of
188 // fitting screen
189 return 1.0;
190 }
191 else if (!optionGetRestrainZoomToScreen ())
192 {
193 // Overflow a bit beyond screen so we can clearly see the corner and the
194 // mouse
195 return (1.0f - zoom) / EZOOM_SCREEN_BORDER_OFFSET;
196 }
197 else
198 {
199 // Keep an exact fit to screen
200 return 1.0f - zoom;
201 }
202}
203
171/* Returns the distance to the defined edge in zoomed pixels. */204/* Returns the distance to the defined edge in zoomed pixels. */
172int205int
173EZoomScreen::distanceToEdge (int out,206EZoomScreen::distanceToEdge (int out,
@@ -197,10 +230,26 @@
197230
198/* Update/set translations based on zoom level and real translate. */231/* Update/set translations based on zoom level and real translate. */
199void232void
200EZoomScreen::ZoomArea::updateActualTranslates ()233EZoomScreen::ZoomArea::updateActualTranslates (EZoomScreen *screen)
201{234{
202 xtrans = -realXTranslate * (1.0f - currentZoom);235 float translateCorrection;
203 ytrans = realYTranslate * (1.0f - currentZoom);236 float limit = 0.5 * (1.0f - currentZoom);
237
238 translateCorrection = screen->getTranslateCorrection(currentZoom);
239
240 xtrans = -realXTranslate * translateCorrection;
241 ytrans = realYTranslate * translateCorrection;
242
243 if (screen->optionGetRestrainZoomToScreen ()) {
244 if (xtrans < -limit)
245 xtrans = -limit;
246 if (xtrans > limit)
247 xtrans = limit;
248 if (ytrans < -limit)
249 ytrans = -limit;
250 if (ytrans > limit)
251 ytrans = limit;
252 }
204}253}
205254
206/* Returns true if the head in question is currently moving.255/* Returns true if the head in question is currently moving.
@@ -244,7 +293,6 @@
244 ytrans (0.0f),293 ytrans (0.0f),
245 locked (false)294 locked (false)
246{295{
247 updateActualTranslates ();
248}296}
249297
250EZoomScreen::ZoomArea::ZoomArea () :298EZoomScreen::ZoomArea::ZoomArea () :
@@ -365,7 +413,7 @@
365413
366 adjustXYVelocity (out, chunk);414 adjustXYVelocity (out, chunk);
367 adjustZoomVelocity (out, chunk);415 adjustZoomVelocity (out, chunk);
368 zooms.at (out).updateActualTranslates ();416 zooms.at (out).updateActualTranslates (this);
369417
370 if (!isZoomed (out))418 if (!isZoomed (out))
371 {419 {
@@ -597,8 +645,6 @@
597 * mouse pointer. This is to allow input, and is NOT necessary645 * mouse pointer. This is to allow input, and is NOT necessary
598 * when input redirection is available to us or if we're cheating646 * when input redirection is available to us or if we're cheating
599 * and using a scaled mouse cursor to imitate IR.647 * and using a scaled mouse cursor to imitate IR.
600 * The center is not the center of the screen. This is the target-center;
601 * that is, it's the point that's the same regardless of zoom level.
602 */648 */
603void649void
604EZoomScreen::setCenter (int x,650EZoomScreen::setCenter (int x,
@@ -616,13 +662,13 @@
616 zooms.at (out).yTranslate = (float)662 zooms.at (out).yTranslate = (float)
617 ((y - o->y1 ()) - o->height () / 2) / (o->height ());663 ((y - o->y1 ()) - o->height () / 2) / (o->height ());
618664
619 if (instant)665 if (instant || optionGetInstantPan ())
620 {666 {
621 zooms.at (out).realXTranslate = zooms.at (out).xTranslate;667 zooms.at (out).realXTranslate = zooms.at (out).xTranslate;
622 zooms.at (out).realYTranslate = zooms.at (out).yTranslate;668 zooms.at (out).realYTranslate = zooms.at (out).yTranslate;
623 zooms.at (out).yVelocity = 0.0f;669 zooms.at (out).yVelocity = 0.0f;
624 zooms.at (out).xVelocity = 0.0f;670 zooms.at (out).xVelocity = 0.0f;
625 zooms.at (out).updateActualTranslates ();671 zooms.at (out).updateActualTranslates (this);
626 }672 }
627673
628 if (optionGetZoomMode () == EzoomOptions::ZoomModePanArea)674 if (optionGetZoomMode () == EzoomOptions::ZoomModePanArea)
@@ -661,7 +707,7 @@
661 {707 {
662 zooms.at (out).realXTranslate = zooms.at (out).xTranslate;708 zooms.at (out).realXTranslate = zooms.at (out).xTranslate;
663 zooms.at (out).realYTranslate = zooms.at (out).yTranslate;709 zooms.at (out).realYTranslate = zooms.at (out).yTranslate;
664 zooms.at (out).updateActualTranslates ();710 zooms.at (out).updateActualTranslates (this);
665 }711 }
666712
667 if (optionGetZoomMode () == EzoomOptions::ZoomModePanArea)713 if (optionGetZoomMode () == EzoomOptions::ZoomModePanArea)
@@ -703,9 +749,21 @@
703void749void
704EZoomScreen::enableMousePolling ()750EZoomScreen::enableMousePolling ()
705{751{
706 pollHandle.start ();752 pollMouseHandle.start ();
707 lastChange = time(NULL);753 lastMouseChange = getTime ();
708 mouse = MousePoller::getCurrentPosition ();754 mouse = MousePoller::getCurrentPosition ();
755}
756
757/* Enables polling of focus position */
758void
759EZoomScreen::enableFocusPolling ()
760{
761 if (!optionGetFollowFocus ())
762 {
763 return;
764 }
765 pollFocusHandle.start ();
766 lastFocusChange = getTime ();
709}767}
710768
711/* Sets the zoom (or scale) level.769/* Sets the zoom (or scale) level.
@@ -722,9 +780,12 @@
722 value = 1.0f;780 value = 1.0f;
723 else781 else
724 {782 {
725 if (!pollHandle.active ())783 if (!pollMouseHandle.active ())
726 enableMousePolling ();784 enableMousePolling ();
727785
786 if (!pollFocusHandle.active ())
787 enableFocusPolling ();
788
728 grabbed |= (1 << zooms.at (out).output);789 grabbed |= (1 << zooms.at (out).output);
729 cursorZoomActive (out);790 cursorZoomActive (out);
730 }791 }
@@ -770,7 +831,7 @@
770{831{
771 int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());832 int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());
772833
773 if (!isInMovement (out))834 if (!isInMovement (out) || nonMouseFocusTracking)
774 return;835 return;
775836
776 CompOutput *o = &screen->outputDevs ().at (out);837 CompOutput *o = &screen->outputDevs ().at (out);
@@ -810,17 +871,30 @@
810 int oHeight = o->height ();871 int oHeight = o->height ();
811 int halfOWidth = oWidth / 2;872 int halfOWidth = oWidth / 2;
812 int halfOHeight = oHeight / 2;873 int halfOHeight = oHeight / 2;
874 float translateCorrection = getTranslateCorrection (za.currentZoom);
875 float xTranslate = za.realXTranslate * translateCorrection ;
876 float yTranslate = za.realYTranslate * translateCorrection ;
877 float limit = 0.5 * (1.0 - za.currentZoom);
878
879 if (optionGetRestrainZoomToScreen ()) {
880 if (xTranslate < -limit)
881 xTranslate = -limit;
882 if (xTranslate > limit)
883 xTranslate = limit;
884 if (yTranslate < -limit)
885 yTranslate = -limit;
886 if (yTranslate > limit)
887 yTranslate = limit;
888 }
813889
814 x -= o->x1 ();890 x -= o->x1 ();
815 y -= o->y1 ();891 y -= o->y1 ();
816892
817 *resultX = x - (za.realXTranslate *893 *resultX = x - (xTranslate * oWidth) - halfOWidth;
818 (1.0f - za.currentZoom) * oWidth) - halfOWidth;
819 *resultX /= za.currentZoom;894 *resultX /= za.currentZoom;
820 *resultX += halfOWidth;895 *resultX += halfOWidth;
821 *resultX += o->x1 ();896 *resultX += o->x1 ();
822 *resultY = y - (za.realYTranslate *897 *resultY = y - (yTranslate * oHeight) - halfOHeight;
823 (1.0f - za.currentZoom) * oHeight) - halfOHeight;
824 *resultY /= za.currentZoom;898 *resultY /= za.currentZoom;
825 *resultY += halfOHeight;899 *resultY += halfOHeight;
826 *resultY += o->y1 ();900 *resultY += o->y1 ();
@@ -1058,7 +1132,7 @@
10581132
1059 if (zooms.at (out).currentZoom == 1.0f)1133 if (zooms.at (out).currentZoom == 1.0f)
1060 {1134 {
1061 lastChange = time(NULL);1135 lastMouseChange = getTime ();
1062 mouse = MousePoller::getCurrentPosition ();1136 mouse = MousePoller::getCurrentPosition ();
1063 }1137 }
10641138
@@ -1129,20 +1203,88 @@
1129void1203void
1130EZoomScreen::updateMousePosition (const CompPoint &p)1204EZoomScreen::updateMousePosition (const CompPoint &p)
1131{1205{
1206 auto localTime = getTime ();
1132 mouse.setX (p.x ());1207 mouse.setX (p.x ());
1133 mouse.setY (p.y ());1208 mouse.setY (p.y ());
11341209
1135 int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());1210 int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ());
1136 lastChange = time(NULL);1211 if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse)
11371212 {
1138 if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse &&1213 if (optionGetWarpMouseToFocus ()) {
1139 !isInMovement (out))1214 CompOutput *o = &screen->outputDevs ().at (out);
1140 setCenter (mouse.x (), mouse.y (), true);1215 int zoomedMouseX, zoomedMouseY;
11411216 bool pointerInZoom;
1217
1218 convertToZoomed (out, mouse.x (), mouse.y (),
1219 &zoomedMouseX, &zoomedMouseY);
1220 pointerInZoom = zoomedMouseX >= o->x1 ()
1221 && zoomedMouseX < o->x1 () + o->width ()
1222 && zoomedMouseY >= o->y1 ()
1223 && zoomedMouseY < o->y1 () + o->height ();
1224
1225 if (lastMouseChange <= lastFocusChange && nonMouseFocusTracking && !pointerInZoom)
1226 {
1227 // Mouse taking back control of focus, but is out of zoom, wrap
1228 // it back to focus
1229 // FocusTracking is always centered
1230 screen->warpPointer (
1231 zooms.at (out).realXTranslate * o->width ()
1232 + o->width () / 2 + o->x1 () - pointerX,
1233 zooms.at (out).realYTranslate * o->height ()
1234 + o->height () / 2 + o->y1 () - pointerY);
1235 setCenter (pointerX, pointerY, true);
1236 }
1237 else
1238 {
1239 if (!isInMovement (out))
1240 {
1241 setCenter (mouse.x (), mouse.y (), true);
1242 }
1243 }
1244 lastMouseChange = localTime;
1245 }
1246 // respect a timing in case user has merely grazed his mouse
1247 else if (localTime - lastFocusChange > optionGetFollowMouseDelay () && !isInMovement (out))
1248 {
1249 setCenter (mouse.x (), mouse.y (), true);
1250 lastMouseChange = localTime;
1251 }
1252 nonMouseFocusTracking = false;
1253 }
1142 cursorMoved ();1254 cursorMoved ();
1143 cScreen->damageScreen ();1255 cScreen->damageScreen ();
1144}1256}
11451257
1258void
1259EZoomScreen::updateFocusPosition (const CompRect &rect)
1260{
1261 auto localTime = getTime ();
1262 int out = screen->outputDeviceForPoint (rect.x (), rect.y ());
1263 if (localTime - lastMouseChange > optionGetFollowFocusDelay ())
1264 {
1265 int zoomAreaWidth = screen->outputDevs ().at (out).width () * zooms.at (out).newZoom;
1266 int zoomAreaHeight = screen->outputDevs ().at (out).height () * zooms.at (out).newZoom;
1267 int posX = rect.x () + rect.width () / 2;
1268 int posY = rect.y () + rect.height () / 2;
1269 if (rect.width () > zoomAreaWidth)
1270 {
1271 // target rectangle is too big for the zoom area, aim at
1272 // the top-left corner of the target
1273 posX -= (rect.width () - zoomAreaWidth) / 2;
1274 }
1275 if (rect.height () > zoomAreaHeight)
1276 {
1277 posY -= (rect.height () - zoomAreaHeight) / 2;
1278 }
1279 setCenter (posX, posY, false);
1280 focus.setX (posX);
1281 focus.setY (posY);
1282 nonMouseFocusTracking = true;
1283 lastFocusChange = localTime;
1284 }
1285 cScreen->damageScreen ();
1286}
1287
1146/* Timeout handler to poll the mouse. Returns false (and thereby does not1288/* Timeout handler to poll the mouse. Returns false (and thereby does not
1147 * get re-added to the queue) when zoom is not active. */1289 * get re-added to the queue) when zoom is not active. */
1148void1290void
@@ -1154,11 +1296,21 @@
1154 {1296 {
1155 cursorMoved ();1297 cursorMoved ();
11561298
1157 if (pollHandle.active ())1299 if (pollMouseHandle.active ())
1158 pollHandle.stop ();1300 pollMouseHandle.stop ();
1159 }1301 }
1160}1302}
11611303
1304/* Timeout handler to focusPoll. */
1305void
1306EZoomScreen::updateFocusInterval (const CompRect &rect)
1307{
1308 updateFocusPosition (rect);
1309
1310 if (!grabbed && pollFocusHandle.active ())
1311 pollFocusHandle.stop ();
1312}
1313
1162/* Free a cursor */1314/* Free a cursor */
1163void1315void
1164EZoomScreen::freeCursor (CursorTexture *cursor)1316EZoomScreen::freeCursor (CursorTexture *cursor)
@@ -1586,7 +1738,7 @@
1586 int out = screen->outputDeviceForPoint (pointerX, pointerY);1738 int out = screen->outputDeviceForPoint (pointerX, pointerY);
15871739
1588 if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse &&1740 if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse &&
1589 !isInMovement (out))1741 !isInMovement (out) && !nonMouseFocusTracking)
1590 setCenter (pointerX, pointerY, true);1742 setCenter (pointerX, pointerY, true);
15911743
1592 setScale (out, zooms.at (out).newZoom / optionGetZoomFactor ());1744 setScale (out, zooms.at (out).newZoom / optionGetZoomFactor ());
@@ -1914,8 +2066,8 @@
19142066
1915 if (w == NULL ||2067 if (w == NULL ||
1916 w->id () == screen->activeWindow () ||2068 w->id () == screen->activeWindow () ||
1917 time(NULL) - lastChange < optionGetFollowFocusDelay () ||2069 getTime () - lastMouseChange < optionGetFollowFocusDelay () ||
1918 !optionGetFollowFocus ())2070 !optionGetFollowWindowFocus ())
1919 return;2071 return;
19202072
1921 int out = screen->outputDeviceForGeometry (w->geometry ());2073 int out = screen->outputDeviceForGeometry (w->geometry ());
@@ -2008,7 +2160,9 @@
2008 gScreen (GLScreen::get (screen)),2160 gScreen (GLScreen::get (screen)),
2009 grabbed (0),2161 grabbed (0),
2010 grabIndex (0),2162 grabIndex (0),
2011 lastChange (0),2163 lastMouseChange (0.),
2164 lastFocusChange (0.),
2165 nonMouseFocusTracking (false),
2012 cursorInfoSelected (false),2166 cursorInfoSelected (false),
2013 cursorHidden (false)2167 cursorHidden (false)
2014{2168{
@@ -2042,8 +2196,10 @@
2042 zooms.push_back (za);2196 zooms.push_back (za);
2043 }2197 }
20442198
2045 pollHandle.setCallback (boost::bind (2199 pollMouseHandle.setCallback (boost::bind (&EZoomScreen::updateMouseInterval,
2046 &EZoomScreen::updateMouseInterval, this, _1));2200 this, _1));
2201 pollFocusHandle.setCallback (boost::bind (&EZoomScreen::updateFocusInterval,
2202 this, _1));
20472203
2048 optionSetZoomInButtonInitiate (boost::bind (&EZoomScreen::zoomIn, this, _1,2204 optionSetZoomInButtonInitiate (boost::bind (&EZoomScreen::zoomIn, this, _1,
2049 _2, _3));2205 _2, _3));
@@ -2136,8 +2292,10 @@
21362292
2137EZoomScreen::~EZoomScreen ()2293EZoomScreen::~EZoomScreen ()
2138{2294{
2139 if (pollHandle.active ())2295 if (pollMouseHandle.active ())
2140 pollHandle.stop ();2296 pollMouseHandle.stop ();
2297 if (pollFocusHandle.active ())
2298 pollFocusHandle.stop ();
21412299
2142 if (zooms.size ())2300 if (zooms.size ())
2143 zooms.clear ();2301 zooms.clear ();
@@ -2152,7 +2310,8 @@
2152 if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) &&2310 if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) &&
2153 CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) &&2311 CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) &&
2154 CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI) &&2312 CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI) &&
2155 CompPlugin::checkPluginABI ("mousepoll", COMPIZ_MOUSEPOLL_ABI))2313 CompPlugin::checkPluginABI ("mousepoll", COMPIZ_MOUSEPOLL_ABI) &&
2314 CompPlugin::checkPluginABI ("focuspoll", COMPIZ_FOCUSPOLL_ABI))
2156 return true;2315 return true;
21572316
2158 return false;2317 return false;
21592318
=== modified file 'plugins/ezoom/src/ezoom.h'
--- plugins/ezoom/src/ezoom.h 2016-07-01 15:37:19 +0000
+++ plugins/ezoom/src/ezoom.h 2018-10-23 08:53:35 +0000
@@ -43,12 +43,19 @@
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// Some Users wish to see better the edges of the screen. Without this, mouse
54// reaches the border of the zoomarea around screen edges, and mouse can become
55// invisible (especially around bottom/right). Zooming out a bit allows to see
56// both the edge and the mouse reaching the edge.
57#define EZOOM_SCREEN_BORDER_OFFSET 0.92
58
52enum SpecificZoomTarget59enum SpecificZoomTarget
53{60{
54 ZoomTarget1 = 0,61 ZoomTarget1 = 0,
@@ -160,25 +167,29 @@
160 ZoomArea ();167 ZoomArea ();
161168
162 void169 void
163 updateActualTranslates ();170 updateActualTranslates (EZoomScreen *screen);
164 };171 };
165172
166 public:173 public:
167174
168 std::vector <ZoomArea> zooms; // list of zooms (different zooms for each output)175 std::vector <ZoomArea> zooms; // list of zooms (different zooms for each output)
169 CompPoint mouse; // we get this from mousepoll176 CompPoint mouse; // we get this from mousepoll
177 CompPoint focus; // we get this from focuspoll
170 unsigned long int grabbed;178 unsigned long int grabbed;
171 CompScreen::GrabHandle grabIndex; // for zoomBox179 CompScreen::GrabHandle grabIndex; // for zoomBox
172 time_t lastChange;180 double lastMouseChange;
181 double lastFocusChange;
173 CursorTexture cursor; // the texture for the faux-cursor182 CursorTexture cursor; // the texture for the faux-cursor
174 // we paint to do fake input183 // we paint to do fake input
175 // handling184 // handling
185 bool nonMouseFocusTracking;
176 bool cursorInfoSelected;186 bool cursorInfoSelected;
177 bool cursorHidden;187 bool cursorHidden;
178 CompRect box;188 CompRect box;
179 CompPoint clickPos;189 CompPoint clickPos;
180190
181 MousePoller pollHandle; // mouse poller object191 MousePoller pollMouseHandle; // mouse poller object
192 FocusPoller pollFocusHandle; // focus poller object
182193
183 private:194 private:
184195
@@ -222,6 +233,9 @@
222 adjustXYVelocity (int out,233 adjustXYVelocity (int out,
223 float chunk);234 float chunk);
224235
236 float
237 getTranslateCorrection (float zoom);
238
225 void239 void
226 drawBox (const GLMatrix &transform,240 drawBox (const GLMatrix &transform,
227 CompOutput *output,241 CompOutput *output,
@@ -231,6 +245,11 @@
231 setCenter (int x,245 setCenter (int x,
232 int y,246 int y,
233 bool instant);247 bool instant);
248 void
249 setCenter (int x,
250 int y,
251 bool instant,
252 bool center);
234253
235 void254 void
236 setZoomArea (int x,255 setZoomArea (int x,
@@ -250,6 +269,9 @@
250 enableMousePolling ();269 enableMousePolling ();
251270
252 void271 void
272 enableFocusPolling ();
273
274 void
253 setScale (int out,275 setScale (int out,
254 float value);276 float value);
255277
@@ -295,6 +317,12 @@
295 void317 void
296 updateMouseInterval (const CompPoint &p);318 updateMouseInterval (const CompPoint &p);
297319
320 void
321 updateFocusPosition (const CompRect &retc);
322
323 void
324 updateFocusInterval (const CompRect &rect);
325
298 /* Make dtor */326 /* Make dtor */
299 void327 void
300 freeCursor (CursorTexture * cursor);328 freeCursor (CursorTexture * cursor);
301329
=== 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 2018-10-23 08:53:35 +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 2018-10-23 08:53:35 +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 2018-10-23 08:53:35 +0000
@@ -0,0 +1,29 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<compiz>
3 <plugin name="focuspoll" useBcop="true">
4 <deps>
5 <relation type="after">
6 <plugin>opengl</plugin>
7 </relation>
8 </deps>
9 <_short>Focus position polling</_short>
10 <_long>Updates the focus pointer position from the accessibility library AT-SPI through dbus</_long>
11 <category>Utility</category>
12 <options>
13 <_short>Misc</_short>
14 <option type="bool" name="ignore_links">
15 <_short>Ignores link focuses</_short>
16 <_long>If enabled, focuspoll will not track the focus events of hypertext link items</_long>
17 <default>true</default>
18 </option>
19 <option type="int" name="focus_poll_interval">
20 <_short>Focus Poll Interval</_short>
21 <_long>How often to poll DBus for focus changes, in milliseconds. Reduce this to reduce choppy behavior.</_long>
22 <default>10</default>
23 <min>1</min>
24 <max>500</max>
25 </option>
26 </options>
27 </plugin>
28</compiz>
29
030
=== 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 2018-10-23 08:53:35 +0000
@@ -0,0 +1,70 @@
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{
32 public:
33 AccessibilityWatcher ();
34 ~AccessibilityWatcher ();
35
36 void setActive (bool);
37
38 void setIgnoreLinks (bool);
39 void setScreenLimits (int, int);
40 int getScreenWidth (void);
41 int getScreenHeight (void);
42
43 std::deque<FocusInfo *> getFocusQueue (void);
44 void resetFocusQueue (void);
45 bool returnToPrevMenu (void);
46
47 void registerEvent (const AtspiEvent *event, const gchar *type);
48
49 private:
50 bool mActive;
51 int screenWidth;
52 int screenHeight;
53 static bool ignoreLinks;
54 std::deque<FocusInfo*> focusList;
55 std::vector<FocusInfo*> previouslyActiveMenus;
56
57 AtspiEventListener *focusListener;
58 AtspiEventListener *caretMoveListener;
59 AtspiEventListener *selectedListener;
60 AtspiEventListener *descendantChangedListener;
61
62 void addWatches (void);
63 void removeWatches (void);
64
65 bool appSpecificFilter (FocusInfo *focusInfo, const AtspiEvent* event);
66 bool filterBadEvents (const FocusInfo *event);
67 void getAlternativeCaret (FocusInfo *focus, const AtspiEvent* event);
68};
69
70#endif
071
=== 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 2018-10-23 08:53:35 +0000
@@ -0,0 +1,71 @@
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 <string>
24#include <sstream>
25#include <glib.h>
26#include <core/point.h>
27#include <core/size.h>
28#include <core/rect.h>
29
30
31class FocusInfo
32{
33 public:
34
35 FocusInfo (const gchar * type = "",
36 gchar * name = g_strdup (""),
37 gchar * label = g_strdup (""),
38 gchar * role = g_strdup (""),
39 gchar * application = g_strdup (""),
40 int x = -1,
41 int y = -1,
42 int width = -1,
43 int height = -1);
44
45 FocusInfo (const FocusInfo &dup);
46
47 ~FocusInfo ();
48
49 int x, y, w, h;
50 int xAlt, yAlt, wAlt, hAlt;
51 const gchar * type;
52 gchar * name;
53 gchar * label;
54 gchar * role;
55 gchar * application;
56
57 // AT-SPI events that are interesting to know about the event
58 bool active;
59 bool focused;
60 bool selected;
61
62 const gchar * getType (void);
63 CompPoint getPosition (void);
64 CompSize getSize (void);
65 CompRect getBBox (void);
66
67 bool operator== (const FocusInfo &other) const;
68 bool operator!= (const FocusInfo &other) const;
69};
70
71#endif
072
=== 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 2018-10-23 08:53:35 +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 <core/rect.h>
26#include <boost/function.hpp>
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 (void);
39 void stop (void);
40 bool active (void);
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 2018-10-23 08:53:35 +0000
@@ -0,0 +1,732 @@
1/*
2 * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net>
3 * Copyright (C) 2018 Samuel Thibault <sthibault@hypra.fr>
4 *
5 * This file is part of compiz.
6 *
7 * this program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by the Free
9 * Software Foundation, either version 3 of the License, or (at your option) any
10 * later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 * details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <iostream>
22#include <memory>
23
24#include <string.h>
25#include <stdlib.h>
26#include <unistd.h>
27
28#include "accessibilitywatcher.h"
29
30// When getting a bounding box bigger than this size, consider that this is not
31// a precise caret bounding box, and try to find another source of precise caret position
32// TODO: make them options
33namespace {
34 const int A11YWATCHER_MAX_CARET_WIDTH = 50;
35 const int A11YWATCHER_MAX_CARET_HEIGHT = 70;
36}
37
38/*
39 * Wrapper that automatically calls g_object_unref on pointer scope release.
40 *
41 * Instead of
42 *
43 * AtspiFoo x = atspi_... ()
44 * atspi_... (x);
45 * g_object_unref (x);
46 *
47 * use
48 *
49 * auto x = unique_gobject (atspi_... ());;
50 * atspi_... (x.get ());
51 *
52 * And instead of
53 *
54 * AtspiFoo x = event->source;
55 * g_object_ref (x);
56 * atspi_... (x);
57 * g_object_unref (x);
58 *
59 * use
60 *
61 * auto x = unique_gobject_ref (event->source);;
62 * atspi_... (x.get ());
63 * */
64
65struct unique_gobject_deleter {
66 void operator () (gpointer obj)
67 {
68 if (obj)
69 g_object_unref (obj);
70 }
71};
72
73template <typename T>
74std::unique_ptr <T, unique_gobject_deleter>
75unique_gobject (T* ptr)
76{
77
78 return std::unique_ptr <T, unique_gobject_deleter> (ptr);
79}
80
81template <typename T>
82std::unique_ptr <T, unique_gobject_deleter>
83unique_gobject_ref (T* ptr)
84{
85 if (ptr)
86 g_object_ref (ptr);
87 return std::unique_ptr <T, unique_gobject_deleter> (ptr);
88}
89
90/*
91 * Similarly for a g_array of gobjects.
92 */
93
94struct unique_gobject_garray_deleter {
95 void operator () (GArray *array)
96 {
97 for (guint i = 0; i < array->len; ++i)
98 {
99 gpointer obj = g_array_index (array, gpointer, i);
100 if (obj)
101 g_object_unref (obj);
102 }
103 g_array_unref (array);
104 }
105};
106
107template <typename T>
108std::unique_ptr <T, unique_gobject_garray_deleter>
109unique_gobject_garray (T* ptr)
110{
111
112 return std::unique_ptr <T, unique_gobject_garray_deleter> (ptr);
113}
114
115/*
116 * Similarly for calling g_free on pointer scope release.
117 */
118
119struct unique_gmem_deleter {
120 void operator () (gpointer obj)
121 {
122 g_free (obj);
123 }
124};
125
126template <typename T>
127std::unique_ptr <T, unique_gmem_deleter>
128unique_gmem (T* ptr)
129{
130
131 return std::unique_ptr <T, unique_gmem_deleter> (ptr);
132}
133
134
135
136bool AccessibilityWatcher::ignoreLinks = false;
137
138static void
139onCaretMove (const AtspiEvent *event, void *data)
140{
141 AccessibilityWatcher *watcher = (AccessibilityWatcher *) data;
142 watcher->registerEvent (event, "caret");
143}
144
145static void
146onSelectedChange (const AtspiEvent *event, void *data)
147{
148 AccessibilityWatcher *watcher = (AccessibilityWatcher *) data;
149 watcher->registerEvent (event, "state-changed:selected");
150}
151
152static void
153onFocus (const AtspiEvent *event, void *data)
154{
155 /* We only care about focus/selection gain
156 * there's no detail1 on focus loss in AT-SPI specs */
157 if (!event->detail1)
158 return;
159
160 AccessibilityWatcher *watcher = (AccessibilityWatcher *) data;
161 watcher->registerEvent (event, "focus");
162}
163
164static void
165onDescendantChanged (const AtspiEvent *event, void *data)
166{
167 AccessibilityWatcher *watcher = (AccessibilityWatcher *) data;
168 watcher->registerEvent (event, "active-descendant-changed");
169}
170
171AccessibilityWatcher::AccessibilityWatcher () :
172 mActive (false),
173 screenWidth (0),
174 screenHeight (0),
175 focusListener (NULL),
176 caretMoveListener (NULL),
177 selectedListener (NULL),
178 descendantChangedListener (NULL)
179{
180 atspi_init ();
181 atspi_set_main_context (g_main_context_default ());
182
183 focusListener = atspi_event_listener_new (reinterpret_cast <AtspiEventListenerCB> (onFocus), this, NULL);
184 caretMoveListener = atspi_event_listener_new (reinterpret_cast <AtspiEventListenerCB> (onCaretMove), this, NULL);
185 selectedListener = atspi_event_listener_new (reinterpret_cast <AtspiEventListenerCB> (onSelectedChange), this, NULL);
186 descendantChangedListener = atspi_event_listener_new (reinterpret_cast <AtspiEventListenerCB> (onDescendantChanged), this, NULL);
187
188 addWatches ();
189}
190
191AccessibilityWatcher::~AccessibilityWatcher ()
192{
193 removeWatches ();
194 g_object_unref (focusListener);
195 g_object_unref (caretMoveListener);
196 g_object_unref (selectedListener);
197 g_object_unref (descendantChangedListener);
198};
199
200static gchar *
201getLabel (AtspiAccessible *accessible)
202{
203 auto relations = unique_gobject_garray (atspi_accessible_get_relation_set (accessible, NULL));
204 if (relations.get () == NULL)
205 {
206 return g_strdup ("");
207 }
208
209 for (guint i = 0; i < relations.get ()->len; ++i) {
210 AtspiRelation *relation = g_array_index (relations.get (), AtspiRelation*, i);
211 if (relation == NULL)
212 continue;
213 if (atspi_relation_get_relation_type (relation) == ATSPI_RELATION_LABELLED_BY)
214 {
215 auto target = unique_gobject (atspi_relation_get_target (relation, 0));
216 gchar * res_label = atspi_accessible_get_name (target.get (), NULL);
217 return (res_label == NULL) ? g_strdup ("") : res_label;
218 }
219 }
220 return g_strdup ("");
221}
222
223void
224AccessibilityWatcher::setIgnoreLinks (bool val)
225{
226 ignoreLinks = val;
227}
228
229void
230AccessibilityWatcher::setScreenLimits (int x, int y)
231{
232 screenWidth = x;
233 screenHeight = y;
234}
235
236int
237AccessibilityWatcher::getScreenWidth ()
238{
239 return screenWidth;
240}
241
242int
243AccessibilityWatcher::getScreenHeight ()
244{
245 return screenHeight;
246}
247
248void
249AccessibilityWatcher::registerEvent (const AtspiEvent *event, const gchar *type)
250{
251 // type is registered from filter on calling event
252 auto application = unique_gobject (atspi_accessible_get_application (event->source, NULL));
253 FocusInfo *res = new FocusInfo (type,
254 atspi_accessible_get_name (event->source, NULL),
255 getLabel (event->source),
256 atspi_accessible_get_role_name (event->source, NULL),
257 atspi_accessible_get_name (application.get (), NULL));
258
259 if (!res->active)
260 {
261 // prevents skipping events that are not designated as active. we check the activeness of parents.
262 auto parent = unique_gobject (atspi_accessible_get_parent (event->source, NULL));
263 while (parent.get ())
264 {
265 auto stateSet = unique_gobject (atspi_accessible_get_state_set (parent.get ()));
266 if (atspi_state_set_contains (stateSet.get (), ATSPI_STATE_ACTIVE))
267 {
268 res->active = true;
269 }
270 if (atspi_state_set_contains (stateSet.get (), ATSPI_STATE_EXPANDABLE))
271 {
272 if (!atspi_state_set_contains (stateSet.get (), ATSPI_STATE_EXPANDED))
273 {
274 auto role = unique_gmem (atspi_accessible_get_role_name (parent.get (), NULL));
275 if (!strcmp (role.get (), "menu"))
276 {
277 // parent is expandable but not expanded, we do not want to track what is happening inside
278 delete (res);
279 return;
280 }
281 }
282 }
283 auto child = unique_gobject (atspi_accessible_get_parent (parent.get (), NULL));
284 if (child.get () == parent.get ())
285 {
286 // parent loop !? escape this trap...
287 break;
288 }
289 parent = unique_gobject_ref (child.get ());
290 }
291 }
292
293 auto component_target = unique_gobject_ref (event->source);
294
295 if (strcmp (res->type, "active-descendant-changed") == 0)
296 {
297 component_target = unique_gobject (atspi_accessible_get_child_at_index (component_target.get (), event->detail1, NULL));
298 if (!component_target.get ())
299 {
300 delete (res);
301 return;
302 }
303 }
304
305 if (strcmp (unique_gmem (atspi_accessible_get_role_name (component_target.get (), NULL)).get (), "tree table") == 0)
306 {
307 // This is a table, it is not generally useful to look at the whole
308 // table, but rather just at the selected file, if any, otherwise the first
309 // file, if any.
310 decltype (component_target) sub_target = NULL;
311 auto selection = unique_gobject (atspi_accessible_get_selection_iface (component_target.get ()));
312
313 if (selection.get ())
314 {
315 sub_target = unique_gobject (atspi_selection_get_selected_child (selection.get (), 0, NULL));
316 }
317
318 if (sub_target.get () == NULL)
319 {
320 // No selection, try to get first real child.
321 unsigned i = 0;
322
323 while (1) {
324 sub_target = unique_gobject (atspi_accessible_get_child_at_index (component_target.get (), i, NULL));
325 i++;
326
327 if (sub_target.get () == NULL)
328 // No other children than table column header
329 break;
330
331 if (strcmp (unique_gmem (atspi_accessible_get_role_name (sub_target.get (), NULL)).get (), "table column header") != 0)
332 // Found a real child
333 break;
334 }
335 }
336
337 if (sub_target.get ())
338 component_target = unique_gobject_ref (sub_target.get ());
339 }
340
341 auto component = unique_gobject (atspi_accessible_get_component (component_target.get ()));
342
343 if (component.get ())
344 {
345 auto size = unique_gmem (atspi_component_get_extents (component.get (), ATSPI_COORD_TYPE_SCREEN, NULL));
346 res->x = size.get ()->x;
347 res->y = size.get ()->y;
348 res->w = size.get ()->width;
349 res->h = size.get ()->height;
350 }
351 // let's get the caret offset, and then its position for a caret event
352 if (strcmp (type, "caret") == 0)
353 {
354 auto text = unique_gobject (atspi_accessible_get_text (event->source));
355 if (!text.get ())
356 {
357 delete (res);
358 return;
359 }
360 auto offset = atspi_text_get_caret_offset (text.get (), NULL);
361 // if we are not at the beginning of the text, take the extent of the character under caret
362 // otherwise keep the whole widget
363 if (event->detail1)
364 {
365 auto size = unique_gmem (atspi_text_get_character_extents (text.get (), offset, ATSPI_COORD_TYPE_SCREEN, NULL));
366 res->x = size.get ()->x;
367 res->y = size.get ()->y;
368 res->w = size.get ()->width;
369 res->h = size.get ()->height;
370 }
371 // correcting a missing offset when caret is at end of text
372 if (((res->x == 0 && res->y == 0) ||
373 res->x + res->w < 0 ||
374 res->y + res->h < 0)
375 && offset > 0)
376 {
377 auto size = unique_gmem (atspi_text_get_character_extents (text.get (), offset-1, ATSPI_COORD_TYPE_SCREEN, NULL));
378 res->x = size.get ()->x;
379 res->y = size.get ()->y;
380 res->w = size.get ()->width;
381 res->h = size.get ()->height;
382 }
383 // when result is obviously not a caret size
384 if (strcmp (event->type, "object:text-caret-moved") == 0 && (res->w > A11YWATCHER_MAX_CARET_WIDTH || res->h > A11YWATCHER_MAX_CARET_HEIGHT))
385 {
386 auto size = unique_gmem (atspi_text_get_character_extents (text.get (), offset, ATSPI_COORD_TYPE_SCREEN, NULL));
387 res->x = size.get ()->x;
388 res->y = size.get ()->y;
389 res->w = size.get ()->width;
390 res->h = size.get ()->height;
391 if (strcmp (type, "caret") == 0 && strcmp (event->type, "object:text-caret-moved") == 0 && (res->w > A11YWATCHER_MAX_CARET_WIDTH || res->h > A11YWATCHER_MAX_CARET_HEIGHT))
392 {
393 res->x = 0;
394 res->y = 0;
395 }
396 }
397
398 // still no offset, it's probably a newline and we're at bugzilla #1319273 (with new paragraph obj)
399 if (res->x == 0 && res->y == 0 &&
400 (strcmp (event->type, "object:text-changed:insert") == 0 ||
401 strcmp (event->type, "object:text-changed:removed") == 0 ||
402 strcmp (event->type, "object:text-caret-moved") == 0)) {
403 res->x = res->xAlt;
404 res->y = res->yAlt;
405 res->w = res->wAlt;
406 res->h = res->hAlt;
407 }
408 }
409
410 // getting the states on event
411 auto stateSet = unique_gobject (atspi_accessible_get_state_set (event->source));
412 if (atspi_state_set_contains (stateSet.get (), ATSPI_STATE_FOCUSED))
413 {
414 res->focused = true;
415 // reset potential menu stack
416 for (auto info: previouslyActiveMenus) {
417 delete (info);
418 }
419 previouslyActiveMenus.clear ();
420 }
421 if (atspi_state_set_contains (stateSet.get (), ATSPI_STATE_SELECTED))
422 {
423 res->selected = true;
424 }
425 if (strcmp (res->type, "state-changed:selected") == 0 && event->detail1 == 1)
426 {
427 res->selected = true;
428 FocusInfo *dup = new FocusInfo (*res);
429 // add to stack of menus
430 previouslyActiveMenus.push_back (dup);
431 }
432
433 if (appSpecificFilter (res, event))
434 {
435 return;
436 }
437 if (filterBadEvents (res))
438 {
439 delete (res);
440 return;
441 }
442 while (focusList.size () >= 5) { // don't keep the whole history
443 auto iter = focusList.begin ();
444 auto info = *iter;
445 focusList.erase (iter);
446 delete (info);
447 }
448 focusList.push_back (res);
449}
450
451bool
452AccessibilityWatcher::appSpecificFilter (FocusInfo *focus, const AtspiEvent* event)
453{
454 if (strcmp (focus->type, "state-changed:selected") == 0 && // emulates on-change:selected missing event for menus
455 (strcmp (focus->role, "menu item") == 0 ||
456 strcmp (focus->role, "menu") == 0 ||
457 strcmp (focus->role, "check menu item") == 0 ||
458 strcmp (focus->role, "radio menu item") == 0 ||
459 strcmp (focus->role, "tearoff menu item") == 0) &&
460 strcmp (focus->application, "mate-panel") != 0)
461 {
462 if (!focus->selected && returnToPrevMenu ())
463 {
464 // The submenu item told us that he lost selection. We have thus
465 // returned to the parent menu (which unfortunately won't tell us
466 // anything)
467 delete (focus);
468 return true;
469 }
470 focus->active = true;
471 }
472 if (strcmp (focus->application, "soffice") == 0 && strcmp (focus->role, "paragraph") == 0)
473 { // LO-calc: avoid spam event from main edit line
474 auto parent = unique_gobject (atspi_accessible_get_parent (event->source, NULL));
475 auto parentLabel = unique_gmem (atspi_accessible_get_name (parent.get (), NULL));
476 if (!strcmp (parentLabel.get (), "Input line") ||
477 !strcmp (parentLabel.get (), "Ligne de saisie"))
478 {
479 delete (focus);
480 return true;
481 }
482 }
483 if (strcmp (focus->application, "Icedove") == 0 || strcmp (focus->application, "Thunderbird") == 0)
484 {
485 if (strcmp (focus->type, "caret") == 0)
486 {
487 auto text = unique_gobject (atspi_accessible_get_text (event->source)); // next if deals with a special newline char, that remained buggy. hypra issue #430
488 auto offset = atspi_text_get_caret_offset (text.get (), NULL);
489 auto string = unique_gmem (atspi_text_get_string_at_offset (text.get (), offset, ATSPI_TEXT_GRANULARITY_CHAR, NULL));
490 auto stringM1 = unique_gmem (atspi_text_get_string_at_offset (text.get (), offset - 1, ATSPI_TEXT_GRANULARITY_CHAR, NULL));
491 gchar character = string.get ()->content[0];
492 gchar characterM1 = stringM1.get ()->content[0];
493
494 if (offset == atspi_text_get_character_count (text.get (), NULL) && character == '\0' && characterM1 == '\n')
495 {
496 getAlternativeCaret (focus, event);
497 focus->x = focus->xAlt;
498 focus->y = focus->yAlt + focus->hAlt;
499 focus->w = focus->wAlt;
500 focus->h = focus->hAlt;
501 }
502 if (!(focus->x == 0 && focus->y == 0))
503 { // prevents compose window loss of tracking in HTML mode (active flag ok, but no focused flag)
504 focusList.push_back (focus);
505 return true;
506 }
507 auto component = unique_gobject (atspi_accessible_get_component (event->source));
508 if (component.get ())
509 {
510 auto size = unique_gmem (atspi_component_get_extents (component.get (), ATSPI_COORD_TYPE_SCREEN, NULL));
511 focus->x = size.get ()->x;
512 focus->y = size.get ()->y;
513 focus->w = 7;
514 focus->h = size.get ()->height;
515 focusList.push_back (focus);
516 return true;
517 }
518 }
519 }
520 if (strcmp (focus->application, "Firefox") == 0)
521 {
522 if (ignoreLinks && strcmp (focus->type, "caret") != 0 && strcmp (focus->role, "link") == 0)
523 {
524 delete (focus);
525 return true;
526 }
527 // prevents status bar focus in firefox
528 if (strcmp (focus->type, "caret") == 0 &&
529 (strcmp (event->type, "object:text-changed:insert:system") == 0 ||
530 strcmp (event->type, "object:text-changed:delete:system") == 0)) {
531 delete (focus);
532 return true;
533 }
534 if (strcmp (focus->type, "focus") == 0 && strcmp (focus->role, "document frame") == 0)
535 { // general page parasite event
536 delete (focus);
537 return true;
538 }
539 if (strcmp (focus->type, "caret") == 0 && !(focus->x == 0 && focus->y == 0))
540 {
541 focusList.push_back (focus);
542 return true;
543 }
544 getAlternativeCaret (focus, event);
545 if (strcmp (focus->type, "caret") == 0 && !(focus->xAlt == 0 && focus->yAlt == 0))
546 {
547 focus->x = focus->xAlt;
548 focus->y = focus->yAlt + focus->hAlt;
549 focus->w = focus->wAlt;
550 focus->h = focus->hAlt;
551 focusList.push_back (focus);
552 return true;
553 }
554 }
555 if (strcmp (focus->application, "evince") == 0 && strcmp (focus->type, "state-changed:selected") == 0 && strcmp (focus->role, "icon") == 0)
556 { // LO-calc: avoid spam event from main edit line
557 delete (focus);
558 return true; // ignores the parasite event from evince icon
559 }
560 return false;
561}
562
563bool
564AccessibilityWatcher::filterBadEvents (const FocusInfo *event)
565{
566 if (strcmp (event->type, "caret") == 0 && event->x ==0 && event->y == 0)
567 {
568 return true;
569 }
570 if (!event->active)
571 {
572 return true;
573 }
574 if (!event->focused && !event->selected)
575 {
576 return true;
577 }
578 if (event->w < 0 ||
579 event->h < 0)
580 {
581 return true;
582 }
583 if (event->x == 0 &&
584 event->y == 0 &&
585 event->w == 0 &&
586 event->h == 0)
587 {
588 return true;
589 }
590 if (event->x + event->w < 0 ||
591 event->y + event->h < 0)
592 {
593 return true;
594 }
595 if (getScreenWidth () != 0 && getScreenHeight () != 0 &&
596 (event->x > getScreenWidth () ||
597 event->y > getScreenHeight () ||
598 event->w > getScreenWidth () ||
599 event->h > getScreenHeight ()))
600 {
601 return true;
602 }
603 return false;
604}
605
606/*
607 * This simulates a "selected" event from the parent menu when closing
608 * a submenu.
609 */
610bool
611AccessibilityWatcher::returnToPrevMenu ()
612{
613 if (previouslyActiveMenus.size () > 1)
614 {
615 previouslyActiveMenus.pop_back ();
616 FocusInfo *dup = new FocusInfo (*previouslyActiveMenus.back ());
617 focusList.push_back (dup);
618 return true;
619 }
620 return false;
621}
622
623/*
624 * Tries to extrapolate a missing caret position from other text characters.
625 * is used as last resort when application doesn't respect at-spi standarts,
626 * or at-spi bugs.
627 */
628void
629AccessibilityWatcher::getAlternativeCaret (FocusInfo *focus, const AtspiEvent* event)
630{
631 auto text = unique_gobject (atspi_accessible_get_text (event->source));
632 if (!text.get ())
633 return;
634 auto offset = atspi_text_get_caret_offset (text.get (), NULL);
635 auto string = unique_gmem (atspi_text_get_string_at_offset (text.get (), offset, ATSPI_TEXT_GRANULARITY_CHAR, NULL));
636 gchar caretChar = string.get ()->content[0];
637
638 // if we're at a newline, sometimes at-spi isn't giving us a caret position. unknown bug in some apps.
639 if (caretChar == '\n' || caretChar == '\0')
640 {
641 // gives the last empty line the right focus.
642 int lines = atspi_text_get_character_count (text.get (), NULL) == offset ? 1 : 0;
643 int charIndex = 1;
644 bool charExtentsFound = false;
645
646 auto size = unique_gmem (atspi_text_get_character_extents (text.get (), offset, ATSPI_COORD_TYPE_SCREEN, NULL));
647 // try and find the character on upper line to extrapolate position from. no more that 300 char, we risk lag.
648 while (!charExtentsFound && charIndex <= offset && charIndex < 300) {
649 size = unique_gmem (atspi_text_get_character_extents (text.get (), offset - charIndex, ATSPI_COORD_TYPE_SCREEN, NULL));
650 string = unique_gmem (atspi_text_get_string_at_offset (text.get (), offset - charIndex, ATSPI_TEXT_GRANULARITY_CHAR, NULL));
651 caretChar = string.get ()->content[0];
652 // if we found a caret, check we're at beginning of line (or of text) to extrapolate position
653 if (size.get ()->x != 0 || size.get ()->y != 0)
654 {
655 if (offset - charIndex -1 >= 0 && unique_gmem (atspi_text_get_string_at_offset (text.get (), offset - charIndex -1, ATSPI_TEXT_GRANULARITY_CHAR, NULL)).get ()->content[0] == '\n')
656 {
657 charExtentsFound = true; // first character of upper line has been found
658 }
659 else if (offset - charIndex -1 == 0)
660 {
661 size = unique_gmem (atspi_text_get_character_extents (text.get (), 0, ATSPI_COORD_TYPE_SCREEN, NULL));
662 // first character of upper line has been found
663 charExtentsFound = true;
664 }
665 }
666 else if (caretChar == '\n')
667 {
668 ++lines;
669 }
670 ++charIndex;
671 }
672 focus->xAlt = size.get ()->x;
673 focus->yAlt = size.get ()->y + (lines-1) * size.get ()->height;
674 focus->wAlt = size.get ()->width;
675 focus->hAlt = size.get ()->height;
676 }
677}
678
679
680/* Register to events */
681void
682AccessibilityWatcher::addWatches ()
683{
684 atspi_event_listener_register (focusListener, "object:state-changed:focused", NULL);
685 atspi_event_listener_register (caretMoveListener, "object:text-caret-moved", NULL);
686 atspi_event_listener_register (caretMoveListener, "object:text-changed:inserted", NULL);
687 atspi_event_listener_register (caretMoveListener, "object:text-changed:removed", NULL);
688 atspi_event_listener_register (selectedListener, "object:state-changed:selected", NULL);
689 atspi_event_listener_register (descendantChangedListener, "object:active-descendant-changed", NULL);
690 mActive = true;
691}
692
693void
694AccessibilityWatcher::removeWatches ()
695{
696 atspi_event_listener_deregister (focusListener, "object:state-changed:focused", NULL);
697 atspi_event_listener_deregister (caretMoveListener, "object:text-caret-moved", NULL);
698 atspi_event_listener_deregister (caretMoveListener, "object:text-changed:inserted", NULL);
699 atspi_event_listener_deregister (caretMoveListener, "object:text-changed:removed", NULL);
700 atspi_event_listener_deregister (selectedListener, "object:state-changed:selected", NULL);
701 atspi_event_listener_deregister (descendantChangedListener, "object:active-descendant-changed", NULL);
702 mActive = false;
703}
704
705void
706AccessibilityWatcher::setActive (bool activate)
707{
708 if (mActive && !activate)
709 {
710 removeWatches ();
711 }
712 else if (!mActive && activate)
713 {
714 addWatches ();
715 }
716}
717
718std::deque <FocusInfo *>
719AccessibilityWatcher::getFocusQueue ()
720{
721 return focusList;
722}
723
724void
725AccessibilityWatcher::resetFocusQueue ()
726{
727 for (auto info: focusList) {
728 delete (info);
729 }
730 focusList.clear ();
731}
732
0733
=== added file 'plugins/focuspoll/src/focusinfo.cpp'
--- plugins/focuspoll/src/focusinfo.cpp 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/src/focusinfo.cpp 2018-10-23 08:53:35 +0000
@@ -0,0 +1,123 @@
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 "focusinfo.h"
21
22#include <stdio.h>
23#include <string.h>
24
25FocusInfo::FocusInfo (const gchar * type,
26 gchar * name,
27 gchar * label,
28 gchar * role,
29 gchar * application,
30 int x,
31 int y,
32 int width,
33 int height) :
34 x (x),
35 y (y),
36 w (width),
37 h (height),
38 xAlt (-1),
39 yAlt (-1),
40 wAlt (-1),
41 hAlt (-1),
42 type (type),
43 name (name),
44 label (label),
45 role (role),
46 application (application),
47 active (false),
48 focused (false),
49 selected (false)
50{
51}
52
53FocusInfo::FocusInfo (const FocusInfo &dup)
54{
55 x = dup.x;
56 y = dup.y;
57 w = dup.w;
58 h = dup.h;
59 xAlt = dup.xAlt;
60 yAlt = dup.yAlt;
61 wAlt = dup.wAlt;
62 hAlt = dup.hAlt;
63 type = dup.type;
64 name = strdup(dup.name);
65 label = strdup(dup.label);
66 role = strdup(dup.role);
67 application = strdup(dup.application);
68 active = dup.active;
69 focused = dup.focused;
70 selected = dup.selected;
71}
72
73FocusInfo::~FocusInfo (void)
74{
75 g_free (name);
76 g_free (label);
77 g_free (role);
78 g_free (application);
79}
80
81const gchar *
82FocusInfo::FocusInfo::getType (void)
83{
84 return type;
85}
86
87CompPoint
88FocusInfo::getPosition (void)
89{
90 return CompPoint (x, y);
91}
92
93CompSize
94FocusInfo::getSize (void)
95{
96 return CompSize (w, h);
97}
98
99CompRect
100FocusInfo::getBBox (void)
101{
102 return CompRect (x, y, w, h);
103}
104
105bool
106FocusInfo::operator== (const FocusInfo& other) const
107{
108 return (other.x == x &&
109 other.y == y &&
110 other.w == w &&
111 other.h == h &&
112 !strcmp (other.type, type) &&
113 !strcmp (other.name, name) &&
114 !strcmp (other.label, label) &&
115 !strcmp (other.application, application) &&
116 !strcmp (other.role, role));
117};
118
119bool
120FocusInfo::operator!= (const FocusInfo& other) const
121{
122 return !(*this == other);
123};
0124
=== added file 'plugins/focuspoll/src/focuspoll.cpp'
--- plugins/focuspoll/src/focuspoll.cpp 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/src/focuspoll.cpp 2018-10-23 08:53:35 +0000
@@ -0,0 +1,225 @@
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
22COMPIZ_PLUGIN_20090315 (focuspoll, FocuspollPluginVTable);
23
24bool
25FocuspollScreen::updatePosition ()
26{
27 if (!a11ywatcher->getFocusQueue ().empty ())
28 {
29 CompRect focusRect = a11ywatcher->getFocusQueue ().back ()->getBBox ();
30 for (std::list<FocusPoller *>::iterator it = pollers.begin (); it != pollers.end ();)
31 {
32 FocusPoller *poller = *it;
33 // Update iterator to next element before calling the callback which
34 // might remove the poller from the list.
35 ++it;
36 poller->mCallback (focusRect);
37 }
38 }
39 a11ywatcher->resetFocusQueue ();
40 return true;
41}
42
43bool
44FocuspollScreen::addTimer (FocusPoller *poller)
45{
46 bool start = pollers.empty ();
47
48 std::list <FocusPoller *>::iterator it =
49 std::find (pollers.begin (), pollers.end (), poller);
50
51 if (it != pollers.end ())
52 return false;
53
54 pollers.insert (it, poller);
55
56 if (start)
57 {
58 a11ywatcher->setActive (true);
59 timer.start ();
60 }
61
62 return true;
63}
64
65void
66FocuspollScreen::removeTimer (FocusPoller *poller)
67{
68 std::list <FocusPoller *>::iterator it =
69 std::find (pollers.begin (), pollers.end (), poller);
70
71 if (it == pollers.end ())
72 return;
73
74 pollers.erase (it);
75
76 if (pollers.empty ())
77 {
78 a11ywatcher->setActive (false);
79 timer.stop ();
80 }
81}
82
83void
84FocusPoller::setCallback (FocusPoller::CallBack callback)
85{
86 bool wasActive = mActive;
87
88 if (mActive)
89 stop ();
90
91 mCallback = callback;
92
93 if (wasActive)
94 start ();
95}
96
97void
98FocusPoller::start ()
99{
100 FOCUSPOLL_SCREEN (screen);
101
102 if (!fs)
103 {
104 compLogMessage ("focuspoll", CompLogLevelWarn,
105 "Plugin version mismatch, can't start focus poller.");
106
107 return;
108 }
109
110 if (mCallback.empty ())
111 {
112 compLogMessage ("focuspoll", CompLogLevelWarn,
113 "Can't start focus poller without callback.");
114 return;
115 }
116
117 fs->addTimer (this);
118
119 mActive = true;
120}
121
122void
123FocusPoller::stop ()
124{
125 FOCUSPOLL_SCREEN (screen);
126
127 /* Prevent broken plugins from calling stop () twice */
128
129 if (!mActive)
130 return;
131
132 if (!fs)
133 {
134 compLogMessage ("focuspoll",
135 CompLogLevelWarn,
136 "Plugin version mismatch, can't stop focus poller.");
137 return;
138 }
139
140 mActive = false;
141 fs->removeTimer (this);
142}
143
144bool
145FocusPoller::active ()
146{
147 return mActive;
148}
149
150FocusPoller::FocusPoller () :
151 mActive (false),
152 mCallback (NULL) {
153 }
154
155CompSize
156FocuspollScreen::getScreenLimits () {
157 int x =0, y = 0;
158 for (auto dev : screen->outputDevs ()) {
159 x = std::max (x, dev.x () + dev.width ());
160 y = std::max (y, dev.y () + dev.height ());
161 }
162 return CompSize (x, y);
163}
164
165void
166FocuspollScreen::updateTimer ()
167{
168 float timeout = optionGetFocusPollInterval ();
169 timer.setTimes (timeout, timeout * 1.5);
170}
171
172FocusPoller::~FocusPoller ()
173{
174 if (mActive) {
175 stop ();
176 }
177}
178
179void
180FocuspollScreen::setOptions ()
181{
182 a11ywatcher->setIgnoreLinks (optionGetIgnoreLinks ());
183}
184
185template class PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI>;
186
187FocuspollScreen::FocuspollScreen (CompScreen *screen) :
188 PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI> (screen)
189{
190 a11ywatcher = new AccessibilityWatcher ();
191
192 CompSize screenLimit = getScreenLimits ();
193 a11ywatcher->setScreenLimits (screenLimit.width (), screenLimit.height ());
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
203FocuspollScreen::~FocuspollScreen ()
204{
205 delete a11ywatcher;
206}
207
208bool
209FocuspollPluginVTable::init ()
210{
211 if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))
212 {
213 CompPrivate p;
214 p.uval = COMPIZ_FOCUSPOLL_ABI;
215 screen->storeValue ("focuspoll_ABI", p);
216 return true;
217 }
218 return false;
219}
220
221void
222FocuspollPluginVTable::fini ()
223{
224 screen->eraseValue ("focuspoll_ABI");
225}
0226
=== added file 'plugins/focuspoll/src/private.h'
--- plugins/focuspoll/src/private.h 1970-01-01 00:00:00 +0000
+++ plugins/focuspoll/src/private.h 2018-10-23 08:53:35 +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 <core/core.h>
21#include <core/pluginclasshandler.h>
22#include <core/timer.h>
23
24#include <focuspoll/focuspoll.h>
25#include <accessibilitywatcher/accessibilitywatcher.h>
26
27#include "focuspoll_options.h"
28
29typedef enum _FocuspollOptions
30{
31 MP_DISPLAY_OPTION_FOCUS_POLL_INTERVAL,
32 MP_DISPLAY_OPTION_NUM
33} FocuspollDisplayOptions;
34
35class FocuspollScreen;
36extern template class PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI>;
37
38class FocuspollScreen :
39 public PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI>,
40 public FocuspollOptions
41{
42 public:
43 FocuspollScreen (CompScreen *screen);
44 ~FocuspollScreen (void);
45
46 std::list <FocusPoller *> pollers;
47 CompTimer timer;
48 CompTimer settingsTimer;
49 CompRect focusRect;
50
51 bool updatePosition (void);
52 bool addTimer (FocusPoller *poller);
53 void removeTimer (FocusPoller *poller);
54 void updateTimer (void);
55
56 private:
57 AccessibilityWatcher* a11ywatcher;
58
59 CompSize getScreenLimits (void);
60 void setOptions (void);
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/mousepoll/src/mousepoll.cpp'
--- plugins/mousepoll/src/mousepoll.cpp 2013-05-16 10:47:54 +0000
+++ plugins/mousepoll/src/mousepoll.cpp 2018-10-23 08:53:35 +0000
@@ -32,6 +32,7 @@
32 int winX, winY;32 int winX, winY;
33 int w = screen->width (), h = screen->height ();33 int w = screen->width (), h = screen->height ();
34 unsigned int maskReturn;34 unsigned int maskReturn;
35 unsigned int newbuttons;
3536
36 bool status = XQueryPointer (screen->dpy (), screen->root (),37 bool status = XQueryPointer (screen->dpy (), screen->root (),
37 &root, &child, &rootX, &rootY,38 &root, &child, &rootX, &rootY,
@@ -40,12 +41,25 @@
40 if (!status || rootX > w || rootY > h || screen->root () != root)41 if (!status || rootX > w || rootY > h || screen->root () != root)
41 return false;42 return false;
4243
44 newbuttons = maskReturn &
45 (Button1Mask |
46 Button2Mask |
47 Button3Mask |
48 Button4Mask |
49 Button5Mask);
50
43 if (rootX != pos.x () || rootY != pos.y ())51 if (rootX != pos.x () || rootY != pos.y ())
44 {52 {
45 pos.set (rootX, rootY);53 pos.set (rootX, rootY);
46 return true;54 return true;
47 }55 }
4856
57 if (newbuttons != buttons)
58 {
59 buttons = newbuttons;
60 return true;
61 }
62
49 return false;63 return false;
50}64}
5165
5266
=== modified file 'plugins/mousepoll/src/private.h'
--- plugins/mousepoll/src/private.h 2013-06-28 10:21:21 +0000
+++ plugins/mousepoll/src/private.h 2018-10-23 08:53:35 +0000
@@ -49,6 +49,7 @@
49 CompTimer timer;49 CompTimer timer;
5050
51 CompPoint pos;51 CompPoint pos;
52 unsigned int buttons;
5253
53 bool54 bool
54 updatePosition ();55 updatePosition ();
5556
=== modified file 'plugins/showmouse/showmouse.xml.in'
--- plugins/showmouse/showmouse.xml.in 2018-04-11 13:25:42 +0000
+++ plugins/showmouse/showmouse.xml.in 2018-10-23 08:53:35 +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>
1720
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2018-04-11 13:07:18 +0000
+++ po/POTFILES.in 2018-10-23 08:53:35 +0000
@@ -31,6 +31,7 @@
31plugins/fade/fade.xml.in31plugins/fade/fade.xml.in
32plugins/fadedesktop/fadedesktop.xml.in32plugins/fadedesktop/fadedesktop.xml.in
33plugins/firepaint/firepaint.xml.in33plugins/firepaint/firepaint.xml.in
34plugins/focuspoll/focuspoll.xml.in
34plugins/freewins/freewins.xml.in35plugins/freewins/freewins.xml.in
35plugins/gears/gears.xml.in36plugins/gears/gears.xml.in
36plugins/gnomecompat/gnomecompat.xml.in37plugins/gnomecompat/gnomecompat.xml.in

Subscribers

People subscribed via source and target branches