elkowar / eww

ElKowars wacky widgets
https://elkowar.github.io/eww
MIT License
9.3k stars 382 forks source link

[BUG] eventbox onclick Script Succeeding/Failing Seemingly At Random #756

Open ghost opened 1 year ago

ghost commented 1 year ago

Checklist before submitting an issue

Description of the bug

I have a liitle crypto widget which calls a script to update the logo and price variables, thereby changing the current crypto in the widget:

image image image

The script works fine if called directly from the command line (even if called repeatedly), but fails maybe 50% of the time when called through clicking the eventbox. I should mention that I have ruled out the API rate-limit being the issue.

Here is the .yuck for the widget:

;(defpoll bitcoin :interval "60s" "runhaskell $HOME/.dotfiles/home-manager/modules/eww/config/scripts/crypto/GetCryptoPrice.hs 0")
(defpoll bitcoin :interval "60s" "runhaskell $HOME/.config/eww/scripts/crypto/GetCryptoPrice.hs 0")
;(defpoll cardano :interval "60s" "runhaskell $HOME/.dotfiles/home-manager/modules/eww/config/scripts/crypto/GetCryptoPrice.hs 1")
(defpoll cardano :interval "60s" "runhaskell $HOME/.config/eww/scripts/crypto/GetCryptoPrice.hs 1")
;(defpoll ethereum :interval "60s" "runhaskell $HOME/.dotfiles/home-manager/modules/eww/config/scripts/crypto/GetCryptoPrice.hs 2")
(defpoll ethereum :interval "60s" "runhaskell $HOME/.config/eww/scripts/crypto/GetCryptoPrice.hs 2")

; The inner box blow is there below so that the price fetching scripts work, 
; if I declare them in the literal they won't fire.

(defwidget crypto []
  (eventbox
    :class "crypto"
    :onclick "runhaskell $HOME/.config/eww/scripts/crypto/CycleCrypto.hs ${logo}"
    :timeout "10s"
    (box 
      :class "cryptoInnerBox"
      :valign "center"
      :space-evenly false
      :spacing 7
      (label :visible false :text "${bitcoin}")
      (label :visible false :text "${ethereum}")
      (image
        :image-height 25
        :image-width 25
        :path "${logo}"
      )
      (literal :valign "center" :content price)
    )
  )
)

(defvar logo "/home/isaac/.config/eww/images/cardano.png")
(defvar price "(label :text \"$\{cardano\}\")") 

And here is the .hs for the script:

import Data.List (find)
import Network.HTTP.Client qualified as HTTP
import Network.HTTP.Simple (Request, parseRequest)
import Network.HostName (getHostName)
import Safe (headMay)
import System.Environment (getArgs)
import System.Process (callCommand)

main :: IO ()
main = do
  args <- getArgs
  callCommand $ case headMay args of
    Just arg ->
      "eww update logo="
        ++ logoPath'
        ++ " && eww update price=\"(label :text \\\"\\${"
        ++ name'
        ++ "}\\\")\""
      where
        logoPath' = maybe "" logoPath (cycleCrypto cryptos arg)
        name' = maybe "" name (cycleCrypto cryptos arg)
    Nothing -> ""

cycleCrypto :: [Crypto] -> String -> Maybe Crypto
cycleCrypto cryptos str =
  case find ((== str) . logoPath) cryptos of
    Just x -> case dropWhile (/= x) (cycle cryptos) of
      x' : y : _ -> Just y
    Nothing -> Nothing

-- Datatypes:
data Crypto = Crypto
  { api :: Request,
    logoPath :: String,
    name :: String
  }
  deriving (Show, Eq)

instance Eq HTTP.Request where
  (==) :: Request -> Request -> Bool
  req1 == req2 =
    (HTTP.host req1, HTTP.path req1, HTTP.queryString req1)
      == (HTTP.host req2, HTTP.path req2, HTTP.queryString req2)

-- Functions:
makeCrypto :: String -> Crypto
makeCrypto str =
  Crypto
    { api = req1,
      logoPath = logoPath',
      name = str
    }
  where
    Right req1 = parseRequest ("https://api.coingecko.com/api/v3/simple/price?ids=" ++ str ++ "&vs_currencies=cad")
    logoPath' = "/home/isaac/.config/eww/images/" ++ str ++ ".png"

-- Data:
cryptos :: [Crypto]
cryptos = map makeCrypto ["bitcoin", "cardano", "ethereum"]

The logs show this in the ~50% of cases where clicking the event box does not change the crypto (the rest of the time it works with no log output):

 2023-04-25T17:11:42.815Z ERROR eww::widgets              > WARNING: command runhaskell $HOME/.config/eww/scripts/crypto/CycleCrypto.hs /home/isaac/.config/eww/images/cardano.png timed out
 2023-04-25T17:11:43.182Z ERROR eww::widgets              > WARNING: command runhaskell $HOME/.config/eww/scripts/crypto/CycleCrypto.hs /home/isaac/.config/eww/images/ethereum.png timed out
 2023-04-25T17:11:43.182Z ERROR eww::widgets              > WARNING: command runhaskell $HOME/.config/eww/scripts/crypto/CycleCrypto.hs /home/isaac/.config/eww/images/ethereum.png timed out
 2023-04-25T17:11:43.203Z ERROR eww::widgets              > WARNING: command runhaskell $HOME/.config/eww/scripts/crypto/CycleCrypto.hs /home/isaac/.config/eww/images/ethereum.png timed out
 2023-04-25T17:11:43.334Z ERROR eww::widgets              > WARNING: command runhaskell $HOME/.config/eww/scripts/crypto/CycleCrypto.hs /home/isaac/.config/eww/images/ethereum.png timed out
 2023-04-25T17:11:43.334Z ERROR eww::widgets              > WARNING: command runhaskell $HOME/.config/eww/scripts/crypto/CycleCrypto.hs /home/isaac/.config/eww/images/ethereum.png timed out

Reproducing the issue

You could spin up my config locally to reproduce the issue: https://gitlab.com/IsaacBrown92/dotfiles/-/tree/main/home-manager/modules/eww/config

Expected behaviour

Crypto widget should flip seamlessly between cryptos on click (unless API rate limit is hit).

Additional context

No response

musjj commented 1 year ago

Probably related to: https://github.com/elkowar/eww/issues/715

ghost commented 1 year ago

I see. Hopefully at some point this is addressed. I've been scripting with Haskell over the last while and I've basically learned that anywhere a Haskell script is called, it will fail some % of the time with a timeout warning in the logs. It's not fixable with the :timeout property, and it doesn't require spamming whatever the thing is that launches the script (button, input, etc) to make the error happen. It just seems to be a statistical inevitability. It's a bit frustrating because no matter how good I get things looking, they won't behave reliably if they for ex need user input (which will require running a script). It doesn't matter in cases where the script is run via polling (maybe it's also failing some amount of the time when run like this, but since the polling happens regularly enough it's not noticed). So really the issue becomes apparent with scripts that need to work everytime (like workspace switching script, or in the case of this thread crypto switching script), not so much with polling scripts.

musjj commented 1 year ago

For now, I'd suggest appending a & to your command like this: your/script.sh &. This will run the command in the background, which effectively nullifies eww's timeout limit.