Source code for libqtile.widget.systray

from __future__ import division

from .. import bar, xcbq, window
from . import base

import xcffib
from xcffib.xproto import ClientMessageEvent, ClientMessageData, EventMask, SetMode
import atexit


class Icon(window._Window):
    _windowMask = EventMask.StructureNotify | \
        EventMask.Exposure

    def __init__(self, win, qtile, systray):
        window._Window.__init__(self, win, qtile)
        self.systray = systray
        self.width = systray.icon_size
        self.height = systray.icon_size

    def handle_ConfigureNotify(self, event):
        icon_size = self.systray.icon_size
        self.updateHints()

        try:
            width = self.hints["min_width"]
            height = self.hints["min_height"]
        except KeyError:
            width = icon_size
            height = icon_size

        if height > icon_size:
            width = width * icon_size // height
            height = icon_size
        if height <= 0:
            width = icon_size
            height = icon_size

        self.width = width
        self.height = height
        self.window.set_attribute(backpixmap=self.systray.drawer.pixmap)
        return False

    def handle_DestroyNotify(self, event):
        wid = event.window
        del(self.qtile.windowMap[wid])
        del(self.systray.icons[wid])
        self.systray.draw()
        return False

    handle_UnmapNotify = handle_DestroyNotify


class TrayWindow(window._Window):
    _windowMask = EventMask.StructureNotify | \
        EventMask.Exposure

    def __init__(self, win, qtile, systray):
        window._Window.__init__(self, win, qtile)
        self.systray = systray

    def handle_ClientMessage(self, event):
        atoms = self.qtile.conn.atoms

        opcode = event.type
        data = event.data.data32
        message = data[1]
        wid = data[2]

        conn = self.qtile.conn.conn
        parent = self.systray.bar.window.window

        # message == 0 corresponds to SYSTEM_TRAY_REQUEST_DOCK
        # TODO: handle system tray messages http://standards.freedesktop.org/systemtray-spec/systemtray-spec-latest.html
        if opcode == atoms['_NET_SYSTEM_TRAY_OPCODE'] and message == 0:
            try:
                w = xcbq.Window(self.qtile.conn, wid)
                icon = Icon(w, self.qtile, self.systray)
                self.systray.icons[wid] = icon
                self.qtile.windowMap[wid] = icon

                # add icon window to the save-set, so it gets reparented
                # to the root window when qtile dies
                conn.core.ChangeSaveSet(SetMode.Insert, wid)

                conn.core.ReparentWindow(wid, parent.wid, 0, 0)
                conn.flush()
                w.map()
            except xcffib.xproto.DrawableError:
                # The icon wasn't ready to be drawn yet... (NetworkManager does
                # this sometimes), so we just forget about it and wait for the
                # next event.
                pass
        return False


[docs]class Systray(base._Widget): """ A widget that manage system tray """ defaults = [ ('icon_size', 20, 'Icon width'), ('padding', 5, 'Padding between icons'), ] def __init__(self, **config): base._Widget.__init__(self, bar.CALCULATED, **config) self.add_defaults(Systray.defaults) self.traywin = None self.icons = {} def button_press(self, x, y, button): pass def calculate_width(self): width = sum([i.width for i in self.icons.values()]) width += self.padding * len(self.icons) return width def _configure(self, qtile, bar): base._Widget._configure(self, qtile, bar) self.qtile = qtile self.bar = bar atoms = qtile.conn.atoms win = qtile.conn.create_window(-1, -1, 1, 1) self.traywin = TrayWindow(win, self.qtile, self) qtile.windowMap[win.wid] = self.traywin qtile.conn.conn.core.SetSelectionOwner( win.wid, atoms['_NET_SYSTEM_TRAY_S0'], xcffib.CurrentTime ) data = [ xcffib.CurrentTime, atoms['_NET_SYSTEM_TRAY_S0'], win.wid, 0, 0 ] union = ClientMessageData.synthetic(data, "I" * 5) event = ClientMessageEvent.synthetic( format=32, window=qtile.root.wid, type=atoms['MANAGER'], data=union ) qtile.root.send_event(event, mask=EventMask.StructureNotify) # cleanup before exit atexit.register(self.cleanup) def draw(self): self.drawer.clear(self.background or self.bar.background) self.drawer.draw(self.offset, self.calculate_width()) xoffset = self.padding for pos, icon in enumerate(self.icons.values()): icon.place( self.offset + xoffset, self.bar.height // 2 - self.icon_size // 2, icon.width, self.icon_size, 0, None ) xoffset += icon.width + self.padding def cleanup(self): atoms = self.qtile.conn.atoms self.qtile.conn.conn.core.SetSelectionOwner( 0, atoms['_NET_SYSTEM_TRAY_S0'], xcffib.CurrentTime, ) self.traywin.hide()