Developer’s Guide¶
Setting up a Development Environment¶
It should be possible to develop QuickTile on any POSIX-compatible platform with X11-based graphics. However, Linux is the only officially supported option.
To ensure your changes get accepted as quickly as possible, please bear in mind the testing requirements.
On the operating system you intend to use for development:
Begin by cloning the QuickTile repository. either directly or from a fork you’ve made on GitHub:
git clone git@github.com:ssokolow/quicktile.git
Install the runtime dependencies.
Install Xvfb, Openbox, and Zenity (eg.
sudo apt install xvfb openbox zenity
) for the functional tests.
Either use the following command to install QuickTile’s additional development-time dependencies, or manually install the dependencies listed therein:
pip3 install -r dev_requirements.txt
These dependencies fall into one of two categories:
Source code verification (Ruff for static analysis and code style, MyPy for checking type hints, PyTest for test discovery, and Coverage.py for determining test coverage)
Documentation generation (Sphinx, sphinx-autodoc-typehints, and sphinxcontrib-autoprogram)
If you intend to modify the illustrations or demonstratory animation, you will also require the following to regenerate the built files:
A POSIX-compatible environment (For tools such as find)
GNU Make (to run the Makefiles used to automate the process)
Inkscape (to render the SVG sources to PNG)
OptiPNG and AdvanceCOMP (to optimize the illustrations)
ImageMagick (to combine the frames of the animation into an animated GIF)
Gifsicle (to optimize the animation)
Rely on the
./quicktile.sh
option for running QuickTile without installing it as described in Setting Up Global Hotkeys.This combination of full access to Git functionality and the ability to run the changed code without needing to install first provides for the simplest development environment, and makes it easy to remove the development version and revert to the release versions once you are finished.
Building Development Documentation¶
QuickTile’s documentation contains extensive TODO notes which are omitted from release versions.
To enable inclusion of these development notes…
Uncomment
todo_include_todos = True
indocs/conf.py
Run
(cd docs; make html)
.Your developer documentation should now exist in
docs/_build/html/
.
The resulting API documentation will include in-line TODO annotations, as well as a complete listing at the bottom of the API Documentation page.
Note
If Sphinx fails to notice that part of the documentation should be
rebuilt, a rebuild can be forced either by deleting the _build/html
directory or by running (cd docs; make html SPHINXOPTS=-E)
instead.
Generated documentation, such Command-Line Arguments may also require deleting other
files under _build
but it is advised to delete individual files such as
_build/doctrees/cli.doctree
instead of the entire _build
folder
to avoid re-downloading the InterSphinx indexes, which could
get you temporarily rate-limited.
There also exist TODO comments in the source code (usually ones that shouldn’t be seen as drawing attention away from the ones in the Sphinx docs) which can be searched for by running the following command in the project root:
grep -E 'XXX|TODO|FIXME' -nR *.py quicktile tests
Regenerating Documentation Graphics¶
To regenerate the illustrations, run the following command:
(cd docs/diagrams; make) && (cd docs; make html)
To regenerate the animation, run the following command:
(cd docs/animation; make) && (cd docs; make html)
You only need to do this if you’ve modified the original SVG files.
Documentation Privacy Policy¶
Out of respect for user privacy and to make offline use of this documentation as robust as possible, this website/manual makes no external HTTP requests.
To mitigate the risk of such requests slipping in through non-obvious means,
such as use of the Sphinx :math:
role pulling in a CDN-hosted copy of
MathJax, a Content Security Policy meta-tag has been added to the header of
the site template.
It is preferred that you check your browser’s developer console for reports of requests blocked by the CSP rules on the relevant pages before submitting changes to the manual or docstrings.
High-Level QuickTile Architecture¶
Quicktile is fundamentally built around a somewhat HTTP-like request-response model. The user requests an action, QuickTile performs that action, and then it goes back to waiting for another event.
Any state which needs to persist between these event handlers should be stored
as X11 window properties using the
quicktile.wm.WindowManager.set_property()
and
quicktile.wm.WindowManager.get_property()
methods.
Quirks of the Codebase’s Structure¶
The
quicktile.__main__.main
function is currently responsible for gluing everything together.At the moment, due to an incomplete refactoring during the GTK+ 3 port, the
quicktile.keybinder
module is still structured as if optional, though it is now required for its role in managing the Xlib connection.Due to oddities in how the X11 protocol behaves when interacting with short-lived connections, you are likely to get strange and confusing bugs if the keybinder is not allowed to properly carry out its responsibility for integrating X11 into the QuickTile event loop.
(Indeed, the bugs that still need to be rooted out of the QuickTile event loop stem from my not having properly rooted out bugs relating to X11 and short-lived applications.)
At present, window management is split between the
quicktile.wm
andquicktile.util
modules, with the former being concerned with communication with the outside world and the latter having temporarily become a grab-bag of everything that is so self-contained as to be easy to unit test.The
quicktile.commands
module also needs to be refactored as it currently contains the framework for registering and executing tiling commands and the shared setup code for them (lumped into a single class) as well as all of the commands themselves.
Good Development Practice¶
Before making changes you intend to have merged back into QuickTile’s
master
branch, please open a feature request on the issue tracker to
propose them. This will allow me to bring up any non-obvious design concerns
which might complicate, delay, or preclude my accepting your changes.
Note
Please bear in mind that QuickTile is still catching up after a decade of spotty maintenance and it may take time for your changes to get proper attention.
When working on QuickTile, please keep the following code-quality goals in mind as, if you do not, then merging your changes may have to wait until I can revise them:
All function arguments should bear complete type annotations which pass MyPy’s scrutiny and use of
typing.Any
or# type: ignore
must be approved on a case-by-case basis.All Ruff complaints must either be resolved or whitelisted. New whitelisting annotations must include comments justifying their presence, except in self-evident cases such as URLs in docstrings which exceed the line-length limit.
All code within the
quicktile
package must have complete API documentation that renders through Sphinx to a standard consistent with existing documentation.doctests count as implicit API requirements and changes to them should not be made frivolously.
The percentage of unit test coverage in the
quicktile.util
module should not decrease. (Enforcing this standard outside of that module will not be feasible until further refactoring and test harness work is completed.)
Once your changes are ready, the standard way to submit them is via pull
request against the master
branch, as this will automatically trigger
a test run, as well as making it as simple as possible for me to examine and
accept them.
Testing Your Changes¶
Testing Environment Concerns¶
As of this writing, QuickTile’s current minimum compatibility target is Kubuntu Linux 24.04 LTS. This may be broadened as the testing infrastructure is modernized.
If this is not what you are running, I suggest using VirtualBox for compatibility testing, as it is easy to set up and has support for virtual machines with multiple monitors.
Warning
When installing VirtualBox, be sure to not install the Oracle VM VirtualBox Extension Pack, as it phones home and Oracle has been making large licensing demands of people who they believe to be using it commercially. [1] [2]
Those using only VirtualBox packages provided by their Linux distribution’s official package repositories should have no need to worry, but its absence can be confirmed by choosing VNC are present.
from the VirtualBox menu bar, and verifying that no extensions other thanShould this prove too concerning, KVM-based solutions such as virt-manager or GNOME Boxes should also serve equally well though I can give no advice on setting them up for multi-monitor support.
For best results, configure your virtual desktop with the following characteristics:
Differently-sized monitors (Certain bugs in moving windows from monitor to monitor can only be triggered if one monitor is larger or smaller than another.)
Panels (taskbars and the like) on an edge where the monitors are adjacent but do not line up.
Suppose you have a 1280x1024 monitor and a 1920x1080 monitor, and the tops are aligned. Place panels on the bottom, so that the reservation for the shorter monitor will also have to cover the dead space below it and has the best chance of triggering any dead-space-related bugs in the code for calculating usable regions.
Note
The following two behaviours are currently known bugs where a proper solution is blocked on reworking how window state is tracked and you don’t need to worry that you’ve caused them:
If a window is so far down or to the right that it would be outside the bounds of the destination monitor, QuickTile will refuse to honor a request to move it to that monitor to avoid the risk of your window manager allowing it to get lost off the edge of the desktop. This can block commands like
monitor-switch
.If a window’s top-left corner is within the bounds of the destination montiro, but its bottom-right corner extends beyond it, the window will be resized to fit, but it won’t remember its old size if it’s moved back to its original monitor.
Automated Testing¶
To run a complete set of all tests, please use the following command from the root of the project:
./run_tests.sh
The following will be run:
MyPy to check for violations of the type annotations.
Ruff for basic static analysis and code style checking
PyTest and doctest to run the unit tests (currently of limited scope)
doctest to check for broken code examples in the API documentation
Sphinx’s
make coverage
to check documentation coverage (currently of questionable reliability)
In lieu of a proper functional test suite, please manually execute all tiling commands which rely on code you’ve touched and watch for misbehaviour.
Semi-Automated Testing¶
While QuickTile does not yet have a proper functional test suite, the
test_functional.sh
script can be used to quickly perform human testing of
most of the commands for both the CLI and D-Bus interfaces.
Open a free-floating terminal window which is not set to be always on top or on all desktops.
Run
./test_functional.sh
Follow the instructions
The script will temporarily make the terminal window always-on-top and on-all-desktops, open a test window (FeatherPad by default), and then walk it through a predefined sequence of QuickTile commands, with a one-second delay between each.
The intended way to use it is to keep your eyes focused on the “Testing [command name]…” messages in the terminal, while you confirm that the movement you’re seeing in your peripheral vision matches what it’s claiming to do.
How to Make A New Release¶
While contributors don’t need to know this, both for maintainer reference and in case QuickTile ever needs to be forked, this is the checklist for making a new release:
Prepare the corresponding commit
Verify that
AUTHORS
andChangeLog
are up to date and remove(git HEAD)
from the newestChangeLog
entry’s title.Make sure that all changes have been committed.
Run (cd docs; make html) and ./run_tests.sh locally and confirm that nothing looks wrong.
Run ./test_functional.sh
Manually test the internal keybinder using ./quicktile.sh --daemonize.
Push any remaining changes to GitHub.
If they pass CI testing, merge them into
master
.Wait for the CI tests and site update to pass on
master
.
Make the release
Copy the commit hash which was merged into
master
and passed CI from GitHub to ensure that you’re getting the exact one you intend.Run git tag vXXX <commit-hash> to mint the release, where
XXX
is the string fromquicktile/VERSION
and<commit-hash>
is the commit hash from GitHub. (At present, QuickTile does not use annotated commits)Run git push --tags to push the release live.
Bump the development version
Bump
quicktile/VERSION
Add a new blank entry to
ChangeLog
with(git HEAD)
before the terminal colon.