diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,12 @@
 # Changelog for gtk-sni-tray
 
+## 0.1.12.0
+
+- Add `trayCenterIcons` field to `TrayParams` and `--center-icons` CLI flag to
+  center tray icons within the bar.
+- Anchor LibDBusMenu popup to the icon image widget instead of the EventBox for
+  more accurate popup positioning.
+
 ## 0.1.11.3
 
 - Fix menu popup positioning: use `menuPopupAtPointer` with the actual button
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -435,6 +435,13 @@
   <> short 'e'
   )
 
+centerIconsP :: Parser Bool
+centerIconsP =
+  switch
+  (  long "center-icons"
+  <> help "Center the tray icons within the bar"
+  )
+
 startWatcherP :: Parser Bool
 startWatcherP =
   switch
@@ -502,12 +509,13 @@
              -> Bool
              -> Bool
              -> Bool
+             -> Bool
              -> Rational
              -> Rational
              -> MenuBackend
              -> IO ()
 buildWindows pos align size padding monitors priority backendChoice maybeColorString expand
-             startWatcher noStrut barLength overlayScale menuBackend = do
+             centerIcons startWatcher noStrut barLength overlayScale menuBackend = do
   _ <- Gtk.init Nothing
   logger <- getLogger "StatusNotifier"
   saveGlobalLogger $ setLevel priority logger
@@ -573,6 +581,7 @@
             , trayMiddleClickAction = SecondaryActivate
             , trayRightClickAction = PopupMenu
             , trayMenuBackend = menuBackend
+            , trayCenterIcons = centerIcons
             }
         window <- Gtk.windowNew Gtk.WindowTypeToplevel
         Gtk.windowSetResizable window False
@@ -605,7 +614,8 @@
 parser :: Parser (IO ())
 parser =
   buildWindows <$> positionP <*> alignmentP <*> sizeP <*> paddingP <*>
-  monitorNumberP <*> logP <*> backendChoiceP <*> colorP <*> expandP <*> startWatcherP <*>
+  monitorNumberP <*> logP <*> backendChoiceP <*> colorP <*> expandP <*>
+  centerIconsP <*> startWatcherP <*>
   noStrutP <*> barLengthP <*> overlayScaleP <*> menuBackendP
 
 versionOption :: Parser (a -> a)
diff --git a/gtk-sni-tray.cabal b/gtk-sni-tray.cabal
--- a/gtk-sni-tray.cabal
+++ b/gtk-sni-tray.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           gtk-sni-tray
-version:        0.1.11.3
+version:        0.1.12.0
 synopsis:       A standalone StatusNotifierItem/AppIndicator tray
 description:    Please see the README on Github at <https://github.com/IvanMalison/gtk-sni-tray#readme>
 category:       System
diff --git a/src/StatusNotifier/Tray.hs b/src/StatusNotifier/Tray.hs
--- a/src/StatusNotifier/Tray.hs
+++ b/src/StatusNotifier/Tray.hs
@@ -216,6 +216,7 @@
   , trayMiddleClickAction :: TrayClickAction
   , trayRightClickAction :: TrayClickAction
   , trayMenuBackend :: MenuBackend
+  , trayCenterIcons :: Bool
   }
 
 defaultTrayParams :: TrayParams
@@ -229,6 +230,7 @@
   , trayMiddleClickAction = SecondaryActivate
   , trayRightClickAction = PopupMenu
   , trayMenuBackend = HaskellDBusMenu
+  , trayCenterIcons = False
   }
 
 buildTray :: Host -> Client -> TrayParams -> IO Gtk.Box
@@ -247,10 +249,14 @@
                      , trayMiddleClickAction = middleClickAction
                      , trayRightClickAction = rightClickAction
                      , trayMenuBackend = menuBackend
+                     , trayCenterIcons = centerIcons
                      } = do
   trayLogger INFO "Building tray"
 
   trayBox <- Gtk.boxNew orientation 0
+  when centerIcons $ case orientation of
+    Gtk.OrientationHorizontal -> Gtk.widgetSetHalign trayBox Gtk.AlignCenter
+    _ -> Gtk.widgetSetValign trayBox Gtk.AlignCenter
   Gtk.widgetGetStyleContext trayBox >>=
     flip Gtk.styleContextAddClass "tray-box"
   contextMap <- MV.newMVar Map.empty
@@ -444,7 +450,18 @@
                                  then return True
                                  else do
                                    Gtk.widgetShowAll gtkMenu
-                                   Gtk.menuPopupAtPointer gtkMenu Nothing
+                                   -- libdbusmenu is populated asynchronously, so we popup later via a
+                                   -- timeout. On Wayland, popups generally need the original trigger
+                                   -- event; use menuPopupAtWidget anchored to the EventBox to avoid
+                                   -- "no trigger event" and invalid rect_window assertions.
+                                   -- Anchor to the actual icon widget so the popup aligns with the
+                                   -- visible image, not the full EventBox allocation.
+                                   Gtk.menuPopupAtWidget
+                                     gtkMenu
+                                     image
+                                     GravitySouth
+                                     GravityNorth
+                                     currentEvent
                                    return False
                          return ()
                        HaskellDBusMenu -> do
