From 0fec9d3fdf9bc86187f9f670dafd2ef57fe03f29 Mon Sep 17 00:00:00 2001 From: Enrico Maria De Angelis Date: Thu, 14 Mar 2024 00:08:42 +0000 Subject: Make the Kbd monitor clickable, with click selecting the next layout --- doc/plugins.org | 5 ++-- src/Xmobar/Plugins/Kbd.hs | 72 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/doc/plugins.org b/doc/plugins.org index 8825281..e408dec 100644 --- a/doc/plugins.org +++ b/doc/plugins.org @@ -658,8 +658,9 @@ ** Keyboard and screen monitors *** =Kbd Opts= - - Registers to XKB/X11-Events and output the currently active keyboard - layout. Supports replacement of layout names. + - Registers to XKB/X11-Events, output the currently active keyboard + layout, and maps left and right clicks to switching to next and + previous layouts respectively. Supports replacement of layout names. - Aliases to =kbd= diff --git a/src/Xmobar/Plugins/Kbd.hs b/src/Xmobar/Plugins/Kbd.hs index 2214628..3871ca8 100644 --- a/src/Xmobar/Plugins/Kbd.hs +++ b/src/Xmobar/Plugins/Kbd.hs @@ -14,9 +14,11 @@ module Xmobar.Plugins.Kbd(Kbd(..)) where -import Data.List (isPrefixOf) +import Data.Bifunctor (bimap) +import Data.List (find, tails, isPrefixOf) import Data.Maybe (fromMaybe) -import Data.Char (toLower) +import Data.Char (toLower, isLetter) +import Data.Function ((&)) import Control.Monad (forever) import Control.Applicative ((<|>)) import Graphics.X11.Xlib @@ -26,6 +28,13 @@ import Xmobar.Run.Exec import Xmobar.X11.Events (nextEvent') import Xmobar.System.Kbd +-- some strong typing +newtype Lay = Lay String deriving (Eq) +newtype Sym = Sym String +instance Show Sym where show (Sym s) = s +type KbdOpts' = [(Lay, Sym)] +typed :: [(String, String)] -> [(Lay, Sym)] +typed = map (bimap Lay Sym) -- 'Bad' prefixes of layouts noLaySymbols :: [String] @@ -49,15 +58,22 @@ split p s = case break p s of (pref, "") -> [pref] -- replaces input string if on search list (exact match) with corresponding --- element on replacement list. +-- element on replacement list, and returns it paired with the following item -- --- if not found, return string unchanged -searchReplaceLayout :: KbdOpts -> String -> String -searchReplaceLayout opts s = fromMaybe s $ lookup s opts - --- returns the active layout -getKbdLay :: Display -> KbdOpts -> IO String -getKbdLay dpy opts = do +-- if not found, return string unchanged, paired with empty string +searchReplaceLayout :: KbdOpts' -> String -> (Lay, Lay, Sym) +searchReplaceLayout opts curr + = maybe (Lay "", Lay "", Sym curr) + pickSymWithAdjLays + (find currLayout (tails $ cycle opts)) + where + pickSymWithAdjLays ((l1, _):(_, s):(l2, _):_) = (l1, l2, s) + pickSymWithAdjLays _ = error "This should never happen" + currLayout = (Lay curr ==) . (!! 1) . map fst + +-- returns the active layout and the following one +getCurAndNextKbdLays :: Display -> KbdOpts' -> IO (Lay, Lay, Sym) +getCurAndNextKbdLays dpy opts = do lay <- splitLayout <$> getLayoutStr dpy grps <- map (map toLower . take 2) <$> getGrpNames dpy curLay <- getKbdLayout dpy @@ -73,6 +89,37 @@ getKbdLay dpy opts = do newtype Kbd = Kbd [(String, String)] deriving (Read, Show) +attachClickAction :: (Lay, Lay, Sym) -> Sym +attachClickAction (Lay prv, Lay nxt, txt) = txt & linkTo nxt `onKey` "1" + & linkTo prv `onKey` "3" + where + splitLayParensPhon :: String -> (String, String, String) + splitLayParensPhon = (\(a, (b, c)) -> (a, b, c)) + . second (second (drop 1) . break (== ')') . drop 1) + . break (== '(') + parseLayPhon :: String -> (Maybe String, Maybe String) + parseLayPhon s = let (l, p, i) = splitLayParensPhon s + l' = if all isLetter l + then Just ("-layout " ++ l) + else Nothing + p' = if (p, i) == ("phonetic", "") + then Just "-variant phonetic" + else Nothing + in (l', p') + linkTo :: String -> String -> Sym -> Sym + linkTo linked button currLay = Sym $ case parseLayPhon linked of + (Nothing, _) -> "??" + (Just linkedLay, phon) -> wrapIn setxkbmap button currLay + where + setxkbmap = unwords ["setxkbmap", linkedLay, fromMaybe "" phon] + wrapIn :: String -> String -> Sym -> String + wrapIn action buttons (Sym sym) = openingTag ++ sym ++ closingTag + where + openingTag = "" + closingTag = "" + onKey = ($) + second = fmap + instance Exec Kbd where alias (Kbd _) = "kbd" start (Kbd opts) cb = do @@ -80,7 +127,7 @@ instance Exec Kbd where dpy <- openDisplay "" -- initial set of layout - cb =<< getKbdLay dpy opts + cb . show . attachClickAction =<< getCurAndNextKbdLays dpy (typed opts) -- enable listing for -- group changes @@ -91,7 +138,6 @@ instance Exec Kbd where allocaXEvent $ \e -> forever $ do nextEvent' dpy e _ <- getEvent e - cb =<< getKbdLay dpy opts + cb . show . attachClickAction =<< getCurAndNextKbdLays dpy (typed opts) closeDisplay dpy - return () -- cgit v1.2.3