AppIndicator: scrolling on the icon cycles through all devices
This commit is contained in:
parent
d78484ff38
commit
39a75a6792
|
@ -12,16 +12,22 @@ import fcntl as _fcntl
|
||||||
import os.path as _path
|
import os.path as _path
|
||||||
import os as _os
|
import os as _os
|
||||||
|
|
||||||
|
from logging import getLogger, DEBUG as _DEBUG
|
||||||
|
_log = getLogger('solaar.appinstance')
|
||||||
|
del getLogger
|
||||||
|
|
||||||
|
|
||||||
def check():
|
def check():
|
||||||
# ensure no more than a single instance runs at a time
|
# ensure no more than a single instance runs at a time
|
||||||
lock_fd = None
|
lock_fd = None
|
||||||
for p in _os.environ.get('XDG_RUNTIME_DIR'), '/run/lock', '/var/lock', _os.environ.get('TMPDIR', '/tmp'):
|
for p in _os.environ.get('XDG_RUNTIME_DIR'), '/run/lock', '/var/lock', _os.environ.get('TMPDIR', '/tmp'):
|
||||||
|
# pick the first temporary writable folder
|
||||||
if p and _path.isdir(p) and _os.access(p, _os.W_OK):
|
if p and _path.isdir(p) and _os.access(p, _os.W_OK):
|
||||||
lock_path = _path.join(p, 'solaar.single-instance.%d' % _os.getuid())
|
lock_path = _path.join(p, 'solaar.single-instance.%d' % _os.getuid())
|
||||||
try:
|
try:
|
||||||
lock_fd = open(lock_path, 'wb')
|
lock_fd = open(lock_path, 'wb')
|
||||||
# print ("Single instance lock file is %s" % lock_path)
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
_log.debug("single-instance lock file is %s", lock_path)
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
@ -29,9 +35,12 @@ def check():
|
||||||
if lock_fd:
|
if lock_fd:
|
||||||
try:
|
try:
|
||||||
_fcntl.flock(lock_fd, _fcntl.LOCK_EX | _fcntl.LOCK_NB)
|
_fcntl.flock(lock_fd, _fcntl.LOCK_EX | _fcntl.LOCK_NB)
|
||||||
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
_log.debug("acquired single-instance lock (%s)", lock_fd)
|
||||||
return lock_fd
|
return lock_fd
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
if e.errno == 11:
|
if e.errno == 11:
|
||||||
|
_log.warn("lock file is busy, solaar already running: %s", e)
|
||||||
import sys
|
import sys
|
||||||
sys.exit("solaar: error: Solaar is already running.")
|
sys.exit("solaar: error: Solaar is already running.")
|
||||||
else:
|
else:
|
||||||
|
@ -42,6 +51,8 @@ def check():
|
||||||
|
|
||||||
|
|
||||||
def close(lock_fd):
|
def close(lock_fd):
|
||||||
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
_log.debug("releasing single-instance lock (%s)", lock_fd)
|
||||||
if lock_fd:
|
if lock_fd:
|
||||||
_fcntl.flock(lock_fd, _fcntl.LOCK_UN)
|
_fcntl.flock(lock_fd, _fcntl.LOCK_UN)
|
||||||
lock_fd.close()
|
lock_fd.close()
|
||||||
|
|
|
@ -40,6 +40,10 @@ def _parse_arguments():
|
||||||
|
|
||||||
|
|
||||||
def _run(args):
|
def _run(args):
|
||||||
|
from logging import getLogger
|
||||||
|
_log = getLogger('solaar.gtk')
|
||||||
|
del getLogger
|
||||||
|
|
||||||
import solaar.ui as ui
|
import solaar.ui as ui
|
||||||
|
|
||||||
ui.notify.init()
|
ui.notify.init()
|
||||||
|
@ -54,6 +58,8 @@ def _run(args):
|
||||||
assert action is not None
|
assert action is not None
|
||||||
assert device_info is not None
|
assert device_info is not None
|
||||||
|
|
||||||
|
_log.info("receiver event %s: %s", action, device_info)
|
||||||
|
|
||||||
# whatever the action, stop any previous receivers at this path
|
# whatever the action, stop any previous receivers at this path
|
||||||
l = listeners.pop(device_info.path, None)
|
l = listeners.pop(device_info.path, None)
|
||||||
if l is not None:
|
if l is not None:
|
||||||
|
@ -94,10 +100,10 @@ def _run(args):
|
||||||
|
|
||||||
GLib.idle_add(ui.status_icon.update, status_icon, device)
|
GLib.idle_add(ui.status_icon.update, status_icon, device)
|
||||||
if alert & ALERT.ATTENTION:
|
if alert & ALERT.ATTENTION:
|
||||||
GLib.idle_add(ui.status_icon.attention, status_icon)
|
GLib.idle_add(ui.status_icon.attention, status_icon, reason)
|
||||||
|
|
||||||
popup_window = alert & (ALERT.SHOW_WINDOW | ALERT.ATTENTION)
|
need_popup = alert & (ALERT.SHOW_WINDOW | ALERT.ATTENTION)
|
||||||
GLib.idle_add(ui.main_window.update, device, popup_window, status_icon)
|
GLib.idle_add(ui.main_window.update, device, need_popup, status_icon)
|
||||||
|
|
||||||
if alert & ALERT.NOTIFICATION:
|
if alert & ALERT.NOTIFICATION:
|
||||||
GLib.idle_add(ui.notify.show, device, reason)
|
GLib.idle_add(ui.notify.show, device, reason)
|
||||||
|
@ -105,15 +111,21 @@ def _run(args):
|
||||||
# ugly...
|
# ugly...
|
||||||
def _startup_check_receiver():
|
def _startup_check_receiver():
|
||||||
if not listeners:
|
if not listeners:
|
||||||
|
# this is called on the Main (GTK) thread, so we can make direct calls
|
||||||
ui.notify.alert('No receiver found.')
|
ui.notify.alert('No receiver found.')
|
||||||
ui.status_icon.update(status_icon)
|
ui.status_icon.update(status_icon)
|
||||||
|
ui.status_icon.attention(status_icon, 'No receiver found.')
|
||||||
|
# check for a receiver 1 second after the app was started
|
||||||
GLib.timeout_add(1000, _startup_check_receiver)
|
GLib.timeout_add(1000, _startup_check_receiver)
|
||||||
|
|
||||||
from logitech.unifying_receiver import base as _base
|
from logitech.unifying_receiver import base as _base
|
||||||
|
# receiver add/remove events will start/stop listener threads
|
||||||
GLib.timeout_add(10, _base.notify_on_receivers, handle_receivers_events)
|
GLib.timeout_add(10, _base.notify_on_receivers, handle_receivers_events)
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
Gtk.main()
|
Gtk.main()
|
||||||
# ui.status_icon.destroy(status_icon)
|
# this is unnecessary for the Gtk.StatusIcon implementation
|
||||||
|
# but the AppIdicator implementation may need it to make the indicator go away
|
||||||
|
ui.status_icon.destroy(status_icon)
|
||||||
|
|
||||||
for l in listeners.values():
|
for l in listeners.values():
|
||||||
l.stop()
|
l.stop()
|
||||||
|
|
|
@ -471,7 +471,7 @@ def _update_device_box(frame, dev):
|
||||||
_config_panel.update(frame)
|
_config_panel.update(frame)
|
||||||
|
|
||||||
|
|
||||||
def update(device, popup=False, status_icon=None):
|
def update(device, need_popup=False, status_icon=None):
|
||||||
assert device is not None
|
assert device is not None
|
||||||
# print ("main_window.update", device)
|
# print ("main_window.update", device)
|
||||||
|
|
||||||
|
@ -483,7 +483,7 @@ def update(device, popup=False, status_icon=None):
|
||||||
|
|
||||||
if w:
|
if w:
|
||||||
if receiver:
|
if receiver:
|
||||||
if popup:
|
if need_popup:
|
||||||
_show(w, status_icon)
|
_show(w, status_icon)
|
||||||
vbox = w.get_child()
|
vbox = w.get_child()
|
||||||
frames = list(vbox.get_children())
|
frames = list(vbox.get_children())
|
||||||
|
|
|
@ -8,18 +8,27 @@ from logging import getLogger, DEBUG as _DEBUG
|
||||||
_log = getLogger('solaar.ui.tray')
|
_log = getLogger('solaar.ui.tray')
|
||||||
del getLogger
|
del getLogger
|
||||||
|
|
||||||
|
from time import time as _timestamp
|
||||||
|
|
||||||
from gi.repository import Gtk, GLib
|
from gi.repository import Gtk, GLib
|
||||||
|
from gi.repository.Gdk import ScrollDirection
|
||||||
|
|
||||||
from solaar import NAME
|
from solaar import NAME
|
||||||
from . import action as _action, icons as _icons
|
from . import action as _action, icons as _icons
|
||||||
from logitech.unifying_receiver import status as _status
|
from logitech.unifying_receiver import status as _status
|
||||||
|
|
||||||
|
_TRAY_ICON_SIZE = 32 # pixels
|
||||||
_MENU_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR
|
_MENU_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# for which device to show the battery info in systray, if more than one
|
||||||
|
_picked_device = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _create_common(icon, menu_activate_callback):
|
def _create_common(icon, menu_activate_callback):
|
||||||
icon._devices_info = []
|
icon._devices_info = []
|
||||||
|
|
||||||
|
@ -28,12 +37,13 @@ def _create_common(icon, menu_activate_callback):
|
||||||
icon._menu_activate_callback = menu_activate_callback
|
icon._menu_activate_callback = menu_activate_callback
|
||||||
icon._menu = menu = Gtk.Menu()
|
icon._menu = menu = Gtk.Menu()
|
||||||
|
|
||||||
|
# per-device menu entries will be generated as-needed
|
||||||
|
|
||||||
no_receiver = Gtk.MenuItem.new_with_label('No receiver found')
|
no_receiver = Gtk.MenuItem.new_with_label('No receiver found')
|
||||||
no_receiver.set_sensitive(False)
|
no_receiver.set_sensitive(False)
|
||||||
menu.append(no_receiver)
|
menu.append(no_receiver)
|
||||||
|
|
||||||
# per-device menu entries will be generated as-needed
|
|
||||||
menu.append(Gtk.SeparatorMenuItem.new())
|
menu.append(Gtk.SeparatorMenuItem.new())
|
||||||
|
|
||||||
menu.append(_action.about.create_menu_item())
|
menu.append(_action.about.create_menu_item())
|
||||||
menu.append(_action.make('application-exit', 'Quit', Gtk.main_quit).create_menu_item())
|
menu.append(_action.make('application-exit', 'Quit', Gtk.main_quit).create_menu_item())
|
||||||
menu.show_all()
|
menu.show_all()
|
||||||
|
@ -42,55 +52,139 @@ def _create_common(icon, menu_activate_callback):
|
||||||
try:
|
try:
|
||||||
from gi.repository import AppIndicator3
|
from gi.repository import AppIndicator3
|
||||||
|
|
||||||
_log.debug("using AppIndicator3")
|
_log.info("using AppIndicator3")
|
||||||
|
|
||||||
|
|
||||||
|
_last_scroll = 0
|
||||||
|
def _scroll(ind, _, direction):
|
||||||
|
if direction != ScrollDirection.UP and direction != ScrollDirection.DOWN:
|
||||||
|
# ignore all other directions
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(ind._devices_info) < 4:
|
||||||
|
# don't bother with scrolling when there's only one receiver
|
||||||
|
# with only one device (3 = [receiver, device, separator])
|
||||||
|
return
|
||||||
|
|
||||||
|
# scroll events come way too fast (at least 5-6 at once)
|
||||||
|
# so take a little break between them
|
||||||
|
global _last_scroll
|
||||||
|
now = _timestamp()
|
||||||
|
if now - _last_scroll < 0.33: # seconds
|
||||||
|
return
|
||||||
|
_last_scroll = now
|
||||||
|
|
||||||
|
# if _log.isEnabledFor(_DEBUG):
|
||||||
|
# _log.debug("scroll direction %s", direction)
|
||||||
|
|
||||||
|
global _picked_device
|
||||||
|
candidate = None
|
||||||
|
|
||||||
|
if _picked_device is None:
|
||||||
|
for info in ind._devices_info:
|
||||||
|
# pick first peripheral found
|
||||||
|
if info[1] is not None:
|
||||||
|
candidate = info
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
found = False
|
||||||
|
for info in ind._devices_info:
|
||||||
|
if not info[1]:
|
||||||
|
# only conside peripherals
|
||||||
|
continue
|
||||||
|
# compare peripheral serials
|
||||||
|
if info[1] == _picked_device[1]:
|
||||||
|
if direction == ScrollDirection.UP and candidate:
|
||||||
|
# select previous device
|
||||||
|
break
|
||||||
|
found = True
|
||||||
|
else:
|
||||||
|
if found:
|
||||||
|
candidate = info
|
||||||
|
if direction == ScrollDirection.DOWN:
|
||||||
|
break
|
||||||
|
# if direction is up, but no candidate found before _picked,
|
||||||
|
# let it run through all candidates, will get stuck with the last one
|
||||||
|
else:
|
||||||
|
if direction == ScrollDirection.DOWN:
|
||||||
|
# only use the first one, in case no candidates are after _picked
|
||||||
|
if candidate is None:
|
||||||
|
candidate = info
|
||||||
|
else:
|
||||||
|
candidate = info
|
||||||
|
|
||||||
|
# if the last _picked_device is gone, clear it
|
||||||
|
# the candidate will be either the first or last one remaining,
|
||||||
|
# depending on the scroll direction
|
||||||
|
if not found:
|
||||||
|
_picked_device = None
|
||||||
|
|
||||||
|
_picked_device = candidate or _picked_device
|
||||||
|
if _log.isEnabledFor(_DEBUG):
|
||||||
|
_log.debug("scroll: picked %s", _picked_device)
|
||||||
|
_update_tray_icon(ind)
|
||||||
|
|
||||||
# def _scroll(ind, delta, direction):
|
|
||||||
# if _log.isEnabledFor(_DEBUG):
|
|
||||||
# _log.debug("scroll delta %s direction %s", delta, direction)
|
|
||||||
|
|
||||||
def create(activate_callback, menu_activate_callback):
|
def create(activate_callback, menu_activate_callback):
|
||||||
assert activate_callback
|
assert activate_callback
|
||||||
assert menu_activate_callback
|
assert menu_activate_callback
|
||||||
|
|
||||||
ind = AppIndicator3.Indicator.new(
|
theme_paths = Gtk.IconTheme.get_default().get_search_path()
|
||||||
|
|
||||||
|
ind = AppIndicator3.Indicator.new_with_path(
|
||||||
'indicator-solaar',
|
'indicator-solaar',
|
||||||
_icons.TRAY_INIT,
|
_icons.TRAY_INIT,
|
||||||
AppIndicator3.IndicatorCategory.HARDWARE)
|
AppIndicator3.IndicatorCategory.HARDWARE,
|
||||||
|
':'.join(theme_paths))
|
||||||
ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
|
ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
|
||||||
ind.set_label(NAME, NAME)
|
ind.set_attention_icon_full(_icons.TRAY_ATTENTION, '')
|
||||||
|
# ind.set_label(NAME, NAME)
|
||||||
# theme_paths = Gtk.IconTheme.get_default().get_search_path()
|
|
||||||
# ind.set_icon_theme_path(':'.join(theme_paths))
|
|
||||||
|
|
||||||
# ind.set_icon(_icons.TRAY_INIT)
|
|
||||||
ind.set_attention_icon(_icons.TRAY_ATTENTION)
|
|
||||||
|
|
||||||
_create_common(ind, menu_activate_callback)
|
_create_common(ind, menu_activate_callback)
|
||||||
ind.set_menu(ind._menu)
|
ind.set_menu(ind._menu)
|
||||||
|
|
||||||
# ind.connect('scroll-event', _scroll)
|
ind.connect('scroll-event', _scroll)
|
||||||
|
|
||||||
return ind
|
return ind
|
||||||
|
|
||||||
|
|
||||||
# def destroy(ind):
|
def destroy(ind):
|
||||||
# ind.set_status(AppIndicator3.IndicatorStatus.PASSIVE)
|
ind.set_status(AppIndicator3.IndicatorStatus.PASSIVE)
|
||||||
|
|
||||||
|
|
||||||
def _update_icon(ind, icon_name, tooltip):
|
def _update_tray_icon(ind):
|
||||||
icon_file = _icons.icon_file(icon_name, 32)
|
if _picked_device:
|
||||||
ind.set_icon(icon_file)
|
_, _, name, _, device_status = _picked_device
|
||||||
# ind.set_icon_full(icon_name, tooltip)
|
battery_level = device_status.get(_status.BATTERY_LEVEL)
|
||||||
|
battery_charging = device_status.get(_status.BATTERY_CHARGING)
|
||||||
|
tray_icon_name = _icons.battery(battery_level, battery_charging)
|
||||||
|
|
||||||
|
description = '%s: %s' % (name, device_status)
|
||||||
|
else:
|
||||||
|
# there may be a receiver, but no peripherals
|
||||||
|
tray_icon_name = _icons.TRAY_OKAY if ind._devices_info else _icons.TRAY_INIT
|
||||||
|
description = '%s: no receivers found' % NAME
|
||||||
|
|
||||||
|
# icon_file = _icons.icon_file(icon_name, _TRAY_ICON_SIZE)
|
||||||
|
ind.set_icon_full(tray_icon_name, description)
|
||||||
|
|
||||||
|
|
||||||
def attention(ind):
|
def _update_menu_icon(image_widget, icon_name):
|
||||||
|
image_widget.set_from_icon_name(icon_name, _MENU_ICON_SIZE)
|
||||||
|
# icon_file = _icons.icon_file(icon_name, _MENU_ICON_SIZE)
|
||||||
|
# image_widget.set_from_file(icon_file)
|
||||||
|
# image_widget.set_pixel_size(_TRAY_ICON_SIZE)
|
||||||
|
|
||||||
|
|
||||||
|
def attention(ind, reason=None):
|
||||||
if ind.get_status != AppIndicator3.IndicatorStatus.ATTENTION:
|
if ind.get_status != AppIndicator3.IndicatorStatus.ATTENTION:
|
||||||
|
ind.set_attention_icon_full(_icons.TRAY_ATTENTION, reason or '')
|
||||||
ind.set_status(AppIndicator3.IndicatorStatus.ATTENTION)
|
ind.set_status(AppIndicator3.IndicatorStatus.ATTENTION)
|
||||||
GLib.timeout_add(10 * 1000, ind.set_status, AppIndicator3.IndicatorStatus.ACTIVE)
|
GLib.timeout_add(10 * 1000, ind.set_status, AppIndicator3.IndicatorStatus.ACTIVE)
|
||||||
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
||||||
_log.debug("using StatusIcon")
|
_log.info("using StatusIcon")
|
||||||
|
|
||||||
def create(activate_callback, menu_activate_callback):
|
def create(activate_callback, menu_activate_callback):
|
||||||
assert activate_callback
|
assert activate_callback
|
||||||
|
@ -110,31 +204,46 @@ except ImportError:
|
||||||
return icon
|
return icon
|
||||||
|
|
||||||
|
|
||||||
# def destroy(icon):
|
def destroy(icon):
|
||||||
# icon.set_visible(False)
|
icon.set_visible(False)
|
||||||
|
|
||||||
|
|
||||||
def _update_icon(icon, icon_name, tooltip):
|
def _update_tray_icon(icon):
|
||||||
icon.set_from_icon_name(icon_name)
|
tooltip_lines = _generate_tooltip_lines(icon._devices_info)
|
||||||
|
tooltip = '\n'.join(tooltip_lines).rstrip('\n')
|
||||||
icon.set_tooltip_markup(tooltip)
|
icon.set_tooltip_markup(tooltip)
|
||||||
|
|
||||||
|
if _picked_device:
|
||||||
|
_, _, name, _, device_status = _picked_device
|
||||||
|
battery_level = device_status.get(_status.BATTERY_LEVEL)
|
||||||
|
battery_charging = device_status.get(_status.BATTERY_CHARGING)
|
||||||
|
tray_icon_name = _icons.battery(battery_level, battery_charging)
|
||||||
|
else:
|
||||||
|
# there may be a receiver, but no peripherals
|
||||||
|
tray_icon_name = _icons.TRAY_OKAY if icon._devices_info else _icons.TRAY_ATTENTION
|
||||||
|
icon.set_from_icon_name(tray_icon_name)
|
||||||
|
|
||||||
_icon_after_attention = None
|
|
||||||
|
def _update_menu_icon(image_widget, icon_name):
|
||||||
|
image_widget.set_from_icon_name(icon_name, _MENU_ICON_SIZE)
|
||||||
|
|
||||||
|
|
||||||
|
_icon_before_attention = None
|
||||||
|
|
||||||
def _blink(icon, count):
|
def _blink(icon, count):
|
||||||
global _icon_after_attention
|
global _icon_before_attention
|
||||||
if count % 2:
|
if count % 2:
|
||||||
icon.set_from_icon_name(_icons.TRAY_ATTENTION)
|
icon.set_from_icon_name(_icons.TRAY_ATTENTION)
|
||||||
else:
|
else:
|
||||||
icon.set_from_icon_name(_icon_after_attention)
|
icon.set_from_icon_name(_icon_before_attention)
|
||||||
|
|
||||||
if count > 0:
|
if count > 0:
|
||||||
GLib.timeout_add(1000, _blink, icon, count - 1)
|
GLib.timeout_add(1000, _blink, icon, count - 1)
|
||||||
|
|
||||||
def attention(icon):
|
def attention(icon, reason=None):
|
||||||
global _icon_after_attention
|
global _icon_before_attention
|
||||||
if _icon_after_attention is None:
|
if _icon_before_attention is None:
|
||||||
_icon_after_attention = icon.get_icon_name()
|
_icon_before_attention = icon.get_icon_name()
|
||||||
GLib.idle_add(_blink, icon, 9)
|
GLib.idle_add(_blink, icon, 9)
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -142,6 +251,10 @@ except ImportError:
|
||||||
#
|
#
|
||||||
|
|
||||||
def _generate_tooltip_lines(devices_info):
|
def _generate_tooltip_lines(devices_info):
|
||||||
|
if not devices_info:
|
||||||
|
yield '<b>%s</b>: no receivers' % NAME
|
||||||
|
return
|
||||||
|
|
||||||
yield '<b>%s</b>' % NAME
|
yield '<b>%s</b>' % NAME
|
||||||
yield ''
|
yield ''
|
||||||
|
|
||||||
|
@ -165,30 +278,26 @@ def _generate_tooltip_lines(devices_info):
|
||||||
yield ''
|
yield ''
|
||||||
|
|
||||||
|
|
||||||
def _generate_icon_name(icon):
|
def _pick_device_with_lowest_battery(devices_info):
|
||||||
if not icon._devices_info:
|
if not devices_info:
|
||||||
return _icons.TRAY_INIT
|
return None
|
||||||
|
|
||||||
battery_status = None
|
picked = None
|
||||||
battery_level = 1000
|
picked_level = 1000
|
||||||
|
|
||||||
for _, serial, name, _, status in icon._devices_info:
|
for info in devices_info:
|
||||||
if serial is None: # is receiver
|
if info[1] is None: # is receiver/separator
|
||||||
continue
|
continue
|
||||||
level = status.get(_status.BATTERY_LEVEL)
|
level = info[-1].get(_status.BATTERY_LEVEL)
|
||||||
if level is not None and level < battery_level:
|
if not picked or (level is not None and picked_level > level):
|
||||||
battery_status = status
|
picked = info
|
||||||
battery_level = level
|
picked_level = level or 0
|
||||||
|
|
||||||
if battery_status is None:
|
if _log.isEnabledFor(_DEBUG):
|
||||||
return _icons.TRAY_OKAY
|
_log.debug("picked device with lowest battery: %s", picked)
|
||||||
|
|
||||||
|
return picked
|
||||||
|
|
||||||
assert battery_level < 1000
|
|
||||||
charging = battery_status.get(_status.BATTERY_CHARGING)
|
|
||||||
icon_name = _icons.battery(battery_level, charging)
|
|
||||||
if icon_name and 'missing' in icon_name:
|
|
||||||
icon_name = None
|
|
||||||
return icon_name or _icons.TRAY_OKAY
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
@ -219,22 +328,31 @@ def _add_device(icon, device):
|
||||||
|
|
||||||
# print ("status_icon: added", index, ":", device_info)
|
# print ("status_icon: added", index, ":", device_info)
|
||||||
|
|
||||||
menu_item = Gtk.ImageMenuItem.new_with_label(' ' + device.name)
|
# label_prefix = b'\xE2\x94\x84 '.decode('utf-8')
|
||||||
icon._menu.insert(menu_item, index)
|
label_prefix = ' '
|
||||||
|
|
||||||
|
menu_item = Gtk.ImageMenuItem.new_with_label(label_prefix + device.name)
|
||||||
menu_item.set_image(Gtk.Image())
|
menu_item.set_image(Gtk.Image())
|
||||||
menu_item.show_all()
|
menu_item.show_all()
|
||||||
menu_item.connect('activate', icon._menu_activate_callback, device.receiver.path, icon)
|
menu_item.connect('activate', icon._menu_activate_callback, device.receiver.path, icon)
|
||||||
|
|
||||||
|
icon._menu.insert(menu_item, index)
|
||||||
|
|
||||||
return index
|
return index
|
||||||
|
|
||||||
|
|
||||||
def _remove_device(icon, index):
|
def _remove_device(icon, index):
|
||||||
# print ("remove device", index)
|
|
||||||
assert index is not None
|
assert index is not None
|
||||||
del icon._devices_info[index]
|
|
||||||
menu_items = icon._menu.get_children()
|
menu_items = icon._menu.get_children()
|
||||||
icon._menu.remove(menu_items[index])
|
icon._menu.remove(menu_items[index])
|
||||||
|
|
||||||
|
removed_device = icon._devices_info.pop(index)
|
||||||
|
global _picked_device
|
||||||
|
if _picked_device and _picked_device[1] == removed_device[1]:
|
||||||
|
# the current pick was unpaired
|
||||||
|
_picked_device = None
|
||||||
|
|
||||||
|
|
||||||
def _add_receiver(icon, receiver):
|
def _add_receiver(icon, receiver):
|
||||||
device_info = (receiver.serial, None, receiver.name, None, None)
|
device_info = (receiver.serial, None, receiver.name, None, None)
|
||||||
|
@ -258,13 +376,15 @@ def _add_receiver(icon, receiver):
|
||||||
def _remove_receiver(icon, receiver):
|
def _remove_receiver(icon, receiver):
|
||||||
index = 0
|
index = 0
|
||||||
found = False
|
found = False
|
||||||
|
|
||||||
|
# remove all entries in devices_info that match this receiver
|
||||||
while index < len(icon._devices_info):
|
while index < len(icon._devices_info):
|
||||||
rserial, _, _, _, _ = icon._devices_info[index]
|
rserial, _, _, _, _ = icon._devices_info[index]
|
||||||
# print ("remove receiver", index, rserial)
|
|
||||||
if rserial == receiver.serial:
|
if rserial == receiver.serial:
|
||||||
found = True
|
found = True
|
||||||
_remove_device(icon, index)
|
_remove_device(icon, index)
|
||||||
elif found and rserial == '-':
|
elif found and rserial == '-':
|
||||||
|
# the separator after this receiver
|
||||||
_remove_device(icon, index)
|
_remove_device(icon, index)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -275,19 +395,19 @@ def _update_menu_item(icon, index, device_status):
|
||||||
menu_items = icon._menu.get_children()
|
menu_items = icon._menu.get_children()
|
||||||
menu_item = menu_items[index]
|
menu_item = menu_items[index]
|
||||||
|
|
||||||
image = menu_item.get_image()
|
|
||||||
level = device_status.get(_status.BATTERY_LEVEL)
|
level = device_status.get(_status.BATTERY_LEVEL)
|
||||||
charging = device_status.get(_status.BATTERY_CHARGING)
|
charging = device_status.get(_status.BATTERY_CHARGING)
|
||||||
image.set_from_icon_name(_icons.battery(level, charging), _MENU_ICON_SIZE)
|
icon_name = _icons.battery(level, charging)
|
||||||
image.set_sensitive(bool(device_status))
|
|
||||||
|
image_widget = menu_item.get_image()
|
||||||
|
image_widget.set_sensitive(bool(device_status))
|
||||||
|
_update_menu_icon(image_widget, icon_name)
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
def update(icon, device=None):
|
def update(icon, device=None):
|
||||||
# print ("icon update", device, icon._devices_info)
|
|
||||||
|
|
||||||
if device is not None:
|
if device is not None:
|
||||||
if device.kind is None:
|
if device.kind is None:
|
||||||
# receiver
|
# receiver
|
||||||
|
@ -325,8 +445,8 @@ def update(icon, device=None):
|
||||||
menu_items[no_receivers_index].set_visible(not icon._devices_info)
|
menu_items[no_receivers_index].set_visible(not icon._devices_info)
|
||||||
menu_items[no_receivers_index + 1].set_visible(not icon._devices_info)
|
menu_items[no_receivers_index + 1].set_visible(not icon._devices_info)
|
||||||
|
|
||||||
tooltip_lines = _generate_tooltip_lines(icon._devices_info)
|
global _picked_device
|
||||||
tooltip = '\n'.join(tooltip_lines).rstrip('\n')
|
if not _picked_device:
|
||||||
_update_icon(icon, _generate_icon_name(icon), tooltip)
|
_picked_device = _pick_device_with_lowest_battery(icon._devices_info)
|
||||||
|
|
||||||
# print ("icon updated", device, icon._devices_info)
|
_update_tray_icon(icon)
|
||||||
|
|
Loading…
Reference in New Issue