6

I've got a Python/Linux application that displays bits of info I need in a GTK window. For the purposes of this discussion, it should behave exactly like a dock - exists on all virtual desktops, and maximized windows do not overlap it.

The first point is pretty easy, but I have spent days bashing my head against my monitor trying to get the second point - preventing overlap. My app should not be covered if another window is maximized. Setting "always on top" is not enough, as the other windows just sit behind my info bar instead of stopping at its edge.

In short: with a dock/panel style window, how can you prevent other windows from maximizing over/under it?

Update: Problem solved thanks to vsemenov

Josh
  • 179
  • 1
  • 9

1 Answers1

12

Use _NET_WM_STRUT and _NET_WM_STRUT_PARTIAL (for backwards compatibility) properties to reserve space at the edge of X Window System desktop.

With PyGtk you can set these properties like so, assuming self.window is an instance of gtk.Window:

self.window.get_toplevel().show() # must call show() before property_change()
self.window.get_toplevel().window.property_change("_NET_WM_STRUT", 
    "CARDINAL", 32, gtk.gdk.PROP_MODE_REPLACE, [0, 0, 0, bottom_width]) 

Clarification on the data parameter [0, 0, 0, bottom_width] in above:

This parameter specifies the width of reserved space at each border of the desktop screen in order: [left, right, top, bottom]. So [0, 0, 0, 50] would reserve 50 pixels at the bottom of the desktop screen for your widget.

Here is a simple working example:

import gtk

class PyGtkWidgetDockExample:
    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_default_size(100, gtk.gdk.screen_height())
        self.window.move(gtk.gdk.screen_width()-100, 0)
        self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)        
        self.window.show()          
        self.window.window.property_change("_NET_WM_STRUT", "CARDINAL", 32, 
            gtk.gdk.PROP_MODE_REPLACE, [0, 100, 0, 0])               

app = PyGtkWidgetDockExample()
gtk.main()
vls
  • 2,304
  • 15
  • 20
  • That's a huge help, and clarifies some of the code I found in PyPanel when attempting to solve this. I see the docs show this as property_change(property, type, format, mode, data) can you clarify the data being sent? I get the height, can you tell me about the zeros? – Josh Oct 04 '10 at 22:21
  • Hmm thanks for reply. I'm using screen = gtk.gdk.screen_get_default() root = screen.get_root_window() root.property_change("_NET_WM_STRUT", "CARDINAL", 32, gtk.gdk.PROP_MODE_REPLACE, [0, 0, 0, 64]) with no luck. It's the root window you're referring to right? Or my application's window? I'm not having luck either way. – Josh Oct 04 '10 at 22:33
  • Same when changing properties application's window. No errors but no apparent effect – Josh Oct 04 '10 at 22:43
  • @Josh: you want to call `property_change` on the top level window of your app, which you can reference from any gtk.Window using `self.window.get_toplevel().window.property_change()`. If you go the route of `gtk.gdk.screen_get_default().get_root_window()`, it will return all gtk apps, through which you will have to loop to find your own, i.e.: `for i in gtk.gdk.screen_get_default().get_root_window().get_children()`. I updated my post with a working example, hopefully you can get this feature working in your application. – vls Oct 05 '10 at 03:38
  • Is it possible you're testing with a window manager that doesn't support these properties? – Havoc P Oct 05 '10 at 13:43
  • Fantastic! Got it! Thanks all, especially, vsemenov! – Josh Oct 05 '10 at 18:40
  • For any future readers of this question, it appears that the properties must be set AFTER the show() method is called. I was stuck on that for about an hour. – Josh Oct 05 '10 at 21:19
  • Updated answer highlighting having to call `show()` before `property_change()`. – vls Oct 06 '10 at 00:22
  • @vls: do you have any idea as to how to do the same with Qt4? [See thread](http://stackoverflow.com/questions/5829585/pyqt4-how-to-make-undercorated-window-with-reserved-space). Thanks. – neydroydrec May 08 '11 at 09:50
  • In Gtk+3 `Gtk.Window.window` (or `Gtk.Window.get_top_level().window`) does not have the `property_change()` method. I haven't been able to port it. Do you have any clue? The above answer will otherwise deprecate as Gtk+3 will replace Gtk+2. Thanks. – neydroydrec May 22 '11 at 00:04
  • Hi again: i've been so far as `Gdk.property_change(self.window.get_toplevel().window, "_NET_WM_STRUT", "CARDINAL", 32, Gdk.PropMode.REPLACE, [0,0,24,0], 4)` using PyGI with Gtk+3, but that won't work because arg 1 and 2 should be of `Gdk.Atom` type and the `Gdk.atom_intern()` method isn't available (?), so I can't make the conversion. Also I am not sure about arg -1. See doc on [Gdk Properties and Atoms](http://developer.gnome.org/gdk/stable/gdk-Properties-and-Atoms.html). – neydroydrec May 22 '11 at 08:36
  • Is this opposite of this possible? Im trying to make my fullscreen window overlap the dock but it wont consistently do it. – yatg Jul 22 '15 at 23:40