-
Notifications
You must be signed in to change notification settings - Fork 0
/
chapter1.tex
224 lines (181 loc) · 18.5 KB
/
chapter1.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
\chapter{Анализ задачи}
\section{Обзор существующих реализаций палитр команд}\label{analogs}
Впервые палитра команд появилась 1 июля 2011 году в редакторе Sublime Text
2~\cite{sublimetext2changelog}. Вслед за этим подобная функция была реализована
в некоторых других программах. Таких, как:
\begin{itemize}
\item Atom\cite{atom},
\item VSCode\cite{vscode},
\item JupyterLab\cite{jupyterlab}.
\end{itemize}
Но это были лишь единичные случаи. В апреле 2017 года появилась альфа-версия
приложения Plotinus\cite{plotinus}, которое позволяет добавлять палитру команд в
любое приложение, использующее графическую библиотеку GTK.
Таким образом можно наблюдать, что частные решения начинают заменяться более
универсальными. Однако на текущий момент эти решения не позволяют покрыть
большинство областей т.к. ограничены лишь программами с GTK, который
используется не более чем в половине прикладных приложений для ОС Linux и
занимает совсем малую долю среди приложений для ОС Windows.
\begin{figure}[h]
\centering
\includegraphics[width=0.9\textwidth]{SublimeText}
\label{sublimetext}
\caption{Sublime Text}
\end{figure}
\begin{figure}[h]
\centering
\includegraphics[width=\textwidth]{Plotinus}
\caption{Plotinus}
\end{figure}
\section{О фреймворке Qt}
Qt — это кроссплатформенный фреймворк для разработки программного обеспечения
на языке программирования C++\cite{qtabout}. Он содержит множество библиотек для
упрощения реализации прикладных задач. Благодаря кроссплатформенности данный
фреймворк позволяет запускать написанное с его помощью ПО на многих операционных
системах путем обычной сборки проекта без внесения изменений в исходный код
самой программы. Отличительной особенностью Qt является наличие метаобъектного
компилятора, который запускается в начале сборки и генерирует
вспомогательный код. Такой подход позволил добавить частичную поддержку
рефлексии.
Qt включает в себя следующие модули:
\begin{itemize}
\item Qt Core — базовые классы, обеспечивающие рефлексию, работу со
строками, механизмы владения и т.п. Используются всеми остальными
модулями;
\item Qt Network — классы, позволяющие легко писать переносимый код для
работы с сетью;
\item Qt SQL — классы, предоставляющие удобный программный интерфейс для
работы с различными реляционными БД;
\item Qt Multimedia — классы, для работы с данными (аудио и видео),
устройствами (камеры, микрофоны);
\item Qt GUI — классы, позволяющие реализовать приложения с графическим
интерфейсом.
\end{itemize}
В ОС Linux библиотеки GTK и Qt являются двумя наиболее популярными средствами
для реализации приложений с графическим интерфейсом. Т.к. средство для
добавления палитры команд уже есть для GTK, в данной работе будет рассмотрена
такая возможность для Qt.
События в Qt — это объекты, унаследованные от абстрактного класса \code{QEvent}.
Они представляют действия, произошедшие внутри приложения, либо созданные в
результате активности пользователя, о которых приложение должно знать. События
могут быть получены и обработаны любым экземпляром подкласса \code{QObject}, но
они особенно актуальны для графических элементов.
Обычно события доставляются объектам через вызов виртуальных функций. Если
разработчик хочет заменить функцию базового класса, он должен реализовать
всю обработку самостоятельно. Однако, если нужно только расширить
функциональность то разработчик должен реализовать нужное расширение, а затем
вызывать базовый класс, чтобы обеспечить поведение по умолчанию для тех случаев,
которые он не хочет обрабатывать.
Не всегда в классе имеется нужная функция для события. Наиболее распространенный
пример — обработка нажатия клавиш, для которой нет соответствующей виртуальной
функции. Для обработки таких событий нужно переопределить метод
\code{QObject::event()}. Он является общим обработчиком событий, и позволяет
выполнить дополнительные действия до или после обработки по-умолчанию.
\section{Организация работы приложения}
Система управления должна позволять контролировать множество приложений. Для ее
реализации лучше всего подходит клиент-серверная архитектура. Для каждого
целевого приложения запускается отдельный клиент, который занимается сбором
информации об элементах управления и передает ее на сервер.
В качестве сервера будет выступать приложение, которое запускает целевые
приложения вместе с клиентами. После этого сервер принимает входящее соединение
от клиента и отображающее окно поиска элемента для текущего активного окна.
Для удобной работы окно поиска должно отображаться окно поверх текущего
активного приложения. Палитра команд является инструментом для помощи
пользователю в поиске нужной функции. Он может не знать точное наименование
команды, поэтому в приложении должна быть поддержка нечеткого поиска.
\subsection{Добавление логики в стороннее приложение}
Разработчики любого приложения не могут учесть желания и капризы всех
пользователей. Благодаря этому приложения не превращаются в комбайны, которые
невозможно было бы поддерживать. Разработчики могут убрать какую-то деталь,
которая нужна малому проценту людей. Ведь надо тратить время на исправление
ошибок в ней. А иногда эти специфичные функции могут даже замедлять работу всего
приложения. В таком случае пользователи могут захотеть добавить какую-то
дополнительную логику или функцию в основное приложение.
Подходы делятся на два типа:
\begin{itemize}
\item добавление функции на этапе сборки приложения;
\item добавление функции в момент выполнения программы.
\end{itemize}
Целью данной работы является добавление функциональности в максимальную группу
приложений. Первый же подход исключает такую возможность для приложений с
закрытым исходным кодом. К тому же при первым подходом может пользоваться только
квалифицированный пользователь. И то, ему пришлось бы пересобирать каждое
приложение в которое он хотел бы добавить нужную функциональность.
Программный модуль подключаемый к уже существующему приложению называется
плагином. Для добавления возможности их подключения разработчики программы
должны или написать свою систему плагинов или воспользоваться готовой. Так,
например, библиотека GTK, начиная с третьей версии, предоставляет возможность
запускать приложения с дополнительными модулями, которые могут расширять
функциональность приложения. Этим воспользовались разработчики библиотеки
Plotinus, реализовав возможность добавления палитры команд в любое приложение,
использующее GTK.
Qt предоставляет возможность встраивать дополнительную функциональность в
приложение, но для этого оно должно иметь специальный код по загрузке
дополнительных модулей, который в большинстве случаев не используется (для
подтверждения можно сравнить число github репозиториев использующих
Qt\cite{githubqt} и использующих функцию Qt для работы с
плагинами\cite{githubqpluginloader}. Соотношение примерно 1:100).
Кроме штатных средств добавления возможностей на уровне приложения есть и более
низкоуровневые. Так например в Windows есть функция автоматизации интерфейса: UI
Automation\cite{windowsuiautomation}, которая изначально была добавлена для
увеличения доступности приложений людям с ограниченными возможностями. С её
помощью можно работать только с видимыми элементами интерфейса, но не получать
внутренние состояния (что можно сделать через плагины), но зато доступен для
любого приложения, использующего стандартные элементы управления. В случае
использования Linux, к сожалению, нет такой возможности на уровне ОС или
графической оболочки.
\subsection{Внедрение модуля}
Рассмотрим в общих чертах механизм работы графического приложения. На
рисунке~\ref{fig:gui} изображено как происходит взаимодействие между элементами
графического приложения и пользователем.
\begin{figure}
\centering
\input{schemes/gui.tex}\\
\caption{Взаимодействие элементов ГИП и пользователя}\label{fig:gui}
\end{figure}
Исходя из данной упрощенной схемы можно предложить еще одно способ добавить
функциональность — создание события от графической библиотеки, которое будет
передано приложению.
\subsection{Механизм подмены функций}
При запуске приложения загрузчик получает из него список всех используемых
динамических библиотек, загружает их в память. Затем получает адреса всех
экспортированных функций динамической библиотеки и сохраняет их для последующего
вызова.
Загрузчик ld, который используется в Linux и FreeBSD позволяет загружать
дополнительные динамические библиотеки, кроме тех, кто запрашивает приложение.
Эта дополнительная библиотека загружается раньше всех остальных, что позволяет
ей подменять функции из других библиотек. Это происходит потому, что при поиске
адреса определенной функции берется первый подходящий.
\subsection{Способ получения информации об элементах}
Для получения информации об элементах интерфейса можно загрузить специальную
библиотеку, которая будет регистрировать создания, изменения и удаления
элементов интерфейса. Затем собранная информация будет передавать на сервер для
последующей работы.
Также данная библиотека может создавать ложные события по командам, приходящим с
сервера. Таким образом можно имитировать нажатия кнопок, открытие меню и т.п.
Пользовательский интерфейс через специальное API сможет получать от сервера
информацию о доступных элементах в текущем приложении. После того, как
пользователь произвел выбор, вызывается специальная функция на стороне сервера,
которая приводит к отправке команды клиенту.
\subsection{Реализация кода для подмены функции}
Для внедрения библиотеки требуется реализовать заглушки функций Qt, которые
будут вызывать специальный обработчик, а затем продолжать нормальное выполнение
функции. Дополнительную сложность создает то, что библиотека Qt написана на
языке C++, который из-за поддержки классов и перегрузок функций использует т.н.
«искажение имен» (name mangling). Таким образом, чтобы создать обработчик,
нужно сконструировать специальное имя функции исходя из названия класса,
метода, набора параметров и возвращаемого значения.
Создание таких обработчиков является рутинной задачей в которой человек легко
может допустить ошибку. Поэтому вместо ручного написания каждого обработчика
нужно написать генератор, который может добавить необходимые обработчики, имея
минимальный и необходимый набор данных (имя класса, метода и т.д.).
\subsection{Требующиеся компоненты}
Исходя из приведенного выше анализа следует, что задачи должны быть сгруппированы
в набор программ. Он должен быть реализован в виде следующих элементов:
\begin{enumerate}
\item\label{lib} Библиотека для внедрения и сбора информации в приложении.
\item Приложение для сохранения информации, полученной из нескольких
приложения с библиотекой из пункта \ref{lib}.
\item Интерфейс для запуска приложений и отображения палитры команд.
\end{enumerate}
\input{chapter1/arch.tex}