Merge lp:~samuel-thibault/compiz/ezoom_focus_tracking into lp:compiz/0.9.13
- ezoom_focus_tracking
- Merge into 0.9.13
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 | ||||
Related bugs: |
|
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:/
- avoid the singleton technique
- avoid useless copies
- using smart pointers to avoid any memory leak
- better document at-spi workarounds
Samuel
- 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.
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
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:/
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
1 | === modified file 'debian/compiz-dev.install' |
2 | --- debian/compiz-dev.install 2016-07-14 15:21:59 +0000 |
3 | +++ debian/compiz-dev.install 2018-10-23 08:53:35 +0000 |
4 | @@ -7,6 +7,7 @@ |
5 | debian/tmp/usr/lib/*/pkgconfig/compiz-composite.pc |
6 | debian/tmp/usr/lib/*/pkgconfig/compiz-cube.pc |
7 | debian/tmp/usr/lib/*/pkgconfig/compiz-mousepoll.pc |
8 | +debian/tmp/usr/lib/*/pkgconfig/compiz-focuspoll.pc |
9 | debian/tmp/usr/lib/*/pkgconfig/compiz-opengl.pc |
10 | debian/tmp/usr/lib/*/pkgconfig/compiz.pc |
11 | debian/tmp/usr/lib/*/pkgconfig/compiz-scale.pc |
12 | |
13 | === modified file 'debian/compiz-plugins-default.install' |
14 | --- debian/compiz-plugins-default.install 2017-04-26 08:18:38 +0000 |
15 | +++ debian/compiz-plugins-default.install 2018-10-23 08:53:35 +0000 |
16 | @@ -26,6 +26,8 @@ |
17 | usr/share/compiz/*matecompat.* |
18 | usr/lib/*/compiz/*mousepoll.* |
19 | usr/share/compiz/*mousepoll.* |
20 | +usr/lib/*/compiz/*focuspoll.* |
21 | +usr/share/compiz/*focuspoll.* |
22 | usr/lib/*/compiz/*colorfilter.* |
23 | usr/share/compiz/*colorfilter.* |
24 | usr/share/compiz/colorfilter |
25 | |
26 | === modified file 'debian/control' |
27 | --- debian/control 2018-07-24 10:32:11 +0000 |
28 | +++ debian/control 2018-10-23 08:53:35 +0000 |
29 | @@ -15,6 +15,7 @@ |
30 | quilt (>= 0.40), |
31 | libcairo2-dev, |
32 | libdbus-glib-1-dev, |
33 | + libatspi2.0-dev, |
34 | libgl1-mesa-dev (>= 6.5.1) [!armhf !armel] | libgl-dev [!armhf !armel], |
35 | libegl1-mesa-dev [armhf armel], libgles2-mesa-dev [armhf armel], |
36 | libboost-dev, |
37 | |
38 | === modified file 'plugins/ezoom/CMakeLists.txt' |
39 | --- plugins/ezoom/CMakeLists.txt 2012-05-16 17:40:27 +0000 |
40 | +++ plugins/ezoom/CMakeLists.txt 2018-10-23 08:53:35 +0000 |
41 | @@ -2,4 +2,4 @@ |
42 | |
43 | include (CompizPlugin) |
44 | |
45 | -compiz_plugin (ezoom PLUGINDEPS composite opengl mousepoll) |
46 | +compiz_plugin (ezoom PLUGINDEPS composite opengl mousepoll focuspoll) |
47 | |
48 | === modified file 'plugins/ezoom/ezoom.xml.in' |
49 | --- plugins/ezoom/ezoom.xml.in 2018-07-11 15:30:30 +0000 |
50 | +++ plugins/ezoom/ezoom.xml.in 2018-10-23 08:53:35 +0000 |
51 | @@ -7,6 +7,8 @@ |
52 | <plugin>expo</plugin> |
53 | <plugin>decor</plugin> |
54 | <plugin>mousepoll</plugin> |
55 | + <plugin>focuspoll</plugin> |
56 | + <plugin>showmouse</plugin> |
57 | </relation> |
58 | <relation type="before"> |
59 | <plugin>staticswitcher</plugin> |
60 | @@ -15,6 +17,7 @@ |
61 | <requirement> |
62 | <plugin>opengl</plugin> |
63 | <plugin>mousepoll</plugin> |
64 | + <plugin>focuspoll</plugin> |
65 | </requirement> |
66 | </deps> |
67 | <_short>Enhanced Zoom Desktop</_short> |
68 | @@ -440,12 +443,17 @@ |
69 | <_short>Focus Tracking</_short> |
70 | <option type="bool" name="follow_focus"> |
71 | <_short>Enable focus tracking</_short> |
72 | - <_long>Move the zoom area when focus changes.</_long> |
73 | + <_long>Move the zoom area when focus changes. You may see the mousepoll and focuspoll modules options for more</_long> |
74 | <default>true</default> |
75 | </option> |
76 | + <option type="bool" name="follow_window_focus"> |
77 | + <_short>Enable focus tracking when switching windows</_short> |
78 | + <_long>Move the zoom area to the center of a newly switched window.</_long> |
79 | + <default>false</default> |
80 | + </option> |
81 | <option type="bool" name="focus_fit_window"> |
82 | - <_short>Fit zoom level to window on focus change</_short> |
83 | - <_long>Fit the zoomed area to the window when the zoomed area moves as a result of focus tracking.</_long> |
84 | + <_short>Fit zoom level to newly switched window</_short> |
85 | + <_long>Fit the zoomed area to the window when it switches.</_long> |
86 | <default>false</default> |
87 | </option> |
88 | <option type="float" name="autoscale_min"> |
89 | @@ -458,15 +466,39 @@ |
90 | </option> |
91 | <option type="bool" name="always_focus_fit_window"> |
92 | <_short>Always fit to window on focus track</_short> |
93 | - <_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> |
94 | + <_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> |
95 | <default>false</default> |
96 | </option> |
97 | - <option type="int" name="follow_focus_delay"> |
98 | - <_short>Follow Focus Delay</_short> |
99 | - <_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> |
100 | - <default>0</default> |
101 | - <min>0</min> |
102 | - <max>15</max> |
103 | + <option type="float" name="follow_focus_delay"> |
104 | + <_short>Tracking delay from mouse to focus mode</_short> |
105 | + <_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> |
106 | + <default>1</default> |
107 | + <min>0</min> |
108 | + <max>5</max> |
109 | + <precision>0.1</precision> |
110 | + </option> |
111 | + <option type="float" name="follow_mouse_delay"> |
112 | + <_short>Tracking delay from focus to mouse mode</_short> |
113 | + <_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> |
114 | + <default>0.1</default> |
115 | + <min>0</min> |
116 | + <max>5</max> |
117 | + <precision>0.1</precision> |
118 | + </option> |
119 | + <option type="bool" name="always_center_mouse"> |
120 | + <_short>Keep Mouse centered inside zoom area</_short> |
121 | + <_long>If activated, the mouse will always be centered when in mouse sync mode, if not, it will move around the zoom area</_long> |
122 | + <default>true</default> |
123 | + </option> |
124 | + <option type="bool" name="restrain_zoom_to_screen"> |
125 | + <_short>Restrain the zoom area inside the screen</_short> |
126 | + <_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> |
127 | + <default>true</default> |
128 | + </option> |
129 | + <option type="bool" name="warp_mouse_to_focus"> |
130 | + <_short>When switching focus back to mouse, warp mouse inside the zoomed area</_short> |
131 | + <_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> |
132 | + <default>true</default> |
133 | </option> |
134 | </group> |
135 | <group> |
136 | @@ -476,7 +508,7 @@ |
137 | <_long>Zoom Change Speed</_long> |
138 | <default>25</default> |
139 | <min>0.1</min> |
140 | - <max>100</max> |
141 | + <max>500</max> |
142 | <precision>0.1</precision> |
143 | </option> |
144 | <option type="float" name="timestep"> |
145 | @@ -487,6 +519,11 @@ |
146 | <max>50</max> |
147 | <precision>0.1</precision> |
148 | </option> |
149 | + <option type="bool" name="instant_pan"> |
150 | + <_short>Move the zoom area instantly</_short> |
151 | + <_long>When enabled, zoom area is warped instantly to focus position. When disabled, zoom area makes a smooth move to the focus position</_long> |
152 | + <default>false</default> |
153 | + </option> |
154 | </group> |
155 | </options> |
156 | </plugin> |
157 | |
158 | === modified file 'plugins/ezoom/src/ezoom.cpp' |
159 | --- plugins/ezoom/src/ezoom.cpp 2017-12-05 14:45:06 +0000 |
160 | +++ plugins/ezoom/src/ezoom.cpp 2018-10-23 08:53:35 +0000 |
161 | @@ -168,6 +168,39 @@ |
162 | zs->gScreen->setTextureFilter (oldFilter); |
163 | } |
164 | |
165 | +/* Return current time in seconds */ |
166 | +static inline double |
167 | +getTime (void) |
168 | +{ |
169 | + struct timespec tp; |
170 | + clock_gettime (CLOCK_MONOTONIC, &tp); |
171 | + return tp.tv_sec + tp.tv_nsec / 1e9; |
172 | +} |
173 | + |
174 | +/* This returns the proper translation correction depending on the desired |
175 | + * centering (exact center, small overflow, or exact screen fit) */ |
176 | +float |
177 | +EZoomScreen::getTranslateCorrection (float zoom) |
178 | +{ |
179 | + if (optionGetAlwaysCenterMouse ()) |
180 | + { |
181 | + // Bring to X,Y position the exact zoom center instead of |
182 | + // fitting screen |
183 | + return 1.0; |
184 | + } |
185 | + else if (!optionGetRestrainZoomToScreen ()) |
186 | + { |
187 | + // Overflow a bit beyond screen so we can clearly see the corner and the |
188 | + // mouse |
189 | + return (1.0f - zoom) / EZOOM_SCREEN_BORDER_OFFSET; |
190 | + } |
191 | + else |
192 | + { |
193 | + // Keep an exact fit to screen |
194 | + return 1.0f - zoom; |
195 | + } |
196 | +} |
197 | + |
198 | /* Returns the distance to the defined edge in zoomed pixels. */ |
199 | int |
200 | EZoomScreen::distanceToEdge (int out, |
201 | @@ -197,10 +230,26 @@ |
202 | |
203 | /* Update/set translations based on zoom level and real translate. */ |
204 | void |
205 | -EZoomScreen::ZoomArea::updateActualTranslates () |
206 | +EZoomScreen::ZoomArea::updateActualTranslates (EZoomScreen *screen) |
207 | { |
208 | - xtrans = -realXTranslate * (1.0f - currentZoom); |
209 | - ytrans = realYTranslate * (1.0f - currentZoom); |
210 | + float translateCorrection; |
211 | + float limit = 0.5 * (1.0f - currentZoom); |
212 | + |
213 | + translateCorrection = screen->getTranslateCorrection(currentZoom); |
214 | + |
215 | + xtrans = -realXTranslate * translateCorrection; |
216 | + ytrans = realYTranslate * translateCorrection; |
217 | + |
218 | + if (screen->optionGetRestrainZoomToScreen ()) { |
219 | + if (xtrans < -limit) |
220 | + xtrans = -limit; |
221 | + if (xtrans > limit) |
222 | + xtrans = limit; |
223 | + if (ytrans < -limit) |
224 | + ytrans = -limit; |
225 | + if (ytrans > limit) |
226 | + ytrans = limit; |
227 | + } |
228 | } |
229 | |
230 | /* Returns true if the head in question is currently moving. |
231 | @@ -244,7 +293,6 @@ |
232 | ytrans (0.0f), |
233 | locked (false) |
234 | { |
235 | - updateActualTranslates (); |
236 | } |
237 | |
238 | EZoomScreen::ZoomArea::ZoomArea () : |
239 | @@ -365,7 +413,7 @@ |
240 | |
241 | adjustXYVelocity (out, chunk); |
242 | adjustZoomVelocity (out, chunk); |
243 | - zooms.at (out).updateActualTranslates (); |
244 | + zooms.at (out).updateActualTranslates (this); |
245 | |
246 | if (!isZoomed (out)) |
247 | { |
248 | @@ -597,8 +645,6 @@ |
249 | * mouse pointer. This is to allow input, and is NOT necessary |
250 | * when input redirection is available to us or if we're cheating |
251 | * and using a scaled mouse cursor to imitate IR. |
252 | - * The center is not the center of the screen. This is the target-center; |
253 | - * that is, it's the point that's the same regardless of zoom level. |
254 | */ |
255 | void |
256 | EZoomScreen::setCenter (int x, |
257 | @@ -616,13 +662,13 @@ |
258 | zooms.at (out).yTranslate = (float) |
259 | ((y - o->y1 ()) - o->height () / 2) / (o->height ()); |
260 | |
261 | - if (instant) |
262 | + if (instant || optionGetInstantPan ()) |
263 | { |
264 | zooms.at (out).realXTranslate = zooms.at (out).xTranslate; |
265 | zooms.at (out).realYTranslate = zooms.at (out).yTranslate; |
266 | zooms.at (out).yVelocity = 0.0f; |
267 | zooms.at (out).xVelocity = 0.0f; |
268 | - zooms.at (out).updateActualTranslates (); |
269 | + zooms.at (out).updateActualTranslates (this); |
270 | } |
271 | |
272 | if (optionGetZoomMode () == EzoomOptions::ZoomModePanArea) |
273 | @@ -661,7 +707,7 @@ |
274 | { |
275 | zooms.at (out).realXTranslate = zooms.at (out).xTranslate; |
276 | zooms.at (out).realYTranslate = zooms.at (out).yTranslate; |
277 | - zooms.at (out).updateActualTranslates (); |
278 | + zooms.at (out).updateActualTranslates (this); |
279 | } |
280 | |
281 | if (optionGetZoomMode () == EzoomOptions::ZoomModePanArea) |
282 | @@ -703,9 +749,21 @@ |
283 | void |
284 | EZoomScreen::enableMousePolling () |
285 | { |
286 | - pollHandle.start (); |
287 | - lastChange = time(NULL); |
288 | - mouse = MousePoller::getCurrentPosition (); |
289 | + pollMouseHandle.start (); |
290 | + lastMouseChange = getTime (); |
291 | + mouse = MousePoller::getCurrentPosition (); |
292 | +} |
293 | + |
294 | +/* Enables polling of focus position */ |
295 | +void |
296 | +EZoomScreen::enableFocusPolling () |
297 | +{ |
298 | + if (!optionGetFollowFocus ()) |
299 | + { |
300 | + return; |
301 | + } |
302 | + pollFocusHandle.start (); |
303 | + lastFocusChange = getTime (); |
304 | } |
305 | |
306 | /* Sets the zoom (or scale) level. |
307 | @@ -722,9 +780,12 @@ |
308 | value = 1.0f; |
309 | else |
310 | { |
311 | - if (!pollHandle.active ()) |
312 | + if (!pollMouseHandle.active ()) |
313 | enableMousePolling (); |
314 | |
315 | + if (!pollFocusHandle.active ()) |
316 | + enableFocusPolling (); |
317 | + |
318 | grabbed |= (1 << zooms.at (out).output); |
319 | cursorZoomActive (out); |
320 | } |
321 | @@ -770,7 +831,7 @@ |
322 | { |
323 | int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ()); |
324 | |
325 | - if (!isInMovement (out)) |
326 | + if (!isInMovement (out) || nonMouseFocusTracking) |
327 | return; |
328 | |
329 | CompOutput *o = &screen->outputDevs ().at (out); |
330 | @@ -810,17 +871,30 @@ |
331 | int oHeight = o->height (); |
332 | int halfOWidth = oWidth / 2; |
333 | int halfOHeight = oHeight / 2; |
334 | + float translateCorrection = getTranslateCorrection (za.currentZoom); |
335 | + float xTranslate = za.realXTranslate * translateCorrection ; |
336 | + float yTranslate = za.realYTranslate * translateCorrection ; |
337 | + float limit = 0.5 * (1.0 - za.currentZoom); |
338 | + |
339 | + if (optionGetRestrainZoomToScreen ()) { |
340 | + if (xTranslate < -limit) |
341 | + xTranslate = -limit; |
342 | + if (xTranslate > limit) |
343 | + xTranslate = limit; |
344 | + if (yTranslate < -limit) |
345 | + yTranslate = -limit; |
346 | + if (yTranslate > limit) |
347 | + yTranslate = limit; |
348 | + } |
349 | |
350 | x -= o->x1 (); |
351 | y -= o->y1 (); |
352 | |
353 | - *resultX = x - (za.realXTranslate * |
354 | - (1.0f - za.currentZoom) * oWidth) - halfOWidth; |
355 | + *resultX = x - (xTranslate * oWidth) - halfOWidth; |
356 | *resultX /= za.currentZoom; |
357 | *resultX += halfOWidth; |
358 | *resultX += o->x1 (); |
359 | - *resultY = y - (za.realYTranslate * |
360 | - (1.0f - za.currentZoom) * oHeight) - halfOHeight; |
361 | + *resultY = y - (yTranslate * oHeight) - halfOHeight; |
362 | *resultY /= za.currentZoom; |
363 | *resultY += halfOHeight; |
364 | *resultY += o->y1 (); |
365 | @@ -1058,7 +1132,7 @@ |
366 | |
367 | if (zooms.at (out).currentZoom == 1.0f) |
368 | { |
369 | - lastChange = time(NULL); |
370 | + lastMouseChange = getTime (); |
371 | mouse = MousePoller::getCurrentPosition (); |
372 | } |
373 | |
374 | @@ -1129,20 +1203,88 @@ |
375 | void |
376 | EZoomScreen::updateMousePosition (const CompPoint &p) |
377 | { |
378 | + auto localTime = getTime (); |
379 | mouse.setX (p.x ()); |
380 | mouse.setY (p.y ()); |
381 | |
382 | int out = screen->outputDeviceForPoint (mouse.x (), mouse.y ()); |
383 | - lastChange = time(NULL); |
384 | - |
385 | - if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse && |
386 | - !isInMovement (out)) |
387 | - setCenter (mouse.x (), mouse.y (), true); |
388 | - |
389 | + if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse) |
390 | + { |
391 | + if (optionGetWarpMouseToFocus ()) { |
392 | + CompOutput *o = &screen->outputDevs ().at (out); |
393 | + int zoomedMouseX, zoomedMouseY; |
394 | + bool pointerInZoom; |
395 | + |
396 | + convertToZoomed (out, mouse.x (), mouse.y (), |
397 | + &zoomedMouseX, &zoomedMouseY); |
398 | + pointerInZoom = zoomedMouseX >= o->x1 () |
399 | + && zoomedMouseX < o->x1 () + o->width () |
400 | + && zoomedMouseY >= o->y1 () |
401 | + && zoomedMouseY < o->y1 () + o->height (); |
402 | + |
403 | + if (lastMouseChange <= lastFocusChange && nonMouseFocusTracking && !pointerInZoom) |
404 | + { |
405 | + // Mouse taking back control of focus, but is out of zoom, wrap |
406 | + // it back to focus |
407 | + // FocusTracking is always centered |
408 | + screen->warpPointer ( |
409 | + zooms.at (out).realXTranslate * o->width () |
410 | + + o->width () / 2 + o->x1 () - pointerX, |
411 | + zooms.at (out).realYTranslate * o->height () |
412 | + + o->height () / 2 + o->y1 () - pointerY); |
413 | + setCenter (pointerX, pointerY, true); |
414 | + } |
415 | + else |
416 | + { |
417 | + if (!isInMovement (out)) |
418 | + { |
419 | + setCenter (mouse.x (), mouse.y (), true); |
420 | + } |
421 | + } |
422 | + lastMouseChange = localTime; |
423 | + } |
424 | + // respect a timing in case user has merely grazed his mouse |
425 | + else if (localTime - lastFocusChange > optionGetFollowMouseDelay () && !isInMovement (out)) |
426 | + { |
427 | + setCenter (mouse.x (), mouse.y (), true); |
428 | + lastMouseChange = localTime; |
429 | + } |
430 | + nonMouseFocusTracking = false; |
431 | + } |
432 | cursorMoved (); |
433 | cScreen->damageScreen (); |
434 | } |
435 | |
436 | +void |
437 | +EZoomScreen::updateFocusPosition (const CompRect &rect) |
438 | +{ |
439 | + auto localTime = getTime (); |
440 | + int out = screen->outputDeviceForPoint (rect.x (), rect.y ()); |
441 | + if (localTime - lastMouseChange > optionGetFollowFocusDelay ()) |
442 | + { |
443 | + int zoomAreaWidth = screen->outputDevs ().at (out).width () * zooms.at (out).newZoom; |
444 | + int zoomAreaHeight = screen->outputDevs ().at (out).height () * zooms.at (out).newZoom; |
445 | + int posX = rect.x () + rect.width () / 2; |
446 | + int posY = rect.y () + rect.height () / 2; |
447 | + if (rect.width () > zoomAreaWidth) |
448 | + { |
449 | + // target rectangle is too big for the zoom area, aim at |
450 | + // the top-left corner of the target |
451 | + posX -= (rect.width () - zoomAreaWidth) / 2; |
452 | + } |
453 | + if (rect.height () > zoomAreaHeight) |
454 | + { |
455 | + posY -= (rect.height () - zoomAreaHeight) / 2; |
456 | + } |
457 | + setCenter (posX, posY, false); |
458 | + focus.setX (posX); |
459 | + focus.setY (posY); |
460 | + nonMouseFocusTracking = true; |
461 | + lastFocusChange = localTime; |
462 | + } |
463 | + cScreen->damageScreen (); |
464 | +} |
465 | + |
466 | /* Timeout handler to poll the mouse. Returns false (and thereby does not |
467 | * get re-added to the queue) when zoom is not active. */ |
468 | void |
469 | @@ -1154,11 +1296,21 @@ |
470 | { |
471 | cursorMoved (); |
472 | |
473 | - if (pollHandle.active ()) |
474 | - pollHandle.stop (); |
475 | + if (pollMouseHandle.active ()) |
476 | + pollMouseHandle.stop (); |
477 | } |
478 | } |
479 | |
480 | +/* Timeout handler to focusPoll. */ |
481 | +void |
482 | +EZoomScreen::updateFocusInterval (const CompRect &rect) |
483 | +{ |
484 | + updateFocusPosition (rect); |
485 | + |
486 | + if (!grabbed && pollFocusHandle.active ()) |
487 | + pollFocusHandle.stop (); |
488 | +} |
489 | + |
490 | /* Free a cursor */ |
491 | void |
492 | EZoomScreen::freeCursor (CursorTexture *cursor) |
493 | @@ -1586,7 +1738,7 @@ |
494 | int out = screen->outputDeviceForPoint (pointerX, pointerY); |
495 | |
496 | if (optionGetZoomMode () == EzoomOptions::ZoomModeSyncMouse && |
497 | - !isInMovement (out)) |
498 | + !isInMovement (out) && !nonMouseFocusTracking) |
499 | setCenter (pointerX, pointerY, true); |
500 | |
501 | setScale (out, zooms.at (out).newZoom / optionGetZoomFactor ()); |
502 | @@ -1914,8 +2066,8 @@ |
503 | |
504 | if (w == NULL || |
505 | w->id () == screen->activeWindow () || |
506 | - time(NULL) - lastChange < optionGetFollowFocusDelay () || |
507 | - !optionGetFollowFocus ()) |
508 | + getTime () - lastMouseChange < optionGetFollowFocusDelay () || |
509 | + !optionGetFollowWindowFocus ()) |
510 | return; |
511 | |
512 | int out = screen->outputDeviceForGeometry (w->geometry ()); |
513 | @@ -2008,7 +2160,9 @@ |
514 | gScreen (GLScreen::get (screen)), |
515 | grabbed (0), |
516 | grabIndex (0), |
517 | - lastChange (0), |
518 | + lastMouseChange (0.), |
519 | + lastFocusChange (0.), |
520 | + nonMouseFocusTracking (false), |
521 | cursorInfoSelected (false), |
522 | cursorHidden (false) |
523 | { |
524 | @@ -2042,8 +2196,10 @@ |
525 | zooms.push_back (za); |
526 | } |
527 | |
528 | - pollHandle.setCallback (boost::bind ( |
529 | - &EZoomScreen::updateMouseInterval, this, _1)); |
530 | + pollMouseHandle.setCallback (boost::bind (&EZoomScreen::updateMouseInterval, |
531 | + this, _1)); |
532 | + pollFocusHandle.setCallback (boost::bind (&EZoomScreen::updateFocusInterval, |
533 | + this, _1)); |
534 | |
535 | optionSetZoomInButtonInitiate (boost::bind (&EZoomScreen::zoomIn, this, _1, |
536 | _2, _3)); |
537 | @@ -2136,8 +2292,10 @@ |
538 | |
539 | EZoomScreen::~EZoomScreen () |
540 | { |
541 | - if (pollHandle.active ()) |
542 | - pollHandle.stop (); |
543 | + if (pollMouseHandle.active ()) |
544 | + pollMouseHandle.stop (); |
545 | + if (pollFocusHandle.active ()) |
546 | + pollFocusHandle.stop (); |
547 | |
548 | if (zooms.size ()) |
549 | zooms.clear (); |
550 | @@ -2152,7 +2310,8 @@ |
551 | if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION) && |
552 | CompPlugin::checkPluginABI ("composite", COMPIZ_COMPOSITE_ABI) && |
553 | CompPlugin::checkPluginABI ("opengl", COMPIZ_OPENGL_ABI) && |
554 | - CompPlugin::checkPluginABI ("mousepoll", COMPIZ_MOUSEPOLL_ABI)) |
555 | + CompPlugin::checkPluginABI ("mousepoll", COMPIZ_MOUSEPOLL_ABI) && |
556 | + CompPlugin::checkPluginABI ("focuspoll", COMPIZ_FOCUSPOLL_ABI)) |
557 | return true; |
558 | |
559 | return false; |
560 | |
561 | === modified file 'plugins/ezoom/src/ezoom.h' |
562 | --- plugins/ezoom/src/ezoom.h 2016-07-01 15:37:19 +0000 |
563 | +++ plugins/ezoom/src/ezoom.h 2018-10-23 08:53:35 +0000 |
564 | @@ -43,12 +43,19 @@ |
565 | #include <composite/composite.h> |
566 | #include <opengl/opengl.h> |
567 | #include <mousepoll/mousepoll.h> |
568 | +#include <focuspoll/focuspoll.h> |
569 | |
570 | |
571 | #include "ezoom_options.h" |
572 | |
573 | #include <cmath> |
574 | |
575 | +// Some Users wish to see better the edges of the screen. Without this, mouse |
576 | +// reaches the border of the zoomarea around screen edges, and mouse can become |
577 | +// invisible (especially around bottom/right). Zooming out a bit allows to see |
578 | +// both the edge and the mouse reaching the edge. |
579 | +#define EZOOM_SCREEN_BORDER_OFFSET 0.92 |
580 | + |
581 | enum SpecificZoomTarget |
582 | { |
583 | ZoomTarget1 = 0, |
584 | @@ -160,25 +167,29 @@ |
585 | ZoomArea (); |
586 | |
587 | void |
588 | - updateActualTranslates (); |
589 | + updateActualTranslates (EZoomScreen *screen); |
590 | }; |
591 | |
592 | public: |
593 | |
594 | std::vector <ZoomArea> zooms; // list of zooms (different zooms for each output) |
595 | CompPoint mouse; // we get this from mousepoll |
596 | + CompPoint focus; // we get this from focuspoll |
597 | unsigned long int grabbed; |
598 | CompScreen::GrabHandle grabIndex; // for zoomBox |
599 | - time_t lastChange; |
600 | + double lastMouseChange; |
601 | + double lastFocusChange; |
602 | CursorTexture cursor; // the texture for the faux-cursor |
603 | // we paint to do fake input |
604 | // handling |
605 | + bool nonMouseFocusTracking; |
606 | bool cursorInfoSelected; |
607 | bool cursorHidden; |
608 | CompRect box; |
609 | CompPoint clickPos; |
610 | |
611 | - MousePoller pollHandle; // mouse poller object |
612 | + MousePoller pollMouseHandle; // mouse poller object |
613 | + FocusPoller pollFocusHandle; // focus poller object |
614 | |
615 | private: |
616 | |
617 | @@ -222,6 +233,9 @@ |
618 | adjustXYVelocity (int out, |
619 | float chunk); |
620 | |
621 | + float |
622 | + getTranslateCorrection (float zoom); |
623 | + |
624 | void |
625 | drawBox (const GLMatrix &transform, |
626 | CompOutput *output, |
627 | @@ -231,6 +245,11 @@ |
628 | setCenter (int x, |
629 | int y, |
630 | bool instant); |
631 | + void |
632 | + setCenter (int x, |
633 | + int y, |
634 | + bool instant, |
635 | + bool center); |
636 | |
637 | void |
638 | setZoomArea (int x, |
639 | @@ -250,6 +269,9 @@ |
640 | enableMousePolling (); |
641 | |
642 | void |
643 | + enableFocusPolling (); |
644 | + |
645 | + void |
646 | setScale (int out, |
647 | float value); |
648 | |
649 | @@ -295,6 +317,12 @@ |
650 | void |
651 | updateMouseInterval (const CompPoint &p); |
652 | |
653 | + void |
654 | + updateFocusPosition (const CompRect &retc); |
655 | + |
656 | + void |
657 | + updateFocusInterval (const CompRect &rect); |
658 | + |
659 | /* Make dtor */ |
660 | void |
661 | freeCursor (CursorTexture * cursor); |
662 | |
663 | === added directory 'plugins/focuspoll' |
664 | === added file 'plugins/focuspoll/CMakeLists.txt' |
665 | --- plugins/focuspoll/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
666 | +++ plugins/focuspoll/CMakeLists.txt 2018-10-23 08:53:35 +0000 |
667 | @@ -0,0 +1,9 @@ |
668 | +find_package (Compiz REQUIRED) |
669 | + |
670 | +include (CompizPlugin) |
671 | +include_directories (include/accessibilitywatcher) |
672 | + |
673 | +pkg_search_module (ATSPI REQUIRED atspi-2) |
674 | + |
675 | +compiz_plugin (focuspoll PKGDEPS atspi-2) |
676 | + |
677 | |
678 | === added file 'plugins/focuspoll/compiz-focuspoll.pc.in' |
679 | --- plugins/focuspoll/compiz-focuspoll.pc.in 1970-01-01 00:00:00 +0000 |
680 | +++ plugins/focuspoll/compiz-focuspoll.pc.in 2018-10-23 08:53:35 +0000 |
681 | @@ -0,0 +1,12 @@ |
682 | +prefix=@prefix@ |
683 | +exec_prefix=@exec_prefix@ |
684 | +libdir=@libdir@ |
685 | +includedir=@includedir@ |
686 | + |
687 | +Name: compiz-focuspoll |
688 | +Description: Focuspoll plugin for compiz |
689 | +Version: @VERSION@ |
690 | + |
691 | +Requires: compiz |
692 | +Libs: -L${libdir}/compiz -lfocuspoll |
693 | +Cflags: @COMPIZ_CFLAGS@ -I${includedir}/compiz |
694 | |
695 | === added file 'plugins/focuspoll/focuspoll.xml.in' |
696 | --- plugins/focuspoll/focuspoll.xml.in 1970-01-01 00:00:00 +0000 |
697 | +++ plugins/focuspoll/focuspoll.xml.in 2018-10-23 08:53:35 +0000 |
698 | @@ -0,0 +1,29 @@ |
699 | +<?xml version="1.0" encoding="UTF-8"?> |
700 | +<compiz> |
701 | + <plugin name="focuspoll" useBcop="true"> |
702 | + <deps> |
703 | + <relation type="after"> |
704 | + <plugin>opengl</plugin> |
705 | + </relation> |
706 | + </deps> |
707 | + <_short>Focus position polling</_short> |
708 | + <_long>Updates the focus pointer position from the accessibility library AT-SPI through dbus</_long> |
709 | + <category>Utility</category> |
710 | + <options> |
711 | + <_short>Misc</_short> |
712 | + <option type="bool" name="ignore_links"> |
713 | + <_short>Ignores link focuses</_short> |
714 | + <_long>If enabled, focuspoll will not track the focus events of hypertext link items</_long> |
715 | + <default>true</default> |
716 | + </option> |
717 | + <option type="int" name="focus_poll_interval"> |
718 | + <_short>Focus Poll Interval</_short> |
719 | + <_long>How often to poll DBus for focus changes, in milliseconds. Reduce this to reduce choppy behavior.</_long> |
720 | + <default>10</default> |
721 | + <min>1</min> |
722 | + <max>500</max> |
723 | + </option> |
724 | + </options> |
725 | + </plugin> |
726 | +</compiz> |
727 | + |
728 | |
729 | === added directory 'plugins/focuspoll/include' |
730 | === added directory 'plugins/focuspoll/include/accessibilitywatcher' |
731 | === added file 'plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h' |
732 | --- plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h 1970-01-01 00:00:00 +0000 |
733 | +++ plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h 2018-10-23 08:53:35 +0000 |
734 | @@ -0,0 +1,70 @@ |
735 | +/* |
736 | + * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net> |
737 | + * |
738 | + * This file is part of compiz. |
739 | + * |
740 | + * this program is free software: you can redistribute it and/or modify |
741 | + * it under the terms of the GNU General Public License as published by the Free |
742 | + * Software Foundation, either version 3 of the License, or (at your option) any |
743 | + * later version. |
744 | + * |
745 | + * This program is distributed in the hope that it will be useful, but |
746 | + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
747 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
748 | + * details. |
749 | + * |
750 | + * You should have received a copy of the GNU General Public License along |
751 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
752 | + */ |
753 | + |
754 | +#ifndef ACCESSIBILITY_WATCHER_H |
755 | +#define ACCESSIBILITY_WATCHER_H |
756 | + |
757 | +#include <deque> |
758 | +#include <vector> |
759 | + |
760 | +#include "focusinfo.h" |
761 | + |
762 | +#include <atspi/atspi.h> |
763 | + |
764 | +class AccessibilityWatcher |
765 | +{ |
766 | + public: |
767 | + AccessibilityWatcher (); |
768 | + ~AccessibilityWatcher (); |
769 | + |
770 | + void setActive (bool); |
771 | + |
772 | + void setIgnoreLinks (bool); |
773 | + void setScreenLimits (int, int); |
774 | + int getScreenWidth (void); |
775 | + int getScreenHeight (void); |
776 | + |
777 | + std::deque<FocusInfo *> getFocusQueue (void); |
778 | + void resetFocusQueue (void); |
779 | + bool returnToPrevMenu (void); |
780 | + |
781 | + void registerEvent (const AtspiEvent *event, const gchar *type); |
782 | + |
783 | + private: |
784 | + bool mActive; |
785 | + int screenWidth; |
786 | + int screenHeight; |
787 | + static bool ignoreLinks; |
788 | + std::deque<FocusInfo*> focusList; |
789 | + std::vector<FocusInfo*> previouslyActiveMenus; |
790 | + |
791 | + AtspiEventListener *focusListener; |
792 | + AtspiEventListener *caretMoveListener; |
793 | + AtspiEventListener *selectedListener; |
794 | + AtspiEventListener *descendantChangedListener; |
795 | + |
796 | + void addWatches (void); |
797 | + void removeWatches (void); |
798 | + |
799 | + bool appSpecificFilter (FocusInfo *focusInfo, const AtspiEvent* event); |
800 | + bool filterBadEvents (const FocusInfo *event); |
801 | + void getAlternativeCaret (FocusInfo *focus, const AtspiEvent* event); |
802 | +}; |
803 | + |
804 | +#endif |
805 | |
806 | === added file 'plugins/focuspoll/include/accessibilitywatcher/focusinfo.h' |
807 | --- plugins/focuspoll/include/accessibilitywatcher/focusinfo.h 1970-01-01 00:00:00 +0000 |
808 | +++ plugins/focuspoll/include/accessibilitywatcher/focusinfo.h 2018-10-23 08:53:35 +0000 |
809 | @@ -0,0 +1,71 @@ |
810 | +/* |
811 | + * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net> |
812 | + * |
813 | + * This file is part of compiz. |
814 | + * |
815 | + * this program is free software: you can redistribute it and/or modify |
816 | + * it under the terms of the GNU General Public License as published by the Free |
817 | + * Software Foundation, either version 3 of the License, or (at your option) any |
818 | + * later version. |
819 | + * |
820 | + * This program is distributed in the hope that it will be useful, but |
821 | + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
822 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
823 | + * details. |
824 | + * |
825 | + * You should have received a copy of the GNU General Public License along |
826 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
827 | + */ |
828 | + |
829 | +#ifndef FOCUS_INFO_H |
830 | +#define FOCUS_INFO_H |
831 | + |
832 | +#include <string> |
833 | +#include <sstream> |
834 | +#include <glib.h> |
835 | +#include <core/point.h> |
836 | +#include <core/size.h> |
837 | +#include <core/rect.h> |
838 | + |
839 | + |
840 | +class FocusInfo |
841 | +{ |
842 | + public: |
843 | + |
844 | + FocusInfo (const gchar * type = "", |
845 | + gchar * name = g_strdup (""), |
846 | + gchar * label = g_strdup (""), |
847 | + gchar * role = g_strdup (""), |
848 | + gchar * application = g_strdup (""), |
849 | + int x = -1, |
850 | + int y = -1, |
851 | + int width = -1, |
852 | + int height = -1); |
853 | + |
854 | + FocusInfo (const FocusInfo &dup); |
855 | + |
856 | + ~FocusInfo (); |
857 | + |
858 | + int x, y, w, h; |
859 | + int xAlt, yAlt, wAlt, hAlt; |
860 | + const gchar * type; |
861 | + gchar * name; |
862 | + gchar * label; |
863 | + gchar * role; |
864 | + gchar * application; |
865 | + |
866 | + // AT-SPI events that are interesting to know about the event |
867 | + bool active; |
868 | + bool focused; |
869 | + bool selected; |
870 | + |
871 | + const gchar * getType (void); |
872 | + CompPoint getPosition (void); |
873 | + CompSize getSize (void); |
874 | + CompRect getBBox (void); |
875 | + |
876 | + bool operator== (const FocusInfo &other) const; |
877 | + bool operator!= (const FocusInfo &other) const; |
878 | +}; |
879 | + |
880 | +#endif |
881 | |
882 | === added directory 'plugins/focuspoll/include/focuspoll' |
883 | === added file 'plugins/focuspoll/include/focuspoll/focuspoll.h' |
884 | --- plugins/focuspoll/include/focuspoll/focuspoll.h 1970-01-01 00:00:00 +0000 |
885 | +++ plugins/focuspoll/include/focuspoll/focuspoll.h 2018-10-23 08:53:35 +0000 |
886 | @@ -0,0 +1,50 @@ |
887 | +/* |
888 | + * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net> |
889 | + * |
890 | + * This file is part of compiz. |
891 | + * |
892 | + * this program is free software: you can redistribute it and/or modify |
893 | + * it under the terms of the GNU General Public License as published by the Free |
894 | + * Software Foundation, either version 3 of the License, or (at your option) any |
895 | + * later version. |
896 | + * |
897 | + * This program is distributed in the hope that it will be useful, but |
898 | + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
899 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
900 | + * details. |
901 | + * |
902 | + * You should have received a copy of the GNU General Public License along |
903 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
904 | + */ |
905 | + |
906 | +#ifndef _COMPIZ_FOCUSPOLL_H |
907 | +#define _COMPIZ_FOCUSPOLL_H |
908 | + |
909 | +#define COMPIZ_FOCUSPOLL_ABI 1 |
910 | + |
911 | +#include <core/rect.h> |
912 | +#include <boost/function.hpp> |
913 | +#include <accessibilitywatcher/focusinfo.h> |
914 | + |
915 | +class FocusPoller |
916 | +{ |
917 | + public: |
918 | + typedef boost::function <void (const CompRect &)> CallBack; |
919 | + |
920 | + FocusPoller (); |
921 | + ~FocusPoller (); |
922 | + |
923 | + void setCallback (CallBack callback); |
924 | + void start (void); |
925 | + void stop (void); |
926 | + bool active (void); |
927 | + |
928 | + private: |
929 | + bool mActive; |
930 | + CallBack mCallback; |
931 | + CompRect focusRect; |
932 | + |
933 | + friend class FocuspollScreen; |
934 | +}; |
935 | + |
936 | +#endif |
937 | |
938 | === added directory 'plugins/focuspoll/src' |
939 | === added file 'plugins/focuspoll/src/accessibilitywatcher.cpp' |
940 | --- plugins/focuspoll/src/accessibilitywatcher.cpp 1970-01-01 00:00:00 +0000 |
941 | +++ plugins/focuspoll/src/accessibilitywatcher.cpp 2018-10-23 08:53:35 +0000 |
942 | @@ -0,0 +1,732 @@ |
943 | +/* |
944 | + * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net> |
945 | + * Copyright (C) 2018 Samuel Thibault <sthibault@hypra.fr> |
946 | + * |
947 | + * This file is part of compiz. |
948 | + * |
949 | + * this program is free software: you can redistribute it and/or modify |
950 | + * it under the terms of the GNU General Public License as published by the Free |
951 | + * Software Foundation, either version 3 of the License, or (at your option) any |
952 | + * later version. |
953 | + * |
954 | + * This program is distributed in the hope that it will be useful, but |
955 | + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
956 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
957 | + * details. |
958 | + * |
959 | + * You should have received a copy of the GNU General Public License along |
960 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
961 | + */ |
962 | + |
963 | +#include <iostream> |
964 | +#include <memory> |
965 | + |
966 | +#include <string.h> |
967 | +#include <stdlib.h> |
968 | +#include <unistd.h> |
969 | + |
970 | +#include "accessibilitywatcher.h" |
971 | + |
972 | +// When getting a bounding box bigger than this size, consider that this is not |
973 | +// a precise caret bounding box, and try to find another source of precise caret position |
974 | +// TODO: make them options |
975 | +namespace { |
976 | + const int A11YWATCHER_MAX_CARET_WIDTH = 50; |
977 | + const int A11YWATCHER_MAX_CARET_HEIGHT = 70; |
978 | +} |
979 | + |
980 | +/* |
981 | + * Wrapper that automatically calls g_object_unref on pointer scope release. |
982 | + * |
983 | + * Instead of |
984 | + * |
985 | + * AtspiFoo x = atspi_... () |
986 | + * atspi_... (x); |
987 | + * g_object_unref (x); |
988 | + * |
989 | + * use |
990 | + * |
991 | + * auto x = unique_gobject (atspi_... ());; |
992 | + * atspi_... (x.get ()); |
993 | + * |
994 | + * And instead of |
995 | + * |
996 | + * AtspiFoo x = event->source; |
997 | + * g_object_ref (x); |
998 | + * atspi_... (x); |
999 | + * g_object_unref (x); |
1000 | + * |
1001 | + * use |
1002 | + * |
1003 | + * auto x = unique_gobject_ref (event->source);; |
1004 | + * atspi_... (x.get ()); |
1005 | + * */ |
1006 | + |
1007 | +struct unique_gobject_deleter { |
1008 | + void operator () (gpointer obj) |
1009 | + { |
1010 | + if (obj) |
1011 | + g_object_unref (obj); |
1012 | + } |
1013 | +}; |
1014 | + |
1015 | +template <typename T> |
1016 | +std::unique_ptr <T, unique_gobject_deleter> |
1017 | +unique_gobject (T* ptr) |
1018 | +{ |
1019 | + |
1020 | + return std::unique_ptr <T, unique_gobject_deleter> (ptr); |
1021 | +} |
1022 | + |
1023 | +template <typename T> |
1024 | +std::unique_ptr <T, unique_gobject_deleter> |
1025 | +unique_gobject_ref (T* ptr) |
1026 | +{ |
1027 | + if (ptr) |
1028 | + g_object_ref (ptr); |
1029 | + return std::unique_ptr <T, unique_gobject_deleter> (ptr); |
1030 | +} |
1031 | + |
1032 | +/* |
1033 | + * Similarly for a g_array of gobjects. |
1034 | + */ |
1035 | + |
1036 | +struct unique_gobject_garray_deleter { |
1037 | + void operator () (GArray *array) |
1038 | + { |
1039 | + for (guint i = 0; i < array->len; ++i) |
1040 | + { |
1041 | + gpointer obj = g_array_index (array, gpointer, i); |
1042 | + if (obj) |
1043 | + g_object_unref (obj); |
1044 | + } |
1045 | + g_array_unref (array); |
1046 | + } |
1047 | +}; |
1048 | + |
1049 | +template <typename T> |
1050 | +std::unique_ptr <T, unique_gobject_garray_deleter> |
1051 | +unique_gobject_garray (T* ptr) |
1052 | +{ |
1053 | + |
1054 | + return std::unique_ptr <T, unique_gobject_garray_deleter> (ptr); |
1055 | +} |
1056 | + |
1057 | +/* |
1058 | + * Similarly for calling g_free on pointer scope release. |
1059 | + */ |
1060 | + |
1061 | +struct unique_gmem_deleter { |
1062 | + void operator () (gpointer obj) |
1063 | + { |
1064 | + g_free (obj); |
1065 | + } |
1066 | +}; |
1067 | + |
1068 | +template <typename T> |
1069 | +std::unique_ptr <T, unique_gmem_deleter> |
1070 | +unique_gmem (T* ptr) |
1071 | +{ |
1072 | + |
1073 | + return std::unique_ptr <T, unique_gmem_deleter> (ptr); |
1074 | +} |
1075 | + |
1076 | + |
1077 | + |
1078 | +bool AccessibilityWatcher::ignoreLinks = false; |
1079 | + |
1080 | +static void |
1081 | +onCaretMove (const AtspiEvent *event, void *data) |
1082 | +{ |
1083 | + AccessibilityWatcher *watcher = (AccessibilityWatcher *) data; |
1084 | + watcher->registerEvent (event, "caret"); |
1085 | +} |
1086 | + |
1087 | +static void |
1088 | +onSelectedChange (const AtspiEvent *event, void *data) |
1089 | +{ |
1090 | + AccessibilityWatcher *watcher = (AccessibilityWatcher *) data; |
1091 | + watcher->registerEvent (event, "state-changed:selected"); |
1092 | +} |
1093 | + |
1094 | +static void |
1095 | +onFocus (const AtspiEvent *event, void *data) |
1096 | +{ |
1097 | + /* We only care about focus/selection gain |
1098 | + * there's no detail1 on focus loss in AT-SPI specs */ |
1099 | + if (!event->detail1) |
1100 | + return; |
1101 | + |
1102 | + AccessibilityWatcher *watcher = (AccessibilityWatcher *) data; |
1103 | + watcher->registerEvent (event, "focus"); |
1104 | +} |
1105 | + |
1106 | +static void |
1107 | +onDescendantChanged (const AtspiEvent *event, void *data) |
1108 | +{ |
1109 | + AccessibilityWatcher *watcher = (AccessibilityWatcher *) data; |
1110 | + watcher->registerEvent (event, "active-descendant-changed"); |
1111 | +} |
1112 | + |
1113 | +AccessibilityWatcher::AccessibilityWatcher () : |
1114 | + mActive (false), |
1115 | + screenWidth (0), |
1116 | + screenHeight (0), |
1117 | + focusListener (NULL), |
1118 | + caretMoveListener (NULL), |
1119 | + selectedListener (NULL), |
1120 | + descendantChangedListener (NULL) |
1121 | +{ |
1122 | + atspi_init (); |
1123 | + atspi_set_main_context (g_main_context_default ()); |
1124 | + |
1125 | + focusListener = atspi_event_listener_new (reinterpret_cast <AtspiEventListenerCB> (onFocus), this, NULL); |
1126 | + caretMoveListener = atspi_event_listener_new (reinterpret_cast <AtspiEventListenerCB> (onCaretMove), this, NULL); |
1127 | + selectedListener = atspi_event_listener_new (reinterpret_cast <AtspiEventListenerCB> (onSelectedChange), this, NULL); |
1128 | + descendantChangedListener = atspi_event_listener_new (reinterpret_cast <AtspiEventListenerCB> (onDescendantChanged), this, NULL); |
1129 | + |
1130 | + addWatches (); |
1131 | +} |
1132 | + |
1133 | +AccessibilityWatcher::~AccessibilityWatcher () |
1134 | +{ |
1135 | + removeWatches (); |
1136 | + g_object_unref (focusListener); |
1137 | + g_object_unref (caretMoveListener); |
1138 | + g_object_unref (selectedListener); |
1139 | + g_object_unref (descendantChangedListener); |
1140 | +}; |
1141 | + |
1142 | +static gchar * |
1143 | +getLabel (AtspiAccessible *accessible) |
1144 | +{ |
1145 | + auto relations = unique_gobject_garray (atspi_accessible_get_relation_set (accessible, NULL)); |
1146 | + if (relations.get () == NULL) |
1147 | + { |
1148 | + return g_strdup (""); |
1149 | + } |
1150 | + |
1151 | + for (guint i = 0; i < relations.get ()->len; ++i) { |
1152 | + AtspiRelation *relation = g_array_index (relations.get (), AtspiRelation*, i); |
1153 | + if (relation == NULL) |
1154 | + continue; |
1155 | + if (atspi_relation_get_relation_type (relation) == ATSPI_RELATION_LABELLED_BY) |
1156 | + { |
1157 | + auto target = unique_gobject (atspi_relation_get_target (relation, 0)); |
1158 | + gchar * res_label = atspi_accessible_get_name (target.get (), NULL); |
1159 | + return (res_label == NULL) ? g_strdup ("") : res_label; |
1160 | + } |
1161 | + } |
1162 | + return g_strdup (""); |
1163 | +} |
1164 | + |
1165 | +void |
1166 | +AccessibilityWatcher::setIgnoreLinks (bool val) |
1167 | +{ |
1168 | + ignoreLinks = val; |
1169 | +} |
1170 | + |
1171 | +void |
1172 | +AccessibilityWatcher::setScreenLimits (int x, int y) |
1173 | +{ |
1174 | + screenWidth = x; |
1175 | + screenHeight = y; |
1176 | +} |
1177 | + |
1178 | +int |
1179 | +AccessibilityWatcher::getScreenWidth () |
1180 | +{ |
1181 | + return screenWidth; |
1182 | +} |
1183 | + |
1184 | +int |
1185 | +AccessibilityWatcher::getScreenHeight () |
1186 | +{ |
1187 | + return screenHeight; |
1188 | +} |
1189 | + |
1190 | +void |
1191 | +AccessibilityWatcher::registerEvent (const AtspiEvent *event, const gchar *type) |
1192 | +{ |
1193 | + // type is registered from filter on calling event |
1194 | + auto application = unique_gobject (atspi_accessible_get_application (event->source, NULL)); |
1195 | + FocusInfo *res = new FocusInfo (type, |
1196 | + atspi_accessible_get_name (event->source, NULL), |
1197 | + getLabel (event->source), |
1198 | + atspi_accessible_get_role_name (event->source, NULL), |
1199 | + atspi_accessible_get_name (application.get (), NULL)); |
1200 | + |
1201 | + if (!res->active) |
1202 | + { |
1203 | + // prevents skipping events that are not designated as active. we check the activeness of parents. |
1204 | + auto parent = unique_gobject (atspi_accessible_get_parent (event->source, NULL)); |
1205 | + while (parent.get ()) |
1206 | + { |
1207 | + auto stateSet = unique_gobject (atspi_accessible_get_state_set (parent.get ())); |
1208 | + if (atspi_state_set_contains (stateSet.get (), ATSPI_STATE_ACTIVE)) |
1209 | + { |
1210 | + res->active = true; |
1211 | + } |
1212 | + if (atspi_state_set_contains (stateSet.get (), ATSPI_STATE_EXPANDABLE)) |
1213 | + { |
1214 | + if (!atspi_state_set_contains (stateSet.get (), ATSPI_STATE_EXPANDED)) |
1215 | + { |
1216 | + auto role = unique_gmem (atspi_accessible_get_role_name (parent.get (), NULL)); |
1217 | + if (!strcmp (role.get (), "menu")) |
1218 | + { |
1219 | + // parent is expandable but not expanded, we do not want to track what is happening inside |
1220 | + delete (res); |
1221 | + return; |
1222 | + } |
1223 | + } |
1224 | + } |
1225 | + auto child = unique_gobject (atspi_accessible_get_parent (parent.get (), NULL)); |
1226 | + if (child.get () == parent.get ()) |
1227 | + { |
1228 | + // parent loop !? escape this trap... |
1229 | + break; |
1230 | + } |
1231 | + parent = unique_gobject_ref (child.get ()); |
1232 | + } |
1233 | + } |
1234 | + |
1235 | + auto component_target = unique_gobject_ref (event->source); |
1236 | + |
1237 | + if (strcmp (res->type, "active-descendant-changed") == 0) |
1238 | + { |
1239 | + component_target = unique_gobject (atspi_accessible_get_child_at_index (component_target.get (), event->detail1, NULL)); |
1240 | + if (!component_target.get ()) |
1241 | + { |
1242 | + delete (res); |
1243 | + return; |
1244 | + } |
1245 | + } |
1246 | + |
1247 | + if (strcmp (unique_gmem (atspi_accessible_get_role_name (component_target.get (), NULL)).get (), "tree table") == 0) |
1248 | + { |
1249 | + // This is a table, it is not generally useful to look at the whole |
1250 | + // table, but rather just at the selected file, if any, otherwise the first |
1251 | + // file, if any. |
1252 | + decltype (component_target) sub_target = NULL; |
1253 | + auto selection = unique_gobject (atspi_accessible_get_selection_iface (component_target.get ())); |
1254 | + |
1255 | + if (selection.get ()) |
1256 | + { |
1257 | + sub_target = unique_gobject (atspi_selection_get_selected_child (selection.get (), 0, NULL)); |
1258 | + } |
1259 | + |
1260 | + if (sub_target.get () == NULL) |
1261 | + { |
1262 | + // No selection, try to get first real child. |
1263 | + unsigned i = 0; |
1264 | + |
1265 | + while (1) { |
1266 | + sub_target = unique_gobject (atspi_accessible_get_child_at_index (component_target.get (), i, NULL)); |
1267 | + i++; |
1268 | + |
1269 | + if (sub_target.get () == NULL) |
1270 | + // No other children than table column header |
1271 | + break; |
1272 | + |
1273 | + if (strcmp (unique_gmem (atspi_accessible_get_role_name (sub_target.get (), NULL)).get (), "table column header") != 0) |
1274 | + // Found a real child |
1275 | + break; |
1276 | + } |
1277 | + } |
1278 | + |
1279 | + if (sub_target.get ()) |
1280 | + component_target = unique_gobject_ref (sub_target.get ()); |
1281 | + } |
1282 | + |
1283 | + auto component = unique_gobject (atspi_accessible_get_component (component_target.get ())); |
1284 | + |
1285 | + if (component.get ()) |
1286 | + { |
1287 | + auto size = unique_gmem (atspi_component_get_extents (component.get (), ATSPI_COORD_TYPE_SCREEN, NULL)); |
1288 | + res->x = size.get ()->x; |
1289 | + res->y = size.get ()->y; |
1290 | + res->w = size.get ()->width; |
1291 | + res->h = size.get ()->height; |
1292 | + } |
1293 | + // let's get the caret offset, and then its position for a caret event |
1294 | + if (strcmp (type, "caret") == 0) |
1295 | + { |
1296 | + auto text = unique_gobject (atspi_accessible_get_text (event->source)); |
1297 | + if (!text.get ()) |
1298 | + { |
1299 | + delete (res); |
1300 | + return; |
1301 | + } |
1302 | + auto offset = atspi_text_get_caret_offset (text.get (), NULL); |
1303 | + // if we are not at the beginning of the text, take the extent of the character under caret |
1304 | + // otherwise keep the whole widget |
1305 | + if (event->detail1) |
1306 | + { |
1307 | + auto size = unique_gmem (atspi_text_get_character_extents (text.get (), offset, ATSPI_COORD_TYPE_SCREEN, NULL)); |
1308 | + res->x = size.get ()->x; |
1309 | + res->y = size.get ()->y; |
1310 | + res->w = size.get ()->width; |
1311 | + res->h = size.get ()->height; |
1312 | + } |
1313 | + // correcting a missing offset when caret is at end of text |
1314 | + if (((res->x == 0 && res->y == 0) || |
1315 | + res->x + res->w < 0 || |
1316 | + res->y + res->h < 0) |
1317 | + && offset > 0) |
1318 | + { |
1319 | + auto size = unique_gmem (atspi_text_get_character_extents (text.get (), offset-1, ATSPI_COORD_TYPE_SCREEN, NULL)); |
1320 | + res->x = size.get ()->x; |
1321 | + res->y = size.get ()->y; |
1322 | + res->w = size.get ()->width; |
1323 | + res->h = size.get ()->height; |
1324 | + } |
1325 | + // when result is obviously not a caret size |
1326 | + if (strcmp (event->type, "object:text-caret-moved") == 0 && (res->w > A11YWATCHER_MAX_CARET_WIDTH || res->h > A11YWATCHER_MAX_CARET_HEIGHT)) |
1327 | + { |
1328 | + auto size = unique_gmem (atspi_text_get_character_extents (text.get (), offset, ATSPI_COORD_TYPE_SCREEN, NULL)); |
1329 | + res->x = size.get ()->x; |
1330 | + res->y = size.get ()->y; |
1331 | + res->w = size.get ()->width; |
1332 | + res->h = size.get ()->height; |
1333 | + 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)) |
1334 | + { |
1335 | + res->x = 0; |
1336 | + res->y = 0; |
1337 | + } |
1338 | + } |
1339 | + |
1340 | + // still no offset, it's probably a newline and we're at bugzilla #1319273 (with new paragraph obj) |
1341 | + if (res->x == 0 && res->y == 0 && |
1342 | + (strcmp (event->type, "object:text-changed:insert") == 0 || |
1343 | + strcmp (event->type, "object:text-changed:removed") == 0 || |
1344 | + strcmp (event->type, "object:text-caret-moved") == 0)) { |
1345 | + res->x = res->xAlt; |
1346 | + res->y = res->yAlt; |
1347 | + res->w = res->wAlt; |
1348 | + res->h = res->hAlt; |
1349 | + } |
1350 | + } |
1351 | + |
1352 | + // getting the states on event |
1353 | + auto stateSet = unique_gobject (atspi_accessible_get_state_set (event->source)); |
1354 | + if (atspi_state_set_contains (stateSet.get (), ATSPI_STATE_FOCUSED)) |
1355 | + { |
1356 | + res->focused = true; |
1357 | + // reset potential menu stack |
1358 | + for (auto info: previouslyActiveMenus) { |
1359 | + delete (info); |
1360 | + } |
1361 | + previouslyActiveMenus.clear (); |
1362 | + } |
1363 | + if (atspi_state_set_contains (stateSet.get (), ATSPI_STATE_SELECTED)) |
1364 | + { |
1365 | + res->selected = true; |
1366 | + } |
1367 | + if (strcmp (res->type, "state-changed:selected") == 0 && event->detail1 == 1) |
1368 | + { |
1369 | + res->selected = true; |
1370 | + FocusInfo *dup = new FocusInfo (*res); |
1371 | + // add to stack of menus |
1372 | + previouslyActiveMenus.push_back (dup); |
1373 | + } |
1374 | + |
1375 | + if (appSpecificFilter (res, event)) |
1376 | + { |
1377 | + return; |
1378 | + } |
1379 | + if (filterBadEvents (res)) |
1380 | + { |
1381 | + delete (res); |
1382 | + return; |
1383 | + } |
1384 | + while (focusList.size () >= 5) { // don't keep the whole history |
1385 | + auto iter = focusList.begin (); |
1386 | + auto info = *iter; |
1387 | + focusList.erase (iter); |
1388 | + delete (info); |
1389 | + } |
1390 | + focusList.push_back (res); |
1391 | +} |
1392 | + |
1393 | +bool |
1394 | +AccessibilityWatcher::appSpecificFilter (FocusInfo *focus, const AtspiEvent* event) |
1395 | +{ |
1396 | + if (strcmp (focus->type, "state-changed:selected") == 0 && // emulates on-change:selected missing event for menus |
1397 | + (strcmp (focus->role, "menu item") == 0 || |
1398 | + strcmp (focus->role, "menu") == 0 || |
1399 | + strcmp (focus->role, "check menu item") == 0 || |
1400 | + strcmp (focus->role, "radio menu item") == 0 || |
1401 | + strcmp (focus->role, "tearoff menu item") == 0) && |
1402 | + strcmp (focus->application, "mate-panel") != 0) |
1403 | + { |
1404 | + if (!focus->selected && returnToPrevMenu ()) |
1405 | + { |
1406 | + // The submenu item told us that he lost selection. We have thus |
1407 | + // returned to the parent menu (which unfortunately won't tell us |
1408 | + // anything) |
1409 | + delete (focus); |
1410 | + return true; |
1411 | + } |
1412 | + focus->active = true; |
1413 | + } |
1414 | + if (strcmp (focus->application, "soffice") == 0 && strcmp (focus->role, "paragraph") == 0) |
1415 | + { // LO-calc: avoid spam event from main edit line |
1416 | + auto parent = unique_gobject (atspi_accessible_get_parent (event->source, NULL)); |
1417 | + auto parentLabel = unique_gmem (atspi_accessible_get_name (parent.get (), NULL)); |
1418 | + if (!strcmp (parentLabel.get (), "Input line") || |
1419 | + !strcmp (parentLabel.get (), "Ligne de saisie")) |
1420 | + { |
1421 | + delete (focus); |
1422 | + return true; |
1423 | + } |
1424 | + } |
1425 | + if (strcmp (focus->application, "Icedove") == 0 || strcmp (focus->application, "Thunderbird") == 0) |
1426 | + { |
1427 | + if (strcmp (focus->type, "caret") == 0) |
1428 | + { |
1429 | + auto text = unique_gobject (atspi_accessible_get_text (event->source)); // next if deals with a special newline char, that remained buggy. hypra issue #430 |
1430 | + auto offset = atspi_text_get_caret_offset (text.get (), NULL); |
1431 | + auto string = unique_gmem (atspi_text_get_string_at_offset (text.get (), offset, ATSPI_TEXT_GRANULARITY_CHAR, NULL)); |
1432 | + auto stringM1 = unique_gmem (atspi_text_get_string_at_offset (text.get (), offset - 1, ATSPI_TEXT_GRANULARITY_CHAR, NULL)); |
1433 | + gchar character = string.get ()->content[0]; |
1434 | + gchar characterM1 = stringM1.get ()->content[0]; |
1435 | + |
1436 | + if (offset == atspi_text_get_character_count (text.get (), NULL) && character == '\0' && characterM1 == '\n') |
1437 | + { |
1438 | + getAlternativeCaret (focus, event); |
1439 | + focus->x = focus->xAlt; |
1440 | + focus->y = focus->yAlt + focus->hAlt; |
1441 | + focus->w = focus->wAlt; |
1442 | + focus->h = focus->hAlt; |
1443 | + } |
1444 | + if (!(focus->x == 0 && focus->y == 0)) |
1445 | + { // prevents compose window loss of tracking in HTML mode (active flag ok, but no focused flag) |
1446 | + focusList.push_back (focus); |
1447 | + return true; |
1448 | + } |
1449 | + auto component = unique_gobject (atspi_accessible_get_component (event->source)); |
1450 | + if (component.get ()) |
1451 | + { |
1452 | + auto size = unique_gmem (atspi_component_get_extents (component.get (), ATSPI_COORD_TYPE_SCREEN, NULL)); |
1453 | + focus->x = size.get ()->x; |
1454 | + focus->y = size.get ()->y; |
1455 | + focus->w = 7; |
1456 | + focus->h = size.get ()->height; |
1457 | + focusList.push_back (focus); |
1458 | + return true; |
1459 | + } |
1460 | + } |
1461 | + } |
1462 | + if (strcmp (focus->application, "Firefox") == 0) |
1463 | + { |
1464 | + if (ignoreLinks && strcmp (focus->type, "caret") != 0 && strcmp (focus->role, "link") == 0) |
1465 | + { |
1466 | + delete (focus); |
1467 | + return true; |
1468 | + } |
1469 | + // prevents status bar focus in firefox |
1470 | + if (strcmp (focus->type, "caret") == 0 && |
1471 | + (strcmp (event->type, "object:text-changed:insert:system") == 0 || |
1472 | + strcmp (event->type, "object:text-changed:delete:system") == 0)) { |
1473 | + delete (focus); |
1474 | + return true; |
1475 | + } |
1476 | + if (strcmp (focus->type, "focus") == 0 && strcmp (focus->role, "document frame") == 0) |
1477 | + { // general page parasite event |
1478 | + delete (focus); |
1479 | + return true; |
1480 | + } |
1481 | + if (strcmp (focus->type, "caret") == 0 && !(focus->x == 0 && focus->y == 0)) |
1482 | + { |
1483 | + focusList.push_back (focus); |
1484 | + return true; |
1485 | + } |
1486 | + getAlternativeCaret (focus, event); |
1487 | + if (strcmp (focus->type, "caret") == 0 && !(focus->xAlt == 0 && focus->yAlt == 0)) |
1488 | + { |
1489 | + focus->x = focus->xAlt; |
1490 | + focus->y = focus->yAlt + focus->hAlt; |
1491 | + focus->w = focus->wAlt; |
1492 | + focus->h = focus->hAlt; |
1493 | + focusList.push_back (focus); |
1494 | + return true; |
1495 | + } |
1496 | + } |
1497 | + if (strcmp (focus->application, "evince") == 0 && strcmp (focus->type, "state-changed:selected") == 0 && strcmp (focus->role, "icon") == 0) |
1498 | + { // LO-calc: avoid spam event from main edit line |
1499 | + delete (focus); |
1500 | + return true; // ignores the parasite event from evince icon |
1501 | + } |
1502 | + return false; |
1503 | +} |
1504 | + |
1505 | +bool |
1506 | +AccessibilityWatcher::filterBadEvents (const FocusInfo *event) |
1507 | +{ |
1508 | + if (strcmp (event->type, "caret") == 0 && event->x ==0 && event->y == 0) |
1509 | + { |
1510 | + return true; |
1511 | + } |
1512 | + if (!event->active) |
1513 | + { |
1514 | + return true; |
1515 | + } |
1516 | + if (!event->focused && !event->selected) |
1517 | + { |
1518 | + return true; |
1519 | + } |
1520 | + if (event->w < 0 || |
1521 | + event->h < 0) |
1522 | + { |
1523 | + return true; |
1524 | + } |
1525 | + if (event->x == 0 && |
1526 | + event->y == 0 && |
1527 | + event->w == 0 && |
1528 | + event->h == 0) |
1529 | + { |
1530 | + return true; |
1531 | + } |
1532 | + if (event->x + event->w < 0 || |
1533 | + event->y + event->h < 0) |
1534 | + { |
1535 | + return true; |
1536 | + } |
1537 | + if (getScreenWidth () != 0 && getScreenHeight () != 0 && |
1538 | + (event->x > getScreenWidth () || |
1539 | + event->y > getScreenHeight () || |
1540 | + event->w > getScreenWidth () || |
1541 | + event->h > getScreenHeight ())) |
1542 | + { |
1543 | + return true; |
1544 | + } |
1545 | + return false; |
1546 | +} |
1547 | + |
1548 | +/* |
1549 | + * This simulates a "selected" event from the parent menu when closing |
1550 | + * a submenu. |
1551 | + */ |
1552 | +bool |
1553 | +AccessibilityWatcher::returnToPrevMenu () |
1554 | +{ |
1555 | + if (previouslyActiveMenus.size () > 1) |
1556 | + { |
1557 | + previouslyActiveMenus.pop_back (); |
1558 | + FocusInfo *dup = new FocusInfo (*previouslyActiveMenus.back ()); |
1559 | + focusList.push_back (dup); |
1560 | + return true; |
1561 | + } |
1562 | + return false; |
1563 | +} |
1564 | + |
1565 | +/* |
1566 | + * Tries to extrapolate a missing caret position from other text characters. |
1567 | + * is used as last resort when application doesn't respect at-spi standarts, |
1568 | + * or at-spi bugs. |
1569 | + */ |
1570 | +void |
1571 | +AccessibilityWatcher::getAlternativeCaret (FocusInfo *focus, const AtspiEvent* event) |
1572 | +{ |
1573 | + auto text = unique_gobject (atspi_accessible_get_text (event->source)); |
1574 | + if (!text.get ()) |
1575 | + return; |
1576 | + auto offset = atspi_text_get_caret_offset (text.get (), NULL); |
1577 | + auto string = unique_gmem (atspi_text_get_string_at_offset (text.get (), offset, ATSPI_TEXT_GRANULARITY_CHAR, NULL)); |
1578 | + gchar caretChar = string.get ()->content[0]; |
1579 | + |
1580 | + // if we're at a newline, sometimes at-spi isn't giving us a caret position. unknown bug in some apps. |
1581 | + if (caretChar == '\n' || caretChar == '\0') |
1582 | + { |
1583 | + // gives the last empty line the right focus. |
1584 | + int lines = atspi_text_get_character_count (text.get (), NULL) == offset ? 1 : 0; |
1585 | + int charIndex = 1; |
1586 | + bool charExtentsFound = false; |
1587 | + |
1588 | + auto size = unique_gmem (atspi_text_get_character_extents (text.get (), offset, ATSPI_COORD_TYPE_SCREEN, NULL)); |
1589 | + // try and find the character on upper line to extrapolate position from. no more that 300 char, we risk lag. |
1590 | + while (!charExtentsFound && charIndex <= offset && charIndex < 300) { |
1591 | + size = unique_gmem (atspi_text_get_character_extents (text.get (), offset - charIndex, ATSPI_COORD_TYPE_SCREEN, NULL)); |
1592 | + string = unique_gmem (atspi_text_get_string_at_offset (text.get (), offset - charIndex, ATSPI_TEXT_GRANULARITY_CHAR, NULL)); |
1593 | + caretChar = string.get ()->content[0]; |
1594 | + // if we found a caret, check we're at beginning of line (or of text) to extrapolate position |
1595 | + if (size.get ()->x != 0 || size.get ()->y != 0) |
1596 | + { |
1597 | + 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') |
1598 | + { |
1599 | + charExtentsFound = true; // first character of upper line has been found |
1600 | + } |
1601 | + else if (offset - charIndex -1 == 0) |
1602 | + { |
1603 | + size = unique_gmem (atspi_text_get_character_extents (text.get (), 0, ATSPI_COORD_TYPE_SCREEN, NULL)); |
1604 | + // first character of upper line has been found |
1605 | + charExtentsFound = true; |
1606 | + } |
1607 | + } |
1608 | + else if (caretChar == '\n') |
1609 | + { |
1610 | + ++lines; |
1611 | + } |
1612 | + ++charIndex; |
1613 | + } |
1614 | + focus->xAlt = size.get ()->x; |
1615 | + focus->yAlt = size.get ()->y + (lines-1) * size.get ()->height; |
1616 | + focus->wAlt = size.get ()->width; |
1617 | + focus->hAlt = size.get ()->height; |
1618 | + } |
1619 | +} |
1620 | + |
1621 | + |
1622 | +/* Register to events */ |
1623 | +void |
1624 | +AccessibilityWatcher::addWatches () |
1625 | +{ |
1626 | + atspi_event_listener_register (focusListener, "object:state-changed:focused", NULL); |
1627 | + atspi_event_listener_register (caretMoveListener, "object:text-caret-moved", NULL); |
1628 | + atspi_event_listener_register (caretMoveListener, "object:text-changed:inserted", NULL); |
1629 | + atspi_event_listener_register (caretMoveListener, "object:text-changed:removed", NULL); |
1630 | + atspi_event_listener_register (selectedListener, "object:state-changed:selected", NULL); |
1631 | + atspi_event_listener_register (descendantChangedListener, "object:active-descendant-changed", NULL); |
1632 | + mActive = true; |
1633 | +} |
1634 | + |
1635 | +void |
1636 | +AccessibilityWatcher::removeWatches () |
1637 | +{ |
1638 | + atspi_event_listener_deregister (focusListener, "object:state-changed:focused", NULL); |
1639 | + atspi_event_listener_deregister (caretMoveListener, "object:text-caret-moved", NULL); |
1640 | + atspi_event_listener_deregister (caretMoveListener, "object:text-changed:inserted", NULL); |
1641 | + atspi_event_listener_deregister (caretMoveListener, "object:text-changed:removed", NULL); |
1642 | + atspi_event_listener_deregister (selectedListener, "object:state-changed:selected", NULL); |
1643 | + atspi_event_listener_deregister (descendantChangedListener, "object:active-descendant-changed", NULL); |
1644 | + mActive = false; |
1645 | +} |
1646 | + |
1647 | +void |
1648 | +AccessibilityWatcher::setActive (bool activate) |
1649 | +{ |
1650 | + if (mActive && !activate) |
1651 | + { |
1652 | + removeWatches (); |
1653 | + } |
1654 | + else if (!mActive && activate) |
1655 | + { |
1656 | + addWatches (); |
1657 | + } |
1658 | +} |
1659 | + |
1660 | +std::deque <FocusInfo *> |
1661 | +AccessibilityWatcher::getFocusQueue () |
1662 | +{ |
1663 | + return focusList; |
1664 | +} |
1665 | + |
1666 | +void |
1667 | +AccessibilityWatcher::resetFocusQueue () |
1668 | +{ |
1669 | + for (auto info: focusList) { |
1670 | + delete (info); |
1671 | + } |
1672 | + focusList.clear (); |
1673 | +} |
1674 | + |
1675 | |
1676 | === added file 'plugins/focuspoll/src/focusinfo.cpp' |
1677 | --- plugins/focuspoll/src/focusinfo.cpp 1970-01-01 00:00:00 +0000 |
1678 | +++ plugins/focuspoll/src/focusinfo.cpp 2018-10-23 08:53:35 +0000 |
1679 | @@ -0,0 +1,123 @@ |
1680 | +/* |
1681 | + * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net> |
1682 | + * |
1683 | + * This file is part of compiz. |
1684 | + * |
1685 | + * this program is free software: you can redistribute it and/or modify |
1686 | + * it under the terms of the GNU General Public License as published by the Free |
1687 | + * Software Foundation, either version 3 of the License, or (at your option) any |
1688 | + * later version. |
1689 | + * |
1690 | + * This program is distributed in the hope that it will be useful, but |
1691 | + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
1692 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
1693 | + * details. |
1694 | + * |
1695 | + * You should have received a copy of the GNU General Public License along |
1696 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1697 | + */ |
1698 | + |
1699 | +#include "focusinfo.h" |
1700 | + |
1701 | +#include <stdio.h> |
1702 | +#include <string.h> |
1703 | + |
1704 | +FocusInfo::FocusInfo (const gchar * type, |
1705 | + gchar * name, |
1706 | + gchar * label, |
1707 | + gchar * role, |
1708 | + gchar * application, |
1709 | + int x, |
1710 | + int y, |
1711 | + int width, |
1712 | + int height) : |
1713 | + x (x), |
1714 | + y (y), |
1715 | + w (width), |
1716 | + h (height), |
1717 | + xAlt (-1), |
1718 | + yAlt (-1), |
1719 | + wAlt (-1), |
1720 | + hAlt (-1), |
1721 | + type (type), |
1722 | + name (name), |
1723 | + label (label), |
1724 | + role (role), |
1725 | + application (application), |
1726 | + active (false), |
1727 | + focused (false), |
1728 | + selected (false) |
1729 | +{ |
1730 | +} |
1731 | + |
1732 | +FocusInfo::FocusInfo (const FocusInfo &dup) |
1733 | +{ |
1734 | + x = dup.x; |
1735 | + y = dup.y; |
1736 | + w = dup.w; |
1737 | + h = dup.h; |
1738 | + xAlt = dup.xAlt; |
1739 | + yAlt = dup.yAlt; |
1740 | + wAlt = dup.wAlt; |
1741 | + hAlt = dup.hAlt; |
1742 | + type = dup.type; |
1743 | + name = strdup(dup.name); |
1744 | + label = strdup(dup.label); |
1745 | + role = strdup(dup.role); |
1746 | + application = strdup(dup.application); |
1747 | + active = dup.active; |
1748 | + focused = dup.focused; |
1749 | + selected = dup.selected; |
1750 | +} |
1751 | + |
1752 | +FocusInfo::~FocusInfo (void) |
1753 | +{ |
1754 | + g_free (name); |
1755 | + g_free (label); |
1756 | + g_free (role); |
1757 | + g_free (application); |
1758 | +} |
1759 | + |
1760 | +const gchar * |
1761 | +FocusInfo::FocusInfo::getType (void) |
1762 | +{ |
1763 | + return type; |
1764 | +} |
1765 | + |
1766 | +CompPoint |
1767 | +FocusInfo::getPosition (void) |
1768 | +{ |
1769 | + return CompPoint (x, y); |
1770 | +} |
1771 | + |
1772 | +CompSize |
1773 | +FocusInfo::getSize (void) |
1774 | +{ |
1775 | + return CompSize (w, h); |
1776 | +} |
1777 | + |
1778 | +CompRect |
1779 | +FocusInfo::getBBox (void) |
1780 | +{ |
1781 | + return CompRect (x, y, w, h); |
1782 | +} |
1783 | + |
1784 | +bool |
1785 | +FocusInfo::operator== (const FocusInfo& other) const |
1786 | +{ |
1787 | + return (other.x == x && |
1788 | + other.y == y && |
1789 | + other.w == w && |
1790 | + other.h == h && |
1791 | + !strcmp (other.type, type) && |
1792 | + !strcmp (other.name, name) && |
1793 | + !strcmp (other.label, label) && |
1794 | + !strcmp (other.application, application) && |
1795 | + !strcmp (other.role, role)); |
1796 | +}; |
1797 | + |
1798 | +bool |
1799 | +FocusInfo::operator!= (const FocusInfo& other) const |
1800 | +{ |
1801 | + return !(*this == other); |
1802 | +}; |
1803 | |
1804 | === added file 'plugins/focuspoll/src/focuspoll.cpp' |
1805 | --- plugins/focuspoll/src/focuspoll.cpp 1970-01-01 00:00:00 +0000 |
1806 | +++ plugins/focuspoll/src/focuspoll.cpp 2018-10-23 08:53:35 +0000 |
1807 | @@ -0,0 +1,225 @@ |
1808 | +/* |
1809 | + * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net> |
1810 | + * |
1811 | + * This file is part of compiz. |
1812 | + * |
1813 | + * this program is free software: you can redistribute it and/or modify |
1814 | + * it under the terms of the GNU General Public License as published by the Free |
1815 | + * Software Foundation, either version 3 of the License, or (at your option) any |
1816 | + * later version. |
1817 | + * |
1818 | + * This program is distributed in the hope that it will be useful, but |
1819 | + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
1820 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
1821 | + * details. |
1822 | + * |
1823 | + * You should have received a copy of the GNU General Public License along |
1824 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1825 | + */ |
1826 | + |
1827 | +#include "private.h" |
1828 | + |
1829 | +COMPIZ_PLUGIN_20090315 (focuspoll, FocuspollPluginVTable); |
1830 | + |
1831 | +bool |
1832 | +FocuspollScreen::updatePosition () |
1833 | +{ |
1834 | + if (!a11ywatcher->getFocusQueue ().empty ()) |
1835 | + { |
1836 | + CompRect focusRect = a11ywatcher->getFocusQueue ().back ()->getBBox (); |
1837 | + for (std::list<FocusPoller *>::iterator it = pollers.begin (); it != pollers.end ();) |
1838 | + { |
1839 | + FocusPoller *poller = *it; |
1840 | + // Update iterator to next element before calling the callback which |
1841 | + // might remove the poller from the list. |
1842 | + ++it; |
1843 | + poller->mCallback (focusRect); |
1844 | + } |
1845 | + } |
1846 | + a11ywatcher->resetFocusQueue (); |
1847 | + return true; |
1848 | +} |
1849 | + |
1850 | +bool |
1851 | +FocuspollScreen::addTimer (FocusPoller *poller) |
1852 | +{ |
1853 | + bool start = pollers.empty (); |
1854 | + |
1855 | + std::list <FocusPoller *>::iterator it = |
1856 | + std::find (pollers.begin (), pollers.end (), poller); |
1857 | + |
1858 | + if (it != pollers.end ()) |
1859 | + return false; |
1860 | + |
1861 | + pollers.insert (it, poller); |
1862 | + |
1863 | + if (start) |
1864 | + { |
1865 | + a11ywatcher->setActive (true); |
1866 | + timer.start (); |
1867 | + } |
1868 | + |
1869 | + return true; |
1870 | +} |
1871 | + |
1872 | +void |
1873 | +FocuspollScreen::removeTimer (FocusPoller *poller) |
1874 | +{ |
1875 | + std::list <FocusPoller *>::iterator it = |
1876 | + std::find (pollers.begin (), pollers.end (), poller); |
1877 | + |
1878 | + if (it == pollers.end ()) |
1879 | + return; |
1880 | + |
1881 | + pollers.erase (it); |
1882 | + |
1883 | + if (pollers.empty ()) |
1884 | + { |
1885 | + a11ywatcher->setActive (false); |
1886 | + timer.stop (); |
1887 | + } |
1888 | +} |
1889 | + |
1890 | +void |
1891 | +FocusPoller::setCallback (FocusPoller::CallBack callback) |
1892 | +{ |
1893 | + bool wasActive = mActive; |
1894 | + |
1895 | + if (mActive) |
1896 | + stop (); |
1897 | + |
1898 | + mCallback = callback; |
1899 | + |
1900 | + if (wasActive) |
1901 | + start (); |
1902 | +} |
1903 | + |
1904 | +void |
1905 | +FocusPoller::start () |
1906 | +{ |
1907 | + FOCUSPOLL_SCREEN (screen); |
1908 | + |
1909 | + if (!fs) |
1910 | + { |
1911 | + compLogMessage ("focuspoll", CompLogLevelWarn, |
1912 | + "Plugin version mismatch, can't start focus poller."); |
1913 | + |
1914 | + return; |
1915 | + } |
1916 | + |
1917 | + if (mCallback.empty ()) |
1918 | + { |
1919 | + compLogMessage ("focuspoll", CompLogLevelWarn, |
1920 | + "Can't start focus poller without callback."); |
1921 | + return; |
1922 | + } |
1923 | + |
1924 | + fs->addTimer (this); |
1925 | + |
1926 | + mActive = true; |
1927 | +} |
1928 | + |
1929 | +void |
1930 | +FocusPoller::stop () |
1931 | +{ |
1932 | + FOCUSPOLL_SCREEN (screen); |
1933 | + |
1934 | + /* Prevent broken plugins from calling stop () twice */ |
1935 | + |
1936 | + if (!mActive) |
1937 | + return; |
1938 | + |
1939 | + if (!fs) |
1940 | + { |
1941 | + compLogMessage ("focuspoll", |
1942 | + CompLogLevelWarn, |
1943 | + "Plugin version mismatch, can't stop focus poller."); |
1944 | + return; |
1945 | + } |
1946 | + |
1947 | + mActive = false; |
1948 | + fs->removeTimer (this); |
1949 | +} |
1950 | + |
1951 | +bool |
1952 | +FocusPoller::active () |
1953 | +{ |
1954 | + return mActive; |
1955 | +} |
1956 | + |
1957 | +FocusPoller::FocusPoller () : |
1958 | + mActive (false), |
1959 | + mCallback (NULL) { |
1960 | + } |
1961 | + |
1962 | +CompSize |
1963 | +FocuspollScreen::getScreenLimits () { |
1964 | + int x =0, y = 0; |
1965 | + for (auto dev : screen->outputDevs ()) { |
1966 | + x = std::max (x, dev.x () + dev.width ()); |
1967 | + y = std::max (y, dev.y () + dev.height ()); |
1968 | + } |
1969 | + return CompSize (x, y); |
1970 | +} |
1971 | + |
1972 | +void |
1973 | +FocuspollScreen::updateTimer () |
1974 | +{ |
1975 | + float timeout = optionGetFocusPollInterval (); |
1976 | + timer.setTimes (timeout, timeout * 1.5); |
1977 | +} |
1978 | + |
1979 | +FocusPoller::~FocusPoller () |
1980 | +{ |
1981 | + if (mActive) { |
1982 | + stop (); |
1983 | + } |
1984 | +} |
1985 | + |
1986 | +void |
1987 | +FocuspollScreen::setOptions () |
1988 | +{ |
1989 | + a11ywatcher->setIgnoreLinks (optionGetIgnoreLinks ()); |
1990 | +} |
1991 | + |
1992 | +template class PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI>; |
1993 | + |
1994 | +FocuspollScreen::FocuspollScreen (CompScreen *screen) : |
1995 | + PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI> (screen) |
1996 | +{ |
1997 | + a11ywatcher = new AccessibilityWatcher (); |
1998 | + |
1999 | + CompSize screenLimit = getScreenLimits (); |
2000 | + a11ywatcher->setScreenLimits (screenLimit.width (), screenLimit.height ()); |
2001 | + |
2002 | + updateTimer (); |
2003 | + timer.setCallback (boost::bind (&FocuspollScreen::updatePosition, this)); |
2004 | + optionSetFocusPollIntervalNotify (boost::bind (&FocuspollScreen::updateTimer, this)); |
2005 | + |
2006 | + optionSetIgnoreLinksNotify (boost::bind (&FocuspollScreen::setOptions, this)); |
2007 | + |
2008 | +} |
2009 | + |
2010 | +FocuspollScreen::~FocuspollScreen () |
2011 | +{ |
2012 | + delete a11ywatcher; |
2013 | +} |
2014 | + |
2015 | +bool |
2016 | +FocuspollPluginVTable::init () |
2017 | +{ |
2018 | + if (CompPlugin::checkPluginABI ("core", CORE_ABIVERSION)) |
2019 | + { |
2020 | + CompPrivate p; |
2021 | + p.uval = COMPIZ_FOCUSPOLL_ABI; |
2022 | + screen->storeValue ("focuspoll_ABI", p); |
2023 | + return true; |
2024 | + } |
2025 | + return false; |
2026 | +} |
2027 | + |
2028 | +void |
2029 | +FocuspollPluginVTable::fini () |
2030 | +{ |
2031 | + screen->eraseValue ("focuspoll_ABI"); |
2032 | +} |
2033 | |
2034 | === added file 'plugins/focuspoll/src/private.h' |
2035 | --- plugins/focuspoll/src/private.h 1970-01-01 00:00:00 +0000 |
2036 | +++ plugins/focuspoll/src/private.h 2018-10-23 08:53:35 +0000 |
2037 | @@ -0,0 +1,75 @@ |
2038 | +/* |
2039 | + * Copyright (C) 2016 Auboyneau Vincent <ksamak@riseup.net> |
2040 | + * |
2041 | + * This file is part of compiz. |
2042 | + * |
2043 | + * this program is free software: you can redistribute it and/or modify |
2044 | + * it under the terms of the GNU General Public License as published by the Free |
2045 | + * Software Foundation, either version 3 of the License, or (at your option) any |
2046 | + * later version. |
2047 | + * |
2048 | + * This program is distributed in the hope that it will be useful, but |
2049 | + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
2050 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
2051 | + * details. |
2052 | + * |
2053 | + * You should have received a copy of the GNU General Public License along |
2054 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2055 | + */ |
2056 | + |
2057 | +#include <core/core.h> |
2058 | +#include <core/pluginclasshandler.h> |
2059 | +#include <core/timer.h> |
2060 | + |
2061 | +#include <focuspoll/focuspoll.h> |
2062 | +#include <accessibilitywatcher/accessibilitywatcher.h> |
2063 | + |
2064 | +#include "focuspoll_options.h" |
2065 | + |
2066 | +typedef enum _FocuspollOptions |
2067 | +{ |
2068 | + MP_DISPLAY_OPTION_FOCUS_POLL_INTERVAL, |
2069 | + MP_DISPLAY_OPTION_NUM |
2070 | +} FocuspollDisplayOptions; |
2071 | + |
2072 | +class FocuspollScreen; |
2073 | +extern template class PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI>; |
2074 | + |
2075 | +class FocuspollScreen : |
2076 | + public PluginClassHandler <FocuspollScreen, CompScreen, COMPIZ_FOCUSPOLL_ABI>, |
2077 | + public FocuspollOptions |
2078 | +{ |
2079 | + public: |
2080 | + FocuspollScreen (CompScreen *screen); |
2081 | + ~FocuspollScreen (void); |
2082 | + |
2083 | + std::list <FocusPoller *> pollers; |
2084 | + CompTimer timer; |
2085 | + CompTimer settingsTimer; |
2086 | + CompRect focusRect; |
2087 | + |
2088 | + bool updatePosition (void); |
2089 | + bool addTimer (FocusPoller *poller); |
2090 | + void removeTimer (FocusPoller *poller); |
2091 | + void updateTimer (void); |
2092 | + |
2093 | + private: |
2094 | + AccessibilityWatcher* a11ywatcher; |
2095 | + |
2096 | + CompSize getScreenLimits (void); |
2097 | + void setOptions (void); |
2098 | + |
2099 | +}; |
2100 | + |
2101 | +#define FOCUSPOLL_SCREEN(s) \ |
2102 | + FocuspollScreen *fs = FocuspollScreen::get (s) |
2103 | + |
2104 | +#define NUM_OPTIONS(s) (sizeof ((s)->opt) / sizeof (CompOption)) |
2105 | + |
2106 | +class FocuspollPluginVTable : |
2107 | + public CompPlugin::VTableForScreen <FocuspollScreen, COMPIZ_FOCUSPOLL_ABI> |
2108 | +{ |
2109 | + public: |
2110 | + bool init (); |
2111 | + void fini (); |
2112 | +}; |
2113 | |
2114 | === modified file 'plugins/mousepoll/src/mousepoll.cpp' |
2115 | --- plugins/mousepoll/src/mousepoll.cpp 2013-05-16 10:47:54 +0000 |
2116 | +++ plugins/mousepoll/src/mousepoll.cpp 2018-10-23 08:53:35 +0000 |
2117 | @@ -32,6 +32,7 @@ |
2118 | int winX, winY; |
2119 | int w = screen->width (), h = screen->height (); |
2120 | unsigned int maskReturn; |
2121 | + unsigned int newbuttons; |
2122 | |
2123 | bool status = XQueryPointer (screen->dpy (), screen->root (), |
2124 | &root, &child, &rootX, &rootY, |
2125 | @@ -40,12 +41,25 @@ |
2126 | if (!status || rootX > w || rootY > h || screen->root () != root) |
2127 | return false; |
2128 | |
2129 | + newbuttons = maskReturn & |
2130 | + (Button1Mask | |
2131 | + Button2Mask | |
2132 | + Button3Mask | |
2133 | + Button4Mask | |
2134 | + Button5Mask); |
2135 | + |
2136 | if (rootX != pos.x () || rootY != pos.y ()) |
2137 | { |
2138 | pos.set (rootX, rootY); |
2139 | return true; |
2140 | } |
2141 | |
2142 | + if (newbuttons != buttons) |
2143 | + { |
2144 | + buttons = newbuttons; |
2145 | + return true; |
2146 | + } |
2147 | + |
2148 | return false; |
2149 | } |
2150 | |
2151 | |
2152 | === modified file 'plugins/mousepoll/src/private.h' |
2153 | --- plugins/mousepoll/src/private.h 2013-06-28 10:21:21 +0000 |
2154 | +++ plugins/mousepoll/src/private.h 2018-10-23 08:53:35 +0000 |
2155 | @@ -49,6 +49,7 @@ |
2156 | CompTimer timer; |
2157 | |
2158 | CompPoint pos; |
2159 | + unsigned int buttons; |
2160 | |
2161 | bool |
2162 | updatePosition (); |
2163 | |
2164 | === modified file 'plugins/showmouse/showmouse.xml.in' |
2165 | --- plugins/showmouse/showmouse.xml.in 2018-04-11 13:25:42 +0000 |
2166 | +++ plugins/showmouse/showmouse.xml.in 2018-10-23 08:53:35 +0000 |
2167 | @@ -11,6 +11,9 @@ |
2168 | <plugin>cube</plugin> |
2169 | <plugin>decor</plugin> |
2170 | </relation> |
2171 | + <relation type="before"> |
2172 | + <plugin>ezoom</plugin> |
2173 | + </relation> |
2174 | <requirement> |
2175 | <plugin>opengl</plugin> |
2176 | <plugin>mousepoll</plugin> |
2177 | |
2178 | === modified file 'po/POTFILES.in' |
2179 | --- po/POTFILES.in 2018-04-11 13:07:18 +0000 |
2180 | +++ po/POTFILES.in 2018-10-23 08:53:35 +0000 |
2181 | @@ -31,6 +31,7 @@ |
2182 | plugins/fade/fade.xml.in |
2183 | plugins/fadedesktop/fadedesktop.xml.in |
2184 | plugins/firepaint/firepaint.xml.in |
2185 | +plugins/focuspoll/focuspoll.xml.in |
2186 | plugins/freewins/freewins.xml.in |
2187 | plugins/gears/gears.xml.in |
2188 | plugins/gnomecompat/gnomecompat.xml.in |
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.