130 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			130 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
| #
 | |
| #
 | |
| #
 | |
| 
 | |
| from __future__ import absolute_import, division, print_function, unicode_literals
 | |
| 
 | |
| from gi.repository import Gtk, GdkPixbuf
 | |
| 
 | |
| from solaar import NAME
 | |
| from . import action as _action, icons as _icons
 | |
| from logitech.unifying_receiver import status as _status
 | |
| 
 | |
| #
 | |
| #
 | |
| #
 | |
| 
 | |
| _NO_DEVICES = [None] * 6
 | |
| 
 | |
| 
 | |
| def create(activate_callback):
 | |
| 	assert activate_callback
 | |
| 
 | |
| 	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', activate_callback)
 | |
| 
 | |
| 	menu = Gtk.Menu()
 | |
| 
 | |
| 	menu.append(Gtk.SeparatorMenuItem.new())
 | |
| 
 | |
| 	menu.append(_action.about.create_menu_item())
 | |
| 	menu.append(_action.make('application-exit', 'Quit', Gtk.main_quit).create_menu_item())
 | |
| 	menu.show_all()
 | |
| 
 | |
| 	for x in _NO_DEVICES:
 | |
| 		m = Gtk.ImageMenuItem()
 | |
| 		m.set_sensitive(False)
 | |
| 		menu.insert(m, 0)
 | |
| 
 | |
| 	icon.connect('popup_menu',
 | |
| 					lambda icon, button, time, menu:
 | |
| 						menu.popup(None, None, icon.position_menu, icon, button, time),
 | |
| 					menu)
 | |
| 	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, device):
 | |
| 	assert device is not None
 | |
| 	# print ("icon update", device)
 | |
| 
 | |
| 	if device.kind is None:
 | |
| 		receiver = device
 | |
| 		if not device:
 | |
| 			icon._devices[:] = _NO_DEVICES
 | |
| 	else:
 | |
| 		icon._devices[device.number] = None if device.status is None else device
 | |
| 		receiver = device.receiver
 | |
| 
 | |
| 	if not icon.is_embedded():
 | |
| 		return
 | |
| 
 | |
| 	def _lines(r, devices):
 | |
| 		yield '<b>%s</b>: %s' % (NAME, 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)))
 |