127 lines
3.4 KiB
Python
127 lines
3.4 KiB
Python
#
|
|
#
|
|
#
|
|
|
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
|
|
from gi.repository import Gtk, GObject, GdkPixbuf
|
|
|
|
from . import action as _action, icons as _icons
|
|
from logitech.unifying_receiver import status as _status
|
|
|
|
#
|
|
#
|
|
#
|
|
|
|
_NO_DEVICES = [None] * 6
|
|
|
|
def create(window, menu_actions=None):
|
|
name = window.get_title()
|
|
icon = Gtk.StatusIcon()
|
|
icon.set_title(name)
|
|
icon.set_name(name)
|
|
icon.set_from_icon_name(_icons.APP_ICON[0])
|
|
icon._devices = list(_NO_DEVICES)
|
|
|
|
icon.set_tooltip_text(name)
|
|
icon.connect('activate', window.toggle_visible)
|
|
|
|
menu = Gtk.Menu()
|
|
for a in menu_actions or ():
|
|
if a:
|
|
menu.append(a.create_menu_item())
|
|
|
|
menu.append(_action.quit.create_menu_item())
|
|
menu.show_all()
|
|
|
|
icon.connect('popup_menu',
|
|
lambda icon, button, time, menu:
|
|
menu.popup(None, None, icon.position_menu, icon, button, time),
|
|
menu)
|
|
|
|
# use size-changed to detect if the systray is available or not
|
|
def _size_changed(i, size, w):
|
|
def _check_systray(i2, w2):
|
|
w2.set_has_systray(i2.is_embedded() and i2.get_visible())
|
|
# first guess
|
|
GObject.timeout_add(250, _check_systray, i, w)
|
|
# just to make sure...
|
|
GObject.timeout_add(1000, _check_systray, i, w)
|
|
icon.connect('size-changed', _size_changed, window)
|
|
|
|
return icon
|
|
|
|
|
|
_PIXMAPS = {}
|
|
def _icon_with_battery(level, active):
|
|
battery_icon = _icons.battery(level)
|
|
name = '%s-%s' % (battery_icon, active)
|
|
if name not in _PIXMAPS:
|
|
mask = _icons.icon_file(_icons.APP_ICON[2], 128)
|
|
assert mask
|
|
mask = GdkPixbuf.Pixbuf.new_from_file(mask)
|
|
assert mask.get_width() == 128 and mask.get_height() == 128
|
|
mask.saturate_and_pixelate(mask, 0.7, False)
|
|
|
|
battery = _icons.icon_file(battery_icon, 128)
|
|
assert battery
|
|
battery = GdkPixbuf.Pixbuf.new_from_file(battery)
|
|
assert battery.get_width() == 128 and battery.get_height() == 128
|
|
if not active:
|
|
battery.saturate_and_pixelate(battery, 0, True)
|
|
|
|
# TODO can the masking be done at runtime?
|
|
battery.composite(mask, 0, 7, 80, 121, -32, 7, 1, 1, GdkPixbuf.InterpType.NEAREST, 255)
|
|
_PIXMAPS[name] = mask
|
|
|
|
return _PIXMAPS[name]
|
|
|
|
def update(icon, receiver, device=None):
|
|
# print ("icon update", receiver, receiver.status, len(receiver), device)
|
|
if device is not None:
|
|
icon._devices[device.number] = None if device.status is None else device
|
|
if not receiver:
|
|
icon._devices[:] = _NO_DEVICES
|
|
if not icon.is_embedded():
|
|
return
|
|
|
|
def _lines(r, devices):
|
|
yield '<b>Solaar</b>: %s' % r.status
|
|
yield ''
|
|
|
|
for dev in devices:
|
|
if dev is None:
|
|
continue
|
|
|
|
yield '<b>%s</b>' % dev.name
|
|
|
|
assert hasattr(dev, 'status') and dev.status is not None
|
|
p = str(dev.status)
|
|
if p: # does it have any properties to print?
|
|
if dev.status:
|
|
yield '\t%s' % p
|
|
else:
|
|
yield '\t%s <small>(inactive)</small>' % p
|
|
else:
|
|
if dev.status:
|
|
yield '\t<small>no status</small>'
|
|
else:
|
|
yield '\t<small>(inactive)</small>'
|
|
yield ''
|
|
|
|
icon.set_tooltip_markup('\n'.join(_lines(receiver, icon._devices)).rstrip('\n'))
|
|
|
|
battery_status = None
|
|
battery_level = 1000
|
|
for dev in icon._devices:
|
|
if dev is not None:
|
|
level = dev.status.get(_status.BATTERY_LEVEL)
|
|
if level is not None and level < battery_level:
|
|
battery_status = dev.status
|
|
battery_level = level
|
|
|
|
if battery_status is None:
|
|
icon.set_from_icon_name(_icons.APP_ICON[1 if receiver else -1])
|
|
else:
|
|
icon.set_from_pixbuf(_icon_with_battery(battery_level, bool(battery_status)))
|