ssokolow / quicktile

Adds window-tiling hotkeys to any X11 desktop. (An analogue to WinSplit Revolution for people who don't want to use Compiz Grid)

Home Page:https://ssokolow.com/quicktile/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

python-wnck removed in Ubuntu 18.04

bl4ck5un opened this issue · comments

python-wnck is no longer available in Ubuntu 18.04. I'm using the package from artful (17.10) as a workaround. Would be great to know how to install quicktile properly in 18.04.

Ahh. I didn't catch that because, as every *buntu upgrade I've ever done has unpredictably broken things, I haven't been able to speculatively budget a huge block of time for an upgrade from 14.04 LTS yet.

I suspect python-wnck is gone because applications are expected to use PyGI and the language-agnostic GObject Introspection bindings for libwnck rather than PyGTK and python-wnck.

The proper solution will be me migrating it to Python 3.x and PyGI when I can spare the time (the API adapter to ease porting PyGTK apps to PyGI segfaults when I hook QuickTile up to it, so I need to do it the long way... which also takes time I haven't had).

Okay. make sense. I'm not familiar with PyGI etc at all so can't help there. For now, using python-wnck from 17.10 works flawlessly.

If you're reading this because you just want a python-wnck that work on Ubuntu Bionic Beaver 18.04, download it for your architecture from https://packages.ubuntu.com/artful/python-wnck . Thanks @bl4ck5un for the tip!

To @ssokolow: thanks for all.

It seems python-wnck package was removed a month ago, ref https://launchpad.net/ubuntu/bionic/amd64/python-wnck with reference to https://bugs.launchpad.net/ubuntu/+source/gnome-python-desktop/+bug/1739797

gnome-python-desktop was recently removed from Debian Testing since it is GNOME2 tech that has been unmaintained for years.

sudo sh -c "echo 'deb http://cz.archive.ubuntu.com/ubuntu artful main universe' >> /etc/apt/sources.list" 

then you can run the apt install command

EDIT by ssokolow: For people who don't scroll down to my reply, don't use > unless you're echoing to a brand new file in /etc/apt/sources.list.d/ or you'll wipe out your regular list of package sources. >> will append instead.

The situation is the same with Debian Testing (Buster) which is what Ubuntu 18.04 is based on; python-wnck is unavailable. But, it's available in Debian Unstable i.e. sid - those of you running Debian Testing can download the sid/stretch packages here: https://packages.debian.org/sid/python-wnck and https://packages.debian.org/stretch/python-wnck

It looks like there is a way to directly have wnck available from python without dedicated python-wnck package, using the GObject introspection infrastructure. Details are on https://stackoverflow.com/a/43349245/1429390

apt-get install python3-gi gir1.2-wnck-3.0

then

>>> import gi
>>> gi.require_version('Wnck', '3.0')
>>> from gi.repository import Wnck

I've been aware of that for a while, but there are three problems with it:

  1. That's libwnck 3.x, which is for GTK+ 3.x, while QuickTile is a GTK+ 2.x application.
  2. I don't think it's possible to mix PyGTK and PyGI in the same event loop and, even if it is, it's likely to introduce random crashes.
  3. I've tried to start porting from PyGTK to PyGI in the past, but, when I just swapped in the API shim PyGI provides to ease porting of PyGTK apps, I got segfaults on my *buntu 14.04 LTS system.

Basically, while I do eventually want to use that, it requires a very involved porting process that I'm still trying to make time for:

  1. Port from Python 2.x to Python 3.x, because Python 2.x is soon to be end-of-lifed
  2. Port from PyGTK to PyGI, because PyGTK is end-of-lifed
  3. Port from GTK+ 2.x to GTK+ 3.x, because GTK+ 2.x is end-of-lifed (that's why libwnck 2 is going away)

...and since PyGTK isn't available for Python 3.x and PyGI's support for GTK+ 2.x is flaky at best, I have to do it all at once.

(And it's impossible to keep it working during the intermediate steps so I can port incrementally and test for bugs along the way. It many ways, the complexity approaches a complete rewrite and, if there were a suitable equivalent to libwnck in the Qt ecosystem, I'd probably port to PyQt 5 instead.)

I'd hoped to write an automated test suite first, but other things kept cropping up.

For those that want to add the artful repo as mentioned by @Falieson I would recommend using the following command:

sudo sh -c "echo 'deb http://cz.archive.ubuntu.com/ubuntu artful main universe' >> /etc/apt/sources.list"
Make sure that you have two >> and not just one > The reason being that > will overwrite the file, but >> will append it to the end of the file. Trust me you don't want to wipe out your /etc/apt/sources.list file. I got my setup running using @Falieson command (thanks!) and my little tweak.

I recommend adding a file to /etc/apt/sources.list.d instead. That way, you're not even touching sources.list.

sudo sh -c "echo 'deb http://cz.archive.ubuntu.com/ubuntu artful main universe' > /etc/apt/sources.list.d/artful-main-universe.list"

@ssokolow obviously same issue on 18.10,
any plan on fixing this? (maybe you are not an Ubuntu user!? :) )

I wasn't really looking forward to play around with sources so I've figured out a crude way of doing quarter tiling using this script:
https://gist.github.com/peteruithoven/db0cba0b0849c8cb5e267f6e75126304
You'll have to adapt some values based on your screen size, top panel size, window margins etc. The default window margins are elementary OS specific for example.

@peteruithoven nice, although this looks very very basic! :) quicktile does a lot more than just quarter tile! I've been using quicktile for years now and couldn't find a better option (I'm biased & used to it). You script looks good for 1 single "fixed" monitor, I use it on my laptop which I connect to different external devices.

@kbsali Oh, I certainly intend to fix it.

It's just that my life has been so busy lately that I'm still on Kubuntu 14.04 LTS despite its support period ending in only a few months.

The problem is that switching to what's supported in newer releases requires a port from GTK+ 2.x to GTK+ 3.x, and that's more complicated than it sounds because of the need to also port from PyGTK to PyGI. (And staying on Python 2.x initially and using the PyGTK->PyGI porting shim to port in stages is complicated by the fact that, last time I tried that, PyGI segfaulted.)

I had some free time today, so I decided "appears to work, but potentially buggy" is better than "nothing testable", and I've started to work on getting it ported to PyGI and Wnck 2.x.

I'm to the point where the only thing left that static analysis is complaining about is the handful of sparse unit tests I have, which are use GdkRegion for mocking... and there's no apparent "rename X to Y" porting path for that, so I'll need a little more time to rewrite the relevant bits.

UPDATE: ...and manual testing reminded me of how heavily I use GdkRegion in the code without directly referencing the type name. Time to figure out what the APIs which used to return it are returning now.

UPDATE: To clarify, GdkRegion was replaced with cairo_region_t, but I'm having trouble figuring out how to access it via a Python binding. (The API exposed by PyGI doesn't seem to have a way for me to instantiate one and I'm still on PyCairo 1.8.8, when cairo.Region didn't get exposed until 1.11.0.)

I decided I'll just leave the GTK+ 2.x version easily accessible in a branch for anyone needing pre-1.11.0 PyCairo, so I upgraded my copy of PyCairo and I've continued to work on the porting to GTK+ 3.x.

Still not complete enough to run without dying from "no such method" errors, but progress is being made.

Great news, thanks for the effort! 💪

Download python-wnck for 18.10

I've been, trying Xubuntu 18.10.
The link I mentioned above is already broken because artful is considered an old release.
You can now find a version for your architecture on:
http://old-releases.ubuntu.com/ubuntu/pool/main/g/gnome-python-desktop/

Fail?

But... it did not work for me. Why? See commands and error below.

# I issued those commands (success, result hidden).
wget http://old-releases.ubuntu.com/ubuntu/pool/main/g/gnome-python-desktop/python-wnck_2.32.0-0ubuntu6_amd64.deb
sudo dpkg --install python-wnck_2.32.0-0ubuntu6_amd64.deb 

# I then ran quicktile which failed, see output.
quicktile --daemonize 

Traceback (most recent call last):
  File "/usr/local/bin/quicktile", line 6, in <module>
    from pkg_resources import load_entry_point
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 3105, in <module>
    @_call_aside
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 3089, in _call_aside
    f(*args, **kwargs)
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 3118, in _initialize_master_working_set
    working_set = WorkingSet._build_master()
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 578, in _build_master
    ws.require(__requires__)
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 895, in require
    needed = self.resolve(parse_requirements(requirements))
  File "/usr/lib/python2.7/dist-packages/pkg_resources/__init__.py", line 781, in resolve
    raise DistributionNotFound(req, requirers)
pkg_resources.DistributionNotFound: The 'python-wnck' distribution was not found and is required by QuickTile

Release info

lsb_release -a

No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 18.10
Release:	18.10
Codename:	cosmic
uname -a

Linux myhostname 4.18.0-11-generic #12-Ubuntu SMP Tue Oct 23 19:22:37 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Package info

dpkg -L python-wnck 

/.
/usr
/usr/share
/usr/share/doc
/usr/share/doc/python-wnck
/usr/share/doc/python-wnck/AUTHORS
/usr/share/doc/python-wnck/examples
/usr/share/doc/python-wnck/examples/wnck_example.py
/usr/share/doc/python-wnck/NEWS.gz
/usr/share/doc/python-wnck/copyright
/usr/share/doc/python-wnck/README
/usr/share/doc/python-wnck/changelog.Debian.gz
/usr/share/pyshared
/usr/share/pyshared/gtk-2.0
/usr/lib
/usr/lib/pyshared
/usr/lib/pyshared/python2.7
/usr/lib/pyshared/python2.7/gtk-2.0
/usr/lib/pyshared/python2.6
/usr/lib/pyshared/python2.6/gtk-2.0
/usr/lib/python2.7
/usr/lib/python2.7/dist-packages
/usr/lib/python2.7/dist-packages/gtk-2.0
/usr/lib/python2.7/dist-packages/gtk-2.0/wnck.so
/usr/lib/python2.6
/usr/lib/python2.6/dist-packages
/usr/lib/python2.6/dist-packages/gtk-2.0
/usr/lib/python2.6/dist-packages/gtk-2.0/wnck.so
/usr/lib/pyshared/python2.7/gtk-2.0/wnck.so
/usr/lib/pyshared/python2.6/gtk-2.0/wnck.so

It looks like setuptools (the thing which generates the /usr/local/bin/quicktile launcher) isn't seeing python-wnck for some reason.

Does running python2 -m quicktile --daemonize (which should bypass that aspect of setuptools) work?

If so, three options come to mind:

  1. Just run that instead
  2. Uninstall and reinstall QuickTile to see if setuptools will un-confuse itself.
  3. Uninstall QuickTile, remove the test_for_imports("wnck", "python-wnck", "python-wnck") line from setup.py, and then reinstall it so that the dependency on python-wnck is invisible to setuptools.

(For the record, I am still working on the port to GTK+ 3.x which will resolve this mess, but things got a little busy so I haven't had time to figure out how to port over a line which got horrendously ugly in the switch from PyGTK to PyGI.)

Hi @ssokolow and thank you for reaching back.

At the moment I can just confirm that python2 -m quicktile --daemonize just works, while running only quicktile --daemonize still fails with the same error message.

I hope I'll try the other steps soon, too.

Hi @ssokolow

Uninstall and reinstall QuickTile to see if setuptools will un-confuse itself.

It appeared to have un-confused itself. Now just running quicktile works.

You will find a slightly edited version of my terminal log (converted from ANSI color to HTML for reading comfort) here: https://gourichon.org/fsi/quicktile/unconfuse_log.html

Reading not only may help you (1) figuring out what became confused, but as it shows the kind of thing that actually happens when trying to follow installation instructions, it might (2) provide insights on how to adjust installation programs or human-readable instructions for maximum efficiency given the variety of people that may follow them.

Thanks again and keep up the good things!

Yeah. I definitely need to make some changes. Here is what I gather from that log:

First, I should have explicitly pointed you at the "Removal" section in the README.

As for the issues you ran into, they seem to mostly fall into two categories:

  1. You used the tooling I provided in an unsupported way without fully understanding what it was doing.
  2. I didn't draw enough attention to the existence of uninstallation instructions.

python2 setup.py install
...
[Errno 13] Permission non accordée: [...]

I can't find any installation instructions where I forgot to say to to either use sudo or to call ./install.sh, which will do it for you, so I can only assume that you copied a line out of install.sh without also copying the earlier line (exec sudo "$0" "$@") which sets up the required environment by re-running install.sh with root permissions if it wasn't run using sudo.

I can't be responsible for things breaking because people copy-pasted random bits of my scripts without understanding what preconditions must be upheld.

sudo python2 setup.py uninstall

[...]

error: invalid command 'uninstall'

Again, you seem to be extrapolating from incorrect assumptions because you didn't know about the "Removal" section at the bottom of the README. I'll have to make it more visible.

sudo pip2 uninstall quicktile

The directory '/home/stephane/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Uninstalling QuickTile-0.3:
/usr/local/lib/python2.7/dist-packages/QuickTile-0.3-py2.7.egg
Proceed (y/n)? ^COperation cancelled by user

I don't know why you cancelled this. You were on the right track.

My best guess is that I should use sudo -H in the instructions so that warning message doesn't pop up and confuse people.

rm: impossible de supprimer '/usr/local/bin/quicktile': Aucun fichier ou dossier de ce type

The locate command doesn't verify that results still exist, so it'll list files that still existed the last time the updatedb cronjob was run. That's why my removal instructions use find instead. It's slower, but doesn't produce potentially confusing results.

rm: impossible de supprimer '/usr/local/share/applications/quicktile.desktop': Aucun fichier ou dossier de ce type

I should consistently use rm -f in my scripts and instructions rather than rm so it won't produce a spurious error message if the file was already removed.

sudo rm /usr/local/bin/quicktile /usr/local/bin/quicktile.py

Removing quicktile.py is just to make sure that ancient single-file versions of QuickTile can't cause problems when someone uses ./install.sh to upgrade. There's a reason it only exists inside install.sh.

sudo: install.sh : commande introuvable

OK, I admit fault here. I never thought to test that sudo "$0" "$@" was robust in the face of less common ways to run install.sh.

sudo sh -c "echo 'deb http://cz.archive.ubuntu.com/ubuntu artful main universe' >> /etc/apt/sources.list" 

then you can run the apt install command

Can you please edit your comment from > to >> ?

Anyone has a solution to work around this without adding an old ppa to my sources?
I managed to install on 18.10, but now that I have upgraded to 19.04 it does not work anymore... :/

commented

this worked for me today on xubuntu 19.04 (in addition to the pre-reqs):

sudo apt install libpango1.0-0 pip
# used https://pkgs.org to find python-wnck
wget http://ftp.br.debian.org/debian/pool/main/g/gnome-python-desktop/python-wnck_2.32.0+dfsg-3_amd64.deb
sudo dpkg -i python-wnck_2.32.0+dfsg-3_amd64.deb 
sudo -H pip2 install https://github.com/ssokolow/quicktile/archive/master.zip

Just a little status update. Time was such a mess for me that I actually spent some time running Kubuntu 14.04 beyond even the LTS support window. (Yes, yes, I know. Bad programmer, no biscuit. That said, I was un-stupid enough to switch my Firefox from Canonical builds to Mozilla builds to ensure it kept getting security fixes and I make heavy use of things like uMatrix-based whitelisting.)

I'm currently on 16.04 while I squash the bugs, feature regressions, and unacceptable annoyances introduced by switching from Upstart to systemd, from KDE 4 to KDE 5, from a pre-CSD version of GTK+ 3.x to a post-CSD version, and just plain breakages that should be expected because I hacked something the first time rather than doing it properly.

Once that's done and I've finished finding Qt replacements for GTK+ 2.x apps that will go GTK+ 3.x in 18.04, I'll upgrade to 18.04 (16.04 isn't a 5-year-supported LTS like 14.04 and 18.04), squash the hopefully smaller number of bugs there, and then get back to work on QuickTile.

just upgraded to 19.10, good same old game again! 😅
So here is what I did this time :

sudo apt install python-gtk2 python-xlib python-pip libpango1.0-0
wget http://ftp.br.debian.org/debian/pool/main/g/gnome-python-desktop/python-wnck_2.32.0+dfsg-3_amd64.deb
sudo dpkg -i python-wnck_2.32.0+dfsg-3_amd64.deb 
sudo -H pip2 install https://github.com/ssokolow/quicktile/archive/master.zip

Another status update. I've been working on this, but it's slow going because I'm still on Kubuntu 16.04 and the GTK+ 3.x and Cairo bindings appear to be broken, so I'm limited in which parts of QuickTile's codebase I can actually work on and test.

(Specifically, cairo.Region and Gdk.Rectangle seem to have no way to construct them directly and what I was able to dig up indicates that this is a bug in the API definitions... and those are pretty central to something like QuickTile.)

Since I'm still not ready to jump up to 18.04 and beyond, I may have to install something newer in a VM and develop in that to get the port done in reasonable time.

This is of course none of my business, but why are you "stuck" on very old versions of Ubuntu? Nowadays the upgrade processes are pretty smooth! :)

I have trouble finding a large block of time where I can risk having to spend time fixing breakages.

Heck, I'm still finding and squashing the odd thing from the 14.04 -> 16.04 migration and that went well compared to what I'm used to.

Status Update: I've been working hard to try to get the GTK+ 3.x port out before New Years.

Here's what I have to report so far:

  1. Even with a Lubuntu 18.04 VM, one of the methods the docs listed wasn't there and the distinctions between methods that are and aren't there don't seem to follow any rhyme or reason.

    (eg. The method to get a monitor object from a monitor number is present, but the method to get total monitor count is missing, despite the API docs saying it should be there and both of them being carry-overs from GTK+ 2.x. I worked around it by using python-xlib to query that before handing off to GDK to query the geometry of each monitor.)

  2. Writing my own Rectangle class to work around the mess Gdk.Rectangle became in GTK+ 3.x has turned out to be a big benefit, because adding a few small features to the Rectangle class allows all the rest of the code to be much cleaner and easier to read:

    • Accepting nifty blends of point/size and point/point coordinates, like Rectangle(x=5, y=5, x2=480, height=600) and offering computed x2 and y2 members.
    • Transparently reinterpreting Rectangle(500, 500, -200, -200) as Rectangle(300, 300, 200, 200).
  3. While puzzling over how to write a replacement for cairo.Region, I realized that it was almost certainly overkill in the first place, so, tomorrow, I'll start work on a much simpler, cleaner solution that I can rethink later. (On that note, if anyone uses panels which don't span the entire width of a monitor, I welcome feedback on how you think QuickTile should handle calculating percentage-based tile grids in their presence.)

  4. Working on Rectangle and the aborted Region made me realize that I could make the parsing of _NET_WM_STRUT_PARTIAL (how panels reserve space that windows aren't allowed to overlap) much simpler. (That was one of the ugliest, most opaque bits of code in the whole thing.)

  5. I've got a complete set of unit tests for the Rectangle class (since that sort of math could easily introduce hair-pullingly subtle bugs) and I intend to do likewise for the StrutPartial class that is now used to provide a clean API around _NET_WM_STRUT_PARTIAL.

    (I also plan to do likewise for window geometry constraints once I finally support them properly, because getting the expected effect from parsing those things was where the work got snarled up on my last push to fix an ancient design flaw.)

  6. Retrofitting the codebase with MyPy type annotations has already revealed over half a dozen corner cases which are now fixed.

Tomorrow, I'm going to try to make a push to get it to the point where, even if it's not doing the right thing, it'll at least be scrambling up windows in the test desktop rather than dying with complaints about failed assertions or GTK+ methods that the API docs say should be there but aren't.

It turns out there's more work left to be done than I expected, but I'm continuing to chew my way through it.

Among other things, I'm continuing to refactor the codebase to make it more maintainable and to finally retire my original decision to rely on GTK+ and Wnck data types just because they're there, rather than creating custom types as appropriate.

I still hope to have the GTK+ 3.x version out before New Years.

OK, I hadn't expected to have no time at all to work on this on the 24th and 25th, but I'm now almost done unit testing the core rectangle-handling math and fixing the bugs thus revealed.

Once that's done, things will speed up a lot, because I can just do minimal tweaks to the rest of the code to get it into a "public alpha" state and then continue refactoring and writing unit tests in January.

At the moment, I'm writing the last set of "rectangle math" tests, which are for the UsableRegion class which wraps everything else and calculates a _NET_WORKAREA equivalent that's per-monitor and, thus, useful for non-rectangular desktops.

(And I just ran into another latent bug. For some reason, it's failing the unit test by not acknowledging the _NET_WM_STRUT_PARTIAL reservation for a 30px panel at the bottom of my centre monitor.)

EDIT: Found the problem. I accidentally wrote usable = monitor.subtract(panel) rather than usable = usable.subtract(panel) so later panel reservations would clobber earlier ones rather than combining with them.

There. There are still some bugs to be fixed in some commands before I feel comfortable pushing a public alpha, but the GTK+ 3.x version is now in a state where it starts and binds keys without crashing and a cursory examination shows that most of the commands seem to work.

Known bugs at this point:

  • move-to-center isn't doing the right thing and I haven't yet diagnosed why. (Answer: It was being given a zero-size window rectangle, which the gravity conversions then dutifully interpreted as designed.)
  • No automatic migration of the middle command to center in quicktile.cfg (I renamed it for maintainability since center can be consistent with move-to-center and both names can be auto-generated from Gravity.CENTER.) (Done. However, I may still want to add an advisory system which pops up an explanatory dialog if someone tries to call the old middle command externally.)
  • Currently, non-corner move-to-* actions don't center the selected window along the axis in question. I need to decide whether to break from the old behaviour to make this more sensible. (Answer: Go for consistency. move-to-top, for example, now centres the window just like top would.)
  • I still need to either clear out or annotate away some MyPy type errors in hard-to-reach branches of the code. (Done. Some were "You didn't handle malformed config files here" and the rest were goofs when refactoring the MyPy type annotations.)
  • Windows with their center outside any known monitor refuse to be manipulated. (I need to amend the code for finding a monitor to fall back to finding the monitor the window has the largest overlap with if its center is off the edge of the desktop.) (Fixed. If a window's center is not inside a monitor, it will pick a monitor by comparing the euclidean distances between the window's center and the center of each monitor's usable rectangle.)
  • I should really fall back to checking _NET_WM_STRUT on windows that don't set _NET_WM_STRUT_PARTIAL to make sure I incorporate all panels into my usable space calculations. (Done. Not tested yet, but, in theory, done.)
  • monitor-switch doesn't adapt for differently-shaped monitors, so positioning on the right edge of a wide monitor, cycling over to a narrow monitor, and then changing the active window could lose the window off the edge of the desktop if the desktop doesn't protect against positiong windows entirely off screen.
  • Currently, workspace-send-* actions toss the active window onto another virtual desktop without switching to it. I need to decide whether they should also switch or whether I should create workspace-bring-* actions which do that.

Writing automated functional tests is something I still want to do, but that can come after I push a public alpha, because it requires me to finish writing a test harness which spins up an Xvfb or Xephyr instance, loads a window manager, generates mock windows, and then asserts window geometry in the little X playpen before and after running QuickTile commands.

EDIT: Converted into a checklist and added more known bugs.

I think I'm now to the point where the only remaining known bugs are logic bugs which were present in some form in the GTK+ 2.x version but didn't manifest to me because my use patterns and monitor layout didn't trigger them.

However, given how problematic the monitor-switch one is, I'd still like to fix at least that one before I make an alpha release for people to try out.

The big question there is how to resolve things.

  1. If I convert the window's dimensions to percentages of the monitor's usable region before moving it and then convert them back on the new monitor, it'd have the effect of faking a unified "snap to grid" system across all monitors, but it won't do well for things like a gVim window that's intended to stay 82 text columns wide regardless of the monitor. (The proper solution is to actually have a grid system, but that's a more involved TODO)
  2. If I bump the window inward, then it becomes important to track its original position so it returns to it if you cycle through all the monitors, but determining when that saved position is stale becomes more complicated.

So far, I'm thinking I'll do solution 2 (bump the window rectangle inward, then crop to the monitor's usable region to produce the target dimensions) since, even without the proper cycle-around behaviour, it's still better than nothing.

From there, I'll ponder whether "remember the dimensions on the original monitor and the dimensions we put it at on the current monitor, then invalidate the former if we no longer match the latter" is sufficient.

I can already imagine one situation where it won't work. Ironically (considering that I just mentioned it as a motivation), gVim... because it resizes in text column/row-sized increments and I don't yet have a reimplementation of the code to process aspect ratio and size-increment restrictions so I can anticipate the dimensions the WM will force the window to.

That won't be a regression though. QuickTile never had that and it's been something I've wanted to get to for years. (Hopefully, now that I'm productive again, it won't be too far off but, once the GTK 3 version of QuickTile is out, I have fix my ITAD Importer userscript first.)

EDIT: OK. Repositioning to another monitor without specifying an explicit destination rectangle will clamp to the bounds of the monitor, so monitor-* commands can't lose windows off the edge of a screen. It doesn't yet have the infrastructure in place to undo the clamping if you cycle back to a bigger monitor, but it's enough to make me comfortable releasing it... just give me a little time to do a little housecleaning and I'll push the development branch.

Done. 🎉

The GTK 3 port is now ready for pre-release testing at https://github.com/ssokolow/quicktile/tree/gtk3_port

(i.e. I couldn't find any more bugs in it, but I only tested on my machine so please poke at it and let me know how it goes.)

However, don't follow the instructions for pip3 installing from a URL because the README assumes that it's been merged into the master branch.

If you want to install that way, instead use this command:

sudo pip3 install https://github.com/ssokolow/quicktile/archive/gtk3_port.zip

(But don't feel like I'm encouraging a single particular install method. I did all my development testing using the "run quicktile.sh without installing" method and I double-checked that install.sh still worked before pushing it.)

I'll switch my attention to some other projects that have been languishing now, to bring them back to proper function but, once that's done, I have a stack of things I still want to do here. (Most importantly, switching from ePyDoc to Sphinx to get rid of my last Python 2.x dependency.)

(I'll leave this open until the gtk3_port branch has gotten a little exercise and then been merged to master.)

I just found a new bug emerging from something I'd meant to solve later.

QuickTile doesn't currently update its view of the desktop when its shape or panel reservations change. This wasn't an issue before because it re-inspected the desktop every time you called a command but it seems that, at least in Kubuntu 16.04, ./install.sh installation results in it launching before Plasma has announced its panel reservations.

I'm currently working on my ITAD Importer as planned, but I may try to fit in a quick fix for that in the next few days. (At the moment, ITAD Importer doesn't work at all. That's why it's prioritized over fixing this bug.)

Thank you for fixing this over the holidays -- I very much look forward to have this working on Xubuntu again!

I have gotten an exception when trying to use it. Only for actions like top/bottom/left/right, the other ones seem to work, including switching monitors. Here are my bindings:
Keybindings defined for use with --daemonize:

Modifier: <Ctrl><Alt>

Key    Action               
------ -------------------- 
 0       monitor-switch       
 C       move-to-center       
 comma   workspace-go-down    
 h       left                 
 i       workspace-send-up    
 j       bottom               
 k       top                  
 l       right                
 m       maximize             
 n       workspace-go-left    
 o       workspace-send-right 
 period  workspace-go-right   
 u       workspace-send-down  
 y       workspace-send-left  

 o       workspace-send-right 
 period  workspace-go-right   
 u       workspace-send-down  
 y       workspace-send-left  

The final error is:

  File '/usr/local/lib/python3.6/dist-packages/Xlib/protocol/rq.py', line 696,
    pack_value(self=<Xlib.protocol.rq.PropertyData object>, value=(8, '[4, 0]'))
                size = fmt // 8
                a = array(array_unsigned_codes[size], val)
                data = encode_array(a)
    Variables (B=Builtin, G=Global, L=Local):
     -            a (N): None
     -        array (G): <class 'array.array'>
     - array_unsigned_codes (G): {1: 'B', 2: 'H', 4: 'I', 8: 'L'}
     -         size (L): 1
     -          val (L): '[4, 0]'
TypeError: cannot use a str to initialize an array with typecode 'B'

I've attached the full stack trace.
quicktile.error.txt

I'm running on Xubuntu (Xfce verion 4.12)

Let me know if you need any other info!

I have gotten an exception when trying to use it. Only for actions like top/bottom/left/right, the other ones seem to work, including switching monitors.

That makes sense. It looks like it's in the code that remembers where in the cycling sequence it is so it won't get confused if the window specifies sizing increments or aspect ratio restrictions and, thus, doesn't wind up at the expected size.

The final error is:

Odd. That looks like a unicode/bytestring mismatch, which I'd expect to be caused by going between Python 2.x and Python 3.x, but we should both be on Python 3.x.

The solution is probably just for me to .encode('utf8') the bit of JSON that I switched to for storing metadata in X window properties before handing it off to python-xlib.

I'm running on Xubuntu (Xfce verion 4.12)

Which Xubuntu release? I'll try to grab a VM image tomorrow and see if I can replicate the problem.

(Which reminds me. I should try adjusting gtkexcepthook so that, if it finds the lsb_release command, it includes the output of lsb_release -a in the dump.)

Hmm, ok, I might have messed something up on my machine, is any other checks that I can do?

❯ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.3 LTS
Release:	18.04
Codename:	bionic

Python version is 3.6.8.

❯ python3
Python 3.6.8 (default, Oct  7 2019, 12:59:55) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from quicktile.__main__ import main

I already had a Lubuntu 18.04 VM on hand, so I was able to reproduce it and it should be fixed now.

It seems that 18.04 has two regressions from whatever version of python3-xlib I'm using on 16.04:

  1. Mine was transparently encoding and decoding Unicode when setting properties of type Xatom.STRING while 18.04's isn't. (I switched to just storing an INTEGER[2]/32 property instead since the JSON was just for future expandability anyway.)
  2. 18.04's version of python-xlib is apparently more fragile if you don't explicitly display.flush() after setting a window property.

The latter seems like a race condition, so I can understand it not cropping up during the testing I was doing on that VM but I'm not sure why the former didn't crop up.

Oh well. More reason to write a bigger automated test suite when I come back to this.

I've verified it works for me - thanks again!

It's very hacky, but if you love Elementary OS like me and neeeeed window tiling, I scripted the entire installation and config of quicktile including old-releases install of python-wnck and other bug fixes such as the large invisible window borders in eos. Works for me. Please comment there if you find issues with my script.

https://raw.githubusercontent.com/nreith/linux_config/master/scripts/quicktile_elementary_os.sh

...and, as I just mentioned two days ago, I'm looking for testing on the pre-release version of QuickTile for GTK 3.x that I just pushed to https://github.com/ssokolow/quicktile/tree/gtk3_port

That includes feedback on the installation instructions. Pointing people at that script is counterproductive when I need people to test the port's installation process and give feedback on how to make it easier.

Just make sure you use this URL rather than the one from the README if you're installing via pip and a URL. The README's URL will only stop pointing to the GTK+ 2.x version when the pre-release gets pushed to the master branch:

sudo pip3 install https://github.com/ssokolow/quicktile/archive/gtk3_port.zip

That said, thank you for giving an example of how to fix things like the borders issue. Once I get back from fixing another thing that's in even more desperate need of fixing, I'll go through your script and add stuff like that to the QuickTile FAQ.

(I'm holding off on the site changes because I'm planning to rewrite the API docs using Sphinx and, when I do, I can make everything into one Sphinx-generated site.)

Apologies if I didn't grasp the etiquette around these issues. I've been using Linux for a long time, but new at contributing. Elementary has a new feature in Gala with a pull request pending for quite a while and that seems like it would meet my needs. I'd be happy to test out your new version too. Will try it out soon.

I guess I had been scouring the web for solutions for tiling and somehow missed this alpha announcement.

Re: Window border, found that on the Arch Wiki of all places. https://www.linuxsecrets.com/archlinux-wiki/wiki.archlinux.org/index.php/Pantheon.html
Seems the user gtk css config is overriden or something.

Thanks for all your great work!

Thanks a lot for this work! This new branch works perfectly for me on Xubuntu 19.10.

Glad to hear it... though, with the ice storm here in Ontario, Canada having brought three power outages in two days, I've become very aware of how easily a certain TODO item can cause very visible bugs, so it's likely I'll need to push and get testing on another release candidate before 0.4.0 gets tagged.

(I was planning to wait for 0.4.1 to add the code to watch for changes like adding/removing/changing monitor rectangles or panel reservations but it turns out that QuickTile starts before Plasma reserves its panels when using ./install.sh on Kubuntu 16.04 LTS.)

It's very hacky, but if you love Elementary OS like me and neeeeed window tiling,

Thanks @nreith for sharing some scripts. Looks like the debug part of it benefited the auhor.

PR for Debian package

Progress by @ssokolow on branch gtk3_port motivated me to create a debian configuration for the package and open PR #106.

This has the benefit of not requiring old packages and kludge. And the package is nicely installed by Debian and uninstallable.

Would you like to test the Debian package on branch gtk3_port?

If so, you can fetch my branch https://github.com/fidergo-stephane-gourichon/quicktile/tree/feature_debian_package and run recompile_local_debian_package.sh there.

Options: local or Docker

Notice that building the Debian package requires you to install some debian-development-related packages on your main system.

Alternatively, you can install docker and perform package creation inside it. I posted a command line on #106 (comment) . That latter option consumes a lot of bandwidth because you need to fetch a whole distribution and set of packages. The plus side is that it allows to build packages for other distributions.

Thank you for your attention.

Glad to hear it...

I spoke a little fast.

There's one regression. I happen to have a xfce panel on the bottom right, with width less than width of window. Usual version of quicktile correctly expands any window on the left side to the full height. New version limits the height uselessly. See attached screenshot, bottom left.

quicktile_regression

though, with the ice storm here in Ontario, Canada having brought three power outages in two days, I've become very aware of how easily a certain TODO item can cause very visible bugs, so it's likely I'll need to push and get testing on another release candidate before 0.4.0 gets tagged.

Here in Paris it's cold, but probably no as cold as in Canada. Because of strikes, the public transportation is severely degraded to the point where it's "safer" for commuters to ride, say, 12-18km on a bike to go to work (then again back in the evening).

"They're professionals. They do this all the time. It can't possibly be this un-optimized. I must be missing something."

"Small steps"

Ahh, yes. That's a known regression. The GTK+ 2.x version used gtk.gdk.Region to figure out what parts of the screen were usable but GTK 3.x replaced it with cairo.Region and the GIR-accessible APIs are a broken mess.

I have no experience implementing something like gtk.gdk.Region and I was stuck on figuring out an internal representation which would allow me to sanely do useful tiling operations on things like "A rectangle, but with the corner nipped out".

I had to write my own Rectangle, UsableRegion, and StrutPartial classes and, in order to get something usable out by New Years, I had to regress the layout calculations to a per-monitor version of _NET_WORKAREA, which returns a rectangle.

I wanted to test this on Fedora 31, but I was not able to find these two dependencies: gir1.2-gtk-3.0 gir1.2-wnck-3.0. I've tried running it without them, but it doesn't work without wnck (at least):

...
  File "/home/torsava/bin/quicktile/quicktile/quicktile/__main__.py", line 17, in <module>
    gi.require_version('Wnck', '3.0')
  File "/usr/lib64/python3.7/site-packages/gi/__init__.py", line 133, in require_version
    (namespace, version))
ValueError: Namespace Wnck not available for version 3.0

I tried looking for wnck for Python 3, but I don't think it's available at all:

$ sudo dnf search wnck
Last metadata expiration check: 2:07:56 ago on Thu 02 Jan 2020 11:35:12 AM CET.
======================================================================= Name & Summary Matched: wnck ========================================================================
libwnck-devel.x86_64 : Libraries and headers for libwnck
libwnck-devel.i686 : Libraries and headers for libwnck
libwnck3-devel.i686 : Libraries and headers for libwnck
libwnck3-devel.x86_64 : Libraries and headers for libwnck
gnome-python2-libwnck.x86_64 : Python bindings for interacting with libwnck
============================================================================ Name Matched: wnck =============================================================================
libwnck.x86_64 : Window Navigator Construction Kit
libwnck.i686 : Window Navigator Construction Kit
libwnck3.i686 : Window Navigator Construction Kit
libwnck3.x86_64 : Window Navigator Construction Kit
perl-Gnome2-Wnck.x86_64 : Perl interface to the Window Navigator Construction Kit

I also haven't found it using pip (PyPI).

@torsava That's because gir1.2-wnck-3.0 isn't a Python-specific dependency.

With Python 2.x and PyGTK, each library had to have a specific Python binding. With Python 3.x and the approach that only really got fully debugged in GTK+ 3, each library has a language-agnostic GObject Introspection (GIR) definition file, then PyGI just knows how to read any GIR metadata you might have installed.

It's probably bundled into libwnck3.x86_64 alongside the library itself.

I'm assuming Debian doesn't do that because it'd be wasted space if all your applications are written in languages like C and C++ where the GIR metadata is only needed at compile time.

You probably don't need to worry about gir1.2-gtk-3.0 because gi.require_version('Gtk', '3.0') comes before gi.require_version('Wnck', '3.0') in that QuickTile file and it didn't complain. If you check which package provides /usr/lib/x86_64-linux-gnu/girepository-1.0/Gtk-3.0.typelib and that'll tell you how Fedora packages the files Debian put in gir1.2-gtk-3.0 which, in turn, should tell you where to look for the equivalent file for Wnck.

Once you find it, let me know which packages need to be installed so I can re-add Fedora to the README.

@ssokolow Thanks for the work on this! It feels great to have quicktile again under 18.04. It's working a treat for me. 😄

@ssokolow I see, interesting system. The paths on Fedora are slightly different, but I found the packages:

$ rpm -qf /usr/lib64/girepository-1.0/Gtk-3.0.typelib
gtk3-3.24.13-1.fc31.x86_64
$ rpm -qf /usr/lib64/girepository-1.0/Wnck-3.0.typelib
libwnck3-3.32.0-2.fc31.x86_64

So the final list of dependencies on Fedora 31 is:

python3 python3-pip python3-setuptools python3-gobject python3-xlib python3-dbus gtk3 libwnck3

And it works like a charm! Thank you so much for your work!

@torsava

I see, interesting system.

It's basically the bindings equivalent to the Language Server Project. Instead of M*N bindings, one per language-library combo, you have M+N bindings, where each library exposes bindings to some intermediate interface definition language layered on top of a stable C ABI and then each language can write a loader or code generator that can work with anything for that IDL and automatically gain access to the whole ecosystem.

And it works like a charm! Thank you so much for your work!

Glad to hear it.

As someone whose last RPM-based distro was Mandrakelinux 10.1, I have one more question. What's the complete command that's currently recommended for installing that set of packages on Fedora? (If following Phoronix has taught me one thing, the answer to that question has changed over the years.)

I'll push an update to the README as soon as I get it.

Oh, also, what Fedora package provides Gio-2.0.typelib? I'm planning to retire python3-dbus in favour of the more modern (and more likely to be installed by default) GDBus via python3-gobject at some point and it wouldn't do to just assume that it'll be a dependency of gtk3.

@ssokolow Yeah, the binding system sounds awesome!

The full command to install the dependencies is:

sudo dnf install python3 python3-pip python3-setuptools python3-gobject python3-xlib python3-dbus gtk3 libwnck3

And Gio-2.0.typelib is provided by the gobject-introspection package. Thanks for supporting Fedora!

Yeah, the binding system sounds awesome!

I just wish Qt would retrofit it or something similar onto their QWidget API. I'm not a fan of the direction GNOME has taken GTK+ 3 and Rust has no mature QWidget bindings.

(The only thing that kept me from following the LXDE guys in upgrading QuickTile from GTK+ 2.x to Qt 5 is that the Qt ecosystem appears to have no libwnck equivalent. As-is, as soon as I have PPA experience from QuickTile work, I need to make a PPA for gtk3-mushrooms.)

And Gio-2.0.typelib is provided by the gobject-introspection package.

Hmm. Is there a site equivalent to http://packages.debian.org/ and https://packages.ubuntu.com/ where I can check what else is inside gobject-introspection? (That's actually one of my biggest unsolved gripes with RPM-based distros. I can never seem to find a good equivalent to those two sites.)

The fact that the Gio GIR file isn't part of a glib or gio package makes me worry that one of the Gtk and Gdk GIR files (or something else I depend on) may also be in there.

Hmm. Is there a site equivalent to http://packages.debian.org/ and https://packages.ubuntu.com/ where I can check what else is inside gobject-introspection? (That's actually one of my biggest unsolved gripes with RPM-based distros. I can never seem to find a good equivalent to those two sites.)

The main go-to page is Fedora Pagure: https://src.fedoraproject.org/rpms/gobject-introspection

From there you can either go to Files -> open the spec file and read how the package is built: https://src.fedoraproject.org/rpms/gobject-introspection/blob/master/f/gobject-introspection.spec

Or better yet in this case, click on Packages -> Contents and you can directly see the files contained in there: https://apps.fedoraproject.org/packages/gobject-introspection/

The main go-to page is Fedora Pagure: https://src.fedoraproject.org/rpms/gobject-introspection

Thanks. For some reason, that never seems to turn up in my searches. Instead, I get RPM-ecosystem search engines and hosts for analogues to PPAs and non-default ebuild sources.

Or better yet in this case, click on Packages -> Contents and you can directly see the files contained in there: https://apps.fedoraproject.org/packages/gobject-introspection/

Thanks. It looks like it's an interesting grab-bag of everything GTK depends on that isn't part of the GTK package itself, so I would have been depending on it if the bindings to Cairo's cario.Region weren't such a mess that I had to find an alternative solution for replacing gtk.gdk.Region.

Done. The README has been updated.

OK, I'm back to working on this and I'm part-way through converting the documentation to Sphinx to get rid of the last Python 2.x dependency.

@fidergo-stephane-gourichon Would you mind opening a separate issue for #95 (comment) to make sure that it doesn't get lost in the shuffle?

Status Update: The Sphinx rework of the documentation is coming along nicely.

I don't want to git push until I've got everything from http://ssokolow.com/quicktile/ adapted so I can update gh-pages at the same time, but:

  1. I ported the basic look and feel of the existing site onto Sphinx's Alabaster theme to maintain a sense of continuity with so much else changing.
  2. I managed to keep all the URLs listed on the landing page unchanged. (I'm still trying to decide whether I prefer simple filenames or continuity of URLs more in naming the per-module API documentation pages. At the moment, I'm using simple filenames.)
  3. I've expanded the keybinding syntax guide from the README into a full reference for the configuration file.
  4. The command guide now has illustrations for all commands except the ones relating to workspaces (where I still need to brainstorm how to depict that).

Here's a preview of the progress so far:

Screenshot_2020-01-15 QuickTile — QuickTile 0 4 documentation

I still want to:

  • Finish translating over the stuff from the landing page of the site
  • Incorporate the MyPy annotations to bring the amount of information it provides back up to parity with ePyDoc. (At the moment, it's still running on the common subset of ePyDoc and Sphinx's understanding of reStructuredText.)
  • Move as much of the rest of the README into the Sphinx manual as I feel is doable without making the README do a poor job of selling QuickTile to people who land there. (At the very least, I want full "Installation" and "Usage" sections in the manual.)

I'm also considering switching to ReadTheDocs-hosted documentation and just hosting some <meta http-equiv="refresh" content="0;url=... pages at http://ssokolow.com/quicktile/ so I don't have to do anything fancy to get automatic regeneration of the docs when I push an update.

EDIT: Never mind. I'll stick to generating my own docs and read up on how to use Travis-CI for deployment to gh-pages. That amount of initial extra setup is worth sticking to my principles about keeping my project sites ad-free. (Docs for libraries, on the other hand, I'm willing to mirror to RTD for availability and discoverability.)

@fidergo-stephane-gourichon Would you mind opening a separate issue for #95 (comment) to make sure that it doesn't get lost in the shuffle?

Done. #108

Another status update:

  • There is now 100% illustration coverage on the command reference.
  • The API documentation is now all polished up (and full of convenient cross-referencing hyperlinks for easy onboarding if you want to hack on the code) and I fixed up some other code smells I noticed while doing that.
  • There's now a "Command-Line Arguments" documentation page generated from the argparse definition, which has also been plumbed into cd docs; make man.

The main remaining things that need to be done in the manual are:

  • Integrating the rest of the stuff from the current landing page and README
  • Adding an "Installation" section to the manual
  • Adding a "Usage" section to the manual

I did accidentally introduce and almost not catch a bug in wm.WindowManager while fixing the code smells so I may decide to make at least a little progress on the functional testing before I git push the changes I've been accumulating.

...and, since I got so much done, another one quick on its heels.

  • The "Installation" and "Usage" sections are done.
  • The README has been pruned down so each section is just the most common case and a link to the manual for alternatives. I also adjusted the phrasing to be simple and concise.
  • I made various tweaks to improve the aesthetics of the rest of the docs and fixed various typos and flaws I managed to shake out.
  • I bashed together a custom Sphinx directive to get the pretty-printing behaviour I wanted out of quicktile.__main__.DEFAULTS.

All that should be left now is the existing site's landing page and the aforementioned "maybe some work on functional testing" before I git push the changes. I'll see how much of that I can fit in tomorrow.

OK. I've just got one more little bit to migrate over on the docs, and then I'll decide whether to do that little bit of functional test improvement or just git push without it.

I did a lot of stuff (including writing a basic Developer's Guide section, polishing up the mobile and print stylesheets, and fixing some accessibility flaws baked into the defaults in Sphinx's Alabaster theme), so I'll just put up a screenshot as a preview:

Screenshot_2020-01-25 QuickTile — QuickTile 0 4 documentation

...and yes, that illustration is a GIF that acts like a hand-drawn screencast. (All 65KiB of it.)

Done. The changes are now pushed to the gtk3_port branch. My next goals will be to resolve regressions #107 and #108 and issue #45 (a partial workaround for which didn't get ported to the GTK+ 3.x version) so I can merge this into master as soon as possible.

If anyone wants to look at the manual, you can generate it by installing the Sphinx-related packages in dev_requirements.txt and then running (cd docs; make html).

(If you're just curious about the animation, it's at docs/animation/animation.gif with no build process needed.)

EDIT: Oh, and hacking together a quick and dirty test window for the functional test harness did prove worthwhile. It turns out that python-xlib sometimes fails to expose the Xinerama method for querying the number of monitors. No clue why, but I switched to using GDK's wrapper for it.

EDIT: I figured out the problem I'd fixed. It was "QuickTile will crash on attempting to retrieve the number of monitors if the X server doesn't have the XINERAMA extension enabled because python-xlib doesn't provide a dummy 'query number of monitors' function that always returns 1 like GDK does." (I also enabled Xinerama in my test harness.)

OK. The 0.4.0 release candidate is now in the gtk3_port branch. Please everyone try it out and let me know if you encounter any problems.

I just ran into a problem (#109) which made me realize a feature change I should do (#110) and another bug to be fixed (#111) but, since #109 is a heisenbug I can't reproduce and I think the GTK+ 2.x version didn't do any better on #111, I'm not letting them block the 0.4.0 release.

Barring unexpected demands on my time, I hope to have 0.4.0 tagged and pushed to master in time for the "14 days ago" anniversary of 0.4.0 entering release candidate status.

Done. The gtk3_port branch has been merged to master, the new site is now live (and auto-generated from master on every push if the tests pass via Travis-CI's deployment support), and I'll be removing the gtk3_port branch momentarily.