From 523ef127e36d91560851d912a2765fa408ce1100 Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Thu, 4 Nov 2021 14:10:52 -0600 Subject: Fix old bug. Old bug where shifting workspaces relatively using mod-n/p would not work as expected where visible workspaces without any windows would be skipped over or plain not work. --- src/Internal/Lib.hs | 22 +++++++++++++++++++++- src/Internal/XMobarLog.hs | 20 +++++--------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/Internal/Lib.hs b/src/Internal/Lib.hs index feb5f26..3ba1eca 100644 --- a/src/Internal/Lib.hs +++ b/src/Internal/Lib.hs @@ -21,6 +21,9 @@ import XMonad hiding (workspaces, Screen) import XMonad.StackSet hiding (filter, focus) import qualified Data.Map as Map import Internal.DMenu +import Data.Ord (comparing) + +import qualified XMonad.StackSet as S type WorkspaceName = Char newtype Selector = Selector (forall a. (Eq a) => a -> [a] -> a) @@ -31,6 +34,23 @@ instance XPrompt WinPrompt where showXPrompt _ = "[Window] " commandToComplete _ = id +data WorkspaceState = Current | Hidden | Visible + +-- Returns all the workspaces that are either visible, current or Hidden but +-- have windows and that workspace's state. +-- +-- In other words, filters out workspaces that have no windows and are not +-- visible. +-- +-- This function will sort the result by the workspace tag. +getPopulatedWorkspaces :: + (Ord i) => S.StackSet i l a sid sd -> [(WorkspaceState, S.Workspace i l a)] +getPopulatedWorkspaces (S.StackSet (S.Screen cur _ _) vis hi _) = + sortBy (comparing (tag . snd)) $ + mapMaybe (\w@(S.Workspace _ _ s) -> fmap (const (Hidden, w)) s) hi ++ + map (\(S.Screen w _ _) -> (Visible, w)) vis ++ + [(Current, cur)] + getHorizontallyOrderedScreens :: StackSet wid l a ScreenId ScreenDetail -> [Screen wid l a ScreenId ScreenDetail] @@ -91,7 +111,7 @@ getString = runQuery $ do relativeWorkspaceShift :: Selector -> X () relativeWorkspaceShift (Selector selector) = do windows $ \ss -> - let tags = sort $ (tag <$> filter (isJust . stack) (workspaces ss)) + let tags = sort $ (tag . snd <$> getPopulatedWorkspaces ss) from = tag $ workspace $ current ss to = selector from tags in greedyView to ss diff --git a/src/Internal/XMobarLog.hs b/src/Internal/XMobarLog.hs index d0ff8f8..c0aa2a7 100644 --- a/src/Internal/XMobarLog.hs +++ b/src/Internal/XMobarLog.hs @@ -1,5 +1,6 @@ module Internal.XMobarLog ( XMobarLog, spawnXMobar, xMobarLogHook ) where +import Control.Arrow (second) import Control.Monad (forM_) import Control.Monad.Writer (tell, execWriter) import Data.List (sortBy) @@ -10,12 +11,11 @@ import System.IO (Handle, hSetEncoding, hPutStrLn, utf8) import XMonad.Util.NamedWindows (getName) import XMonad.Util.Run (spawnPipe) import XMonad (X) +import Internal.Lib (getPopulatedWorkspaces, WorkspaceState(..)) import qualified XMonad as X import qualified XMonad.StackSet as S -data WorkspaceState = Current | Hidden | Visible - data XMobarLog = XMobarLog Handle -- The log hook for XMobar. This is a custom log hook that does not use any @@ -39,19 +39,19 @@ xMobarLogHook (XMobarLog xmproc) = do winset <- X.gets X.windowset title <- maybe (pure "") (fmap show . getName) . S.peek $ winset - let wss = getWorkspaces winset + let wss = getPopulatedWorkspaces winset X.liftIO $ do hPutStrLn xmproc $ trunc 80 $ execWriter $ do tell layoutXpm tell $ " │ " - forM_ wss $ \(t, name) -> do + forM_ wss $ \(t, ws) -> do case t of Current -> tell "" Visible -> tell "" Hidden -> tell "" - tell name + tell (S.tag ws) tell " " tell $ "" @@ -76,13 +76,3 @@ trunc amt str = reverse $ trunc' False amt str [] 0 -> trunc' False 0 as acc 3 -> trunc' False 0 as ("..." ++ acc) _ -> trunc' False (amt - 1) as (a : acc) - --- Returns all the workspaces with a stack on them and if that workspace is --- Visible, Current or Hidden. -getWorkspaces :: (Ord i) => S.StackSet i l a sid sd -> [(WorkspaceState, i)] -getWorkspaces (S.StackSet (S.Screen cur _ _) vis hi _) = - sortBy (comparing snd) $ - mapMaybe (\(a, S.Workspace t _ s) -> fmap (const (a, t)) s) $ - map (\w -> (Hidden, w)) hi ++ - map (\(S.Screen w _ _) -> (Visible, w)) vis ++ - [(Current, cur)] -- cgit From db70d254688fbeca119ca29e4968513df07bd34b Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Fri, 5 Nov 2021 13:15:37 -0600 Subject: Add my own xmobar-weather perl script. The builtin xmobar weather is not feature rich enough. My new script will change icons depending on the time of day. --- extras/HOME/.xmobarrc | 17 +------- extras/HOME/.xmonad/xmobar-weather | 86 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 15 deletions(-) create mode 100755 extras/HOME/.xmonad/xmobar-weather diff --git a/extras/HOME/.xmobarrc b/extras/HOME/.xmobarrc index 52798af..90141e7 100644 --- a/extras/HOME/.xmobarrc +++ b/extras/HOME/.xmobarrc @@ -31,7 +31,7 @@ Config \ %date% │ \ \%StdinReader%}%time%\ \{ %cpu% %memory% \ - \│ %KLMO% │\ + \│ %weather% │\ \ %mpris2% │ \ \%bluetooth%%bat% " , commands = [ @@ -56,23 +56,10 @@ Config "--normal", "#88ff88", "--high", "#ff8888" ] 10, - Run WeatherX "KLMO" - [ ("clear", "") - , ("sunny", "") - , ("mostly clear", "") - , ("mostly sunny", "") - , ("partly sunny", "") - , ("fair", "🌑") - , ("cloudy","摒") - , ("overcast","") - , ("partly cloudy", "杖") - , ("mostly cloudy", "") - , ("considerable cloudiness", "ﭽ")] - ["--template", " \ - \°F"] 360000, Run Mpris2 "spotify" [ "-t", "</fn>", "--nastring", "<fc=#404040> </fc>"] 20, + Run Com ".xmonad/xmobar-weather" [] "weather" 9000, Run Com ".xmonad/xmobar-logo" [] "logo" 0, Run Com "uname" ["-r"] "uname" 0, Run Com ".xmonad/xmobar-bluetooth" [] "bluetooth" 50, diff --git a/extras/HOME/.xmonad/xmobar-weather b/extras/HOME/.xmonad/xmobar-weather new file mode 100755 index 0000000..bf84870 --- /dev/null +++ b/extras/HOME/.xmonad/xmobar-weather @@ -0,0 +1,86 @@ +#!/usr/bin/perl + +use LWP::Simple; +use Time::Local; +use POSIX; + +$content = get( + "https://api.sunrise-sunset.org/json?lat=40.1672117&lng=-105.1019286&formatted=0"); + +die "Unable to get sunrise/sunset data" unless defined $content; + +$sunrise_str=$content; +$sunset_str=$content; +$sunrise_str =~ s#.*"sunrise":"([^"]*)".*#\1#; +$sunset_str =~ s#.*"sunset":"([^"]*)".*#\1#; +$current_str=strftime "%Y-%m-%dT%H:%M:%S+00:00", gmtime(); + +$content = get( + "https://tgftp.nws.noaa.gov/data/observations/metar/decoded/KLMO.TXT"); + +die "Unable to get weather data" unless defined $content; + +$sky_conditions = $content; +$sky_conditions =~ s#.*Sky conditions:\s+([^\n]+).*#\1#ims; +$sky_conditions =~ s#\s#_#g; + +$wind = $content; +$wind =~ s#.*Wind:\s+([^\n]+).*#\1#ims; +($wind_direction, $wind_speed) = + ($wind =~ m/from the ([A-Z]+).*at (\d+) MPH.*/g); + + +$temp = $content; +$temp =~ s#.*Temperature:\s+(-?[0-9.]+) F.*#\1#ims; + +if ($current_str gt $sunrise_str and $current_str lt $sunset_str) { + $is_day = 1; +} else { + $is_day = 0; +} + +%directions = ( + NE => "↙", + SE => "↖", + NW => "↘", + SW => "↗", + N => "↓", + S => "↑", + W => "→", + E => "←" ); + +$dir=%directions{$wind_direction}; + +%conditions_day = ( + clear => "<fc=#ddcf04>", + sunny => "<fc=#ddcf04>", + mostly_clear => "<fc=#00a3c4>", + mostly_sunny => "<fc=#ddcf04>", + partly_sunny => "<fc=#ddcf04>", + fair => "<fc=#a0a0a0>🌑", + cloudy =>"<fc=#a0a0a0>摒", + overcast =>"<fc=#808080>", + partly_cloudy => "<fc=#a0a0a0>杖", + mostly_cloudy => "<fc=#808080>", + considerable_cloudiness => "<fc=#a0a0a0>ﭽ" ); + +%conditions_night = ( + clear => "<fc=#00a3c4>", + sunny => "<fc=#00a3c4>", + mostly_clear => "<fc=#00a3c4>", + mostly_sunny => "<fc=#00a3c4>", + partly_sunny => "<fc=#00a3c4>", + fair => "<fc=#808080>🌑", + cloudy =>"<fc=#808080>摒", + overcast =>"<fc=#404040>", + partly_cloudy => "<fc=#a0a0a0>", + mostly_cloudy => "<fc=#808080>", + considerable_cloudiness => "<fc=#a0a0a0>ﭽ" ); + +if ($is_day) { + $conditions = %conditions_day{$sky_conditions}; +} else { + $conditions = %conditions_night{$sky_conditions}; +} + +printf("<fc=#a0a0a0>$dir <fn=3>${wind_speed}mph</fn></fc> $conditions</fc><fn=3> <fc=#a0a0a0>%.0f°F</fc></fn>\n", $temp); -- cgit From 168b02cfea52164379c59ae77e44d34ce8409974 Mon Sep 17 00:00:00 2001 From: Josh Rahm <rahm@google.com> Date: Fri, 5 Nov 2021 16:20:28 -0600 Subject: Add new Corner layout to keep the master window in the corner. As a part of this add the Rotatable layout modifier that can rotate the windows 90 degrees using Shift+Mod+r. --- src/Internal/CornerLayout.hs | 58 ++++++++++++++++++++++++++++++++++++++++++++ src/Internal/Keys.hs | 2 +- src/Internal/Layout.hs | 58 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 src/Internal/CornerLayout.hs diff --git a/src/Internal/CornerLayout.hs b/src/Internal/CornerLayout.hs new file mode 100644 index 0000000..10fbe5b --- /dev/null +++ b/src/Internal/CornerLayout.hs @@ -0,0 +1,58 @@ +{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} +-- Creates a layout, the "corner layout" that keeps the master window in the +-- corner and the other windows go around it. +module Internal.CornerLayout where + +import Data.Typeable (Typeable) +import XMonad (LayoutClass(..), Rectangle(..), Resize(..), fromMessage) +import qualified XMonad.StackSet as S + +data Corner a = Corner Rational Rational + deriving (Show, Typeable, Read) + +instance LayoutClass Corner a where + pureLayout (Corner frac _) screen@(Rectangle x y w h) ss = + let w' = floor $ fromIntegral w * frac + h' = floor $ fromIntegral h * frac + corner = Rectangle 0 0 w' h' + vertRect = Rectangle (fromIntegral w') 0 (w - w') h + horizRect = Rectangle 0 (fromIntegral h') w' (h - h') + ws = S.integrate ss + + vn = (length ws - 1) `div` 2 + hn = (length ws - 1) - vn + in + case ws of + [a] -> [(a, screen)] + [a, b] -> [ + (a, Rectangle x y w' h), + (b, Rectangle (x + fromIntegral w') y (w - w') h)] + _ -> + zip ws $ map ( + \(Rectangle x' y' w h) -> Rectangle (x + x') (y + y') w h) $ + corner : + (splitVert vertRect vn) ++ + (splitHoriz horizRect hn) + + pureMessage (Corner frac delta) m = fmap resize (fromMessage m) + where + resize Shrink = Corner (frac - delta) delta + resize Expand = Corner (frac + delta) delta + +splitVert :: Rectangle -> Int -> [Rectangle] +splitVert (Rectangle x y w h) i' = + map + (\i -> Rectangle x (y + fromIntegral (step * i)) w step) + [0 .. i - 1] + where + i = fromIntegral i' + step = h `div` i + +splitHoriz :: Rectangle -> Int -> [Rectangle] +splitHoriz (Rectangle x y w h) i' = + map + (\i -> Rectangle (x + fromIntegral (step * i)) y step h) + [0 .. i - 1] + where + step = w `div` i + i = fromIntegral i' diff --git a/src/Internal/Keys.hs b/src/Internal/Keys.hs index bc27750..d02e1f4 100644 --- a/src/Internal/Keys.hs +++ b/src/Internal/Keys.hs @@ -90,7 +90,7 @@ newKeys markContext = , ((modm , xK_Return), windows W.swapMaster) , ((modm, xK_j), sendMessage Shrink) , ((modm, xK_k), sendMessage Expand) - , ((modm .|. shiftMask, xK_r), (void $ spawn "gmrun")) + , ((modm .|. shiftMask, xK_r), sendMessage DoRotate) , ((modm .|. mod1Mask, xK_l), (void $ spawn "xsecurelock")) , ((modm .|. mod1Mask, xK_s), (void $ spawn "sudo systemctl suspend && xsecurelock")) , ((modm .|. shiftMask, xK_c), kill) diff --git a/src/Internal/Layout.hs b/src/Internal/Layout.hs index cb8c19b..632e912 100644 --- a/src/Internal/Layout.hs +++ b/src/Internal/Layout.hs @@ -1,6 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} module Internal.Layout where +import Internal.CornerLayout (Corner(..)) import Control.Arrow (second) import XMonad.Hooks.ManageDocks import XMonad.Layout.Circle @@ -24,18 +25,20 @@ import qualified XMonad.StackSet as W myLayout = avoidStruts $ - ModifiedLayout (Zoomable False 0.05 0.05) $ - ModifiedLayout (Flippable False) $ - ModifiedLayout (HFlippable False) $ - spacingRaw True (Border 5 5 5 5) True (Border 5 5 5 5) True $ - spiral (6/7) ||| - ModifyDescription TallDescriptionModifier (Tall 1 (3/100) (1/2)) ||| - ModifyDescription ThreeColDescMod (ThreeCol 1 (3/100) (1/2)) ||| - Full ||| - Grid ||| - Dishes 2 (1/6) ||| - (MosaicAlt M.empty :: MosaicAlt Window) ||| - (D.Dwindle D.R D.CW 1.5 1.1) + spacingRaw True (Border 5 5 5 5) True (Border 5 5 5 5) True $ + ModifiedLayout (Zoomable False 0.05 0.05) $ + ModifiedLayout (Flippable False) $ + ModifiedLayout (HFlippable False) $ + ModifiedLayout (Rotateable False) $ + spiral (6/7) ||| + (Corner (3/4) (3/100) :: Corner Window) ||| + ModifyDescription TallDescriptionModifier (Tall 1 (3/100) (1/2)) ||| + ModifyDescription ThreeColDescMod (ThreeCol 1 (3/100) (1/2)) ||| + Full ||| + Grid ||| + Dishes 2 (1/6) ||| + (MosaicAlt M.empty :: MosaicAlt Window) ||| + (D.Dwindle D.R D.CW 1.5 1.1) data ModifyDescription m l a = ModifyDescription m (l a) deriving (Show, Read) @@ -90,10 +93,15 @@ data Flippable a = Flippable Bool -- True if flipped data HFlippable a = HFlippable Bool -- True if flipped deriving (Show, Read) +data Rotateable a = Rotateable Bool -- True if rotated + deriving (Show, Read) + data FlipLayout = FlipLayout deriving (Typeable) data HFlipLayout = HFlipLayout deriving (Typeable) +data DoRotate = DoRotate deriving (Typeable) + data Zoomable a = Zoomable Bool Float Float -- True if zooming in on the focused window. deriving (Show, Read) @@ -111,6 +119,32 @@ instance Message HFlipLayout where instance Message ZoomModifier where +instance Message DoRotate where + +instance (Eq a) => LayoutModifier Rotateable a where + pureModifier (Rotateable rotate) (Rectangle _ _ sw sh) _ returned = + if rotate + then (map (second (scaleRect . mirrorRect)) returned, Nothing) + else (returned, Nothing) + where + scaleRect (Rectangle x y w h) = + Rectangle (x * fi sw `div` fi sh) + (y * fi sh `div` fi sw) + (w * sw `div` sh) + (h * sh `div` sw) + + fi = fromIntegral + + + pureMess (Rotateable rot) mess = + fmap (\(DoRotate) -> Rotateable (not rot)) (fromMessage mess) + + modifyDescription (Rotateable rot) underlying = + let descr = description underlying in + if rot + then descr ++ " Rotated" + else descr + instance (Eq a) => LayoutModifier Flippable a where pureModifier (Flippable flip) (Rectangle sx _ sw _) stack returned = if flip -- cgit From 62eccced2d4a756b719dae9c25dc3859360608c2 Mon Sep 17 00:00:00 2001 From: Josh Rahm <rahm@google.com> Date: Fri, 5 Nov 2021 16:23:46 -0600 Subject: Fix more things with the weather. --- extras/HOME/.config/compton.conf | 4 ++-- extras/HOME/.xmobarrc | 7 ++++--- extras/HOME/.xmonad/xmobar-weather | 35 +++++++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/extras/HOME/.config/compton.conf b/extras/HOME/.config/compton.conf index 828d949..c669100 100644 --- a/extras/HOME/.config/compton.conf +++ b/extras/HOME/.config/compton.conf @@ -13,8 +13,8 @@ shadow-red = 0.0; shadow-green = 0.0; shadow-blue = 0.0; -inactive-dim=0.1 -inactive-opacity=0.99 +inactive-dim=0.2 +inactive-opacity=0.98 shadow-exclude = [ # From the Ubuntu forums link ('screaminj3sus') diff --git a/extras/HOME/.xmobarrc b/extras/HOME/.xmobarrc index 90141e7..e2a3e9f 100644 --- a/extras/HOME/.xmobarrc +++ b/extras/HOME/.xmobarrc @@ -1,12 +1,13 @@ Config - { font = "xft:Monofur Nerd Font:size=15" + { font = "xft:Monofur Nerd Font:size=12" , additionalFonts = [ "xft:Monofur bold Nerd Font:style=bold:size=12", "xft:Monofur Nerd Font:size=9", "xft:Monofur Nerd Font:size=9", "xft:Monofur Nerd Font:size=6", "xft:Monofur bold Nerd Font:size=15", - "xft:Monofur Nerd Font:style=bold:size=10" + "xft:Monofur Nerd Font:style=bold:size=10", + "xft:Noto Sans Mono CJK JP:style=bold:size=10" ] , borderColor = "black" , border = FullBM -1 @@ -14,7 +15,7 @@ Config , bgColor = "#000000" , fgColor = "white" , alpha = 220 -- default: 255 - , position = TopSize L 100 40 + , position = TopSize L 100 50 , textOffset = -1 -- default: -1 , iconOffset = -1 -- default: -1 , lowerOnStart = True diff --git a/extras/HOME/.xmonad/xmobar-weather b/extras/HOME/.xmonad/xmobar-weather index bf84870..d9dc88b 100755 --- a/extras/HOME/.xmonad/xmobar-weather +++ b/extras/HOME/.xmonad/xmobar-weather @@ -4,8 +4,15 @@ use LWP::Simple; use Time::Local; use POSIX; +$content = get("https://ipinfo.io"); + +die "Unable to get IP info" unless defined $content; + +($city, $lat, $lon) = + ($content =~ m/.*"city":\s+"([^"]+)".*"loc":\s+"(-?[0-9.]+),(-?[0-9.]+).*"/ims); + $content = get( - "https://api.sunrise-sunset.org/json?lat=40.1672117&lng=-105.1019286&formatted=0"); + "https://api.sunrise-sunset.org/json?lat=$lat&lng=$lon&formatted=0"); die "Unable to get sunrise/sunset data" unless defined $content; @@ -40,14 +47,22 @@ if ($current_str gt $sunrise_str and $current_str lt $sunset_str) { } %directions = ( - NE => "↙", - SE => "↖", - NW => "↘", - SW => "↗", - N => "↓", - S => "↑", - W => "→", - E => "←" ); + NE => "", + NNE => "", + ENE => "", + SE => "", + SSE => "", + ESE => "", + NW => "", + NNW => "", + WNW => "", + SW => "", + SSW => "", + WSW => "", + N => "", + S => "", + W => "", + E => "" ); $dir=%directions{$wind_direction}; @@ -83,4 +98,4 @@ if ($is_day) { $conditions = %conditions_night{$sky_conditions}; } -printf("<fc=#a0a0a0>$dir <fn=3>${wind_speed}mph</fn></fc> $conditions</fc><fn=3> <fc=#a0a0a0>%.0f°F</fc></fn>\n", $temp); +printf("<fc=#a0a0a0><fn=3>$city</fn> <fn=3>$dir</fn> <fn=3>${wind_speed}</fn></fc> $conditions</fc><fn=3> <fc=#a0a0a0>%.0f°F</fc></fn>\n", $temp); -- cgit