[Merge] lp:~ksamak/compiz/ezoom_focus_tracking into lp:compiz
Samuel thibault
samuel.thibault at ens-lyon.org
Tue Sep 19 10:01:38 UTC 2017
Hello,
I have gone through the previous review from azzar1, and commented further.
Samuel
Diff comments:
>
> === modified file 'plugins/ezoom/src/ezoom.h'
> --- plugins/ezoom/src/ezoom.h 2016-07-01 15:37:19 +0000
> +++ plugins/ezoom/src/ezoom.h 2017-04-28 09:59:04 +0000
> @@ -43,12 +43,15 @@
> #include <composite/composite.h>
> #include <opengl/opengl.h>
> #include <mousepoll/mousepoll.h>
> +#include <focuspoll/focuspoll.h>
>
>
> #include "ezoom_options.h"
>
> #include <cmath>
>
> +#define EZOOM_SCREEN_BORDER_OFFSET 0.92
This is still a magic number :)
What does it mean actually? How was it determined: why not more or less than 0.92?
Saying "experience shows it's a nice balance" is fine, but it also needs to say something like "if more, we get blah and blah bad effects, if less, we get bleh and bleh effects", so we get to at least have an idea why this is a good figure and what consequences changing it would have.
> +
> enum SpecificZoomTarget
> {
> ZoomTarget1 = 0,
>
> === added file 'plugins/focuspoll/focuspoll.xml.in'
> --- plugins/focuspoll/focuspoll.xml.in 1970-01-01 00:00:00 +0000
> +++ plugins/focuspoll/focuspoll.xml.in 2017-04-28 09:59:04 +0000
> @@ -0,0 +1,34 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<compiz>
> + <plugin name="focuspoll" useBcop="true">
> + <_short>Focus position polling</_short>
> + <_long>Updates the focus pointer position from the accessibility library AT-SPI through dbus</_long>
> + <category>Utility</category>
> + <options>
> + <_short>Misc</_short>
> + <option type="bool" name="track_components">
> + <_short>track movement of components (like a menu, label, or button)</_short>
> + <_long>if enabled, focuspoll will track the movements of every component able to provide focus.</_long>
> + <default>true</default>
> + </option>
> + <option type="bool" name="track_caret">
> + <_short>track movement of the caret (the cursor in text)</_short>
> + <_long>if enabled, focuspoll will track the movements of the caret.</_long>
> + <default>true</default>
> + </option>
> + <option type="bool" name="ignore_links">
> + <_short>ignores link focuses</_short>
> + <_long>if enabled, focuspoll will not track the focus events of link items</_long>
The question about links means that the description should probably be clearer :)
Perhaps just add "such as web links", and mention the annoying user-visible effect when they are tracked.
> + <default>true</default>
> + </option>
> + <option type="int" name="focus_poll_interval">
> + <_short>Focus Poll Interval</_short>
> + <_long>How often to poll DBus for focus changes, in milliseconds. Reduce this to reduce choppy behavior.</_long>
> + <default>10</default>
> + <min>1</min>
> + <max>500</max>
> + </option>
> + </options>
> + </plugin>
> +</compiz>
> +
>
> === added directory 'plugins/focuspoll/include'
> === added directory 'plugins/focuspoll/include/accessibilitywatcher'
> === added file 'plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h'
> --- plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h 1970-01-01 00:00:00 +0000
> +++ plugins/focuspoll/include/accessibilitywatcher/accessibilitywatcher.h 2017-04-28 09:59:04 +0000
> @@ -0,0 +1,82 @@
> +/*
> + * Copyright (C) 2016 Auboyneau Vincent <ksamak at riseup.net>
> + *
> + * This file is part of compiz.
> + *
> + * this program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by the Free
> + * Software Foundation, either version 3 of the License, or (at your option) any
> + * later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef ACCESSIBILITY_WATCHER_H
> +#define ACCESSIBILITY_WATCHER_H
> +
> +#include <deque>
> +#include <vector>
> +
> +#include "focusinfo.h"
> +
> +#include <atspi/atspi.h>
> +
> +class AccessibilityWatcher {
> + public:
> + ~AccessibilityWatcher();
> + static AccessibilityWatcher* getInstance();
> +
> + void init();
> + void quit();
> +
> + void setIgnoreLinks(bool);
> + void setScreenLimits(int, int);
> + int getScreenWidth();
> + int getScreenHeight();
> +
> + std::deque<FocusInfo> getFocusQueue();
> + void resetFocusQueue();
> + bool returnToPrevMenu();
> +
> + private:
> + AccessibilityWatcher();
> + static AccessibilityWatcher* instance;
> +
> + bool initialized;
> + int screenWidth;
> + int screenHeight;
> + static bool ignoreLinks;
> + std::deque<FocusInfo> focusList;
> + std::vector<FocusInfo> previouslyActiveMenus;
> +
> + AtspiEventListener *focusListener;
> + AtspiEventListener *caretMoveListener;
> + AtspiEventListener *selectedListener;
> + AtspiEventListener *descendantChangedListener;
> +
> + void addWatches();
> + void removeWatches();
> +
> + static std::string getLabel(AtspiAccessible *accessible);
> + const static gchar* atspiStateGetName (gint state);
> + static std::string getStateSet (AtspiAccessible *accessible);
> +
> + static void registerEvent(const AtspiEvent *event, const std::string& type);
> + static bool appSpecificFilter(FocusInfo& focusInfo, const AtspiEvent* event);
> + static bool filterBadEvents(const FocusInfo& event);
> + static void getAlternativeCaret(FocusInfo& focus, const AtspiEvent* event);
> +
> + static void onFocus(const AtspiEvent *event, void *data);
> + static void onSelectedChange(const AtspiEvent *event, void *data);
> + static void onCaretMove(const AtspiEvent *event, void *data);
> + static void onDescendantChanged(const AtspiEvent *event, void *data);
> +
There was a comment about moving them to the cpp file which wasn't acted on. If they are static, they can just be removed from the .h file, since they are not exported outside. If a forward declaration is really needed in the .cpp file, it can be put there, there is no need to put it in the .h file.
> +};
> +
> +#endif
>
> === added file 'plugins/focuspoll/include/accessibilitywatcher/focusinfo.h'
> --- plugins/focuspoll/include/accessibilitywatcher/focusinfo.h 1970-01-01 00:00:00 +0000
> +++ plugins/focuspoll/include/accessibilitywatcher/focusinfo.h 2017-04-28 09:59:04 +0000
> @@ -0,0 +1,76 @@
> +/*
I don't find the corresponding focusinfo.cpp file?
> + * Copyright (C) 2016 Auboyneau Vincent <ksamak at riseup.net>
> + *
> + * This file is part of compiz.
> + *
> + * this program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by the Free
> + * Software Foundation, either version 3 of the License, or (at your option) any
> + * later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef FOCUS_INFO_H
> +#define FOCUS_INFO_H
> +
> +#include <tuple>
> +#include <string>
> +#include <sstream>
> +
> +
> +class FocusInfo {
> + public:
> +
> + FocusInfo(const std::string& type,
> + const std::string& name,
> + const std::string& label,
> + const std::string& role,
> + const std::string& application,
> + int x,
> + int y,
> + int width,
> + int height);
> +
> + FocusInfo(const std::string& type,
> + const std::string& name,
> + const std::string& label,
> + const std::string& role,
> + const std::string& application);
> +
> + FocusInfo();
> +
> + int x, y, w, h;
> + int xAlt, yAlt, wAlt, hAlt;
> + std::string type;
> + std::string name;
> + std::string label;
> + std::string role;
> + std::string application;
> +
> + // AT-SPI events that are interesting to know about the event
> + bool enabled;
> + bool active;
> + bool focusable;
> + bool focused;
> + bool selected;
> + bool showing;
> + bool visible;
> +
> + std::string getType();
> + std::string summary();
> + std::tuple<int, int> getPosition();
> + std::tuple<int, int> getSize();
> + std::tuple<int, int, int, int> getBBox();
> +
> + bool operator== (const FocusInfo& other) const;
> + bool operator!= (const FocusInfo& other) const;
> +};
> +
> +#endif
>
> === added directory 'plugins/focuspoll/src'
> === added file 'plugins/focuspoll/src/accessibilitywatcher.cpp'
> --- plugins/focuspoll/src/accessibilitywatcher.cpp 1970-01-01 00:00:00 +0000
> +++ plugins/focuspoll/src/accessibilitywatcher.cpp 2017-04-28 09:59:04 +0000
> @@ -0,0 +1,506 @@
> +/*
> + * Copyright (C) 2016 Auboyneau Vincent <ksamak at riseup.net>
> + *
> + * This file is part of compiz.
> + *
> + * this program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by the Free
> + * Software Foundation, either version 3 of the License, or (at your option) any
> + * later version.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
> + * details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <iostream>
> +#include <functional>
> +
> +#include <boost/mem_fn.hpp>
> +#include <boost/bind.hpp>
> +#include <boost/function.hpp>
> +
> +#include <string.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +
> +#include "accessibilitywatcher.h"
> +
> +namespace {
> +const int A11YWATCHER_MAX_CARET_WIDTH = 50;
> +const int A11YWATCHER_MAX_CARET_HEIGHT = 70;
> +}
> +
> +AccessibilityWatcher* AccessibilityWatcher::instance = NULL;
> +bool AccessibilityWatcher::ignoreLinks = false;
> +
> +AccessibilityWatcher::AccessibilityWatcher() :
> + initialized(false),
> + screenWidth(0),
> + screenHeight(0)
> +{
> +}
> +
> +AccessibilityWatcher::~AccessibilityWatcher() {
> + quit();
> +};
> +
> +AccessibilityWatcher *AccessibilityWatcher::getInstance () {
> + if (instance == NULL)
> + {
> + instance = new AccessibilityWatcher();
> + }
> + return instance;
> +}
> +
> +std::string AccessibilityWatcher::getLabel(AtspiAccessible *accessible) {
What was meant here was a const function, not a const parameter, i.e.
std::string AccessibilityWatcher::getLabel(AtspiAccessible *accessible) const {
A const function is a function whose returned value is always the same, and thus several calls to the function with the same parameter can be optimized in just one call. Is this a safe thing to do here?
> + GArray *relations = atspi_accessible_get_relation_set (accessible, NULL);
> + if (relations == NULL)
> + {
> + return "";
> + }
> +
> + AtspiRelation *relation;
> + for (unsigned i = 0; i < relations->len; ++i) {
> + relation = g_array_index (relations, AtspiRelation*, i);
> + if (atspi_relation_get_relation_type (relation) == ATSPI_RELATION_LABELLED_BY)
> + {
> + g_array_free (relations, true);
> + g_object_unref(relations);
> + auto res_label = atspi_accessible_get_name(atspi_relation_get_target(relation, 0), NULL);
AIUI, "auto" here will turn into gchar *, and thus there won't be an object creation. And thus when unreferencing "relation" below, the gchar* will be released, and thus the return becomes bogus. I believe you really need to specify std::string instead of auto, so that the object creation and copy of the string happens before unreferencing "relation".
> + g_object_unref(relation);
> + return (res_label == NULL) ? "" : res_label;
> + }
> + }
> + if (relations->len > 0)
> + {
> + g_object_unref(relations);
> + }
> + return "";
> +}
> +
> +void AccessibilityWatcher::setIgnoreLinks(bool val) {
> + ignoreLinks = val;
> +}
> +
> +void AccessibilityWatcher::setScreenLimits(int x, int y) {
> + screenWidth = x;
> + screenHeight = y;
> +}
> +
> +int AccessibilityWatcher::getScreenWidth () {
> + return screenWidth;
> +}
> +
> +int AccessibilityWatcher::getScreenHeight () {
> + return screenHeight;
> +}
> +
> +void AccessibilityWatcher::registerEvent(const AtspiEvent *event, const std::string &type) {
> + // type is registered from filter on calling event
> + FocusInfo res(type,
> + atspi_accessible_get_name (event->source, NULL),
As previously mentioned by azzar1, we need to check for the value returned by atspi_accessible_get_name() not being NULL, otherwise the FocusInfo object creation will crash.
> + getLabel (event->source),
> + atspi_accessible_get_role_name (event->source, NULL),
> + atspi_accessible_get_name(atspi_accessible_get_application (event->source, NULL), NULL));
> +
> + if (!res.active)
> + {
> + // prevents skipping events that are not designated as active. we check the parent.
> + auto parent = atspi_accessible_get_parent(event->source, NULL);
> + while (!res.active && parent) {
> + auto stateSet = atspi_accessible_get_state_set (child);
Err, did you actually try to build this code? :) child is not defined, I guess you meant parent here and below.
> + if (atspi_state_set_contains(stateSet, ATSPI_STATE_ACTIVE))
> + {
> + res.active = true;
> + }
> + g_object_unref(stateSet);
> + g_object_unref(parent);
> + child = atspi_accessible_get_parent(child, NULL);
> + }
> + g_object_unref(parent);
> + }
> +
> + AtspiComponent* component;
> + if (res.type == "active-descendant-changed")
> + {
> + auto accessible = atspi_accessible_get_child_at_index(event->source, event->detail1, NULL);
> + component = atspi_accessible_get_component(accessible);
> + g_object_unref(accessible);
> + } else {
> + component = atspi_accessible_get_component(event->source);
> + }
> +
> + if (component)
> + {
> + AtspiRect* size = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL);
> + res.x = size->x;
> + res.y = size->y;
> + res.w = size->width;
> + res.h = size->height;
> + g_object_unref(component);
> + }
> + // let's get the caret offset, and then its position for a caret event
> + if (type == "caret")
> + {
> + auto text = atspi_accessible_get_text(event->source);
> + if (!text)
> + {
> + return;
> + }
> + auto offset = atspi_text_get_caret_offset(text, NULL);
> + // if we're at the beginning of text, let the widget pos decide
> + if (event->detail1)
> + {
> + AtspiRect *size = atspi_text_get_character_extents(text, offset, ATSPI_COORD_TYPE_SCREEN, NULL);
> + res.x = size->x;
> + res.y = size->y;
> + res.w = size->width;
> + res.h = size->height;
> + }
> + // correcting a missing offset to a caret move event in firefox
> + if (res.x == 0 && res.y == 0 && offset > 0)
> + {
> + AtspiRect *size = atspi_text_get_character_extents(text, offset-1, ATSPI_COORD_TYPE_SCREEN, NULL);
> + res.x = size->x;
> + res.y = size->y;
> + res.w = size->width;
> + res.h = size->height;
> + }
> + // when result is obviously not a caret size
> + if (strcmp(event->type, "object:text-caret-moved") == 0 && (res.w > A11YWATCHER_MAX_CARET_WIDTH || res.h > A11YWATCHER_MAX_CARET_HEIGHT))
> + {
> + AtspiRect *size = atspi_text_get_character_extents(text, offset, ATSPI_COORD_TYPE_SCREEN, NULL);
> + res.x = size->x;
> + res.y = size->y;
> + res.w = size->width;
> + res.h = size->height;
> + if (type == "caret" && strcmp(event->type, "object:text-caret-moved") == 0 && (res.w > A11YWATCHER_MAX_CARET_WIDTH || res.h > A11YWATCHER_MAX_CARET_HEIGHT))
> + {
> + res.x = 0;
> + res.y = 0;
> + }
> + }
> + g_object_unref(text);
> +
> + // still no offset, it's probably a newline and we're at bugzilla #1319273 (with new paragraph obj)
> + if (res.x == 0 && res.y == 0 &&
> + (strcmp(event->type, "object:text-changed:insert") == 0 ||
> + strcmp(event->type, "object:text-changed:removed") == 0 ||
> + strcmp(event->type, "object:text-caret-moved") == 0)) {
> + res.x = res.xAlt;
> + res.y = res.yAlt;
> + res.w = res.wAlt;
> + res.h = res.hAlt;
> + }
> + }
> +
> + // getting the states on event
> + auto stateSet = atspi_accessible_get_state_set (event->source);
> + if (atspi_state_set_contains(stateSet, ATSPI_STATE_FOCUSED))
> + {
> + res.focused = true;
> + AccessibilityWatcher::getInstance()->previouslyActiveMenus.clear(); // reset potential menu stack
> + }
> + if (atspi_state_set_contains(stateSet, ATSPI_STATE_SELECTED))
> + { // never works, GTK Bad implem?
> + res.selected = true;
> + }
> + if (res.type == "state-changed:selected" && event->detail1 == 1)
> + {
> + res.selected = true;
> + AccessibilityWatcher::getInstance()->previouslyActiveMenus.push_back(res); // add to stack of menus
> + }
> +
> + if (appSpecificFilter(res, event))
> + {
> + return;
> + }
> + if (filterBadEvents(res))
> + {
> + return;
> + }
> + while (AccessibilityWatcher::getInstance()->focusList.size() >= 5) { // don't keep the whole history
> + AccessibilityWatcher::getInstance()->focusList.erase(AccessibilityWatcher::getInstance()->focusList.begin());
> + }
> + AccessibilityWatcher::getInstance()->focusList.push_back(res);
> +}
> +
> +bool AccessibilityWatcher::appSpecificFilter(FocusInfo& focus, const AtspiEvent* event)
> +{
> + if (focus.type == "state-changed:selected" && // emulates on-change:selected false behaviour. eg: for menus
> + (focus.role == "menu item" ||
> + focus.role == "menu" ||
> + focus.role == "check menu item" ||
> + focus.role == "radio menu item") &&
> + focus.application != "mate-panel")
> + {
> + if (!focus.selected && AccessibilityWatcher::getInstance()->returnToPrevMenu())
> + {
> + return true;
> + }
> + focus.active = true;
> + }
> + if (focus.application == "soffice" && focus.role == "paragraph")
> + { // LO-calc: avoid spam event from main edit line
> + auto parent = atspi_accessible_get_parent(event->source, NULL);
> + std::string parentLabel = atspi_accessible_get_name(parent, NULL);
> + if (parentLabel == "Input line" ||
> + parentLabel == "Ligne de saisie") {
> + return true;
> + }
> + }
> + if (focus.application == "Icedove" || focus.application == "Thunderbird")
> + {
> + if (focus.type == "caret")
> + {
> + auto text = atspi_accessible_get_text(event->source); // next if deals with a special newline char, that remained buggy. hypra issue #430
> + auto offset = atspi_text_get_caret_offset(text, NULL);
> + auto string = std::string(atspi_text_get_text_at_offset(text, offset, ATSPI_TEXT_BOUNDARY_CHAR, NULL)->content);
> + auto stringM1 = std::string(atspi_text_get_text_at_offset(text, offset -1, ATSPI_TEXT_BOUNDARY_CHAR, NULL)->content);
> + if (offset == atspi_text_get_character_count(text, NULL) && string == "\0" && (stringM1 == "\n" || int(stringM1.c_str()[0]) == -17))
> + {
> + getAlternativeCaret(focus, event);
> + focus.x = focus.xAlt;
> + focus.y = focus.yAlt + focus.hAlt;
> + focus.w = focus.wAlt;
> + focus.h = focus.hAlt;
> + }
> + if (!(focus.x == 0 && focus.y == 0))
> + { // prevents compose window loss of tracking in HTML mode (active flag ok, but no focused flag)
> + AccessibilityWatcher::getInstance()->focusList.push_back(focus);
> + return true;
> + }
> + auto component = atspi_accessible_get_component(event->source);
> + if (component)
> + {
> + AtspiRect* size = atspi_component_get_extents(component, ATSPI_COORD_TYPE_SCREEN, NULL);
> + focus.x = size->x;
> + focus.y = size->y;
> + focus.w = 7;
> + focus.h = size->height;
> + AccessibilityWatcher::getInstance()->focusList.push_back(focus);
> + return true;
> + }
> + }
> + }
> + if (focus.application == "Firefox")
> + {
> + if (ignoreLinks && focus.type != "caret" && focus.role == "link")
> + {
> + return true;
> + }
> + if (focus.type == "caret" &&
> + (static_cast<std::string>(event->type) == "object:text-changed:insert:system" ||
> + static_cast<std::string>(event->type) == "object:text-changed:delete:system")) { // prevents status bar focus in firefox
> + return true;
> + }
> + if (focus.type == "focus" && focus.role == "document frame")
> + { // general page parasite event
> + return true;
> + }
> + if (focus.type == "caret" && !(focus.x == 0 && focus.y == 0))
> + {
> + AccessibilityWatcher::getInstance()->focusList.push_back(focus);
> + return true;
> + }
> + getAlternativeCaret(focus, event);
> + if (focus.type == "caret" && !(focus.xAlt == 0 && focus.yAlt == 0))
> + {
> + focus.x = focus.xAlt;
> + focus.y = focus.yAlt + focus.hAlt;
> + focus.w = focus.wAlt;
> + focus.h = focus.hAlt;
> + AccessibilityWatcher::getInstance()->focusList.push_back(focus);
> + return true;
> + }
> + }
> + if (focus.application == "evince" && focus.type == "state-changed:selected" && focus.role == "icon")
> + { // LO-calc: avoid spam event from main edit line
> + return true; // ignores the parasite event from evince icon
> + }
> + return false;
> +}
> +
> +bool AccessibilityWatcher::filterBadEvents(const FocusInfo& event) {
> + if (event.type == "caret" && event.x ==0 && event.y == 0)
> + {
> + return true;
> + }
> + if (!event.active)
> + {
> + return true;
> + }
> + if (!event.focused && !event.selected)
> + {
> + return true;
> + }
> + if (AccessibilityWatcher::getInstance()->getScreenWidth() != 0 && AccessibilityWatcher::getInstance()->getScreenHeight() !=0 &&
> + (event.x > AccessibilityWatcher::getInstance()->getScreenWidth() ||
> + event.y > AccessibilityWatcher::getInstance()->getScreenHeight() || // TODO remove when non-singleton
> + event.x < 0 ||
> + event.y < 0 )) {
> + return true;
> + }
> + return false;
> +}
> +
> +/*
> + * this is meant to emulate the missing "selected" argument in menu hierarchy.
> + * no idea whether this is a fail in GTK, or in ATSPI
> + */
> +bool AccessibilityWatcher::returnToPrevMenu() {
> + if (previouslyActiveMenus.size() > 1)
> + {
> + previouslyActiveMenus.pop_back();
> + focusList.push_back(previouslyActiveMenus.back());
> + return true;
> + }
> + return false;
> +}
> +
> +/*
> + * Tries to extrapolate a missing caret position from other text characters.
> + * is used as last resort when application doesn't respect at-spi standarts,
> + * or at-spi bugs.
> + */
> +void AccessibilityWatcher::getAlternativeCaret(FocusInfo& focus, const AtspiEvent* event) {
> + auto text = atspi_accessible_get_text(event->source);
> + if (!text) { return; }
> + auto offset = atspi_text_get_caret_offset(text, NULL);
> + auto caretChar = std::string(atspi_text_get_string_at_offset(text, offset, ATSPI_TEXT_GRANULARITY_CHAR, NULL)->content);
> + bool hasSeenDeviceControl1 = false;
> +
> + // if we're at a newline, sometimes at-spi isn't giving us a caret position. unknown bug in some apps.
> + if (caretChar == "\n" || caretChar == "\0")
> + {
> + // gives the last empty line the right focus.
> + int lines = atspi_text_get_character_count(text, NULL) == offset ? 1 : 0;
> + int charIndex = 1;
> + bool charExtentsFound = false;
> +
> + AtspiRect *size = atspi_text_get_character_extents(text, offset, ATSPI_COORD_TYPE_SCREEN, NULL);
> + // try and find the character on upper line to extrapolate position from. no more that 300 char, we risk lag.
> + while (!charExtentsFound && charIndex <= offset && charIndex < 300) {
> + size = atspi_text_get_character_extents(text, offset - charIndex, ATSPI_COORD_TYPE_SCREEN, NULL);
> + caretChar = atspi_text_get_string_at_offset(text, offset - charIndex, ATSPI_TEXT_GRANULARITY_CHAR, NULL)->content;
> + // if we found a caret, check we're at beginning of line (or of text) to extrapolate position
> + if ((size->x != 0 || size->y != 0) && int(caretChar[0]) != -17)
> + {
> + if (offset - charIndex -1 >= 0 && std::string(atspi_text_get_string_at_offset(text, offset - charIndex -1, ATSPI_TEXT_GRANULARITY_CHAR, NULL)->content) == "\n")
> + {
> + charExtentsFound = true; // first character of upper line has been found
> + }
> + else if (offset - charIndex -1 == 0)
> + {
> + size = atspi_text_get_character_extents(text, 0, ATSPI_COORD_TYPE_SCREEN, NULL);
> + // first character of upper line has been found
> + charExtentsFound = true;
> + }
> + }
> + else if (caretChar == "\n" || int(caretChar[0]) == -17)
> + {
> + ++lines;
> + }
> + if (!hasSeenDeviceControl1 && int(caretChar[0]) == -17 )
> + {
> + hasSeenDeviceControl1 = true;
> + }
> + ++charIndex;
> + }
> + if (!hasSeenDeviceControl1)
> + {
> + lines--;
> + }
> + focus.xAlt = size->x;
> + focus.yAlt = size->y + (lines-1) * size->height;
> + focus.wAlt = size->width;
> + focus.hAlt = size->height;
> + }
> +}
> +
> +
> +void AccessibilityWatcher::onCaretMove (const AtspiEvent *event, void *data) {
> + registerEvent(event, "caret");
> +}
> +
> +void AccessibilityWatcher::onSelectedChange (const AtspiEvent *event, void *data) {
> + registerEvent(event, "state-changed:selected");
> +}
> +
> +void AccessibilityWatcher::onFocus (const AtspiEvent *event, void *data) {
> + /* We only care about focus/selection gain
> + * there's no detail1 on focus loss in AT-SPI specs */
> + if (!event->detail1) {return;}
> +
> + registerEvent(event, "focus");
> +}
> +
> +void AccessibilityWatcher::onDescendantChanged (const AtspiEvent *event, void *data) {
> + registerEvent(event, "active-descendant-changed");
> +}
> +
> +/* Register to events */
> +void AccessibilityWatcher::addWatches() {
> +
> + //auto callback = boost::bind (&AccessibilityWatcher::onFocus, this, _1, _2);
> + //auto bound_fn = boost::bind<void>( boost::mem_fn(&AccessibilityWatcher::onFocus), this, _1, _2);
> + //auto bound_fn = boost::bind( &AccessibilityWatcher::onFocus, this, _1, _2);
> + //boost::function<void (const AtspiEvent *event, void *data)> callback(functor, this);
> +
> +
> + //std::function<void (AtspiEvent*, void*)> bound_fn = std::bind(&AccessibilityWatcher::onFocus, this, std::placeholders::_1, std::placeholders::_2);
> + //using sig = void (AtspiEvent*, void*);
> + //using tamere = std::function<sig>;
> + //AtspiEventListenerCB bound_fn = std::bind(&AccessibilityWatcher::onFocus, this, std::placeholders::_1, std::placeholders::_2);
> + //focusListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(bound_fn), NULL, NULL);
> +
> + focusListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(onFocus), NULL, NULL);
> + caretMoveListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(onCaretMove), NULL, NULL);
> + selectedListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(onSelectedChange), NULL, NULL);
> + descendantChangedListener = atspi_event_listener_new (reinterpret_cast<AtspiEventListenerCB>(onDescendantChanged), NULL, NULL);
> +
> + atspi_event_listener_register (focusListener, "object:state-changed:focused", NULL);
> + atspi_event_listener_register (caretMoveListener, "object:text-caret-moved", NULL);
> + atspi_event_listener_register (caretMoveListener, "object:text-changed:inserted", NULL);
> + atspi_event_listener_register (caretMoveListener, "object:text-changed:removed", NULL);
> + atspi_event_listener_register (selectedListener, "object:state-changed:selected", NULL);
> + atspi_event_listener_register (descendantChangedListener, "object:active-descendant-changed", NULL);
> +}
> +
> +void AccessibilityWatcher::removeWatches() {
> + atspi_event_listener_deregister (focusListener, "object:state-changed:focused", NULL);
> + atspi_event_listener_deregister (caretMoveListener, "object:text-caret-moved", NULL);
> + atspi_event_listener_deregister (caretMoveListener, "object:text-changed:inserted", NULL);
> + atspi_event_listener_deregister (caretMoveListener, "object:text-changed:removed", NULL);
> + atspi_event_listener_deregister (selectedListener, "object:state-changed:selected", NULL);
> + atspi_event_listener_deregister (descendantChangedListener, "object:active-descendant-changed", NULL);
> +}
> +
> +void AccessibilityWatcher::init() {
> + if (initialized) { return; }
> +
> + atspi_init ();
> + atspi_set_main_context(g_main_context_default());
> + addWatches();
> +
> + initialized = true;
> +}
> +
> +void AccessibilityWatcher::quit() {
> + removeWatches();
> + initialized = false;
> +}
> +
> +std::deque<FocusInfo> AccessibilityWatcher::getFocusQueue() {
> + return focusList;
> +}
> +
> +void AccessibilityWatcher::resetFocusQueue() {
> + focusList.clear();
> +}
> +
--
https://code.launchpad.net/~ksamak/compiz/ezoom_focus_tracking/+merge/321643
Your team Compiz Maintainers is subscribed to branch lp:compiz.
More information about the Ubuntu-reviews
mailing list