diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,34 @@
 
+0.5.0.0
+=======
+
+Enhancements:
+
+* Overall performance has been improved due to a change from the lazy to
+  CPS-based Writer monad. (thanks Andrzej Rybczak)
+* Terminal titles are now UTF-8 encoded. (thanks Andrzej Rybczak)
+
+0.4.0.0
+=======
+
+* Added support for terminfo operators "*", "/", and "m" (thanks Chris
+  Forno)
+
+0.3.0.0
+=======
+
+* Added support in `Input.Mouse` for parsing horizontal scrolling inputs
+  to work with Vty 6.6.
+* Improved the `vty-unix-build-width-table` tool (thanks Eric Mertens)
+  to make it more robust when the terminal fails to recognize the
+  `getCursorPosition` escape sequence.
+
 0.2.0.0
 =======
 
 API changes:
+* The `buildOutput` function in `Graphics.Vty.Platform.Unix.Output` now
+  takes a new first argument of type `VtyUserConfig`.
 * The `settingColorMode` field of `UnixSettings` was removed in favor of
   Vty 6.1's new `configPreferredColorMode` field of the `VtyUserConfig`
   type. This package now uses that setting if present; otherwise it does
diff --git a/src/Data/Terminfo/Eval.hs b/src/Data/Terminfo/Eval.hs
--- a/src/Data/Terminfo/Eval.hs
+++ b/src/Data/Terminfo/Eval.hs
@@ -16,7 +16,7 @@
 
 import Control.Monad
 import Control.Monad.State.Strict
-import Control.Monad.Writer
+import Control.Monad.Writer.CPS
 
 import Data.Bits ((.|.), (.&.), xor)
 import Data.List
@@ -120,6 +120,18 @@
     v1 <- pop
     v0 <- pop
     push $ v0 - v1
+writeCapOp ArithMult = do
+    v1 <- pop
+    v0 <- pop
+    push $ v0 * v1
+writeCapOp ArithDiv = do
+    v1 <- pop
+    v0 <- pop
+    push $ v0 `div` v1
+writeCapOp ArithMod = do
+    v1 <- pop
+    v0 <- pop
+    push $ v0 `mod` v1
 writeCapOp CompareEq = do
     v1 <- pop
     v0 <- pop
diff --git a/src/Data/Terminfo/Parse.hs b/src/Data/Terminfo/Parse.hs
--- a/src/Data/Terminfo/Parse.hs
+++ b/src/Data/Terminfo/Parse.hs
@@ -61,7 +61,7 @@
       , conditionalParts :: ![(CapOps, CapOps)]
       }
     | BitwiseOr | BitwiseXOr | BitwiseAnd
-    | ArithPlus | ArithMinus
+    | ArithPlus | ArithMinus | ArithMult | ArithDiv | ArithMod
     | CompareEq | CompareLt | CompareGt
     deriving (Show, Eq)
 
@@ -75,6 +75,9 @@
     rnf BitwiseAnd = ()
     rnf ArithPlus = ()
     rnf ArithMinus = ()
+    rnf ArithMult = ()
+    rnf ArithDiv = ()
+    rnf ArithMod = ()
     rnf CompareEq = ()
     rnf CompareLt = ()
     rnf CompareGt = ()
@@ -256,6 +259,9 @@
 arithOpParser
     =   plusOp
     <|> minusOp
+    <|> multOp
+    <|> divOp
+    <|> modOp
     where
         plusOp = do
             _ <- char '+'
@@ -265,6 +271,18 @@
             _ <- char '-'
             incOffset 1
             return $ BuildResults 0 [ ArithMinus ] [ ]
+        multOp = do
+            _ <- char '*'
+            incOffset 1
+            return $ BuildResults 0 [ ArithMult ] [ ]
+        divOp = do
+            _ <- char '/'
+            incOffset 1
+            return $ BuildResults 0 [ ArithDiv ] [ ]
+        modOp = do
+            _ <- char 'm'
+            incOffset 1
+            return $ BuildResults 0 [ ArithMod ] [ ]
 
 literalIntOpParser :: CapParser BuildResults
 literalIntOpParser = do
diff --git a/src/Graphics/Vty/Platform/Unix/Input/Mouse.hs b/src/Graphics/Vty/Platform/Unix/Input/Mouse.hs
--- a/src/Graphics/Vty/Platform/Unix/Input/Mouse.hs
+++ b/src/Graphics/Vty/Platform/Unix/Input/Mouse.hs
@@ -83,12 +83,20 @@
 rightButton :: Int
 rightButton = 2
 
+-- | See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Wheel-mice
 scrollUp :: Int
 scrollUp = 64
 
 scrollDown :: Int
 scrollDown = 65
 
+-- | See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Other-buttons
+scrollLeft :: Int
+scrollLeft = 66
+
+scrollRight :: Int
+scrollRight = 67
+
 hasBitSet :: Int -> Int -> Bool
 hasBitSet val bit = val .&. bit > 0
 
@@ -113,6 +121,8 @@
                     , (rightButton,  BRight)
                     , (scrollUp,     BScrollUp)
                     , (scrollDown,   BScrollDown)
+                    , (scrollLeft,   BScrollLeft)
+                    , (scrollRight,  BScrollRight)
                     ]
     in case lookup (mods .&. buttonMask) buttonMap of
         Nothing -> failParse
diff --git a/src/Graphics/Vty/Platform/Unix/Output/XTermColor.hs b/src/Graphics/Vty/Platform/Unix/Output/XTermColor.hs
--- a/src/Graphics/Vty/Platform/Unix/Output/XTermColor.hs
+++ b/src/Graphics/Vty/Platform/Unix/Output/XTermColor.hs
@@ -18,6 +18,7 @@
 import Blaze.ByteString.Builder.Word (writeWord8)
 
 import qualified Data.ByteString.Char8 as BS8
+import qualified Data.ByteString.UTF8 as UTF8
 import Data.ByteString.Char8 (ByteString)
 import Foreign.Ptr (castPtr)
 
@@ -148,5 +149,5 @@
         sanitize = concatMap sanitizeChar
         sanitizeChar c | not (isPrint c) = showLitChar c ""
                        | otherwise = [c]
-    let buf = BS8.pack $ "\ESC]2;" <> sanitize title <> "\007"
+    let buf = UTF8.fromString $ "\ESC]2;" <> sanitize title <> "\007"
     outputByteBuffer o buf
diff --git a/tools/BuildWidthTable.hs b/tools/BuildWidthTable.hs
--- a/tools/BuildWidthTable.hs
+++ b/tools/BuildWidthTable.hs
@@ -1,18 +1,29 @@
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE CPP #-}
-module Main where
+module Main (main) where
 
+import Graphics.Vty.UnicodeWidthTable.Main (defaultMain)
 import System.Console.ANSI (getCursorPosition)
-import Text.Printf (printf)
 
-import Graphics.Vty.UnicodeWidthTable.Main (defaultMain)
+main :: IO ()
+main = defaultMain charWidth
 
+-- | The number of times we'll attempt to compute a character's width
+-- before defaulting to @1@ and continuing.
+attempts :: Int
+attempts = 3
+
+-- | Experimentally determine the console width of the character.
 charWidth :: Char -> IO Int
-charWidth c = do
-    printf "\r"
-    putChar c
-    Just (_, col) <- getCursorPosition
-    return col
+charWidth = charWidth' 0
 
-main :: IO ()
-main = defaultMain charWidth
+-- | Helper for 'charWidth' with the number of failed attempts counted.
+charWidth' :: Int {- ^ failed attempts -} -> Char -> IO Int
+charWidth' n c
+    | n >= attempts =
+     do putStrLn ("\rUnable to check: " ++ [c] ++ " (" ++ show c ++ ")")
+        pure 1 -- fallback default to 1
+    | otherwise =
+     do putStr ['\r', c]
+        mb <- getCursorPosition
+        case mb of
+            Nothing -> charWidth' (n+1) c
+            Just (_, col) -> pure col
diff --git a/vty-unix.cabal b/vty-unix.cabal
--- a/vty-unix.cabal
+++ b/vty-unix.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               vty-unix
-version:            0.2.0.0
+version:            0.5.0.0
 synopsis:           Unix backend for Vty
 description:        This package provides Unix terminal support for Vty.
 license:            BSD-3-Clause
@@ -8,7 +8,7 @@
 author:             Jonathan Daugherty
 maintainer:         cygnus@foobox.com
 category:           User Interfaces
-copyright:          (c) 2023 Jonathan Daugherty
+copyright:          (c) 2023-2026 Jonathan Daugherty
 build-type:         Simple
 extra-doc-files:    CHANGELOG.md
 extra-source-files: cbits/gwinsz.h
@@ -50,10 +50,10 @@
     build-depends:    base >= 4.8 && < 5,
                       blaze-builder,
                       bytestring,
-                      mtl,
+                      mtl >= 2.3,
                       unix,
                       terminfo,
-                      vty >= 6.1,
+                      vty >= 6.6,
                       deepseq,
                       vector,
                       parsec,
@@ -74,6 +74,6 @@
   if !impl(ghc >= 8.0)
     build-depends:     semigroups >= 0.16
 
-  build-depends:       base,
+  build-depends:       base >= 4.8 && < 5,
                        vty,
                        ansi-terminal
