Beispiel #1
0
def _generate_tooltip_lines():
	if not _devices_info:
		yield '<b>%s</b>: ' % NAME + _("no receiver")
		return

	yield '<b>%s</b>' % NAME
	yield ''

	for _ignore, number, name, status in _devices_info:
		if number is None:  # receiver
			continue

		p = status.to_string()
		if p:  # does it have any properties to print?
			yield '<b>%s</b>' % name
			if status:
				yield '\t%s' % p
			else:
				yield '\t%s <small>(' % p + _("offline") + ')</small>'
		else:
			if status:
				yield '<b>%s</b> <small>(' % name + _("no status") + ')</small>'
			else:
				yield '<b>%s</b> <small>(' % name + _("offline") + ')</small>'
		yield ''
Beispiel #2
0
def _create_buttons_box():
	bb = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL)
	bb.set_layout(Gtk.ButtonBoxStyle.END)

	bb._details = _new_button(None, 'dialog-information', _SMALL_BUTTON_ICON_SIZE,
					tooltip=_("Show Technical Details"), toggle=True, clicked=_update_details)
	bb.add(bb._details)
	bb.set_child_secondary(bb._details, True)
	bb.set_child_non_homogeneous(bb._details, True)

	def _pair_new_device(trigger):
		assert _find_selected_device_id() is not None
		receiver = _find_selected_device()
		assert receiver is not None
		assert bool(receiver)
		assert receiver.kind is None
		_action.pair(_window, receiver)

	bb._pair = _new_button(_("Pair new device"), 'list-add', clicked=_pair_new_device)
	bb.add(bb._pair)

	def _unpair_current_device(trigger):
		assert _find_selected_device_id() is not None
		device = _find_selected_device()
		assert device is not None
		assert bool(device)
		assert device.kind is not None
		_action.unpair(_window, device)

	bb._unpair = _new_button(_("Unpair"), 'edit-delete', clicked=_unpair_current_device)
	bb.add(bb._unpair)

	return bb
Beispiel #3
0
def _create_device_panel():
	p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4)

	def _status_line(label_text):
		b = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8)
		b.set_size_request(10, 28)

		b._label = Gtk.Label(label_text)
		b._label.set_alignment(0, 0.5)
		b._label.set_size_request(170, 10)
		b.pack_start(b._label, False, False, 0)

		b._icon = Gtk.Image()
		b.pack_start(b._icon, False, False, 0)

		b._text = Gtk.Label()
		b._text.set_alignment(0, 0.5)
		b.pack_start(b._text, True, True, 0)

		return b

	p._battery = _status_line(_("Battery"))
	p.pack_start(p._battery, False, False, 0)

	p._secure = _status_line(_("Wireless Link"))
	p._secure._icon.set_from_icon_name('dialog-warning', _INFO_ICON_SIZE)
	p.pack_start(p._secure, False, False, 0)

	p._lux = _status_line(_("Lighting"))
	p.pack_start(p._lux, False, False, 0)

	p._config = _config_panel.create()
	p.pack_end(p._config, False, False, 4)

	return p
Beispiel #4
0
def create(receiver):
    assert receiver is not None
    assert receiver.kind is None

    assistant = Gtk.Assistant()
    assistant.set_title(_("%(receiver_name)s: pair new device") % {"receiver_name": receiver.name})
    assistant.set_icon_name("list-add")

    assistant.set_size_request(400, 240)
    assistant.set_resizable(False)
    assistant.set_role("pair-device")

    page_intro = _create_page(
        assistant,
        Gtk.AssistantPageType.PROGRESS,
        _("Turn on the device you want to pair."),
        "preferences-desktop-peripherals",
        _("If the device is already turned on,\nturn if off and on again."),
    )
    spinner = Gtk.Spinner()
    spinner.set_visible(True)
    page_intro.pack_end(spinner, True, True, 24)

    assistant.connect("prepare", _prepare, receiver)
    assistant.connect("cancel", _finish, receiver)
    assistant.connect("close", _finish, receiver)

    return assistant
Beispiel #5
0
def _create_sbox(s):
	sbox = Gtk.HBox(homogeneous=False, spacing=6)
	sbox.pack_start(Gtk.Label(s.label), False, False, 0)

	spinner = Gtk.Spinner()
	spinner.set_tooltip_text(_("Working") + '...')

	failed = Gtk.Image.new_from_icon_name('dialog-warning', Gtk.IconSize.SMALL_TOOLBAR)
	failed.set_tooltip_text(_("Read/write operation failed."))

	if s.kind == _SETTING_KIND.toggle:
		control = _create_toggle_control(s)
		sbox.pack_end(control, False, False, 0)
	elif s.kind == _SETTING_KIND.choice:
		control = _create_choice_control(s)
		sbox.pack_end(control, False, False, 0)
	elif s.kind == _SETTING_KIND.range:
		control = _create_slider_control(s)
		sbox.pack_end(control, True, True, 0)
	else:
		raise NotImplemented

	control.set_sensitive(False)  # the first read will enable it
	sbox.pack_end(spinner, False, False, 0)
	sbox.pack_end(failed, False, False, 0)

	if s.description:
		sbox.set_tooltip_text(s.description)

	sbox.show_all()
	spinner.start()  # the first read will stop it
	failed.set_visible(False)

	return sbox
Beispiel #6
0
	def show(dev, reason=None, icon=None):
		"""Show a notification with title and text."""
		if available and Notify.is_initted():
			summary = dev.name

			# if a notification with same name is already visible, reuse it to avoid spamming
			n = _notifications.get(summary)
			if n is None:
				n = _notifications[summary] = Notify.Notification()

			if reason:
				message = reason
			elif dev.status is None:
				message = _("unpaired")
			elif bool(dev.status):
				message = dev.status.to_string() or _("connected")
			else:
				message = _("offline")

			# we need to use the filename here because the notifications daemon
			# is an external application that does not know about our icon sets
			icon_file = _icons.device_icon_file(dev.name, dev.kind) if icon is None \
						else _icons.icon_file(icon)

			n.update(summary, message, icon_file)
			urgency = Notify.Urgency.LOW if dev.status else Notify.Urgency.NORMAL
			n.set_urgency(urgency)

			try:
				# if _log.isEnabledFor(_DEBUG):
				# 	_log.debug("showing %s", n)
				n.show()
			except Exception:
				_log.exception("showing %s", n)
Beispiel #7
0
def _create():
	about = Gtk.AboutDialog()

	about.set_program_name(NAME)
	about.set_version(__version__)
	about.set_comments(_("Shows status of devices connected\nthrough wireless Logitech receivers."))

	about.set_logo_icon_name(NAME.lower())

	about.set_copyright('© 2012-2013 Daniel Pavel')
	about.set_license_type(Gtk.License.GPL_2_0)

	about.set_authors(('Daniel Pavel http://github.com/pwr',))
	try:
		about.add_credit_section(_("GUI design"), ('Julien Gascard', 'Daniel Pavel'))
		about.add_credit_section(_("Testing"), (
						'Douglas Wagner',
						'Julien Gascard',
						'Peter Wu http://www.lekensteyn.nl/logitech-unifying.html',
						))
		about.add_credit_section(_("Logitech documentation"), (
						'Julien Danjou http://julien.danjou.info/blog/2012/logitech-unifying-upower',
						'Nestor Lopez Casado http://drive.google.com/folderview?id=0BxbRzx7vEV7eWmgwazJ3NUFfQ28',
						))
	except TypeError:
		# gtk3 < ~3.6.4 has incorrect gi bindings
		import logging
		logging.exception("failed to fully create the about dialog")
	except:
		# the Gtk3 version may be too old, and the function does not exist
		import logging
		logging.exception("failed to fully create the about dialog")

	about.set_translator_credits('\n'.join((
					'gogo (croatian)',
					'Papoteur, David Geiger, Damien Lallement (français)',
					'Michele Olivo (italiano)',
					'Adrian Piotrowicz (polski)',
					'Drovetto, JrBenito (Portuguese-BR)',
					'Daniel Pavel (română)',
					'Daniel Zippert, Emelie Snecker (svensk)',
					'Dimitriy Ryazantcev (Russian)',
					)))

	about.set_website('http://pwr.github.io/Solaar/')
	about.set_website_label(NAME)

	about.connect('response', lambda x, y: x.hide())

	def _hide(dialog, event):
		dialog.hide()
		return True
	about.connect('delete-event', _hide)

	return about
 def create_widgets(self):
     self.widgets = {}
     self.label_b = Gtk.Label(label=_('Button'), halign=Gtk.Align.START, valign=Gtk.Align.END, hexpand=True, vexpand=True)
     self.label_c = Gtk.Label(label=_('Count'), halign=Gtk.Align.START, valign=Gtk.Align.END, hexpand=True, vexpand=True)
     self.field_b = CompletionEntry(self.BUTTONS)
     self.field_c = Gtk.SpinButton.new_with_range(self.MIN_VALUE, self.MAX_VALUE, 1)
     for field in [self.field_b, self.field_c]:
         field.set_halign(Gtk.Align.CENTER)
         field.set_valign(Gtk.Align.START)
         field.set_vexpand(True)
     self.field_b.connect('changed', self._on_update)
     self.field_c.connect('changed', self._on_update)
     self.widgets[self.label_b] = (0, 0, 1, 1)
     self.widgets[self.label_c] = (1, 0, 1, 1)
     self.widgets[self.field_b] = (0, 1, 1, 1)
     self.widgets[self.field_c] = (1, 1, 1, 1)
def update_device(device, item, selected_device_id, need_popup, full=False):
    was_online = _model.get_value(item, _COLUMN.ACTIVE)
    is_online = bool(device.online)
    _model.set_value(item, _COLUMN.ACTIVE, is_online)

    battery_level = device.status.get(_K.BATTERY_LEVEL)
    battery_voltage = device.status.get(_K.BATTERY_VOLTAGE)
    if battery_level is None:
        _model.set_value(item, _COLUMN.STATUS_TEXT, _CAN_SET_ROW_NONE)
        _model.set_value(item, _COLUMN.STATUS_ICON, _CAN_SET_ROW_NONE)
    else:
        if battery_voltage is not None:
            status_text = '%(battery_voltage)dmV' % {'battery_voltage': battery_voltage}
        elif isinstance(battery_level, _NamedInt):
            status_text = _(str(battery_level))
        else:
            status_text = '%(battery_percent)d%%' % {'battery_percent': battery_level}
        _model.set_value(item, _COLUMN.STATUS_TEXT, status_text)

        charging = device.status.get(_K.BATTERY_CHARGING)
        icon_name = _icons.battery(battery_level, charging)
        _model.set_value(item, _COLUMN.STATUS_ICON, icon_name)

    if selected_device_id is None or need_popup:
        select(device.receiver.path if device.receiver else device.path, device.number)
    elif selected_device_id == (device.receiver.path if device.receiver else device.path, device.number):
        full_update = full or was_online != is_online
        _update_info_panel(device, full=full_update)
Beispiel #10
0
 def create_widgets(self):
     self.widgets = {}
     self.fields = []
     self.del_btns = []
     self.add_btn = Gtk.Button(_('Add key'), halign=Gtk.Align.CENTER, valign=Gtk.Align.END, hexpand=True, vexpand=True)
     self.add_btn.connect('clicked', self._clicked_add)
     self.widgets[self.add_btn] = (1, 0, 1, 1)
Beispiel #11
0
def main():
    _require('pyudev', 'python3-pyudev')

    # handle ^C in console
    import signal
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    signal.signal(signal.SIGINT, _handlesigint)

    args = _parse_arguments()
    if not args:
        return
    if args.action:
        # if any argument, run comandline and exit
        return _cli.run(args.action, args.hidraw_path)

    gi = _require('gi', 'python3-gi or python3-gobject')
    _require('gi.repository.Gtk', 'gir1.2-gtk-3.0', gi, 'Gtk', '3.0')

    udev_file = '42-logitech-unify-permissions.rules'
    if not os.path.isfile('/etc/udev/rules.d/' +
                          udev_file) and not os.path.isfile(
                              '/lib/udev/rules.d/' + udev_file):
        print(_i18n._('ERROR: '), end='')
        print(_i18n._('Solaar depends on a udev file that is not present'),
              end='.\n')
        print(
            _i18n._(
                'For more information see the Solaar installation directions\n'
                'at https://pwr-solaar.github.io/Solaar/installation'))
    try:
        import solaar.ui as ui
        import solaar.listener as listener
        listener.setup_scanner(ui.status_changed, ui.error_dialog)

        import solaar.upower as _upower
        if args.restart_on_wake_up:
            _upower.watch(listener.start_all, listener.stop_all)
        else:
            _upower.watch(lambda: listener.ping_all(True))

        # main UI event loop
        ui.run_loop(listener.start_all, listener.stop_all,
                    args.window != 'only', args.window != 'hide')
    except Exception:
        import sys
        from traceback import format_exc
        sys.exit('%s: error: %s' % (NAME.lower(), format_exc()))
Beispiel #12
0
def _create_sbox(s, device):
    sbox = Gtk.HBox(homogeneous=False, spacing=6)
    sbox.setting = s
    sbox.kind = s.kind
    if s.description:
        sbox.set_tooltip_text(s.description)
    lbl = Gtk.Label(s.label)
    lbl.set_alignment(0.0, 0.5)
    label = Gtk.EventBox()
    label.add(lbl)
    spinner = Gtk.Spinner()
    spinner.set_tooltip_text(_('Working') + '...')
    sbox._spinner = spinner
    failed = Gtk.Image.new_from_icon_name('dialog-warning', Gtk.IconSize.SMALL_TOOLBAR)
    failed.set_tooltip_text(_('Read/write operation failed.'))
    sbox._failed = failed
    change_icon = Gtk.Image.new_from_icon_name('changes-prevent', Gtk.IconSize.LARGE_TOOLBAR)
    sbox._change_icon = change_icon
    _change_icon(False, change_icon)
    change = Gtk.Button()
    change.set_relief(Gtk.ReliefStyle.NONE)
    change.add(change_icon)
    change.set_sensitive(True)
    change.connect('clicked', _change_click, sbox)

    if s.kind == _SETTING_KIND.toggle:
        control = ToggleControl(sbox)
    elif s.kind == _SETTING_KIND.range:
        control = SliderControl(sbox)
    elif s.kind == _SETTING_KIND.choice:
        control = _create_choice_control(sbox)
    elif s.kind == _SETTING_KIND.map_choice:
        control = MapChoiceControl(sbox)
    elif s.kind == _SETTING_KIND.multiple_toggle:
        control = MultipleToggleControl(sbox, change)
    elif s.kind == _SETTING_KIND.multiple_range:
        control = MultipleRangeControl(sbox, change)
    else:
        raise Exception('NotImplemented')
    control.set_sensitive(False)  # the first read will enable it
    control.layout(sbox, label, change, spinner, failed)
    sbox._control = control
    sbox.show_all()
    spinner.start()  # the first read will stop it
    failed.set_visible(False)
    return sbox
Beispiel #13
0
 def _check_encrypted(dev):
     if assistant.is_drawable():
         if device.status.get(_K.LINK_ENCRYPTED) is False:
             hbox.pack_start(Gtk.Image.new_from_icon_name('security-low', Gtk.IconSize.MENU), False, False, 0)
             hbox.pack_start(Gtk.Label(_('The wireless link is not encrypted') + '!'), False, False, 0)
             hbox.show_all()
         else:
             return True
Beispiel #14
0
 def _check_encrypted(dev):
     if assistant.is_drawable():
         if device.status.get(_K.LINK_ENCRYPTED) == False:
             hbox.pack_start(Gtk.Image.new_from_icon_name("security-low", Gtk.IconSize.MENU), False, False, 0)
             hbox.pack_start(Gtk.Label(_("The wireless link is not encrypted") + "!"), False, False, 0)
             hbox.show_all()
         else:
             return True
Beispiel #15
0
 def _event_button_released(self, v, e):
     if e.button == 3:  # right click
         m, it = v.get_selection().get_selected()
         wrapped = m[it][0]
         c = wrapped.component
         parent_it = m.iter_parent(it)
         parent_c = m[parent_it][0].component if wrapped.level > 0 else None
         menu = Gtk.Menu()
         can_wrap = wrapped.editable and wrapped.component is not None and wrapped.level >= 2
         can_delete = wrapped.editable and not isinstance(parent_c, _DIV.Not) and c is not None and wrapped.level >= 1
         can_insert = wrapped.editable and not isinstance(parent_c, _DIV.Not) and wrapped.level >= 2
         can_insert_only_rule = wrapped.editable and wrapped.level == 1
         can_flatten = wrapped.editable and not isinstance(parent_c, _DIV.Not) and isinstance(
             c, (_DIV.Rule, _DIV.And, _DIV.Or)
         ) and wrapped.level >= 2 and len(c.components)
         can_copy = wrapped.level >= 1
         can_insert_root = wrapped.editable and wrapped.level == 0
         for item in self.__get_insert_menus(m, it, c, can_insert, can_insert_only_rule, can_insert_root):
             menu.append(item)
         if can_flatten:
             menu.append(self._menu_flatten(m, it))
         if can_wrap:
             menu.append(self._menu_wrap(m, it))
             menu.append(self._menu_negate(m, it))
         if menu.get_children():
             menu.append(Gtk.SeparatorMenuItem(visible=True))
         if can_delete:
             menu.append(self._menu_cut(m, it))
         if can_copy and c is not None:
             menu.append(self._menu_copy(m, it))
         if can_insert and _rule_component_clipboard is not None:
             p = self._menu_paste(m, it)
             menu.append(p)
             if c is None:  # just a placeholder
                 p.set_label(_('Paste here'))
             else:
                 p.set_label(_('Paste above'))
                 p2 = self._menu_paste(m, it, below=True)
                 p2.set_label(_('Paste below'))
                 menu.append(p2)
         elif can_insert_only_rule and isinstance(_rule_component_clipboard, _DIV.Rule):
             p = self._menu_paste(m, it)
             menu.append(p)
             if c is None:
                 p.set_label(_('Paste rule here'))
             else:
                 p.set_label(_('Paste rule above'))
                 p2 = self._menu_paste(m, it, below=True)
                 p2.set_label(_('Paste rule below'))
                 menu.append(p2)
         elif can_insert_root and isinstance(_rule_component_clipboard, _DIV.Rule):
             p = self._menu_paste(m, m.iter_nth_child(it, 0))
             p.set_label(_('Paste rule'))
             menu.append(p)
         if menu.get_children() and can_delete:
             menu.append(Gtk.SeparatorMenuItem(visible=True))
         if can_delete:
             menu.append(self._menu_delete(m, it))
         if menu.get_children():
             menu.popup_at_pointer(e)
Beispiel #16
0
def _update_receiver_panel(receiver, panel, buttons, full=False):
	assert receiver

	devices_count = len(receiver)

	paired_text = _('No device paired.') if devices_count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', devices_count) % { 'count': devices_count }

	if(receiver.max_devices > 0):
		paired_text += '\n\n<small>%s</small>' % ngettext('Up to %(max_count)s device can be paired to this receiver.', 'Up to %(max_count)s devices can be paired to this receiver.', receiver.max_devices) % { 'max_count': receiver.max_devices }
	elif devices_count > 0:
		paired_text += '\n\n<small>%s</small>' % _('Only one device can be paired to this receiver.')
	pairings = receiver.remaining_pairings(False) 
	if ( pairings is not None and pairings >= 0 ) :
		paired_text += '\n<small>%s</small>' % _('This receiver has %d pairing(s) remaining.') % pairings

	panel._count.set_markup(paired_text)

	is_pairing = receiver.status.lock_open
	if is_pairing:
		panel._scanning.set_visible(True)
		if not panel._spinner.get_visible():
			panel._spinner.start()
		panel._spinner.set_visible(True)
	else:
		panel._scanning.set_visible(False)
		if panel._spinner.get_visible():
			panel._spinner.stop()
		panel._spinner.set_visible(False)

	panel.set_visible(True)

	# b._insecure.set_visible(False)
	buttons._unpair.set_visible(False)

	if ( receiver.may_unpair or receiver.re_pairs ) and not is_pairing and \
	   ( receiver.remaining_pairings() is None or receiver.remaining_pairings() != 0 ):
		if not receiver.re_pairs and devices_count >= receiver.max_devices:
			paired_devices = tuple(n for n in range(1, receiver.max_devices+1) if n in receiver)
			buttons._pair.set_sensitive(len(paired_devices) < receiver.max_devices)
		else:
			buttons._pair.set_sensitive(True)
	else:
		buttons._pair.set_sensitive(False)

	buttons._pair.set_visible(True)
Beispiel #17
0
def _create_menu(quit_handler):
    menu = Gtk.Menu()

    # per-device menu entries will be generated as-needed

    no_receiver = Gtk.MenuItem.new_with_label(_('No Logitech device found'))
    no_receiver.set_sensitive(False)
    menu.append(no_receiver)
    menu.append(Gtk.SeparatorMenuItem.new())

    from .action import make
    menu.append(make('help-about', _('About %s') % NAME, _show_about_window, stock_id='help-about').create_menu_item())
    menu.append(make('application-exit', _('Quit %s') % NAME, quit_handler, stock_id='application-exit').create_menu_item())
    del make

    menu.show_all()

    return menu
Beispiel #18
0
def _create_menu(quit_handler):
	menu = Gtk.Menu()

	# per-device menu entries will be generated as-needed

	no_receiver = Gtk.MenuItem.new_with_label(_("No Logitech receiver found"))
	no_receiver.set_sensitive(False)
	menu.append(no_receiver)
	menu.append(Gtk.SeparatorMenuItem.new())

	from .action import about, make
	menu.append(about.create_menu_item())
	menu.append(make('application-exit', _("Quit"), quit_handler, stock_id=Gtk.STOCK_QUIT).create_menu_item())
	del about, make

	menu.show_all()

	return menu
Beispiel #19
0
def _create_menu(quit_handler):
	menu = Gtk.Menu()

	# per-device menu entries will be generated as-needed

	no_receiver = Gtk.MenuItem.new_with_label(_("No Logitech receiver found"))
	no_receiver.set_sensitive(False)
	menu.append(no_receiver)
	menu.append(Gtk.SeparatorMenuItem.new())

	from .action import about, make
	menu.append(about.create_menu_item())
	menu.append(make('application-exit', _("Quit"), quit_handler, stock_id=Gtk.STOCK_QUIT).create_menu_item())
	del about, make

	menu.show_all()

	return menu
Beispiel #20
0
def _create_buttons_box():
    bb = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL)
    bb.set_layout(Gtk.ButtonBoxStyle.END)

    bb._details = _new_button(None,
                              'dialog-information',
                              _SMALL_BUTTON_ICON_SIZE,
                              tooltip=_("Show Technical Details"),
                              toggle=True,
                              clicked=_update_details)
    bb.add(bb._details)
    bb.set_child_secondary(bb._details, True)
    bb.set_child_non_homogeneous(bb._details, True)

    def _pair_new_device(trigger):
        assert _find_selected_device_id() is not None
        receiver = _find_selected_device()
        assert receiver is not None
        assert bool(receiver)
        assert receiver.kind is None
        _action.pair(_window, receiver)

    bb._pair = _new_button(_("Pair new device"),
                           'list-add',
                           clicked=_pair_new_device)
    bb.add(bb._pair)

    def _unpair_current_device(trigger):
        assert _find_selected_device_id() is not None
        device = _find_selected_device()
        assert device is not None
        assert bool(device)
        assert device.kind is not None
        _action.unpair(_window, device)

    bb._unpair = _new_button(_("Unpair"),
                             'edit-delete',
                             clicked=_unpair_current_device)
    bb.add(bb._unpair)

    return bb
Beispiel #21
0
def _generate_description_lines():
    if not _devices_info:
        yield _('no receiver')
        return

    for _ignore, number, name, status in _devices_info:
        if number is None:  # receiver
            continue

        p = status.to_string()
        if p:  # does it have any properties to print?
            yield '<b>%s</b>' % name
            if status:
                yield '\t%s' % p
            else:
                yield '\t%s <small>(' % p + _('offline') + ')</small>'
        else:
            if status:
                yield '<b>%s</b> <small>(' % name + _('no status') + ')</small>'
            else:
                yield '<b>%s</b> <small>(' % name + _('offline') + ')</small>'
Beispiel #22
0
def _error_dialog(reason, object):
    _log.error("error: %s %s", reason, object)

    if reason == 'permissions':
        title = _("Permissions error")
        text = _("Found a Logitech Receiver (%s), but did not have permission to open it.") % object + \
          '\n\n' + \
          _("If you've just installed Solaar, try removing the receiver and plugging it back in.")
    elif reason == 'unpair':
        title = _("Unpairing failed")
        text = _("Failed to unpair %s from %s.") % (object.name, object.receiver.name) + \
          '\n\n' + \
          _("The receiver returned an error, with no further details.")
    else:
        raise Exception("ui.error_dialog: don't know how to handle (%s, %s)",
                        reason, object)

    assert title
    assert text

    m = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR,
                          Gtk.ButtonsType.CLOSE, text)
    m.set_title(title)
    m.run()
    m.destroy()
Beispiel #23
0
def _error_dialog(reason, object):
    _log.error('error: %s %s', reason, object)

    if reason == 'permissions':
        title = _('Permissions error')
        text = (_(
            'Found a Logitech Receiver (%s), but did not have permission to open it.'
        ) % object + '\n\n' + _(
            "If you've just installed Solaar, try removing the receiver and plugging it back in."
        ))
    elif reason == 'unpair':
        title = _('Unpairing failed')
        text = (_('Failed to unpair %{device} from %{receiver}.').format(
            device=object.name, receiver=object.receiver.name) + '\n\n' +
                _('The receiver returned an error, with no further details.'))
    else:
        raise Exception("ui.error_dialog: don't know how to handle (%s, %s)",
                        reason, object)

    assert title
    assert text

    m = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR,
                          Gtk.ButtonsType.CLOSE, text)
    m.set_title(title)
    m.run()
    m.destroy()
Beispiel #24
0
def _show_passcode(assistant, receiver, passkey):
    if _log.isEnabledFor(_DEBUG):
        _log.debug('%s show passkey: %s', receiver, passkey)
    name = receiver.status.device_name
    authentication = receiver.status.device_authentication
    intro_text = _('%(receiver_name)s: pair new device') % {
        'receiver_name': receiver.name
    }
    page_text = _('Enter passcode on %(name)s.') % {'name': name}
    page_text += '\n'
    if authentication & 0x01:
        page_text += _('Type %(passcode)s and then press the enter key.') % {
            'passcode': receiver.status.device_passkey
        }
    else:
        passcode = ', '.join([
            _('right') if bit == '1' else _('left')
            for bit in f'{int(receiver.status.device_passkey):010b}'
        ])
        page_text += _(
            'Press %(code)s\nand then press left and right buttons simultaneously.'
        ) % {
            'code': passcode
        }
    page = _create_page(assistant, Gtk.AssistantPageType.PROGRESS, intro_text,
                        'preferences-desktop-peripherals', page_text)
    assistant.set_page_complete(page, True)
    assistant.next_page()
Beispiel #25
0
 def __get_insert_menus(self, m, it, c, can_insert, can_insert_only_rule, can_insert_root):
     items = []
     if can_insert:
         ins = self._menu_insert(m, it)
         items.append(ins)
         if c is None:  # just a placeholder
             ins.set_label(_('Insert here'))
         else:
             ins.set_label(_('Insert above'))
             ins2 = self._menu_insert(m, it, below=True)
             ins2.set_label(_('Insert below'))
             items.append(ins2)
     elif can_insert_only_rule:
         ins = self._menu_create_rule(m, it)
         items.append(ins)
         if c is None:
             ins.set_label(_('Insert new rule here'))
         else:
             ins.set_label(_('Insert new rule above'))
             ins2 = self._menu_create_rule(m, it, below=True)
             ins2.set_label(_('Insert new rule below'))
             items.append(ins2)
     elif can_insert_root:
         ins = self._menu_create_rule(m, m.iter_nth_child(it, 0))
         items.append(ins)
     return items
Beispiel #26
0
    def show(dev, reason=None, icon=None, progress=None):
        """Show a notification with title and text.
        Optionally displays the `progress` integer value
        in [0, 100] as a progress bar."""
        if available and Notify.is_initted():
            summary = dev.name

            # if a notification with same name is already visible, reuse it to avoid spamming
            n = _notifications.get(summary)
            if n is None:
                n = _notifications[summary] = Notify.Notification()

            if reason:
                message = reason
            elif dev.status is None:
                message = _('unpaired')
            elif bool(dev.status):
                message = dev.status.to_string() or _('connected')
            else:
                message = _('offline')

            # we need to use the filename here because the notifications daemon
            # is an external application that does not know about our icon sets
            icon_file = _icons.device_icon_file(
                dev.name, dev.kind) if icon is None else _icons.icon_file(icon)

            n.update(summary, message, icon_file)
            urgency = Notify.Urgency.LOW if dev.status else Notify.Urgency.NORMAL
            n.set_urgency(urgency)
            n.set_hint('desktop-entry', GLib.Variant('s', NAME.lower()))
            if progress:
                n.set_hint('value', GLib.Variant('i', progress))

            try:
                # if _log.isEnabledFor(_DEBUG):
                #     _log.debug("showing %s", n)
                n.show()
            except Exception:
                _log.exception('showing %s', n)
Beispiel #27
0
 def __init__(self, sbox, delegate=None):
     super().__init__(homogeneous=False, spacing=6)
     self.init(sbox, delegate)
     self.keyBox = Gtk.ComboBoxText()
     for entry in sbox.setting.choices:
         self.keyBox.append(str(int(entry)), _(str(entry)))
     self.keyBox.set_active(0)
     key_choice = int(self.keyBox.get_active_id())
     self.value_choices = self.sbox.setting.choices[key_choice]
     self.valueBox = _create_choice_control(sbox.setting, choices=self.value_choices, delegate=self)
     self.pack_start(self.keyBox, False, False, 0)
     self.pack_end(self.valueBox, False, False, 0)
     self.keyBox.connect('changed', self.map_value_notify_key)
Beispiel #28
0
def unpair(window, device):
    assert device
    assert device.kind is not None

    qdialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.QUESTION,
                                Gtk.ButtonsType.NONE,
                                _('Unpair') + ' ' + device.name + ' ?')
    qdialog.set_icon_name('remove')
    qdialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
    qdialog.add_button(_('Unpair'), Gtk.ResponseType.ACCEPT)
    choice = qdialog.run()
    qdialog.destroy()
    if choice == Gtk.ResponseType.ACCEPT:
        receiver = device.receiver
        assert receiver
        device_number = device.number

        try:
            del receiver[device_number]
        except Exception:
            # _log.exception("unpairing %s", device)
            error_dialog('unpair', device)
Beispiel #29
0
def unpair(window, device):
	assert device
	assert device.kind is not None

	qdialog = Gtk.MessageDialog(window, 0,
								Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE,
								_("Unpair") + ' ' + device.name + ' ?')
	qdialog.set_icon_name('remove')
	qdialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
	qdialog.add_button(_("Unpair"), Gtk.ResponseType.ACCEPT)
	choice = qdialog.run()
	qdialog.destroy()
	if choice == Gtk.ResponseType.ACCEPT:
		receiver = device.receiver
		assert receiver
		device_number = device.number

		try:
			del receiver[device_number]
		except:
			# _log.exception("unpairing %s", device)
			error_dialog('unpair', device)
Beispiel #30
0
def _generate_tooltip_lines():
	if not _devices_info:
		yield '<b>%s</b>: ' % NAME + _("no receiver")
		return

	for _ignore, number, name, status in _devices_info:
		if number is None:  # receiver
			continue

		p = status.to_string()
		if p:  # does it have any properties to print?
			yield '<b>%s</b>' % name
			if status:
				yield '\t%s' % p
			else:
				yield '\t%s <small>(' % p + _("offline") + ')</small>'
		else:
			if status:
				yield '<b>%s</b> <small>(' % name + _("no status") + ')</small>'
			else:
				yield '<b>%s</b> <small>(' % name + _("offline") + ')</small>'
		yield ''
Beispiel #31
0
def _create_device_panel():
    p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4)

    def _status_line(label_text):
        b = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8)
        b.set_size_request(10, 28)

        b._label = Gtk.Label(label_text)
        b._label.set_alignment(0, 0.5)
        b._label.set_size_request(170, 10)
        b.pack_start(b._label, False, False, 0)

        b._icon = Gtk.Image()
        b.pack_start(b._icon, False, False, 0)

        b._text = Gtk.Label()
        b._text.set_alignment(0, 0.5)
        b.pack_start(b._text, True, True, 0)

        return b

    p._battery = _status_line(_('Battery'))
    p.pack_start(p._battery, False, False, 0)

    p._secure = _status_line(_('Wireless Link'))
    p._secure._icon.set_from_icon_name('dialog-warning', _INFO_ICON_SIZE)
    p.pack_start(p._secure, False, False, 0)

    p._lux = _status_line(_('Lighting'))
    p.pack_start(p._lux, False, False, 0)

    p.pack_start(Gtk.Separator.new(Gtk.Orientation.HORIZONTAL), False, False,
                 0)  # spacer

    p._config = _config_panel.create()
    p.pack_end(p._config, True, True, 4)

    return p
Beispiel #32
0
def create(receiver):
    assert receiver is not None
    assert receiver.kind is None

    global address, kind, authentication, name, passcode
    address = name = kind = authentication = passcode = None

    assistant = Gtk.Assistant()
    assistant.set_title(
        _('%(receiver_name)s: pair new device') %
        {'receiver_name': receiver.name})
    assistant.set_icon_name('list-add')

    assistant.set_size_request(400, 240)
    assistant.set_resizable(False)
    assistant.set_role('pair-device')

    if receiver.receiver_kind == 'bolt':
        page_text = _(
            'Press a pairing button or key until the pairing light flashes quickly.'
        )
        page_text += '\n'
        page_text += _(
            'You may have to first turn the device off and on again.')
    else:
        page_text = _('Turn on the device you want to pair.')
        page_text += '\n'
        page_text += _(
            'If the device is already turned on, turn it off and on again.')
    if receiver.remaining_pairings() and receiver.remaining_pairings() >= 0:
        page_text += ngettext(
            '\n\nThis receiver has %d pairing remaining.',
            '\n\nThis receiver has %d pairings remaining.',
            receiver.remaining_pairings()) % receiver.remaining_pairings()
        page_text += _('\nCancelling at this point will not use up a pairing.')

    intro_text = _('%(receiver_name)s: pair new device') % {
        'receiver_name': receiver.name
    }

    page_intro = _create_page(assistant, Gtk.AssistantPageType.PROGRESS,
                              intro_text, 'preferences-desktop-peripherals',
                              page_text)
    spinner = Gtk.Spinner()
    spinner.set_visible(True)
    page_intro.pack_end(spinner, True, True, 24)

    assistant.connect('prepare', _prepare, receiver)
    assistant.connect('cancel', _finish, receiver)
    assistant.connect('close', _finish, receiver)

    return assistant
Beispiel #33
0
def _update_receiver_panel(receiver, panel, buttons, full=False):
	assert receiver

	devices_count = len(receiver)

	paired_text = _('No device paired.') if devices_count == 0 else ngettext('%(count)s paired device.', '%(count)s paired devices.', devices_count) % { 'count': devices_count }

	if(receiver.max_devices > 0):
		paired_text += '\n\n<small>%s</small>' % ngettext('Up to %(max_count)s device can be paired to this receiver.', 'Up to %(max_count)s devices can be paired to this receiver.', receiver.max_devices) % { 'max_count': receiver.max_devices }
	elif(devices_count > 0):
		paired_text += '\n\n<small>%s</small>' % _('Only one device can be paired to this receiver.')

	panel._count.set_markup(paired_text)

	is_pairing = receiver.status.lock_open
	if is_pairing:
		panel._scanning.set_visible(True)
		if not panel._spinner.get_visible():
			panel._spinner.start()
		panel._spinner.set_visible(True)
	else:
		panel._scanning.set_visible(False)
		if panel._spinner.get_visible():
			panel._spinner.stop()
		panel._spinner.set_visible(False)

	panel.set_visible(True)

	# b._insecure.set_visible(False)
	buttons._unpair.set_visible(False)

	may_pair = receiver.may_unpair and not is_pairing
	if may_pair and devices_count >= receiver.max_devices:
		online_devices = tuple(n for n in range(1, receiver.max_devices) if n in receiver and receiver[n].online)
		may_pair &= len(online_devices) < receiver.max_devices
	buttons._pair.set_sensitive(may_pair)
	buttons._pair.set_visible(True)
Beispiel #34
0
def _pairing_succeeded(assistant, receiver, device):
    assert device
    if _log.isEnabledFor(_DEBUG):
        _log.debug("%s success: %s", receiver, device)

    page = _create_page(assistant, Gtk.AssistantPageType.SUMMARY)

    header = Gtk.Label(_("Found a new device:"))
    header.set_alignment(0.5, 0)
    page.pack_start(header, False, False, 0)

    device_icon = Gtk.Image()
    icon_set = _icons.device_icon_set(device.name, device.kind)
    device_icon.set_from_icon_set(icon_set, Gtk.IconSize.LARGE)
    device_icon.set_alignment(0.5, 1)
    page.pack_start(device_icon, True, True, 0)

    device_label = Gtk.Label()
    device_label.set_markup('<b>%s</b>' % device.name)
    device_label.set_alignment(0.5, 0)
    page.pack_start(device_label, True, True, 0)

    hbox = Gtk.HBox(False, 8)
    hbox.pack_start(Gtk.Label(' '), False, False, 0)
    hbox.set_property('expand', False)
    hbox.set_property('halign', Gtk.Align.CENTER)
    page.pack_start(hbox, False, False, 0)

    def _check_encrypted(dev):
        if assistant.is_drawable():
            if device.status.get(_K.LINK_ENCRYPTED) == False:
                hbox.pack_start(
                    Gtk.Image.new_from_icon_name('security-low',
                                                 Gtk.IconSize.MENU), False,
                    False, 0)
                hbox.pack_start(
                    Gtk.Label(_("The wireless link is not encrypted") + '!'),
                    False, False, 0)
                hbox.show_all()
            else:
                return True

    GLib.timeout_add(_STATUS_CHECK, _check_encrypted, device)

    page.show_all()

    assistant.next_page()
    assistant.commit()
Beispiel #35
0
def _create_window_layout():
    assert _tree is not None
    assert _details is not None
    assert _info is not None
    assert _empty is not None

    assert _tree.get_selection().get_mode() == Gtk.SelectionMode.SINGLE
    _tree.get_selection().connect('changed', _device_selected)

    tree_scroll = Gtk.ScrolledWindow()
    tree_scroll.add(_tree)
    tree_scroll.set_min_content_width(_tree.get_size_request()[0])
    tree_scroll.set_shadow_type(Gtk.ShadowType.IN)

    tree_panel = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
    tree_panel.set_homogeneous(False)
    tree_panel.pack_start(tree_scroll, True, True, 0)
    tree_panel.pack_start(_details, False, False, 0)

    panel = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 16)
    panel.pack_start(tree_panel, True, True, 0)
    panel.pack_start(_info, True, True, 0)
    panel.pack_start(_empty, True, True, 0)

    about_button = _new_button(_("About") + ' ' + NAME,
                               'help-about',
                               icon_size=_SMALL_BUTTON_ICON_SIZE,
                               clicked=_show_about_window)

    bottom_buttons_box = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL)
    bottom_buttons_box.set_layout(Gtk.ButtonBoxStyle.START)
    bottom_buttons_box.add(about_button)

    # solaar_version = Gtk.Label()
    # solaar_version.set_markup('<small>' + NAME + ' v' + VERSION + '</small>')
    # bottom_buttons_box.add(solaar_version)
    # bottom_buttons_box.set_child_secondary(solaar_version, True)

    vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 8)
    vbox.set_border_width(8)
    vbox.pack_start(panel, True, True, 0)
    vbox.pack_end(bottom_buttons_box, False, False, 0)
    vbox.show_all()

    _details.set_visible(False)
    _info.set_visible(False)
    return vbox
Beispiel #36
0
    def __init__(self):

        window = Gtk.Window()
        window.set_title(_('Solaar Rule Editor'))
        window.connect('delete-event', self._closing)
        vbox = Gtk.VBox()

        self.top_panel, self.view = self._create_top_panel()
        for col in self._create_view_columns():
            self.view.append_column(col)
        vbox.pack_start(self.top_panel, True, True, 0)

        self.dirty = False  # if dirty, there are pending changes to be saved

        self.type_ui = {}
        self.update_ui = {}
        self.bottom_panel = self._create_bottom_panel()
        self.ui = defaultdict(lambda: UnsupportedRuleComponentUI(self.bottom_panel))
        self.ui.update({  # one instance per type
            rc_class: rc_ui_class(self.bottom_panel, on_update=self.on_update)
            for rc_class, rc_ui_class in COMPONENT_UI.items()
        })
        bottom_box = Gtk.ScrolledWindow()
        bottom_box.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER)
        bottom_box.add(self.bottom_panel)
        vbox.pack_start(bottom_box, True, True, 0)

        self.model = self._create_model()
        self.view.set_model(self.model)
        self.view.expand_all()

        window.add(vbox)

        geometry = Gdk.Geometry()
        geometry.min_width = 800
        geometry.min_height = 800
        window.set_geometry_hints(None, geometry, Gdk.WindowHints.MIN_SIZE)
        window.set_position(Gtk.WindowPosition.CENTER)

        window.show_all()

        window.connect('delete-event', lambda w, e: w.hide_on_delete() or True)

        style = window.get_style_context()
        style.add_class('solaar')
        self.window = window
        self._editing_component = None
Beispiel #37
0
	def has_stopped(self):
		r, self.receiver = self.receiver, None
		assert r is not None
		if _log.isEnabledFor(_INFO):
			_log.info("%s: notifications listener has stopped", r)

		# because udev is not notifying us about device removal,
		# make sure to clean up in _all_listeners
		_all_listeners.pop(r.path, None)

		r.status = _("The receiver was unplugged.")
		if r:
			try:
				r.close()
			except:
				_log.exception("closing receiver %s" % r.path)
		self.status_changed_callback(r)  #, _status.ALERT.NOTIFICATION)
Beispiel #38
0
    def has_stopped(self):
        r, self.receiver = self.receiver, None
        assert r is not None
        if _log.isEnabledFor(_INFO):
            _log.info("%s: notifications listener has stopped", r)

        # because udev is not notifying us about device removal,
        # make sure to clean up in _all_listeners
        _all_listeners.pop(r.path, None)

        r.status = _("The receiver was unplugged.")
        if r:
            try:
                r.close()
            except:
                _log.exception("closing receiver %s" % r.path)
        self.status_changed_callback(r)  #, _status.ALERT.NOTIFICATION)
Beispiel #39
0
def _create_window_layout():
	assert _tree is not None
	assert _details is not None
	assert _info is not None
	assert _empty is not None

	assert _tree.get_selection().get_mode() == Gtk.SelectionMode.SINGLE
	_tree.get_selection().connect('changed', _device_selected)

	tree_scroll = Gtk.ScrolledWindow()
	tree_scroll.add(_tree)
	tree_scroll.set_min_content_width(_tree.get_size_request()[0])
	tree_scroll.set_shadow_type(Gtk.ShadowType.IN)

	tree_panel = Gtk.Box.new(Gtk.Orientation.VERTICAL, 0)
	tree_panel.set_homogeneous(False)
	tree_panel.pack_start(tree_scroll, True, True, 0)
	tree_panel.pack_start(_details, False, False, 0)

	panel = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 16)
	panel.pack_start(tree_panel, False, False, 0)
	panel.pack_start(_info, True, True, 0)
	panel.pack_start(_empty, True, True, 0)

	about_button = _new_button(_("About") + ' ' + NAME, 'help-about',
					icon_size=_SMALL_BUTTON_ICON_SIZE, clicked=_show_about_window)

	bottom_buttons_box = Gtk.ButtonBox(Gtk.Orientation.HORIZONTAL)
	bottom_buttons_box.set_layout(Gtk.ButtonBoxStyle.START)
	bottom_buttons_box.add(about_button)

	# solaar_version = Gtk.Label()
	# solaar_version.set_markup('<small>' + NAME + ' v' + VERSION + '</small>')
	# bottom_buttons_box.add(solaar_version)
	# bottom_buttons_box.set_child_secondary(solaar_version, True)

	vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, 8)
	vbox.set_border_width(8)
	vbox.pack_start(panel, True, True, 0)
	vbox.pack_end(bottom_buttons_box, False, False, 0)
	vbox.show_all()

	_details.set_visible(False)
	_info.set_visible(False)
	return vbox
Beispiel #40
0
def _create_receiver_panel():
    p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4)

    p._count = Gtk.Label()
    p._count.set_padding(24, 0)
    p._count.set_alignment(0, 0.5)
    p.pack_start(p._count, True, True, 0)

    p._scanning = Gtk.Label(_("Scanning") + '...')
    p._spinner = Gtk.Spinner()

    bp = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8)
    bp.pack_start(Gtk.Label(' '), True, True, 0)
    bp.pack_start(p._scanning, False, False, 0)
    bp.pack_end(p._spinner, False, False, 0)
    p.pack_end(bp, False, False, 0)

    return p
Beispiel #41
0
def _create_receiver_panel():
	p = Gtk.Box.new(Gtk.Orientation.VERTICAL, 4)

	p._count = Gtk.Label()
	p._count.set_padding(24, 0)
	p._count.set_alignment(0, 0.5)
	p.pack_start(p._count, True, True, 0)

	p._scanning = Gtk.Label(_("Scanning") + '...')
	p._spinner = Gtk.Spinner()

	bp = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 8)
	bp.pack_start(Gtk.Label(' '), True, True, 0)
	bp.pack_start(p._scanning, False, False, 0)
	bp.pack_end(p._spinner, False, False, 0)
	p.pack_end(bp, False, False, 0)

	return p
Beispiel #42
0
def _pairing_succeeded(assistant, receiver, device):
    assert device
    if _log.isEnabledFor(_DEBUG):
        _log.debug("%s success: %s", receiver, device)

    page = _create_page(assistant, Gtk.AssistantPageType.SUMMARY)

    header = Gtk.Label(_("Found a new device:"))
    header.set_alignment(0.5, 0)
    page.pack_start(header, False, False, 0)

    device_icon = Gtk.Image()
    icon_set = _icons.device_icon_set(device.name, device.kind)
    device_icon.set_from_icon_set(icon_set, Gtk.IconSize.LARGE)
    device_icon.set_alignment(0.5, 1)
    page.pack_start(device_icon, True, True, 0)

    device_label = Gtk.Label()
    device_label.set_markup("<b>%s</b>" % device.name)
    device_label.set_alignment(0.5, 0)
    page.pack_start(device_label, True, True, 0)

    hbox = Gtk.HBox(False, 8)
    hbox.pack_start(Gtk.Label(" "), False, False, 0)
    hbox.set_property("expand", False)
    hbox.set_property("halign", Gtk.Align.CENTER)
    page.pack_start(hbox, False, False, 0)

    def _check_encrypted(dev):
        if assistant.is_drawable():
            if device.status.get(_K.LINK_ENCRYPTED) == False:
                hbox.pack_start(Gtk.Image.new_from_icon_name("security-low", Gtk.IconSize.MENU), False, False, 0)
                hbox.pack_start(Gtk.Label(_("The wireless link is not encrypted") + "!"), False, False, 0)
                hbox.show_all()
            else:
                return True

    GLib.timeout_add(_STATUS_CHECK, _check_encrypted, device)

    page.show_all()

    assistant.next_page()
    assistant.commit()
Beispiel #43
0
def run_loop(startup_hook, shutdown_hook, use_tray, show_window):
    assert use_tray or show_window, 'need either tray or visible window'
    # from gi.repository.Gio import ApplicationFlags as _ApplicationFlags
    APP_ID = 'io.github.pwr_solaar.solaar'
    application = Gtk.Application.new(
        APP_ID, Gio.ApplicationFlags.HANDLES_COMMAND_LINE)

    application.connect(
        'startup', lambda app, startup_hook: _startup(
            app, startup_hook, use_tray, show_window), startup_hook)
    application.connect('command-line', _command_line)
    application.connect('activate', _activate)
    application.connect('shutdown', _shutdown, shutdown_hook)

    application.register()
    if application.get_is_remote():
        print(
            _('Another Solaar process is already running so just expose its window'
              ))
    application.run()
Beispiel #44
0
def _pairing_failed(assistant, receiver, error):
    if _log.isEnabledFor(_DEBUG):
        _log.debug('%s fail: %s', receiver, error)

    assistant.commit()

    header = _('Pairing failed') + ': ' + _(str(error)) + '.'
    if 'timeout' in str(error):
        text = _('Make sure your device is within range, and has a decent battery charge.')
    elif str(error) == 'device not supported':
        text = _('A new device was detected, but it is not compatible with this receiver.')
    elif 'many' in str(error):
        text = _('More paired devices than receiver can support.')
    else:
        text = _('No further details are available about the error.')
    _create_page(assistant, Gtk.AssistantPageType.SUMMARY, header, 'dialog-error', text)

    assistant.next_page()
    assistant.commit()
Beispiel #45
0
def _pairing_failed(assistant, receiver, error):
    if _log.isEnabledFor(_DEBUG):
        _log.debug("%s fail: %s", receiver, error)

    assistant.commit()

    header = _("Pairing failed") + ": " + _(str(error)) + "."
    if "timeout" in str(error):
        text = _("Make sure your device is within range, and has a decent battery charge.")
    elif str(error) == "device not supported":
        text = _("A new device was detected, but it is not compatible with this receiver.")
    elif "many" in str(error):
        text = _("The receiver only supports %d paired device(s).")
    else:
        text = _("No further details are available about the error.")
    _create_page(assistant, Gtk.AssistantPageType.SUMMARY, header, "dialog-error", text)

    assistant.next_page()
    assistant.commit()
Beispiel #46
0
def _error_dialog(reason, object):
	_log.error("error: %s %s", reason, object)

	if reason == 'permissions':
		title = _("Permissions error")
		text = _("Found a Logitech Receiver (%s), but did not have permission to open it.") % object + \
				'\n\n' + \
				_("If you've just installed Solaar, try removing the receiver and plugging it back in.")
	elif reason == 'unpair':
		title = _("Unpairing failed")
		text = _("Failed to unpair %{device} from %{receiver}.").format(device=object.name, receiver=object.receiver.name) + \
				'\n\n' + \
				_("The receiver returned an error, with no further details.")
	else:
		raise Exception("ui.error_dialog: don't know how to handle (%s, %s)", reason, object)

	assert title
	assert text

	m = Gtk.MessageDialog(None, Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, text)
	m.set_title(title)
	m.run()
	m.destroy()
Beispiel #47
0
#
#
#

# def _toggle_notifications(action):
# 	if action.get_active():
# 		notify.init('Solaar')
# 	else:
# 		notify.uninit()
# 	action.set_sensitive(notify.available)
# toggle_notifications = make_toggle('notifications', 'Notifications', _toggle_notifications)


from .about import show_window as _show_about_window
from solaar import NAME
about = make('help-about', _("About") + ' ' + NAME, _show_about_window, stock_id=Gtk.STOCK_ABOUT)

#
#
#

from . import pair_window
def pair(window, receiver):
	assert receiver
	assert receiver.kind is None

	pair_dialog = pair_window.create(receiver)
	pair_dialog.set_transient_for(window)
	pair_dialog.set_destroy_with_parent(True)
	pair_dialog.set_modal(True)
	pair_dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG)
Beispiel #48
0
def _create_empty_panel():
	p = Gtk.Label()
	p.set_markup('<small>' + _("Select a device") + '</small>')
	p.set_sensitive(False)

	return p
Beispiel #49
0
#

_SMALL_BUTTON_ICON_SIZE = Gtk.IconSize.MENU
_NORMAL_BUTTON_ICON_SIZE = Gtk.IconSize.BUTTON
_TREE_ICON_SIZE = Gtk.IconSize.BUTTON
_INFO_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR
_DEVICE_ICON_SIZE = Gtk.IconSize.DND

# tree model columns
_COLUMN = _NamedInts(PATH=0, NUMBER=1, ACTIVE=2, NAME=3, ICON=4, STATUS_TEXT=5, STATUS_ICON=6, DEVICE=7)
_COLUMN_TYPES = (str, int, bool, str, str, str, str, TYPE_PYOBJECT)
_TREE_SEPATATOR = (None, 0, False, None, None, None, None, None)
assert len(_TREE_SEPATATOR) == len(_COLUMN_TYPES)
assert len(_COLUMN_TYPES) == len(_COLUMN)

_TOOLTIP_LINK_SECURE = _("The wireless link between this device and its receiver is encrypted.")
_TOOLTIP_LINK_INSECURE = _("The wireless link between this device and its receiver is not encrypted.\n"
						"\n"
						"For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n"
						"\n"
						"It is, however, a major security issue for text-input devices (keyboards, numpads),\n"
						"because typed text can be sniffed inconspicuously by 3rd parties within range.")

_UNIFYING_RECEIVER_TEXT = (
		_("No device paired") + '.\n\n<small>' + _("Up to %d devices can be paired to this receiver") + '.</small>',
		'%d ' + _("paired devices") + '\n\n<small>' + _("Up to %d devices can be paired to this receiver") + '.</small>',
	)
_NANO_RECEIVER_TEXT = (
		_("No device paired") + '.\n\n<small> </small>',
		' \n\n<small>' + _("Only one device can be paired to this receiver") + '.</small>',
	)
Beispiel #50
0
def _update_device_panel(device, panel, buttons, full=False):
	assert device
	is_online = bool(device.online)
	panel.set_sensitive(is_online)

	battery_level = device.status.get(_K.BATTERY_LEVEL)
	if battery_level is None:
		icon_name = _icons.battery()
		panel._battery._icon.set_sensitive(False)
		panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE)
		panel._battery._text.set_sensitive(True)
		panel._battery._text.set_markup('<small>%s</small>' % _("unknown"))
	else:
		charging = device.status.get(_K.BATTERY_CHARGING)
		icon_name = _icons.battery(battery_level, charging)
		panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE)
		panel._battery._icon.set_sensitive(True)

		if isinstance(battery_level, _NamedInt):
			text = _(str(battery_level))
		else:
			text = _("%(battery_percent)d%%") % { 'battery_percent': battery_level }
		if is_online:
			if charging:
				text += ' <small>(%s)</small>' % _("charging")
		else:
			text += ' <small>(%s)</small>' % _("last known")
		panel._battery._text.set_sensitive(is_online)
		panel._battery._text.set_markup(text)

	if is_online:
		not_secure = device.status.get(_K.LINK_ENCRYPTED) == False
		if not_secure:
			panel._secure._text.set_text(_("not encrypted"))
			panel._secure._icon.set_from_icon_name('security-low', _INFO_ICON_SIZE)
			panel._secure.set_tooltip_text(_("The wireless link between this device and its receiver is not encrypted.\n"
						"\n"
						"For pointing devices (mice, trackballs, trackpads), this is a minor security issue.\n"
						"\n"
						"It is, however, a major security issue for text-input devices (keyboards, numpads),\n"
						"because typed text can be sniffed inconspicuously by 3rd parties within range."))
		else:
			panel._secure._text.set_text(_("encrypted"))
			panel._secure._icon.set_from_icon_name('security-high', _INFO_ICON_SIZE)
			panel._secure.set_tooltip_text(_("The wireless link between this device and its receiver is encrypted."))
		panel._secure._icon.set_visible(True)
	else:
		panel._secure._text.set_markup('<small>%s</small>' % _("offline"))
		panel._secure._icon.set_visible(False)
		panel._secure.set_tooltip_text('')

	if is_online:
		light_level = device.status.get(_K.LIGHT_LEVEL)
		if light_level is None:
			panel._lux.set_visible(False)
		else:
			panel._lux._icon.set_from_icon_name(_icons.lux(light_level), _INFO_ICON_SIZE)
			panel._lux._text.set_text(_("%(light_level)d lux") % { 'light_level': light_level })
			panel._lux.set_visible(True)
	else:
		panel._lux.set_visible(False)

	buttons._pair.set_visible(False)
	buttons._unpair.set_sensitive(device.receiver.may_unpair)
	buttons._unpair.set_visible(True)

	panel.set_visible(True)

	if full:
		_config_panel.update(device, is_online)
Beispiel #51
0
def update(device, need_popup=False):
    if _window is None:
        return

    assert device is not None

    if need_popup:
        popup()

    selected_device_id = _find_selected_device_id()

    if device.kind is None:
        # receiver
        is_alive = bool(device)
        item = _receiver_row(device.path, device if is_alive else None)
        assert item

        if is_alive and item:
            was_pairing = bool(_model.get_value(item, _COLUMN.STATUS_ICON))
            is_pairing = bool(device.status.lock_open)
            _model.set_value(
                item, _COLUMN.STATUS_ICON,
                'network-wireless' if is_pairing else _CAN_SET_ROW_NONE)

            if selected_device_id == (device.path, 0):
                full_update = need_popup or was_pairing != is_pairing
                _update_info_panel(device, full=full_update)

        elif item:
            if _TREE_SEPATATOR:
                separator = _model.iter_next(item)
                _model.remove(separator)
            _model.remove(item)

    else:
        # peripheral
        is_paired = bool(device)
        assert device.receiver
        assert device.number is not None and device.number > 0, "invalid device number" + str(
            device.number)
        item = _device_row(device.receiver.path, device.number,
                           device if is_paired else None)

        if is_paired and item:
            was_online = _model.get_value(item, _COLUMN.ACTIVE)
            is_online = bool(device.online)
            _model.set_value(item, _COLUMN.ACTIVE, is_online)

            battery_level = device.status.get(_K.BATTERY_LEVEL)
            if battery_level is None:
                _model.set_value(item, _COLUMN.STATUS_TEXT, _CAN_SET_ROW_NONE)
                _model.set_value(item, _COLUMN.STATUS_ICON, _CAN_SET_ROW_NONE)
            else:
                if isinstance(battery_level, _NamedInt):
                    status_text = _("%(battery_level)s") % {
                        'battery_level': _(str(battery_level))
                    }
                else:
                    status_text = _("%(battery_percent)d%%") % {
                        'battery_percent': battery_level
                    }
                _model.set_value(item, _COLUMN.STATUS_TEXT, status_text)

                charging = device.status.get(_K.BATTERY_CHARGING)
                icon_name = _icons.battery(battery_level, charging)
                _model.set_value(item, _COLUMN.STATUS_ICON, icon_name)

            if selected_device_id is None or need_popup:
                select(device.receiver.path, device.number)
            elif selected_device_id == (device.receiver.path, device.number):
                full_update = need_popup or was_online != is_online
                _update_info_panel(device, full=full_update)

        elif item:
            _model.remove(item)
            _config_panel.clean(device)

    # make sure all rows are visible
    _tree.expand_all()
Beispiel #52
0
		def _details_items(device, read_all=False):
			# If read_all is False, only return stuff that is ~100% already
			# cached, and involves no HID++ calls.

			if device.kind is None:
				yield (_("Path"), device.path)
				# 046d is the Logitech vendor id
				yield (_("USB id"), '046d:' + device.product_id)

				if read_all:
					yield (_("Serial"), device.serial)
				else:
					yield (_("Serial"), '...')

			else:
				# yield ('Codename', device.codename)
				yield (_("Index"), device.number)
				yield (_("Wireless PID"), device.wpid)
				hid_version = device.protocol
				yield (_("Protocol"), 'HID++ %1.1f' % hid_version if hid_version else 'unknown')
				if read_all and device.polling_rate:
					yield (_("Polling rate"), '%d ms (%dHz)' % (device.polling_rate, 1000 // device.polling_rate))

				if read_all or not device.online:
					yield (_("Serial"), device.serial)
				else:
					yield (_("Serial"), '...')

			if read_all:
				for fw in list(device.firmware):
					yield ('  ' + _(str(fw.kind)), (fw.name + ' ' + fw.version).strip())
			elif device.kind is None or device.online:
				yield ('  %s' % _("Firmware"), '...')

			flag_bits = device.status.get(_K.NOTIFICATION_FLAGS)
			if flag_bits is not None:
				flag_names = ('(%s)' % _("none"),) if flag_bits == 0 else _hidpp10.NOTIFICATION_FLAG.flag_names(flag_bits)
				yield (_("Notifications"), ('\n%15s' % ' ').join(flag_names))
Beispiel #53
0
def update(device, need_popup=False):
	if _window is None:
		return

	assert device is not None

	if need_popup:
		popup()

	selected_device_id = _find_selected_device_id()

	if device.kind is None:
		# receiver
		is_alive = bool(device)
		item = _receiver_row(device.path, device if is_alive else None)
		assert item

		if is_alive and item:
			was_pairing = bool(_model.get_value(item, _COLUMN.STATUS_ICON))
			is_pairing = bool(device.status.lock_open)
			_model.set_value(item, _COLUMN.STATUS_ICON, 'network-wireless' if is_pairing else _CAN_SET_ROW_NONE)

			if selected_device_id == (device.path, 0):
				full_update = need_popup or was_pairing != is_pairing
				_update_info_panel(device, full=full_update)

		elif item:
			if _TREE_SEPATATOR:
				separator = _model.iter_next(item)
				_model.remove(separator)
			_model.remove(item)

	else:
		# peripheral
		is_paired = bool(device)
		assert device.receiver
		assert device.number is not None and device.number > 0, "invalid device number" + str(device.number)
		item = _device_row(device.receiver.path, device.number, device if is_paired else None)

		if is_paired and item:
			was_online = _model.get_value(item, _COLUMN.ACTIVE)
			is_online = bool(device.online)
			_model.set_value(item, _COLUMN.ACTIVE, is_online)

			battery_level = device.status.get(_K.BATTERY_LEVEL)
			if battery_level is None:
				_model.set_value(item, _COLUMN.STATUS_TEXT, _CAN_SET_ROW_NONE)
				_model.set_value(item, _COLUMN.STATUS_ICON, _CAN_SET_ROW_NONE)
			else:
				if isinstance(battery_level, _NamedInt):
					status_text = _("%(battery_level)s") % { 'battery_level': _(str(battery_level)) }
				else:
					status_text = _("%(battery_percent)d%%") % { 'battery_percent': battery_level }
				_model.set_value(item, _COLUMN.STATUS_TEXT, status_text)

				charging = device.status.get(_K.BATTERY_CHARGING)
				icon_name = _icons.battery(battery_level, charging)
				_model.set_value(item, _COLUMN.STATUS_ICON, icon_name)

			if selected_device_id is None or need_popup:
				select(device.receiver.path, device.number)
			elif selected_device_id == (device.receiver.path, device.number):
				full_update = need_popup or was_online != is_online
				_update_info_panel(device, full=full_update)

		elif item:
			_model.remove(item)
			_config_panel.clean(device)

	# make sure all rows are visible
	_tree.expand_all()
Beispiel #54
0
def _update_device_panel(device, panel, buttons, full=False):
	assert device
	is_online = bool(device.online)
	panel.set_sensitive(is_online)

	battery_level = device.status.get(_K.BATTERY_LEVEL)
	if battery_level is None:
		icon_name = _icons.battery()
		panel._battery._icon.set_sensitive(False)
		panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE)
		panel._battery._text.set_sensitive(True)
		panel._battery._text.set_markup('<small>%s</small>' % _("unknown"))
	else:
		charging = device.status.get(_K.BATTERY_CHARGING)
		icon_name = _icons.battery(battery_level, charging)
		panel._battery._icon.set_from_icon_name(icon_name, _INFO_ICON_SIZE)
		panel._battery._icon.set_sensitive(True)

		if isinstance(battery_level, _NamedInt):
			text = _(str(battery_level))
		else:
			text = '%d%%' % battery_level
		if is_online:
			if charging:
				text += ' <small>(%s)</small>' % _("charging")
		else:
			text += ' <small>(%s)</small>' % _("last known")
		panel._battery._text.set_sensitive(is_online)
		panel._battery._text.set_markup(text)

	if is_online:
		not_secure = device.status.get(_K.LINK_ENCRYPTED) == False
		if not_secure:
			panel._secure._text.set_text(_("not encrypted"))
			panel._secure._icon.set_from_icon_name('security-low', _INFO_ICON_SIZE)
			panel._secure.set_tooltip_text(_TOOLTIP_LINK_INSECURE)
		else:
			panel._secure._text.set_text(_("encrypted"))
			panel._secure._icon.set_from_icon_name('security-high', _INFO_ICON_SIZE)
			panel._secure.set_tooltip_text(_TOOLTIP_LINK_SECURE)
		panel._secure._icon.set_visible(True)
	else:
		panel._secure._text.set_markup('<small>%s</small>' % _("offline"))
		panel._secure._icon.set_visible(False)
		panel._secure.set_tooltip_text('')

	if is_online:
		light_level = device.status.get(_K.LIGHT_LEVEL)
		if light_level is None:
			panel._lux.set_visible(False)
		else:
			panel._lux._icon.set_from_icon_name(_icons.lux(light_level), _INFO_ICON_SIZE)
			panel._lux._text.set_text('%d %s' % (light_level, _("lux")))
			panel._lux.set_visible(True)
	else:
		panel._lux.set_visible(False)

	buttons._pair.set_visible(False)
	buttons._unpair.set_sensitive(device.receiver.may_unpair)
	buttons._unpair.set_visible(True)

	panel.set_visible(True)

	if full:
		_config_panel.update(device, is_online)