We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Systray widget crash
2022-01-27 09:48:40,366 ERROR libqtile bar.py:_configure_widget():L324 Systray widget crashed during _configure with error: ConfigError('Only one Systray can be used.')
[config.txt](https://github.com/qtile/qtile/files/7948239/config.txt)



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `libqtile/widget/systray.py`
Content:
```
1 # Copyright (c) 2010 Aldo Cortesi
2 # Copyright (c) 2010-2011 dequis
3 # Copyright (c) 2010, 2012 roger
4 # Copyright (c) 2011 Mounier Florian
5 # Copyright (c) 2011-2012, 2014 Tycho Andersen
6 # Copyright (c) 2012 dmpayton
7 # Copyright (c) 2012-2013 Craig Barnes
8 # Copyright (c) 2013 hbc
9 # Copyright (c) 2013 Tao Sauvage
10 # Copyright (c) 2014 Sean Vig
11 #
12 # Permission is hereby granted, free of charge, to any person obtaining a copy
13 # of this software and associated documentation files (the "Software"), to deal
14 # in the Software without restriction, including without limitation the rights
15 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 # copies of the Software, and to permit persons to whom the Software is
17 # furnished to do so, subject to the following conditions:
18 #
19 # The above copyright notice and this permission notice shall be included in
20 # all copies or substantial portions of the Software.
21 #
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 # SOFTWARE.
29 from typing import TYPE_CHECKING
30 
31 import xcffib
32 from xcffib.xproto import ClientMessageData, ClientMessageEvent, EventMask, SetMode
33 
34 from libqtile import bar
35 from libqtile.backend.x11 import window
36 from libqtile.confreader import ConfigError
37 from libqtile.widget import base
38 
39 if TYPE_CHECKING:
40     from typing import List, Optional
41 
42 XEMBED_PROTOCOL_VERSION = 0
43 
44 
45 class Icon(window._Window):
46     _window_mask = EventMask.StructureNotify | EventMask.PropertyChange | EventMask.Exposure
47 
48     def __init__(self, win, qtile, systray):
49         window._Window.__init__(self, win, qtile)
50         self.systray = systray
51         # win.get_name() may return None when apps provide a temporary window before the icon window
52         # we need something in self.name in order to sort icons so we use the window's WID.
53         self.name = win.get_name() or str(win.wid)
54         self.update_size()
55 
56     def __eq__(self, other):
57         if not isinstance(other, Icon):
58             return False
59 
60         return self.window.wid == other.window.wid
61 
62     def update_size(self):
63         icon_size = self.systray.icon_size
64         self.update_hints()
65 
66         width = self.hints.get("min_width", icon_size)
67         height = self.hints.get("min_height", icon_size)
68 
69         width = max(width, icon_size)
70         height = max(height, icon_size)
71 
72         if height > icon_size:
73             width = width * icon_size // height
74             height = icon_size
75 
76         self.width = width
77         self.height = height
78         return False
79 
80     def handle_PropertyNotify(self, e):  # noqa: N802
81         name = self.qtile.core.conn.atoms.get_name(e.atom)
82         if name == "_XEMBED_INFO":
83             info = self.window.get_property("_XEMBED_INFO", unpack=int)
84             if info and info[1]:
85                 self.systray.bar.draw()
86 
87         return False
88 
89     def handle_DestroyNotify(self, event):  # noqa: N802
90         wid = event.window
91         icon = self.qtile.windows_map.pop(wid)
92         self.systray.tray_icons.remove(icon)
93         self.systray.bar.draw()
94         return False
95 
96     handle_UnmapNotify = handle_DestroyNotify  # noqa: N815
97 
98 
99 class Systray(window._Window, base._Widget):
100     """
101     A widget that manages system tray.
102 
103     Only one Systray widget is allowed. Adding additional Systray
104     widgets will result in a ConfigError.
105 
106     .. note::
107         Icons will not render correctly where the bar/widget is
108         drawn with a semi-transparent background. Instead, icons
109         will be drawn with a transparent background.
110 
111         If using this widget it is therefore recommended to use
112         a fully opaque background colour or a fully transparent
113         one.
114     """
115 
116     _instances = 0
117 
118     _window_mask = EventMask.StructureNotify | EventMask.Exposure
119 
120     orientations = base.ORIENTATION_BOTH
121 
122     defaults = [
123         ("icon_size", 20, "Icon width"),
124         ("padding", 5, "Padding between icons"),
125     ]
126 
127     def __init__(self, **config):
128         base._Widget.__init__(self, bar.CALCULATED, **config)
129         self.add_defaults(Systray.defaults)
130         self.tray_icons = []
131         self.screen = 0
132         self._name = config.get("name", "systray")
133         self._wm_class: Optional[List[str]] = None
134 
135     def calculate_length(self):
136         if self.bar.horizontal:
137             length = sum(i.width for i in self.tray_icons)
138         else:
139             length = sum(i.height for i in self.tray_icons)
140         length += self.padding * len(self.tray_icons)
141         return length
142 
143     def _configure(self, qtile, bar):
144         base._Widget._configure(self, qtile, bar)
145 
146         if Systray._instances > 0:
147             raise ConfigError("Only one Systray can be used.")
148 
149         if self.configured:
150             return
151 
152         self.conn = conn = qtile.core.conn
153         win = conn.create_window(-1, -1, 1, 1)
154         window._Window.__init__(self, window.XWindow(conn, win.wid), qtile)
155         qtile.windows_map[win.wid] = self
156 
157         # window._Window.__init__ overwrites the widget name so we need to restore it
158         self.name = self._name
159 
160         # Even when we have multiple "Screen"s, we are setting up as the system
161         # tray on a particular X display, that is the screen we need to
162         # reference in the atom
163         if qtile.current_screen:
164             self.screen = qtile.current_screen.index
165         self.bar = bar
166         atoms = conn.atoms
167 
168         # We need tray to tell icons which visual to use.
169         # This needs to be the same as the bar/widget.
170         # This mainly benefits transparent bars.
171         conn.conn.core.ChangeProperty(
172             xcffib.xproto.PropMode.Replace,
173             win.wid,
174             atoms["_NET_SYSTEM_TRAY_VISUAL"],
175             xcffib.xproto.Atom.VISUALID,
176             32,
177             1,
178             [self.drawer._visual.visual_id],
179         )
180 
181         conn.conn.core.SetSelectionOwner(
182             win.wid, atoms["_NET_SYSTEM_TRAY_S{:d}".format(self.screen)], xcffib.CurrentTime
183         )
184         data = [
185             xcffib.CurrentTime,
186             atoms["_NET_SYSTEM_TRAY_S{:d}".format(self.screen)],
187             win.wid,
188             0,
189             0,
190         ]
191         union = ClientMessageData.synthetic(data, "I" * 5)
192         event = ClientMessageEvent.synthetic(
193             format=32, window=qtile.core._root.wid, type=atoms["MANAGER"], data=union
194         )
195         qtile.core._root.send_event(event, mask=EventMask.StructureNotify)
196 
197         Systray._instances += 1
198 
199     def create_mirror(self):
200         """
201         Systray cannot be mirrored as we do not use a Drawer object to render icons.
202 
203         Return itself so that, when the bar tries to configure it again, a ConfigError
204         is raised.
205         """
206         return self
207 
208     def handle_ClientMessage(self, event):  # noqa: N802
209         atoms = self.conn.atoms
210 
211         opcode = event.type
212         data = event.data.data32
213         message = data[1]
214         wid = data[2]
215 
216         parent = self.bar.window.window
217 
218         if opcode == atoms["_NET_SYSTEM_TRAY_OPCODE"] and message == 0:
219             w = window.XWindow(self.conn, wid)
220             icon = Icon(w, self.qtile, self)
221             if icon not in self.tray_icons:
222                 self.tray_icons.append(icon)
223                 self.tray_icons.sort(key=lambda icon: icon.name)
224                 self.qtile.windows_map[wid] = icon
225 
226             self.conn.conn.core.ChangeSaveSet(SetMode.Insert, wid)
227             self.conn.conn.core.ReparentWindow(wid, parent.wid, 0, 0)
228             self.conn.conn.flush()
229 
230             info = icon.window.get_property("_XEMBED_INFO", unpack=int)
231 
232             if not info:
233                 self.bar.draw()
234                 return False
235 
236             if info[1]:
237                 self.bar.draw()
238 
239         return False
240 
241     def draw(self):
242         offset = self.padding
243         self.drawer.clear(self.background or self.bar.background)
244         self.drawer.draw(offsetx=self.offset, offsety=self.offsety, width=self.length)
245         for pos, icon in enumerate(self.tray_icons):
246             icon.window.set_attribute(backpixmap=self.drawer.pixmap)
247             if self.bar.horizontal:
248                 xoffset = self.offsetx + offset
249                 yoffset = self.bar.height // 2 - self.icon_size // 2 + self.offsety
250                 step = icon.width
251             else:
252                 xoffset = self.bar.width // 2 - self.icon_size // 2 + self.offsetx
253                 yoffset = self.offsety + offset
254                 step = icon.height
255 
256             icon.place(xoffset, yoffset, icon.width, self.icon_size, 0, None)
257             if icon.hidden:
258                 icon.unhide()
259                 data = [
260                     self.conn.atoms["_XEMBED_EMBEDDED_NOTIFY"],
261                     xcffib.xproto.Time.CurrentTime,
262                     0,
263                     self.bar.window.wid,
264                     XEMBED_PROTOCOL_VERSION,
265                 ]
266                 u = xcffib.xproto.ClientMessageData.synthetic(data, "I" * 5)
267                 event = xcffib.xproto.ClientMessageEvent.synthetic(
268                     format=32, window=icon.wid, type=self.conn.atoms["_XEMBED"], data=u
269                 )
270                 self.window.send_event(event)
271 
272             offset += step + self.padding
273 
274     def finalize(self):
275         base._Widget.finalize(self)
276         atoms = self.conn.atoms
277         self.conn.conn.core.SetSelectionOwner(
278             0,
279             atoms["_NET_SYSTEM_TRAY_S{:d}".format(self.screen)],
280             xcffib.CurrentTime,
281         )
282         self.hide()
283 
284         root = self.qtile.core._root.wid
285         for icon in self.tray_icons:
286             self.conn.conn.core.ReparentWindow(icon.window.wid, root, 0, 0)
287         self.conn.conn.flush()
288 
289         del self.qtile.windows_map[self.wid]
290         self.conn.conn.core.DestroyWindow(self.wid)
291 
292         Systray._instances -= 1
293
```
--- 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/libqtile/widget/systray.py b/libqtile/widget/systray.py
--- a/libqtile/widget/systray.py
+++ b/libqtile/widget/systray.py
@@ -143,12 +143,12 @@
     def _configure(self, qtile, bar):
         base._Widget._configure(self, qtile, bar)
 
-        if Systray._instances > 0:
-            raise ConfigError("Only one Systray can be used.")
-
         if self.configured:
             return
 
+        if Systray._instances > 0:
+            raise ConfigError("Only one Systray can be used.")
+
         self.conn = conn = qtile.core.conn
         win = conn.create_window(-1, -1, 1, 1)
         window._Window.__init__(self, window.XWindow(conn, win.wid), qtile)
@@ -200,10 +200,10 @@
         """
         Systray cannot be mirrored as we do not use a Drawer object to render icons.
 
-        Return itself so that, when the bar tries to configure it again, a ConfigError
-        is raised.
+        Return new, unconfigured instance so that, when the bar tries to configure it
+        again, a ConfigError is raised.
         """
-        return self
+        return Systray()
 
     def handle_ClientMessage(self, event):  # noqa: N802
         atoms = self.conn.atoms

{"golden_diff": "diff --git a/libqtile/widget/systray.py b/libqtile/widget/systray.py\n--- a/libqtile/widget/systray.py\n+++ b/libqtile/widget/systray.py\n@@ -143,12 +143,12 @@\n     def _configure(self, qtile, bar):\n         base._Widget._configure(self, qtile, bar)\n \n-        if Systray._instances > 0:\n-            raise ConfigError(\"Only one Systray can be used.\")\n-\n         if self.configured:\n             return\n \n+        if Systray._instances > 0:\n+            raise ConfigError(\"Only one Systray can be used.\")\n+\n         self.conn = conn = qtile.core.conn\n         win = conn.create_window(-1, -1, 1, 1)\n         window._Window.__init__(self, window.XWindow(conn, win.wid), qtile)\n@@ -200,10 +200,10 @@\n         \"\"\"\n         Systray cannot be mirrored as we do not use a Drawer object to render icons.\n \n-        Return itself so that, when the bar tries to configure it again, a ConfigError\n-        is raised.\n+        Return new, unconfigured instance so that, when the bar tries to configure it\n+        again, a ConfigError is raised.\n         \"\"\"\n-        return self\n+        return Systray()\n \n     def handle_ClientMessage(self, event):  # noqa: N802\n         atoms = self.conn.atoms\n", "issue": "Systray widget crash\n2022-01-27 09:48:40,366 ERROR libqtile bar.py:_configure_widget():L324 Systray widget crashed during _configure with error: ConfigError('Only one Systray can be used.')\r\n[config.txt](https://github.com/qtile/qtile/files/7948239/config.txt)\r\n\r\n\n", "before_files": [{"content": "# Copyright (c) 2010 Aldo Cortesi\n# Copyright (c) 2010-2011 dequis\n# Copyright (c) 2010, 2012 roger\n# Copyright (c) 2011 Mounier Florian\n# Copyright (c) 2011-2012, 2014 Tycho Andersen\n# Copyright (c) 2012 dmpayton\n# Copyright (c) 2012-2013 Craig Barnes\n# Copyright (c) 2013 hbc\n# Copyright (c) 2013 Tao Sauvage\n# Copyright (c) 2014 Sean Vig\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\nfrom typing import TYPE_CHECKING\n\nimport xcffib\nfrom xcffib.xproto import ClientMessageData, ClientMessageEvent, EventMask, SetMode\n\nfrom libqtile import bar\nfrom libqtile.backend.x11 import window\nfrom libqtile.confreader import ConfigError\nfrom libqtile.widget import base\n\nif TYPE_CHECKING:\n    from typing import List, Optional\n\nXEMBED_PROTOCOL_VERSION = 0\n\n\nclass Icon(window._Window):\n    _window_mask = EventMask.StructureNotify | EventMask.PropertyChange | EventMask.Exposure\n\n    def __init__(self, win, qtile, systray):\n        window._Window.__init__(self, win, qtile)\n        self.systray = systray\n        # win.get_name() may return None when apps provide a temporary window before the icon window\n        # we need something in self.name in order to sort icons so we use the window's WID.\n        self.name = win.get_name() or str(win.wid)\n        self.update_size()\n\n    def __eq__(self, other):\n        if not isinstance(other, Icon):\n            return False\n\n        return self.window.wid == other.window.wid\n\n    def update_size(self):\n        icon_size = self.systray.icon_size\n        self.update_hints()\n\n        width = self.hints.get(\"min_width\", icon_size)\n        height = self.hints.get(\"min_height\", icon_size)\n\n        width = max(width, icon_size)\n        height = max(height, icon_size)\n\n        if height > icon_size:\n            width = width * icon_size // height\n            height = icon_size\n\n        self.width = width\n        self.height = height\n        return False\n\n    def handle_PropertyNotify(self, e):  # noqa: N802\n        name = self.qtile.core.conn.atoms.get_name(e.atom)\n        if name == \"_XEMBED_INFO\":\n            info = self.window.get_property(\"_XEMBED_INFO\", unpack=int)\n            if info and info[1]:\n                self.systray.bar.draw()\n\n        return False\n\n    def handle_DestroyNotify(self, event):  # noqa: N802\n        wid = event.window\n        icon = self.qtile.windows_map.pop(wid)\n        self.systray.tray_icons.remove(icon)\n        self.systray.bar.draw()\n        return False\n\n    handle_UnmapNotify = handle_DestroyNotify  # noqa: N815\n\n\nclass Systray(window._Window, base._Widget):\n    \"\"\"\n    A widget that manages system tray.\n\n    Only one Systray widget is allowed. Adding additional Systray\n    widgets will result in a ConfigError.\n\n    .. note::\n        Icons will not render correctly where the bar/widget is\n        drawn with a semi-transparent background. Instead, icons\n        will be drawn with a transparent background.\n\n        If using this widget it is therefore recommended to use\n        a fully opaque background colour or a fully transparent\n        one.\n    \"\"\"\n\n    _instances = 0\n\n    _window_mask = EventMask.StructureNotify | EventMask.Exposure\n\n    orientations = base.ORIENTATION_BOTH\n\n    defaults = [\n        (\"icon_size\", 20, \"Icon width\"),\n        (\"padding\", 5, \"Padding between icons\"),\n    ]\n\n    def __init__(self, **config):\n        base._Widget.__init__(self, bar.CALCULATED, **config)\n        self.add_defaults(Systray.defaults)\n        self.tray_icons = []\n        self.screen = 0\n        self._name = config.get(\"name\", \"systray\")\n        self._wm_class: Optional[List[str]] = None\n\n    def calculate_length(self):\n        if self.bar.horizontal:\n            length = sum(i.width for i in self.tray_icons)\n        else:\n            length = sum(i.height for i in self.tray_icons)\n        length += self.padding * len(self.tray_icons)\n        return length\n\n    def _configure(self, qtile, bar):\n        base._Widget._configure(self, qtile, bar)\n\n        if Systray._instances > 0:\n            raise ConfigError(\"Only one Systray can be used.\")\n\n        if self.configured:\n            return\n\n        self.conn = conn = qtile.core.conn\n        win = conn.create_window(-1, -1, 1, 1)\n        window._Window.__init__(self, window.XWindow(conn, win.wid), qtile)\n        qtile.windows_map[win.wid] = self\n\n        # window._Window.__init__ overwrites the widget name so we need to restore it\n        self.name = self._name\n\n        # Even when we have multiple \"Screen\"s, we are setting up as the system\n        # tray on a particular X display, that is the screen we need to\n        # reference in the atom\n        if qtile.current_screen:\n            self.screen = qtile.current_screen.index\n        self.bar = bar\n        atoms = conn.atoms\n\n        # We need tray to tell icons which visual to use.\n        # This needs to be the same as the bar/widget.\n        # This mainly benefits transparent bars.\n        conn.conn.core.ChangeProperty(\n            xcffib.xproto.PropMode.Replace,\n            win.wid,\n            atoms[\"_NET_SYSTEM_TRAY_VISUAL\"],\n            xcffib.xproto.Atom.VISUALID,\n            32,\n            1,\n            [self.drawer._visual.visual_id],\n        )\n\n        conn.conn.core.SetSelectionOwner(\n            win.wid, atoms[\"_NET_SYSTEM_TRAY_S{:d}\".format(self.screen)], xcffib.CurrentTime\n        )\n        data = [\n            xcffib.CurrentTime,\n            atoms[\"_NET_SYSTEM_TRAY_S{:d}\".format(self.screen)],\n            win.wid,\n            0,\n            0,\n        ]\n        union = ClientMessageData.synthetic(data, \"I\" * 5)\n        event = ClientMessageEvent.synthetic(\n            format=32, window=qtile.core._root.wid, type=atoms[\"MANAGER\"], data=union\n        )\n        qtile.core._root.send_event(event, mask=EventMask.StructureNotify)\n\n        Systray._instances += 1\n\n    def create_mirror(self):\n        \"\"\"\n        Systray cannot be mirrored as we do not use a Drawer object to render icons.\n\n        Return itself so that, when the bar tries to configure it again, a ConfigError\n        is raised.\n        \"\"\"\n        return self\n\n    def handle_ClientMessage(self, event):  # noqa: N802\n        atoms = self.conn.atoms\n\n        opcode = event.type\n        data = event.data.data32\n        message = data[1]\n        wid = data[2]\n\n        parent = self.bar.window.window\n\n        if opcode == atoms[\"_NET_SYSTEM_TRAY_OPCODE\"] and message == 0:\n            w = window.XWindow(self.conn, wid)\n            icon = Icon(w, self.qtile, self)\n            if icon not in self.tray_icons:\n                self.tray_icons.append(icon)\n                self.tray_icons.sort(key=lambda icon: icon.name)\n                self.qtile.windows_map[wid] = icon\n\n            self.conn.conn.core.ChangeSaveSet(SetMode.Insert, wid)\n            self.conn.conn.core.ReparentWindow(wid, parent.wid, 0, 0)\n            self.conn.conn.flush()\n\n            info = icon.window.get_property(\"_XEMBED_INFO\", unpack=int)\n\n            if not info:\n                self.bar.draw()\n                return False\n\n            if info[1]:\n                self.bar.draw()\n\n        return False\n\n    def draw(self):\n        offset = self.padding\n        self.drawer.clear(self.background or self.bar.background)\n        self.drawer.draw(offsetx=self.offset, offsety=self.offsety, width=self.length)\n        for pos, icon in enumerate(self.tray_icons):\n            icon.window.set_attribute(backpixmap=self.drawer.pixmap)\n            if self.bar.horizontal:\n                xoffset = self.offsetx + offset\n                yoffset = self.bar.height // 2 - self.icon_size // 2 + self.offsety\n                step = icon.width\n            else:\n                xoffset = self.bar.width // 2 - self.icon_size // 2 + self.offsetx\n                yoffset = self.offsety + offset\n                step = icon.height\n\n            icon.place(xoffset, yoffset, icon.width, self.icon_size, 0, None)\n            if icon.hidden:\n                icon.unhide()\n                data = [\n                    self.conn.atoms[\"_XEMBED_EMBEDDED_NOTIFY\"],\n                    xcffib.xproto.Time.CurrentTime,\n                    0,\n                    self.bar.window.wid,\n                    XEMBED_PROTOCOL_VERSION,\n                ]\n                u = xcffib.xproto.ClientMessageData.synthetic(data, \"I\" * 5)\n                event = xcffib.xproto.ClientMessageEvent.synthetic(\n                    format=32, window=icon.wid, type=self.conn.atoms[\"_XEMBED\"], data=u\n                )\n                self.window.send_event(event)\n\n            offset += step + self.padding\n\n    def finalize(self):\n        base._Widget.finalize(self)\n        atoms = self.conn.atoms\n        self.conn.conn.core.SetSelectionOwner(\n            0,\n            atoms[\"_NET_SYSTEM_TRAY_S{:d}\".format(self.screen)],\n            xcffib.CurrentTime,\n        )\n        self.hide()\n\n        root = self.qtile.core._root.wid\n        for icon in self.tray_icons:\n            self.conn.conn.core.ReparentWindow(icon.window.wid, root, 0, 0)\n        self.conn.conn.flush()\n\n        del self.qtile.windows_map[self.wid]\n        self.conn.conn.core.DestroyWindow(self.wid)\n\n        Systray._instances -= 1\n", "path": "libqtile/widget/systray.py"}], "after_files": [{"content": "# Copyright (c) 2010 Aldo Cortesi\n# Copyright (c) 2010-2011 dequis\n# Copyright (c) 2010, 2012 roger\n# Copyright (c) 2011 Mounier Florian\n# Copyright (c) 2011-2012, 2014 Tycho Andersen\n# Copyright (c) 2012 dmpayton\n# Copyright (c) 2012-2013 Craig Barnes\n# Copyright (c) 2013 hbc\n# Copyright (c) 2013 Tao Sauvage\n# Copyright (c) 2014 Sean Vig\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in\n# all copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\nfrom typing import TYPE_CHECKING\n\nimport xcffib\nfrom xcffib.xproto import ClientMessageData, ClientMessageEvent, EventMask, SetMode\n\nfrom libqtile import bar\nfrom libqtile.backend.x11 import window\nfrom libqtile.confreader import ConfigError\nfrom libqtile.widget import base\n\nif TYPE_CHECKING:\n    from typing import List, Optional\n\nXEMBED_PROTOCOL_VERSION = 0\n\n\nclass Icon(window._Window):\n    _window_mask = EventMask.StructureNotify | EventMask.PropertyChange | EventMask.Exposure\n\n    def __init__(self, win, qtile, systray):\n        window._Window.__init__(self, win, qtile)\n        self.systray = systray\n        # win.get_name() may return None when apps provide a temporary window before the icon window\n        # we need something in self.name in order to sort icons so we use the window's WID.\n        self.name = win.get_name() or str(win.wid)\n        self.update_size()\n\n    def __eq__(self, other):\n        if not isinstance(other, Icon):\n            return False\n\n        return self.window.wid == other.window.wid\n\n    def update_size(self):\n        icon_size = self.systray.icon_size\n        self.update_hints()\n\n        width = self.hints.get(\"min_width\", icon_size)\n        height = self.hints.get(\"min_height\", icon_size)\n\n        width = max(width, icon_size)\n        height = max(height, icon_size)\n\n        if height > icon_size:\n            width = width * icon_size // height\n            height = icon_size\n\n        self.width = width\n        self.height = height\n        return False\n\n    def handle_PropertyNotify(self, e):  # noqa: N802\n        name = self.qtile.core.conn.atoms.get_name(e.atom)\n        if name == \"_XEMBED_INFO\":\n            info = self.window.get_property(\"_XEMBED_INFO\", unpack=int)\n            if info and info[1]:\n                self.systray.bar.draw()\n\n        return False\n\n    def handle_DestroyNotify(self, event):  # noqa: N802\n        wid = event.window\n        icon = self.qtile.windows_map.pop(wid)\n        self.systray.tray_icons.remove(icon)\n        self.systray.bar.draw()\n        return False\n\n    handle_UnmapNotify = handle_DestroyNotify  # noqa: N815\n\n\nclass Systray(window._Window, base._Widget):\n    \"\"\"\n    A widget that manages system tray.\n\n    Only one Systray widget is allowed. Adding additional Systray\n    widgets will result in a ConfigError.\n\n    .. note::\n        Icons will not render correctly where the bar/widget is\n        drawn with a semi-transparent background. Instead, icons\n        will be drawn with a transparent background.\n\n        If using this widget it is therefore recommended to use\n        a fully opaque background colour or a fully transparent\n        one.\n    \"\"\"\n\n    _instances = 0\n\n    _window_mask = EventMask.StructureNotify | EventMask.Exposure\n\n    orientations = base.ORIENTATION_BOTH\n\n    defaults = [\n        (\"icon_size\", 20, \"Icon width\"),\n        (\"padding\", 5, \"Padding between icons\"),\n    ]\n\n    def __init__(self, **config):\n        base._Widget.__init__(self, bar.CALCULATED, **config)\n        self.add_defaults(Systray.defaults)\n        self.tray_icons = []\n        self.screen = 0\n        self._name = config.get(\"name\", \"systray\")\n        self._wm_class: Optional[List[str]] = None\n\n    def calculate_length(self):\n        if self.bar.horizontal:\n            length = sum(i.width for i in self.tray_icons)\n        else:\n            length = sum(i.height for i in self.tray_icons)\n        length += self.padding * len(self.tray_icons)\n        return length\n\n    def _configure(self, qtile, bar):\n        base._Widget._configure(self, qtile, bar)\n\n        if self.configured:\n            return\n\n        if Systray._instances > 0:\n            raise ConfigError(\"Only one Systray can be used.\")\n\n        self.conn = conn = qtile.core.conn\n        win = conn.create_window(-1, -1, 1, 1)\n        window._Window.__init__(self, window.XWindow(conn, win.wid), qtile)\n        qtile.windows_map[win.wid] = self\n\n        # window._Window.__init__ overwrites the widget name so we need to restore it\n        self.name = self._name\n\n        # Even when we have multiple \"Screen\"s, we are setting up as the system\n        # tray on a particular X display, that is the screen we need to\n        # reference in the atom\n        if qtile.current_screen:\n            self.screen = qtile.current_screen.index\n        self.bar = bar\n        atoms = conn.atoms\n\n        # We need tray to tell icons which visual to use.\n        # This needs to be the same as the bar/widget.\n        # This mainly benefits transparent bars.\n        conn.conn.core.ChangeProperty(\n            xcffib.xproto.PropMode.Replace,\n            win.wid,\n            atoms[\"_NET_SYSTEM_TRAY_VISUAL\"],\n            xcffib.xproto.Atom.VISUALID,\n            32,\n            1,\n            [self.drawer._visual.visual_id],\n        )\n\n        conn.conn.core.SetSelectionOwner(\n            win.wid, atoms[\"_NET_SYSTEM_TRAY_S{:d}\".format(self.screen)], xcffib.CurrentTime\n        )\n        data = [\n            xcffib.CurrentTime,\n            atoms[\"_NET_SYSTEM_TRAY_S{:d}\".format(self.screen)],\n            win.wid,\n            0,\n            0,\n        ]\n        union = ClientMessageData.synthetic(data, \"I\" * 5)\n        event = ClientMessageEvent.synthetic(\n            format=32, window=qtile.core._root.wid, type=atoms[\"MANAGER\"], data=union\n        )\n        qtile.core._root.send_event(event, mask=EventMask.StructureNotify)\n\n        Systray._instances += 1\n\n    def create_mirror(self):\n        \"\"\"\n        Systray cannot be mirrored as we do not use a Drawer object to render icons.\n\n        Return new, unconfigured instance so that, when the bar tries to configure it\n        again, a ConfigError is raised.\n        \"\"\"\n        return Systray()\n\n    def handle_ClientMessage(self, event):  # noqa: N802\n        atoms = self.conn.atoms\n\n        opcode = event.type\n        data = event.data.data32\n        message = data[1]\n        wid = data[2]\n\n        parent = self.bar.window.window\n\n        if opcode == atoms[\"_NET_SYSTEM_TRAY_OPCODE\"] and message == 0:\n            w = window.XWindow(self.conn, wid)\n            icon = Icon(w, self.qtile, self)\n            if icon not in self.tray_icons:\n                self.tray_icons.append(icon)\n                self.tray_icons.sort(key=lambda icon: icon.name)\n                self.qtile.windows_map[wid] = icon\n\n            self.conn.conn.core.ChangeSaveSet(SetMode.Insert, wid)\n            self.conn.conn.core.ReparentWindow(wid, parent.wid, 0, 0)\n            self.conn.conn.flush()\n\n            info = icon.window.get_property(\"_XEMBED_INFO\", unpack=int)\n\n            if not info:\n                self.bar.draw()\n                return False\n\n            if info[1]:\n                self.bar.draw()\n\n        return False\n\n    def draw(self):\n        offset = self.padding\n        self.drawer.clear(self.background or self.bar.background)\n        self.drawer.draw(offsetx=self.offset, offsety=self.offsety, width=self.length)\n        for pos, icon in enumerate(self.tray_icons):\n            icon.window.set_attribute(backpixmap=self.drawer.pixmap)\n            if self.bar.horizontal:\n                xoffset = self.offsetx + offset\n                yoffset = self.bar.height // 2 - self.icon_size // 2 + self.offsety\n                step = icon.width\n            else:\n                xoffset = self.bar.width // 2 - self.icon_size // 2 + self.offsetx\n                yoffset = self.offsety + offset\n                step = icon.height\n\n            icon.place(xoffset, yoffset, icon.width, self.icon_size, 0, None)\n            if icon.hidden:\n                icon.unhide()\n                data = [\n                    self.conn.atoms[\"_XEMBED_EMBEDDED_NOTIFY\"],\n                    xcffib.xproto.Time.CurrentTime,\n                    0,\n                    self.bar.window.wid,\n                    XEMBED_PROTOCOL_VERSION,\n                ]\n                u = xcffib.xproto.ClientMessageData.synthetic(data, \"I\" * 5)\n                event = xcffib.xproto.ClientMessageEvent.synthetic(\n                    format=32, window=icon.wid, type=self.conn.atoms[\"_XEMBED\"], data=u\n                )\n                self.window.send_event(event)\n\n            offset += step + self.padding\n\n    def finalize(self):\n        base._Widget.finalize(self)\n        atoms = self.conn.atoms\n        self.conn.conn.core.SetSelectionOwner(\n            0,\n            atoms[\"_NET_SYSTEM_TRAY_S{:d}\".format(self.screen)],\n            xcffib.CurrentTime,\n        )\n        self.hide()\n\n        root = self.qtile.core._root.wid\n        for icon in self.tray_icons:\n            self.conn.conn.core.ReparentWindow(icon.window.wid, root, 0, 0)\n        self.conn.conn.flush()\n\n        del self.qtile.windows_map[self.wid]\n        self.conn.conn.core.DestroyWindow(self.wid)\n\n        Systray._instances -= 1\n", "path": "libqtile/widget/systray.py"}]}