diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,34 @@
 # Changelog
 
+## 0.9.2.0 (2023-09-26)
+
+ *  Read configuration from XDG standard directory (#146)
+
+    The per-user patat configuration file was `$HOME/.patat.yaml`,
+    which does not follow the XDG standard.  We now support
+    `$XDG_CONFIG_DIRECTORY/patat/config.yaml` (typically `$XDG_CONFIG_DIRECTORY`
+    is set to `$HOME/.config`) which is compliant with the standard.
+
+    Note that `$HOME/.patat.yaml` is still supported for backward-compatibility,
+    but anything in `$XDG_CONFIG_DIRECTORY` takes precedence.
+
+ *  Support filenames in bash completion (#145) (#126)
+
+## 0.9.1.0 (2023-09-25)
+
+ *  Fall back to forcing UTF-8 if decoding fails (#144) (#127)
+
+    When we try to read a file that is encoded in UTF-8, and the system locale
+    is not set to UTF-8, the GHC runtime system will throw an error.
+
+    While this typically indicates that the user should update their system
+    locale using e.g. the `LANG` environment variable, we want to provide a good
+    initial experience for people unfamiliar with this, and in 2023 it's
+    reasonable to assume files may be encoded in UTF-8.
+
+ *  Dependency updates:
+     -  Bump `skylighting` upper bound to 0.15 (#143)
+
 ## 0.9.0.0 (2023-09-13)
 
  *  Add proper support for speaker notes (#142)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,34 +1,31 @@
-patat
-=====
+🥔 patat
+========
 
 ![CI](https://github.com/jaspervdj/patat/workflows/CI/badge.svg) [![Hackage](https://img.shields.io/hackage/v/patat.svg)](https://hackage.haskell.org/package/patat) [![GitHub tag](https://img.shields.io/github/tag/jaspervdj/patat.svg)]()
 
-`patat` (**P**resentations **A**top **T**he **A**NSI **T**erminal) is a small
-tool that allows you to show presentations using only an ANSI terminal.  It does
-not require `ncurses`.
-
-Features:
+`patat` (**P**resentations **A**top **T**he **A**NSI **T**erminal) is a
+feature-rich presentation tool that runs in the terminal.
 
-- Leverages the great [Pandoc] library to support many input formats including
-  [Literate Haskell].
-- Supports [smart slide splitting](#input-format).
-- Slides can be split up into [multiple fragments](#fragmented-slides)
-- There is a [live reload](#running) mode.
-- Supports [evaluating code snippets and showing the result](#evaluating-code).
+- Understands most markdown extensions and many other input formats
+  (rST, Org-mode...) by building on top of [Pandoc].
+- [Evaluate code snippets and show the result](#evaluating-code).
+- Syntax highlighting for nearly one hundred languages generated from [Kate]
+  syntax files.
+- [Automatically reload](#running) your slides as you edit them.
 - Display [speaker notes](#speaker-notes) in a second window or monitor.
-- [Auto advancing](#auto-advancing) with configurable delay.
+- [Incremental slide display](#fragmented-slides).
 - Experimental [images](#images) support.
+- Supports [smart slide splitting](#input-format).
+- [Auto advancing](#auto-advancing) with configurable delay.
 - Optionally [re-wrapping](#line-wrapping) text to terminal width with proper
   indentation.
-- Syntax highlighting for nearly one hundred languages generated from [Kate]
-  syntax files.
 - [Theming](#theming) support including 24-bit RGB.
-- Written in [Haskell].
+- Hihgly portable as it only requires an ANSI terminal as opposed to
+  something like `ncurses`.
 
 ![screenshot](extra/demo.gif?raw=true)
 
 [Kate]: https://kate-editor.org/
-[Haskell]: http://haskell.org/
 [Pandoc]: http://pandoc.org/
 
 Table of Contents
@@ -67,6 +64,7 @@
 - Ubuntu: <https://packages.ubuntu.com/bionic/patat>
 - openSUSE: <https://build.opensuse.org/package/show/openSUSE:Factory:ARM/patat>
 - Fedora: <https://src.fedoraproject.org/rpms/patat>
+- NixOS: <https://search.nixos.org/packages?show=haskellPackages.patat>
 
 You can also find generic Linux and Mac OS binaries here:
 <https://github.com/jaspervdj/patat/releases>.
@@ -247,10 +245,14 @@
 -------------
 
 `patat` is fairly configurable.  The configuration is done using [YAML].  There
-are two places where you can put your configuration:
+are several places where you can put your configuration.
 
-1. In the presentation file itself, using the [Pandoc metadata header].
-2. In `$HOME/.patat.yaml`
+1.  For per-user configuration you can use
+    `$XDG_CONFIG_DIRECTORY/patat/config.yaml`
+    (typically `$HOME/.config/patat/config.yaml`) or `$HOME/.patat.yaml`.
+2.  In the presentation file itself, using the [Pandoc metadata header].
+    These settings take precedence over anything specified in the per-user
+    configuration file.
 
 [YAML]: http://yaml.org/
 [Pandoc metadata header]: http://pandoc.org/MANUAL.html#extension-yaml_metadata_block
@@ -268,7 +270,8 @@
 Hello world.
 ```
 
-Or we can use a normal presentation and have the following `$HOME/.patat.yaml`:
+Or we can use a "plain" presentation and have the following
+`$XDG_CONFIG_DIRECTORY/patat/config.yaml`:
 
     key: val
 
diff --git a/lib/Patat/EncodingFallback.hs b/lib/Patat/EncodingFallback.hs
new file mode 100644
--- /dev/null
+++ b/lib/Patat/EncodingFallback.hs
@@ -0,0 +1,56 @@
+-- | When we try to read a file that is encoded in UTF-8, and the system locale
+-- is not set to UTF-8, the GHC runtime system will throw an error:
+--
+-- <https://github.com/jaspervdj/patat/issues/127>
+--
+-- However, we don't want to force people to use UTF-8 for everything.  So what
+-- we do is provide a replacement readFile, which first tries to read the file
+-- in the system locale, and then falls back to forcing UTF-8.
+--
+-- If we forced UTF-8, we also want to propagate that to the output handle;
+-- otherwise will get errors when we try to display these characters; so
+-- withHandle should be used on the output handle (typically stdout).
+module Patat.EncodingFallback
+    ( EncodingFallback (..)
+    , readFile
+    , withHandle
+    ) where
+
+
+--------------------------------------------------------------------------------
+import           Control.Exception (bracket, throwIO)
+import           Control.Monad     (when)
+import qualified Data.Text         as T
+import qualified Data.Text.IO      as T
+import           Prelude           hiding (readFile)
+import qualified System.IO         as IO
+import qualified System.IO.Error   as IO
+
+
+--------------------------------------------------------------------------------
+data EncodingFallback = NoFallback | Utf8Fallback
+    deriving (Eq, Show)
+
+
+--------------------------------------------------------------------------------
+readFile :: FilePath -> IO (EncodingFallback, T.Text)
+readFile path = IO.catchIOError readSystem $ \ioe -> do
+    when (IO.isDoesNotExistError ioe) $ throwIO ioe  -- Don't retry on these
+    readUtf8
+  where
+    readSystem = ((,) NoFallback <$> T.readFile path)
+    readUtf8   = IO.withFile path IO.ReadMode $ \h -> do
+        IO.hSetEncoding h IO.utf8_bom
+        (,) Utf8Fallback <$> T.hGetContents h
+
+
+--------------------------------------------------------------------------------
+withHandle :: IO.Handle -> EncodingFallback -> IO a -> IO a
+withHandle _ NoFallback   mx = mx
+withHandle h Utf8Fallback mx = bracket
+    (do
+        mbOld <- IO.hGetEncoding h
+        IO.hSetEncoding h IO.utf8
+        pure mbOld)
+    (\mbOld -> traverse (IO.hSetEncoding h) mbOld)
+    (\_ -> mx)
diff --git a/lib/Patat/Main.hs b/lib/Patat/Main.hs
--- a/lib/Patat/Main.hs
+++ b/lib/Patat/Main.hs
@@ -22,6 +22,7 @@
 import qualified Options.Applicative             as OA
 import qualified Options.Applicative.Help.Pretty as OA.PP
 import           Patat.AutoAdvance
+import qualified Patat.EncodingFallback          as EncodingFallback
 import qualified Patat.Images                    as Images
 import           Patat.Presentation
 import qualified Patat.Presentation.SpeakerNotes as SpeakerNotes
@@ -51,6 +52,7 @@
 parseOptions = Options
     <$> (OA.optional $ OA.strArgument $
             OA.metavar "FILENAME" <>
+            OA.action  "file" <>  -- For bash file completion
             OA.help    "Input file")
     <*> (OA.switch $
             OA.long    "force" <>
@@ -145,9 +147,11 @@
     withMaybeHandle SpeakerNotes.with
         (psSpeakerNotes $ pSettings pres) $ \speakerNotes ->
 
-        if oDump options
-            then dumpPresentation pres
-            else interactiveLoop options images speakerNotes pres
+        if oDump options then
+            EncodingFallback.withHandle IO.stdout (pEncodingFallback pres) $
+            dumpPresentation pres
+        else
+            interactiveLoop options images speakerNotes pres
   where
     interactiveLoop
         :: Options -> Maybe Images.Handle -> Maybe SpeakerNotes.Handle
@@ -168,8 +172,9 @@
 
         let loop :: Presentation -> Maybe String -> IO ()
             loop pres mbError = do
-                for_ speakerNotes $ \sn ->
-                    SpeakerNotes.write sn $ activeSpeakerNotes pres
+                for_ speakerNotes $ \sn -> SpeakerNotes.write sn
+                    (pEncodingFallback pres)
+                    (activeSpeakerNotes pres)
 
                 size <- getDisplaySize pres
                 let display = case mbError of
@@ -180,7 +185,9 @@
                 Ansi.clearScreen
                 Ansi.setCursorPosition 0 0
                 cleanup <- case display of
-                    DisplayDoc doc -> PP.putDoc doc $> mempty
+                    DisplayDoc doc -> EncodingFallback.withHandle
+                        IO.stdout (pEncodingFallback pres) $
+                        PP.putDoc doc $> mempty
                     DisplayImage path -> case images of
                         Nothing -> do
                             PP.putDoc $ displayPresentationError
diff --git a/lib/Patat/Presentation/Internal.hs b/lib/Patat/Presentation/Internal.hs
--- a/lib/Patat/Presentation/Internal.hs
+++ b/lib/Patat/Presentation/Internal.hs
@@ -45,6 +45,7 @@
 import           Data.Sequence.Extended          (Seq)
 import qualified Data.Sequence.Extended          as Seq
 import qualified Data.Text                       as T
+import           Patat.EncodingFallback          (EncodingFallback)
 import qualified Patat.Presentation.Instruction  as Instruction
 import qualified Patat.Presentation.SpeakerNotes as SpeakerNotes
 import qualified Patat.Theme                     as Theme
@@ -60,14 +61,15 @@
 
 --------------------------------------------------------------------------------
 data Presentation = Presentation
-    { pFilePath       :: !FilePath
-    , pTitle          :: ![Pandoc.Inline]
-    , pAuthor         :: ![Pandoc.Inline]
-    , pSettings       :: !PresentationSettings
-    , pSlides         :: !(Seq Slide)
-    , pBreadcrumbs    :: !(Seq Breadcrumbs)  -- One for each slide.
-    , pActiveFragment :: !Index
-    , pSyntaxMap      :: !Skylighting.SyntaxMap
+    { pFilePath         :: !FilePath
+    , pEncodingFallback :: !EncodingFallback
+    , pTitle            :: ![Pandoc.Inline]
+    , pAuthor           :: ![Pandoc.Inline]
+    , pSettings         :: !PresentationSettings
+    , pSlides           :: !(Seq Slide)
+    , pBreadcrumbs      :: !(Seq Breadcrumbs)  -- One for each slide.
+    , pActiveFragment   :: !Index
+    , pSyntaxMap        :: !Skylighting.SyntaxMap
     } deriving (Show)
 
 
diff --git a/lib/Patat/Presentation/Read.hs b/lib/Patat/Presentation/Read.hs
--- a/lib/Patat/Presentation/Read.hs
+++ b/lib/Patat/Presentation/Read.hs
@@ -24,8 +24,9 @@
 import qualified Data.Sequence.Extended          as Seq
 import qualified Data.Text                       as T
 import qualified Data.Text.Encoding              as T
-import qualified Data.Text.IO                    as T
 import qualified Data.Yaml                       as Yaml
+import           Patat.EncodingFallback          (EncodingFallback)
+import qualified Patat.EncodingFallback          as EncodingFallback
 import           Patat.Eval                      (eval)
 import           Patat.Presentation.Fragment
 import qualified Patat.Presentation.Instruction  as Instruction
@@ -33,8 +34,10 @@
 import qualified Patat.Presentation.SpeakerNotes as SpeakerNotes
 import           Prelude
 import qualified Skylighting                     as Skylighting
-import           System.Directory                (doesFileExist,
-                                                  getHomeDirectory)
+import           System.Directory                (XdgDirectory (XdgConfig),
+                                                  doesFileExist,
+                                                  getHomeDirectory,
+                                                  getXdgDirectory)
 import           System.FilePath                 (splitFileName, takeExtension,
                                                   (</>))
 import qualified Text.Pandoc.Error               as Pandoc
@@ -45,10 +48,15 @@
 readPresentation :: FilePath -> IO (Either String Presentation)
 readPresentation filePath = runExceptT $ do
     -- We need to read the settings first.
-    src          <- liftIO $ T.readFile filePath
+    (enc, src)   <- liftIO $ EncodingFallback.readFile filePath
     homeSettings <- ExceptT readHomeSettings
+    xdgSettings  <- ExceptT readXdgSettings
     metaSettings <- ExceptT $ return $ readMetaSettings src
-    let settings = metaSettings <> homeSettings <> defaultPresentationSettings
+    let settings =
+            metaSettings <>
+            xdgSettings  <>
+            homeSettings <>
+            defaultPresentationSettings
 
     syntaxMap <- ExceptT $ readSyntaxMap $ fromMaybe [] $
         psSyntaxDefinitions settings
@@ -61,7 +69,7 @@
         Right x -> return x
 
     pres <- ExceptT $ pure $
-        pandocToPresentation filePath settings syntaxMap doc
+        pandocToPresentation filePath enc settings syntaxMap doc
     liftIO $ eval pres
   where
     ext = takeExtension filePath
@@ -112,10 +120,9 @@
 
 --------------------------------------------------------------------------------
 pandocToPresentation
-    :: FilePath -> PresentationSettings -> Skylighting.SyntaxMap
-    -> Pandoc.Pandoc
-    -> Either String Presentation
-pandocToPresentation pFilePath pSettings pSyntaxMap
+    :: FilePath -> EncodingFallback -> PresentationSettings
+    -> Skylighting.SyntaxMap -> Pandoc.Pandoc -> Either String Presentation
+pandocToPresentation pFilePath pEncodingFallback pSettings pSyntaxMap
         pandoc@(Pandoc.Pandoc meta _) = do
     let !pTitle          = case Pandoc.docTitle meta of
             []    -> [Pandoc.Str . T.pack . snd $ splitFileName pFilePath]
@@ -162,7 +169,20 @@
 readHomeSettings :: IO (Either String PresentationSettings)
 readHomeSettings = do
     home <- getHomeDirectory
-    let path = home </> ".patat.yaml"
+    readSettings $ home </> ".patat.yaml"
+
+
+--------------------------------------------------------------------------------
+-- | Read settings from "$XDG_CONFIG_DIRECTORY/patat/config.yaml".
+readXdgSettings :: IO (Either String PresentationSettings)
+readXdgSettings =
+    getXdgDirectory XdgConfig ("patat" </> "config.yaml") >>= readSettings
+
+
+--------------------------------------------------------------------------------
+-- | Read settings from the specified path, if it exists.
+readSettings :: FilePath -> IO (Either String PresentationSettings)
+readSettings path = do
     exists <- doesFileExist path
     if not exists
         then return (Right mempty)
diff --git a/lib/Patat/Presentation/SpeakerNotes.hs b/lib/Patat/Presentation/SpeakerNotes.hs
--- a/lib/Patat/Presentation/SpeakerNotes.hs
+++ b/lib/Patat/Presentation/SpeakerNotes.hs
@@ -25,7 +25,10 @@
 import           Data.List              (intersperse)
 import qualified Data.Text              as T
 import qualified Data.Text.IO           as T
+import           Patat.EncodingFallback (EncodingFallback)
+import qualified Patat.EncodingFallback as EncodingFallback
 import           System.Directory       (removeFile)
+import qualified System.IO              as IO
 import qualified Text.Pandoc            as Pandoc
 
 
@@ -95,10 +98,12 @@
 
 
 --------------------------------------------------------------------------------
-write :: Handle -> SpeakerNotes -> IO ()
-write h sn = do
+write :: Handle -> EncodingFallback -> SpeakerNotes -> IO ()
+write h encodingFallback sn = do
     change <- IORef.atomicModifyIORef' (hActive h) $ \old -> (sn, old /= sn)
-    when change $ T.writeFile (sFile $ hSettings h) $ toText sn
+    when change $ IO.withFile (sFile $ hSettings h) IO.WriteMode $ \ioh ->
+        EncodingFallback.withHandle ioh encodingFallback $
+        T.hPutStr ioh $ toText sn
 
 
 --------------------------------------------------------------------------------
diff --git a/patat.cabal b/patat.cabal
--- a/patat.cabal
+++ b/patat.cabal
@@ -1,5 +1,5 @@
 Name:                patat
-Version:             0.9.0.0
+Version:             0.9.2.0
 Synopsis:            Terminal-based presentations using Pandoc
 Description:         Terminal-based presentations using Pandoc.
 License:             GPL-2
@@ -47,7 +47,7 @@
     pandoc               >= 3.1  && < 3.2,
     pandoc-types         >= 1.23 && < 1.24,
     process              >= 1.6  && < 1.7,
-    skylighting          >= 0.10 && < 0.14,
+    skylighting          >= 0.10 && < 0.15,
     terminal-size        >= 0.3  && < 0.4,
     text                 >= 1.2  && < 2.1,
     time                 >= 1.4  && < 1.13,
@@ -66,6 +66,7 @@
   Exposed-modules:
     Patat.AutoAdvance
     Patat.Cleanup
+    Patat.EncodingFallback
     Patat.Eval
     Patat.Images
     Patat.Images.Internal
