prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Ampersand in window name return an error
  ### The issue:
  
  ## Qtile version
  0.22.1
  
  ## Issue
  Ampersands in window name return an error with the WindowTabs widget
  ```
  Traceback (most recent call last):
    File "/usr/lib/python3.10/site-packages/libqtile/hook.py", line 404, in fire
      i(*args, **kwargs)
    File "/usr/lib/python3.10/site-packages/libqtile/widget/windowtabs.py", line 82, in update
      self.text = self.separator.join(names)
    File "/usr/lib/python3.10/site-packages/libqtile/widget/base.py", line 483, in text
      self.layout.text = self.formatted_text
    File "/usr/lib/python3.10/site-packages/libqtile/drawer.py", line 72, in text
      attrlist, value, accel_char = pangocffi.parse_markup(value)
    File "/usr/lib/python3.10/site-packages/libqtile/pangocffi.py", line 186, in parse_markup
      raise Exception("parse_markup() failed for %s" % value)
  Exception: parse_markup() failed for b'<b>Search \xc2\xb7 & \xe2\x80\x94 Mozilla Firefox</b>'
  ```
  
  The same goes for the Mpris2 widget
  ```
  2023-01-07 17:07:22,656 ERROR libqtile loop.py:_handle_exception():L63 parse_markup() failed for b'Fireman & Dancer - Royal Republic'
  NoneType: None
  2023-01-07 17:07:22,656 ERROR libqtile loop.py:_handle_exception():L63 parse_markup() failed for b'Fireman & Dancer - Royal Republic'
  NoneType: None
  ````
  
  Found a similar issue [#1685](https://github.com/qtile/qtile/issues/1685) but for the WindowName widget
  
  ### Required:
  
  - [X] I have searched past issues to see if this bug has already been reported.
  
  </issue>
  <code>
  [start of libqtile/widget/windowtabs.py]
  1 # Copyright (c) 2012-2013 Craig Barnes
  2 # Copyright (c) 2012 roger
  3 # Copyright (c) 2012, 2014 Tycho Andersen
  4 # Copyright (c) 2014 Sean Vig
  5 # Copyright (c) 2014 Adi Sieker
  6 #
  7 # Permission is hereby granted, free of charge, to any person obtaining a copy
  8 # of this software and associated documentation files (the "Software"), to deal
  9 # in the Software without restriction, including without limitation the rights
  10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11 # copies of the Software, and to permit persons to whom the Software is
  12 # furnished to do so, subject to the following conditions:
  13 #
  14 # The above copyright notice and this permission notice shall be included in
  15 # all copies or substantial portions of the Software.
  16 #
  17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23 # SOFTWARE.
  24 
  25 from libqtile import bar, hook
  26 from libqtile.log_utils import logger
  27 from libqtile.widget import base
  28 
  29 
  30 class WindowTabs(base._TextBox):
  31     """
  32     Displays the name of each window in the current group.
  33     Contrary to TaskList this is not an interactive widget.
  34     The window that currently has focus is highlighted.
  35     """
  36 
  37     defaults = [
  38         ("separator", " | ", "Task separator text."),
  39         ("selected", ("<b>", "</b>"), "Selected task indicator"),
  40         (
  41             "parse_text",
  42             None,
  43             "Function to parse and modify window names. "
  44             "e.g. function in config that removes excess "
  45             "strings from window name: "
  46             "def my_func(text)"
  47             '    for string in [" - Chromium", " - Firefox"]:'
  48             '        text = text.replace(string, "")'
  49             "   return text"
  50             "then set option parse_text=my_func",
  51         ),
  52     ]
  53 
  54     def __init__(self, **config):
  55         width = config.pop("width", bar.STRETCH)
  56         base._TextBox.__init__(self, width=width, **config)
  57         self.add_defaults(WindowTabs.defaults)
  58         if not isinstance(self.selected, (tuple, list)):
  59             self.selected = (self.selected, self.selected)
  60 
  61     def _configure(self, qtile, bar):
  62         base._TextBox._configure(self, qtile, bar)
  63         hook.subscribe.client_name_updated(self.update)
  64         hook.subscribe.focus_change(self.update)
  65         hook.subscribe.float_change(self.update)
  66         self.add_callbacks({"Button1": self.bar.screen.group.next_window})
  67 
  68     def update(self, *args):
  69         names = []
  70         for w in self.bar.screen.group.windows:
  71             state = ""
  72             if w.maximized:
  73                 state = "[] "
  74             elif w.minimized:
  75                 state = "_ "
  76             elif w.floating:
  77                 state = "V "
  78             task = "%s%s" % (state, w.name if w and w.name else " ")
  79             if w is self.bar.screen.group.current_window:
  80                 task = task.join(self.selected)
  81             names.append(task)
  82         self.text = self.separator.join(names)
  83         if callable(self.parse_text):
  84             try:
  85                 self.text = self.parse_text(self.text)
  86             except:  # noqa: E722
  87                 logger.exception("parse_text function failed:")
  88         self.bar.draw()
  89
  [end of libqtile/widget/windowtabs.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/libqtile/widget/windowtabs.py b/libqtile/widget/windowtabs.py
  --- a/libqtile/widget/windowtabs.py
  +++ b/libqtile/widget/windowtabs.py
  @@ -22,7 +22,7 @@
   # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   # SOFTWARE.
   
  -from libqtile import bar, hook
  +from libqtile import bar, hook, pangocffi
   from libqtile.log_utils import logger
   from libqtile.widget import base
   
  @@ -76,6 +76,7 @@
               elif w.floating:
                   state = "V "
               task = "%s%s" % (state, w.name if w and w.name else " ")
  +            task = pangocffi.markup_escape_text(task)
               if w is self.bar.screen.group.current_window:
                   task = task.join(self.selected)
               names.append(task)
  
verification_info: {"golden_diff": "diff --git a/libqtile/widget/windowtabs.py b/libqtile/widget/windowtabs.py\n--- a/libqtile/widget/windowtabs.py\n+++ b/libqtile/widget/windowtabs.py\n@@ -22,7 +22,7 @@\n # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n # SOFTWARE.\n \n-from libqtile import bar, hook\n+from libqtile import bar, hook, pangocffi\n from libqtile.log_utils import logger\n from libqtile.widget import base\n \n@@ -76,6 +76,7 @@\n             elif w.floating:\n                 state = \"V \"\n             task = \"%s%s\" % (state, w.name if w and w.name else \" \")\n+            task = pangocffi.markup_escape_text(task)\n             if w is self.bar.screen.group.current_window:\n                 task = task.join(self.selected)\n             names.append(task)\n", "issue": "Ampersand in window name return an error\n### The issue:\n\n## Qtile version\r\n0.22.1\r\n\r\n## Issue\r\nAmpersands in window name return an error with the WindowTabs widget\r\n```\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.10/site-packages/libqtile/hook.py\", line 404, in fire\r\n    i(*args, **kwargs)\r\n  File \"/usr/lib/python3.10/site-packages/libqtile/widget/windowtabs.py\", line 82, in update\r\n    self.text = self.separator.join(names)\r\n  File \"/usr/lib/python3.10/site-packages/libqtile/widget/base.py\", line 483, in text\r\n    self.layout.text = self.formatted_text\r\n  File \"/usr/lib/python3.10/site-packages/libqtile/drawer.py\", line 72, in text\r\n    attrlist, value, accel_char = pangocffi.parse_markup(value)\r\n  File \"/usr/lib/python3.10/site-packages/libqtile/pangocffi.py\", line 186, in parse_markup\r\n    raise Exception(\"parse_markup() failed for %s\" % value)\r\nException: parse_markup() failed for b'<b>Search \\xc2\\xb7 & \\xe2\\x80\\x94 Mozilla Firefox</b>'\r\n```\r\n\r\nThe same goes for the Mpris2 widget\r\n```\r\n2023-01-07 17:07:22,656 ERROR libqtile loop.py:_handle_exception():L63 parse_markup() failed for b'Fireman & Dancer - Royal Republic'\r\nNoneType: None\r\n2023-01-07 17:07:22,656 ERROR libqtile loop.py:_handle_exception():L63 parse_markup() failed for b'Fireman & Dancer - Royal Republic'\r\nNoneType: None\r\n````\r\n\r\nFound a similar issue [#1685](https://github.com/qtile/qtile/issues/1685) but for the WindowName widget\n\n### Required:\n\n- [X] I have searched past issues to see if this bug has already been reported.\n", "before_files": [{"content": "# Copyright (c) 2012-2013 Craig Barnes\n# Copyright (c) 2012 roger\n# Copyright (c) 2012, 2014 Tycho Andersen\n# Copyright (c) 2014 Sean Vig\n# Copyright (c) 2014 Adi Sieker\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.\n\nfrom libqtile import bar, hook\nfrom libqtile.log_utils import logger\nfrom libqtile.widget import base\n\n\nclass WindowTabs(base._TextBox):\n    \"\"\"\n    Displays the name of each window in the current group.\n    Contrary to TaskList this is not an interactive widget.\n    The window that currently has focus is highlighted.\n    \"\"\"\n\n    defaults = [\n        (\"separator\", \" | \", \"Task separator text.\"),\n        (\"selected\", (\"<b>\", \"</b>\"), \"Selected task indicator\"),\n        (\n            \"parse_text\",\n            None,\n            \"Function to parse and modify window names. \"\n            \"e.g. function in config that removes excess \"\n            \"strings from window name: \"\n            \"def my_func(text)\"\n            '    for string in [\" - Chromium\", \" - Firefox\"]:'\n            '        text = text.replace(string, \"\")'\n            \"   return text\"\n            \"then set option parse_text=my_func\",\n        ),\n    ]\n\n    def __init__(self, **config):\n        width = config.pop(\"width\", bar.STRETCH)\n        base._TextBox.__init__(self, width=width, **config)\n        self.add_defaults(WindowTabs.defaults)\n        if not isinstance(self.selected, (tuple, list)):\n            self.selected = (self.selected, self.selected)\n\n    def _configure(self, qtile, bar):\n        base._TextBox._configure(self, qtile, bar)\n        hook.subscribe.client_name_updated(self.update)\n        hook.subscribe.focus_change(self.update)\n        hook.subscribe.float_change(self.update)\n        self.add_callbacks({\"Button1\": self.bar.screen.group.next_window})\n\n    def update(self, *args):\n        names = []\n        for w in self.bar.screen.group.windows:\n            state = \"\"\n            if w.maximized:\n                state = \"[] \"\n            elif w.minimized:\n                state = \"_ \"\n            elif w.floating:\n                state = \"V \"\n            task = \"%s%s\" % (state, w.name if w and w.name else \" \")\n            if w is self.bar.screen.group.current_window:\n                task = task.join(self.selected)\n            names.append(task)\n        self.text = self.separator.join(names)\n        if callable(self.parse_text):\n            try:\n                self.text = self.parse_text(self.text)\n            except:  # noqa: E722\n                logger.exception(\"parse_text function failed:\")\n        self.bar.draw()\n", "path": "libqtile/widget/windowtabs.py"}]}