We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
PNG icons installed as scalable icons
The 1.1.11 pre-releases install various PNG icons in `/usr/share/icons/hicolor/scalable/apps/`, but the `scalable` hierarchy is reserved for scalable (SVG) icons.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/solaar/ui/icons.py`
Content:
```
1 # -*- python-mode -*-
2 
3 ## Copyright (C) 2012-2013  Daniel Pavel
4 ##
5 ## This program is free software; you can redistribute it and/or modify
6 ## it under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 2 of the License, or
8 ## (at your option) any later version.
9 ##
10 ## This program is distributed in the hope that it will be useful,
11 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 ## GNU General Public License for more details.
14 ##
15 ## You should have received a copy of the GNU General Public License along
16 ## with this program; if not, write to the Free Software Foundation, Inc.,
17 ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 
19 import logging
20 
21 import solaar.gtk as gtk
22 
23 from gi.repository import Gtk
24 
25 logger = logging.getLogger(__name__)
26 
27 #
28 #
29 #
30 
31 _LARGE_SIZE = 64
32 Gtk.IconSize.LARGE = Gtk.icon_size_register('large', _LARGE_SIZE, _LARGE_SIZE)
33 # Gtk.IconSize.XLARGE = Gtk.icon_size_register('x-large', _LARGE_SIZE * 2, _LARGE_SIZE * 2)
34 
35 TRAY_INIT = 'solaar-init'
36 TRAY_OKAY = 'solaar'
37 TRAY_ATTENTION = 'solaar-attention'
38 
39 _default_theme = None
40 
41 
42 def _init_icon_paths():
43     global _default_theme
44     if _default_theme:
45         return
46 
47     _default_theme = Gtk.IconTheme.get_default()
48     if logger.isEnabledFor(logging.DEBUG):
49         logger.debug('icon theme paths: %s', _default_theme.get_search_path())
50 
51     if gtk.battery_icons_style == 'symbolic':
52         global TRAY_OKAY
53         TRAY_OKAY = TRAY_INIT  # use monochrome tray icon
54         if not _default_theme.has_icon('battery-good-symbolic'):
55             logger.warning('failed to detect symbolic icons')
56             gtk.battery_icons_style = 'regular'
57     if gtk.battery_icons_style == 'regular':
58         if not _default_theme.has_icon('battery-good'):
59             logger.warning('failed to detect icons')
60             gtk.battery_icons_style = 'solaar'
61 
62 
63 #
64 #
65 #
66 
67 
68 def battery(level=None, charging=False):
69     icon_name = _battery_icon_name(level, charging)
70     if not _default_theme.has_icon(icon_name):
71         logger.warning('icon %s not found in current theme', icon_name)
72         return TRAY_OKAY  # use Solaar icon if battery icon not available
73     elif logger.isEnabledFor(logging.DEBUG):
74         logger.debug('battery icon for %s:%s = %s', level, charging, icon_name)
75     return icon_name
76 
77 
78 # return first res where val >= guard
79 # _first_res(val,((guard,res),...))
80 def _first_res(val, pairs):
81     return next((res for guard, res in pairs if val >= guard), None)
82 
83 
84 def _battery_icon_name(level, charging):
85     _init_icon_paths()
86 
87     if level is None or level < 0:
88         return 'battery-missing' + ('-symbolic' if gtk.battery_icons_style == 'symbolic' else '')
89 
90     level_name = _first_res(level, ((90, 'full'), (30, 'good'), (20, 'low'), (5, 'caution'), (0, 'empty')))
91     return 'battery-%s%s%s' % (
92         level_name, '-charging' if charging else '', '-symbolic' if gtk.battery_icons_style == 'symbolic' else ''
93     )
94 
95 
96 #
97 #
98 #
99 
100 
101 def lux(level=None):
102     if level is None or level < 0:
103         return 'light_unknown'
104     return 'light_%03d' % (20 * ((level + 50) // 100))
105 
106 
107 #
108 #
109 #
110 
111 _ICON_SETS = {}
112 
113 
114 def device_icon_set(name='_', kind=None):
115     icon_set = _ICON_SETS.get(name)
116     if icon_set is None:
117         icon_set = Gtk.IconSet.new()
118         _ICON_SETS[name] = icon_set
119 
120         # names of possible icons, in reverse order of likelihood
121         # the theme will hopefully pick up the most appropriate
122         names = ['preferences-desktop-peripherals']
123         if kind:
124             if str(kind) == 'numpad':
125                 names += ('input-keyboard', 'input-dialpad')
126             elif str(kind) == 'touchpad':
127                 names += ('input-mouse', 'input-tablet')
128             elif str(kind) == 'trackball':
129                 names += ('input-mouse', )
130             elif str(kind) == 'headset':
131                 names += ('audio-headphones', 'audio-headset')
132             names += ('input-' + str(kind), )
133         # names += (name.replace(' ', '-'),)
134 
135         source = Gtk.IconSource.new()
136         for n in names:
137             source.set_icon_name(n)
138             icon_set.add_source(source)
139         icon_set.names = names
140 
141     return icon_set
142 
143 
144 def device_icon_file(name, kind=None, size=_LARGE_SIZE):
145     _init_icon_paths()
146 
147     icon_set = device_icon_set(name, kind)
148     assert icon_set
149     for n in reversed(icon_set.names):
150         if _default_theme.has_icon(n):
151             return _default_theme.lookup_icon(n, size, 0).get_filename()
152 
153 
154 def device_icon_name(name, kind=None):
155     _init_icon_paths()
156 
157     icon_set = device_icon_set(name, kind)
158     assert icon_set
159     for n in reversed(icon_set.names):
160         if _default_theme.has_icon(n):
161             return n
162 
163 
164 def icon_file(name, size=_LARGE_SIZE):
165     _init_icon_paths()
166 
167     # has_icon() somehow returned False while lookup_icon returns non-None.
168     # I guess it happens because share/solaar/icons/ has no hicolor and
169     # resolution subdirs
170     theme_icon = _default_theme.lookup_icon(name, size, 0)
171     if theme_icon:
172         file_name = theme_icon.get_filename()
173         # if logger.isEnabledFor(logging.DEBUG):
174         #     logger.debug("icon %s(%d) => %s", name, size, file_name)
175         return file_name
176 
177     logger.warning('icon %s(%d) not found in current theme', name, size)
178
```

Path: `setup.py`
Content:
```
1 #!/usr/bin/env python3
2 import subprocess
3 
4 from glob import glob as _glob
5 
6 try:
7     from setuptools import setup
8 except ImportError:
9     from distutils.core import setup
10 
11 NAME = 'Solaar'
12 
13 with open('lib/solaar/version', 'r') as vfile:
14     version = vfile.read().strip()
15 
16 try:  # get commit from git describe
17     commit = subprocess.check_output(['git', 'describe', '--always'], stderr=subprocess.DEVNULL).strip().decode()
18     with open('lib/solaar/commit', 'w') as vfile:
19         vfile.write(f'{commit}\n')
20 except Exception:  # get commit from Ubuntu dpkg-parsechangelog
21     try:
22         commit = subprocess.check_output(['dpkg-parsechangelog', '--show-field', 'Version'],
23                                          stderr=subprocess.DEVNULL).strip().decode()
24         commit = commit.split('~')
25         with open('lib/solaar/commit', 'w') as vfile:
26             vfile.write(f'{commit[0]}\n')
27     except Exception as e:
28         print('Exception using dpkg-parsechangelog', e)
29 
30 
31 def _data_files():
32     from os.path import dirname as _dirname
33 
34     yield 'share/icons/hicolor/scalable/apps', _glob('share/solaar/icons/solaar*.svg')
35     yield 'share/icons/hicolor/scalable/apps', _glob('share/solaar/icons/light_*.png')
36 
37     for mo in _glob('share/locale/*/LC_MESSAGES/solaar.mo'):
38         yield _dirname(mo), [mo]
39 
40     yield 'share/applications', ['share/applications/solaar.desktop']
41     yield 'lib/udev/rules.d', ['rules.d/42-logitech-unify-permissions.rules']
42     yield 'share/metainfo', ['share/solaar/io.github.pwr_solaar.solaar.metainfo.xml']
43 
44     del _dirname
45 
46 
47 setup(
48     name=NAME.lower(),
49     version=version,
50     description='Linux device manager for Logitech receivers, keyboards, mice, and tablets.',
51     long_description='''
52 Solaar is a Linux device manager for many Logitech peripherals that connect through
53 Unifying and other receivers or via USB or Bluetooth.
54 Solaar is able to pair/unpair devices with receivers and show and modify some of the
55 modifiable features of devices.
56 For instructions on installing Solaar see https://pwr-solaar.github.io/Solaar/installation'''.strip(),
57     author='Daniel Pavel',
58     license='GPLv2',
59     url='http://pwr-solaar.github.io/Solaar/',
60     classifiers=[
61         'Development Status :: 4 - Beta',
62         'Environment :: X11 Applications :: GTK',
63         'Environment :: Console',
64         'Intended Audience :: End Users/Desktop',
65         'License :: DFSG approved',
66         'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
67         'Natural Language :: English',
68         'Programming Language :: Python :: 3 :: Only',
69         'Operating System :: POSIX :: Linux',
70         'Topic :: Utilities',
71     ],
72     platforms=['linux'],
73 
74     # sudo apt install python-gi python3-gi \
75     #        gir1.2-gtk-3.0 gir1.2-notify-0.7 gir1.2-ayatanaappindicator3-0.1
76     # os_requires=['gi.repository.GObject (>= 2.0)', 'gi.repository.Gtk (>= 3.0)'],
77     python_requires='>=3.7',
78     install_requires=[
79         'evdev (>= 1.1.2) ; platform_system=="Linux"',
80         'pyudev (>= 0.13)',
81         'PyYAML (>= 3.12)',
82         'python-xlib (>= 0.27)',
83         'psutil (>= 5.4.3)',
84         'dbus-python ; platform_system=="Linux"',
85     ],
86     extras_require={
87         'report-descriptor': ['hid-parser'],
88         'desktop-notifications': ['Notify (>= 0.7)'],
89         'git-commit': ['python-git-info'],
90         'test': ['pytest'],
91     },
92     package_dir={'': 'lib'},
93     packages=['keysyms', 'hidapi', 'logitech_receiver', 'solaar', 'solaar.ui', 'solaar.cli'],
94     data_files=list(_data_files()),
95     include_package_data=True,
96     scripts=_glob('bin/*'),
97 )
98
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/solaar/ui/icons.py b/lib/solaar/ui/icons.py
--- a/lib/solaar/ui/icons.py
+++ b/lib/solaar/ui/icons.py
@@ -101,7 +101,7 @@
 def lux(level=None):
     if level is None or level < 0:
         return 'light_unknown'
-    return 'light_%03d' % (20 * ((level + 50) // 100))
+    return 'solaar-light_%03d' % (20 * ((level + 50) // 100))
 
 
 #
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -32,7 +32,7 @@
     from os.path import dirname as _dirname
 
     yield 'share/icons/hicolor/scalable/apps', _glob('share/solaar/icons/solaar*.svg')
-    yield 'share/icons/hicolor/scalable/apps', _glob('share/solaar/icons/light_*.png')
+    yield 'share/icons/hicolor/32x32/apps', _glob('share/solaar/icons/light_*.png')
 
     for mo in _glob('share/locale/*/LC_MESSAGES/solaar.mo'):
         yield _dirname(mo), [mo]

{"golden_diff": "diff --git a/lib/solaar/ui/icons.py b/lib/solaar/ui/icons.py\n--- a/lib/solaar/ui/icons.py\n+++ b/lib/solaar/ui/icons.py\n@@ -101,7 +101,7 @@\n def lux(level=None):\n     if level is None or level < 0:\n         return 'light_unknown'\n-    return 'light_%03d' % (20 * ((level + 50) // 100))\n+    return 'solaar-light_%03d' % (20 * ((level + 50) // 100))\n \n \n #\ndiff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -32,7 +32,7 @@\n     from os.path import dirname as _dirname\n \n     yield 'share/icons/hicolor/scalable/apps', _glob('share/solaar/icons/solaar*.svg')\n-    yield 'share/icons/hicolor/scalable/apps', _glob('share/solaar/icons/light_*.png')\n+    yield 'share/icons/hicolor/32x32/apps', _glob('share/solaar/icons/light_*.png')\n \n     for mo in _glob('share/locale/*/LC_MESSAGES/solaar.mo'):\n         yield _dirname(mo), [mo]\n", "issue": "PNG icons installed as scalable icons\nThe 1.1.11 pre-releases install various PNG icons in `/usr/share/icons/hicolor/scalable/apps/`, but the `scalable` hierarchy is reserved for scalable (SVG) icons.\n", "before_files": [{"content": "# -*- python-mode -*-\n\n## Copyright (C) 2012-2013  Daniel Pavel\n##\n## This program is free software; you can redistribute it and/or modify\n## it under the terms of the GNU General Public License as published by\n## the Free Software Foundation; either version 2 of the License, or\n## (at your option) any later version.\n##\n## This program is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n## GNU General Public License for more details.\n##\n## You should have received a copy of the GNU General Public License along\n## with this program; if not, write to the Free Software Foundation, Inc.,\n## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nimport logging\n\nimport solaar.gtk as gtk\n\nfrom gi.repository import Gtk\n\nlogger = logging.getLogger(__name__)\n\n#\n#\n#\n\n_LARGE_SIZE = 64\nGtk.IconSize.LARGE = Gtk.icon_size_register('large', _LARGE_SIZE, _LARGE_SIZE)\n# Gtk.IconSize.XLARGE = Gtk.icon_size_register('x-large', _LARGE_SIZE * 2, _LARGE_SIZE * 2)\n\nTRAY_INIT = 'solaar-init'\nTRAY_OKAY = 'solaar'\nTRAY_ATTENTION = 'solaar-attention'\n\n_default_theme = None\n\n\ndef _init_icon_paths():\n    global _default_theme\n    if _default_theme:\n        return\n\n    _default_theme = Gtk.IconTheme.get_default()\n    if logger.isEnabledFor(logging.DEBUG):\n        logger.debug('icon theme paths: %s', _default_theme.get_search_path())\n\n    if gtk.battery_icons_style == 'symbolic':\n        global TRAY_OKAY\n        TRAY_OKAY = TRAY_INIT  # use monochrome tray icon\n        if not _default_theme.has_icon('battery-good-symbolic'):\n            logger.warning('failed to detect symbolic icons')\n            gtk.battery_icons_style = 'regular'\n    if gtk.battery_icons_style == 'regular':\n        if not _default_theme.has_icon('battery-good'):\n            logger.warning('failed to detect icons')\n            gtk.battery_icons_style = 'solaar'\n\n\n#\n#\n#\n\n\ndef battery(level=None, charging=False):\n    icon_name = _battery_icon_name(level, charging)\n    if not _default_theme.has_icon(icon_name):\n        logger.warning('icon %s not found in current theme', icon_name)\n        return TRAY_OKAY  # use Solaar icon if battery icon not available\n    elif logger.isEnabledFor(logging.DEBUG):\n        logger.debug('battery icon for %s:%s = %s', level, charging, icon_name)\n    return icon_name\n\n\n# return first res where val >= guard\n# _first_res(val,((guard,res),...))\ndef _first_res(val, pairs):\n    return next((res for guard, res in pairs if val >= guard), None)\n\n\ndef _battery_icon_name(level, charging):\n    _init_icon_paths()\n\n    if level is None or level < 0:\n        return 'battery-missing' + ('-symbolic' if gtk.battery_icons_style == 'symbolic' else '')\n\n    level_name = _first_res(level, ((90, 'full'), (30, 'good'), (20, 'low'), (5, 'caution'), (0, 'empty')))\n    return 'battery-%s%s%s' % (\n        level_name, '-charging' if charging else '', '-symbolic' if gtk.battery_icons_style == 'symbolic' else ''\n    )\n\n\n#\n#\n#\n\n\ndef lux(level=None):\n    if level is None or level < 0:\n        return 'light_unknown'\n    return 'light_%03d' % (20 * ((level + 50) // 100))\n\n\n#\n#\n#\n\n_ICON_SETS = {}\n\n\ndef device_icon_set(name='_', kind=None):\n    icon_set = _ICON_SETS.get(name)\n    if icon_set is None:\n        icon_set = Gtk.IconSet.new()\n        _ICON_SETS[name] = icon_set\n\n        # names of possible icons, in reverse order of likelihood\n        # the theme will hopefully pick up the most appropriate\n        names = ['preferences-desktop-peripherals']\n        if kind:\n            if str(kind) == 'numpad':\n                names += ('input-keyboard', 'input-dialpad')\n            elif str(kind) == 'touchpad':\n                names += ('input-mouse', 'input-tablet')\n            elif str(kind) == 'trackball':\n                names += ('input-mouse', )\n            elif str(kind) == 'headset':\n                names += ('audio-headphones', 'audio-headset')\n            names += ('input-' + str(kind), )\n        # names += (name.replace(' ', '-'),)\n\n        source = Gtk.IconSource.new()\n        for n in names:\n            source.set_icon_name(n)\n            icon_set.add_source(source)\n        icon_set.names = names\n\n    return icon_set\n\n\ndef device_icon_file(name, kind=None, size=_LARGE_SIZE):\n    _init_icon_paths()\n\n    icon_set = device_icon_set(name, kind)\n    assert icon_set\n    for n in reversed(icon_set.names):\n        if _default_theme.has_icon(n):\n            return _default_theme.lookup_icon(n, size, 0).get_filename()\n\n\ndef device_icon_name(name, kind=None):\n    _init_icon_paths()\n\n    icon_set = device_icon_set(name, kind)\n    assert icon_set\n    for n in reversed(icon_set.names):\n        if _default_theme.has_icon(n):\n            return n\n\n\ndef icon_file(name, size=_LARGE_SIZE):\n    _init_icon_paths()\n\n    # has_icon() somehow returned False while lookup_icon returns non-None.\n    # I guess it happens because share/solaar/icons/ has no hicolor and\n    # resolution subdirs\n    theme_icon = _default_theme.lookup_icon(name, size, 0)\n    if theme_icon:\n        file_name = theme_icon.get_filename()\n        # if logger.isEnabledFor(logging.DEBUG):\n        #     logger.debug(\"icon %s(%d) => %s\", name, size, file_name)\n        return file_name\n\n    logger.warning('icon %s(%d) not found in current theme', name, size)\n", "path": "lib/solaar/ui/icons.py"}, {"content": "#!/usr/bin/env python3\nimport subprocess\n\nfrom glob import glob as _glob\n\ntry:\n    from setuptools import setup\nexcept ImportError:\n    from distutils.core import setup\n\nNAME = 'Solaar'\n\nwith open('lib/solaar/version', 'r') as vfile:\n    version = vfile.read().strip()\n\ntry:  # get commit from git describe\n    commit = subprocess.check_output(['git', 'describe', '--always'], stderr=subprocess.DEVNULL).strip().decode()\n    with open('lib/solaar/commit', 'w') as vfile:\n        vfile.write(f'{commit}\\n')\nexcept Exception:  # get commit from Ubuntu dpkg-parsechangelog\n    try:\n        commit = subprocess.check_output(['dpkg-parsechangelog', '--show-field', 'Version'],\n                                         stderr=subprocess.DEVNULL).strip().decode()\n        commit = commit.split('~')\n        with open('lib/solaar/commit', 'w') as vfile:\n            vfile.write(f'{commit[0]}\\n')\n    except Exception as e:\n        print('Exception using dpkg-parsechangelog', e)\n\n\ndef _data_files():\n    from os.path import dirname as _dirname\n\n    yield 'share/icons/hicolor/scalable/apps', _glob('share/solaar/icons/solaar*.svg')\n    yield 'share/icons/hicolor/scalable/apps', _glob('share/solaar/icons/light_*.png')\n\n    for mo in _glob('share/locale/*/LC_MESSAGES/solaar.mo'):\n        yield _dirname(mo), [mo]\n\n    yield 'share/applications', ['share/applications/solaar.desktop']\n    yield 'lib/udev/rules.d', ['rules.d/42-logitech-unify-permissions.rules']\n    yield 'share/metainfo', ['share/solaar/io.github.pwr_solaar.solaar.metainfo.xml']\n\n    del _dirname\n\n\nsetup(\n    name=NAME.lower(),\n    version=version,\n    description='Linux device manager for Logitech receivers, keyboards, mice, and tablets.',\n    long_description='''\nSolaar is a Linux device manager for many Logitech peripherals that connect through\nUnifying and other receivers or via USB or Bluetooth.\nSolaar is able to pair/unpair devices with receivers and show and modify some of the\nmodifiable features of devices.\nFor instructions on installing Solaar see https://pwr-solaar.github.io/Solaar/installation'''.strip(),\n    author='Daniel Pavel',\n    license='GPLv2',\n    url='http://pwr-solaar.github.io/Solaar/',\n    classifiers=[\n        'Development Status :: 4 - Beta',\n        'Environment :: X11 Applications :: GTK',\n        'Environment :: Console',\n        'Intended Audience :: End Users/Desktop',\n        'License :: DFSG approved',\n        'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',\n        'Natural Language :: English',\n        'Programming Language :: Python :: 3 :: Only',\n        'Operating System :: POSIX :: Linux',\n        'Topic :: Utilities',\n    ],\n    platforms=['linux'],\n\n    # sudo apt install python-gi python3-gi \\\n    #        gir1.2-gtk-3.0 gir1.2-notify-0.7 gir1.2-ayatanaappindicator3-0.1\n    # os_requires=['gi.repository.GObject (>= 2.0)', 'gi.repository.Gtk (>= 3.0)'],\n    python_requires='>=3.7',\n    install_requires=[\n        'evdev (>= 1.1.2) ; platform_system==\"Linux\"',\n        'pyudev (>= 0.13)',\n        'PyYAML (>= 3.12)',\n        'python-xlib (>= 0.27)',\n        'psutil (>= 5.4.3)',\n        'dbus-python ; platform_system==\"Linux\"',\n    ],\n    extras_require={\n        'report-descriptor': ['hid-parser'],\n        'desktop-notifications': ['Notify (>= 0.7)'],\n        'git-commit': ['python-git-info'],\n        'test': ['pytest'],\n    },\n    package_dir={'': 'lib'},\n    packages=['keysyms', 'hidapi', 'logitech_receiver', 'solaar', 'solaar.ui', 'solaar.cli'],\n    data_files=list(_data_files()),\n    include_package_data=True,\n    scripts=_glob('bin/*'),\n)\n", "path": "setup.py"}], "after_files": [{"content": "# -*- python-mode -*-\n\n## Copyright (C) 2012-2013  Daniel Pavel\n##\n## This program is free software; you can redistribute it and/or modify\n## it under the terms of the GNU General Public License as published by\n## the Free Software Foundation; either version 2 of the License, or\n## (at your option) any later version.\n##\n## This program is distributed in the hope that it will be useful,\n## but WITHOUT ANY WARRANTY; without even the implied warranty of\n## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n## GNU General Public License for more details.\n##\n## You should have received a copy of the GNU General Public License along\n## with this program; if not, write to the Free Software Foundation, Inc.,\n## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nimport logging\n\nimport solaar.gtk as gtk\n\nfrom gi.repository import Gtk\n\nlogger = logging.getLogger(__name__)\n\n#\n#\n#\n\n_LARGE_SIZE = 64\nGtk.IconSize.LARGE = Gtk.icon_size_register('large', _LARGE_SIZE, _LARGE_SIZE)\n# Gtk.IconSize.XLARGE = Gtk.icon_size_register('x-large', _LARGE_SIZE * 2, _LARGE_SIZE * 2)\n\nTRAY_INIT = 'solaar-init'\nTRAY_OKAY = 'solaar'\nTRAY_ATTENTION = 'solaar-attention'\n\n_default_theme = None\n\n\ndef _init_icon_paths():\n    global _default_theme\n    if _default_theme:\n        return\n\n    _default_theme = Gtk.IconTheme.get_default()\n    if logger.isEnabledFor(logging.DEBUG):\n        logger.debug('icon theme paths: %s', _default_theme.get_search_path())\n\n    if gtk.battery_icons_style == 'symbolic':\n        global TRAY_OKAY\n        TRAY_OKAY = TRAY_INIT  # use monochrome tray icon\n        if not _default_theme.has_icon('battery-good-symbolic'):\n            logger.warning('failed to detect symbolic icons')\n            gtk.battery_icons_style = 'regular'\n    if gtk.battery_icons_style == 'regular':\n        if not _default_theme.has_icon('battery-good'):\n            logger.warning('failed to detect icons')\n            gtk.battery_icons_style = 'solaar'\n\n\n#\n#\n#\n\n\ndef battery(level=None, charging=False):\n    icon_name = _battery_icon_name(level, charging)\n    if not _default_theme.has_icon(icon_name):\n        logger.warning('icon %s not found in current theme', icon_name)\n        return TRAY_OKAY  # use Solaar icon if battery icon not available\n    elif logger.isEnabledFor(logging.DEBUG):\n        logger.debug('battery icon for %s:%s = %s', level, charging, icon_name)\n    return icon_name\n\n\n# return first res where val >= guard\n# _first_res(val,((guard,res),...))\ndef _first_res(val, pairs):\n    return next((res for guard, res in pairs if val >= guard), None)\n\n\ndef _battery_icon_name(level, charging):\n    _init_icon_paths()\n\n    if level is None or level < 0:\n        return 'battery-missing' + ('-symbolic' if gtk.battery_icons_style == 'symbolic' else '')\n\n    level_name = _first_res(level, ((90, 'full'), (30, 'good'), (20, 'low'), (5, 'caution'), (0, 'empty')))\n    return 'battery-%s%s%s' % (\n        level_name, '-charging' if charging else '', '-symbolic' if gtk.battery_icons_style == 'symbolic' else ''\n    )\n\n\n#\n#\n#\n\n\ndef lux(level=None):\n    if level is None or level < 0:\n        return 'light_unknown'\n    return 'solaar-light_%03d' % (20 * ((level + 50) // 100))\n\n\n#\n#\n#\n\n_ICON_SETS = {}\n\n\ndef device_icon_set(name='_', kind=None):\n    icon_set = _ICON_SETS.get(name)\n    if icon_set is None:\n        icon_set = Gtk.IconSet.new()\n        _ICON_SETS[name] = icon_set\n\n        # names of possible icons, in reverse order of likelihood\n        # the theme will hopefully pick up the most appropriate\n        names = ['preferences-desktop-peripherals']\n        if kind:\n            if str(kind) == 'numpad':\n                names += ('input-keyboard', 'input-dialpad')\n            elif str(kind) == 'touchpad':\n                names += ('input-mouse', 'input-tablet')\n            elif str(kind) == 'trackball':\n                names += ('input-mouse', )\n            elif str(kind) == 'headset':\n                names += ('audio-headphones', 'audio-headset')\n            names += ('input-' + str(kind), )\n        # names += (name.replace(' ', '-'),)\n\n        source = Gtk.IconSource.new()\n        for n in names:\n            source.set_icon_name(n)\n            icon_set.add_source(source)\n        icon_set.names = names\n\n    return icon_set\n\n\ndef device_icon_file(name, kind=None, size=_LARGE_SIZE):\n    _init_icon_paths()\n\n    icon_set = device_icon_set(name, kind)\n    assert icon_set\n    for n in reversed(icon_set.names):\n        if _default_theme.has_icon(n):\n            return _default_theme.lookup_icon(n, size, 0).get_filename()\n\n\ndef device_icon_name(name, kind=None):\n    _init_icon_paths()\n\n    icon_set = device_icon_set(name, kind)\n    assert icon_set\n    for n in reversed(icon_set.names):\n        if _default_theme.has_icon(n):\n            return n\n\n\ndef icon_file(name, size=_LARGE_SIZE):\n    _init_icon_paths()\n\n    # has_icon() somehow returned False while lookup_icon returns non-None.\n    # I guess it happens because share/solaar/icons/ has no hicolor and\n    # resolution subdirs\n    theme_icon = _default_theme.lookup_icon(name, size, 0)\n    if theme_icon:\n        file_name = theme_icon.get_filename()\n        # if logger.isEnabledFor(logging.DEBUG):\n        #     logger.debug(\"icon %s(%d) => %s\", name, size, file_name)\n        return file_name\n\n    logger.warning('icon %s(%d) not found in current theme', name, size)\n", "path": "lib/solaar/ui/icons.py"}, {"content": "#!/usr/bin/env python3\nimport subprocess\n\nfrom glob import glob as _glob\n\ntry:\n    from setuptools import setup\nexcept ImportError:\n    from distutils.core import setup\n\nNAME = 'Solaar'\n\nwith open('lib/solaar/version', 'r') as vfile:\n    version = vfile.read().strip()\n\ntry:  # get commit from git describe\n    commit = subprocess.check_output(['git', 'describe', '--always'], stderr=subprocess.DEVNULL).strip().decode()\n    with open('lib/solaar/commit', 'w') as vfile:\n        vfile.write(f'{commit}\\n')\nexcept Exception:  # get commit from Ubuntu dpkg-parsechangelog\n    try:\n        commit = subprocess.check_output(['dpkg-parsechangelog', '--show-field', 'Version'],\n                                         stderr=subprocess.DEVNULL).strip().decode()\n        commit = commit.split('~')\n        with open('lib/solaar/commit', 'w') as vfile:\n            vfile.write(f'{commit[0]}\\n')\n    except Exception as e:\n        print('Exception using dpkg-parsechangelog', e)\n\n\ndef _data_files():\n    from os.path import dirname as _dirname\n\n    yield 'share/icons/hicolor/scalable/apps', _glob('share/solaar/icons/solaar*.svg')\n    yield 'share/icons/hicolor/32x32/apps', _glob('share/solaar/icons/light_*.png')\n\n    for mo in _glob('share/locale/*/LC_MESSAGES/solaar.mo'):\n        yield _dirname(mo), [mo]\n\n    yield 'share/applications', ['share/applications/solaar.desktop']\n    yield 'lib/udev/rules.d', ['rules.d/42-logitech-unify-permissions.rules']\n    yield 'share/metainfo', ['share/solaar/io.github.pwr_solaar.solaar.metainfo.xml']\n\n    del _dirname\n\n\nsetup(\n    name=NAME.lower(),\n    version=version,\n    description='Linux device manager for Logitech receivers, keyboards, mice, and tablets.',\n    long_description='''\nSolaar is a Linux device manager for many Logitech peripherals that connect through\nUnifying and other receivers or via USB or Bluetooth.\nSolaar is able to pair/unpair devices with receivers and show and modify some of the\nmodifiable features of devices.\nFor instructions on installing Solaar see https://pwr-solaar.github.io/Solaar/installation'''.strip(),\n    author='Daniel Pavel',\n    license='GPLv2',\n    url='http://pwr-solaar.github.io/Solaar/',\n    classifiers=[\n        'Development Status :: 4 - Beta',\n        'Environment :: X11 Applications :: GTK',\n        'Environment :: Console',\n        'Intended Audience :: End Users/Desktop',\n        'License :: DFSG approved',\n        'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',\n        'Natural Language :: English',\n        'Programming Language :: Python :: 3 :: Only',\n        'Operating System :: POSIX :: Linux',\n        'Topic :: Utilities',\n    ],\n    platforms=['linux'],\n\n    # sudo apt install python-gi python3-gi \\\n    #        gir1.2-gtk-3.0 gir1.2-notify-0.7 gir1.2-ayatanaappindicator3-0.1\n    # os_requires=['gi.repository.GObject (>= 2.0)', 'gi.repository.Gtk (>= 3.0)'],\n    python_requires='>=3.7',\n    install_requires=[\n        'evdev (>= 1.1.2) ; platform_system==\"Linux\"',\n        'pyudev (>= 0.13)',\n        'PyYAML (>= 3.12)',\n        'python-xlib (>= 0.27)',\n        'psutil (>= 5.4.3)',\n        'dbus-python ; platform_system==\"Linux\"',\n    ],\n    extras_require={\n        'report-descriptor': ['hid-parser'],\n        'desktop-notifications': ['Notify (>= 0.7)'],\n        'git-commit': ['python-git-info'],\n        'test': ['pytest'],\n    },\n    package_dir={'': 'lib'},\n    packages=['keysyms', 'hidapi', 'logitech_receiver', 'solaar', 'solaar.ui', 'solaar.cli'],\n    data_files=list(_data_files()),\n    include_package_data=True,\n    scripts=_glob('bin/*'),\n)\n", "path": "setup.py"}]}