diff options
65 files changed, 2452 insertions, 1258 deletions
diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 4b6e816..0000000 --- a/.drone.yml +++ /dev/null @@ -1,19 +0,0 @@ -matrix: - GHC_VERSION: - - 9 - - 8 - -pipeline: - test: - image: haskell:${GHC_VERSION} - commands: - - apt-get update - - apt-get install -y xorg-dev libxrandr-dev libpango1.0-dev - - apt-get install -y libasound2-dev libxpm-dev libmpd-dev - - apt-get install -y hspec-discover hlint - - - hlint src - - - cabal update - - cabal test --enable-tests -fall_extensions - - cabal test --enable-tests -fall_extensions -f-with_xrender diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml new file mode 100644 index 0000000..ef33379 --- /dev/null +++ b/.woodpecker/build.yaml @@ -0,0 +1,32 @@ +when: + - event: [push, pull_request, manual] + +matrix: + GHC: + # - '9.14' currently broken due to: https://github.com/gtk2hs/gtk2hs/issues/348 + # - '9.12' currently broken due to: https://github.com/gtk2hs/gtk2hs/issues/348 + - '9.10' + - '9.8' + - '9.6' + +steps: + - name: hlint + image: haskell:${GHC} + commands: + - apt-get update && apt-get install -y hlint + - hlint src + + - name: build-and-test + image: haskell:${GHC} + commands: + - apt-get update + - >- + apt-get install -y + xorg-dev libxrandr-dev libpango1.0-dev + libasound2-dev libxpm-dev libmpd-dev + hspec-discover + - cabal update + - cabal build all --only-dependencies --enable-tests -fall_extensions --disable-documentation + - cabal build all --enable-tests -fall_extensions --disable-documentation + - cabal test all --enable-tests -fall_extensions --disable-documentation + - cabal test all --enable-tests -fall_extensions -f-with_xrender --disable-documentation diff --git a/changelog.md b/changelog.md index b4f5009..404d59f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,85 @@ +## Version 0.51.1 (June, 2026) + +- Missing upper bounds for dependencies + +## Version 0.51 (June, 2026) + +- Improvements for `PacmanUpdates` (thanks, Enrico Maria and Alexander) +- Fixes and improvements to `Accordion` (thanks, Enrico Maria) +- Fixes to `Batt` (thanks, Leana) +- Fix BadDrawable crash when wallpaper setters free the root pixmap while + alpha is 255 (thanks, Ashesh) +- base dependency relaxed to 4.21 + +## Version 0.50 (June, 2025) + +- New plugins: `PacmanUpdates` (thanks, Alexander) +- `ArchUpdates` deprecated in favor of `PacmanUpdates` + - a deprecation notice will be shown to users of that plugin in the bar in + the zero updates case +- `Accordion`: new constructor to allow short version to have plugins too +- `Swap`: update for FreeBSD 15 +- `MPD` compiled again by default with `all_extensions`. + +## Version 0.49 (April, 2025) + +- New plugins: `ArchUpdates` and `Accordion` (thanks, Enrico Maria) +- New template variable `weatherS` for `WeatherX`. +- New Nix flake (thanks, refaelsh) +- `MPRIS`: Fix for duration reported by Spotify (thanks, Claudio) +- Base dep up to 4.20 (thanks, Alexander) + +## Version 0.48.1 (May, 2024) + +- Removing for now mouse support for `Kbd`, which can be recovered by + interested users using regular actions (see issue #703 for further details). + +## Version 0.48 (April, 2024) + +- The `Kbd` monitor is now clickable (thanks, Enrico Maria) +- Fix zombie processes left by `<action>` tag and low battery action (thanks, + Ulrik) +- Fix plugins such as `Alsa` and `Com` not working when configuration is + recompiled (#657, ditto) +- New `Lock'` monitor with configurable labels (thanks, Enrico Maria) + +## Version 0.47.4 (March, 2024) + +- Bug fixes (launching processes from self-compiled instances) + +## Version 0.47.3 (February, 2024) + +- New flag `with_shared` to use xmobar with dynamic linking (see PR #690) +- Fix for actions invoked from recompiled xmobar instances (issue #688) +- Maximum base version bumped to 4.20 + +## Version 0.47.2 (November, 2023) + +- Compatibility with GHC 9.6 + +## Version 0.47.1 (September, 2023) + +- Allow compilation with libmpd even with GHC > 9.4, by requesting it + explicitly with `with_mpd`. See #667 for further details. + +## Version 0.47 (August, 2023) + +- Fix: center middle section for templates of the for }M{ +- Documentation fixes and improvements +- Better error handling in network connections +- Dependency fixes (libmpd excluded for GHC > 9.2) + +## Version 0.46 (January, 2023) + +- New bar position specifiers TopHM, BottomHM. +- New configuration option, `dpi`, to set the font scaling factor. +- Fixes and extensions for section alignment behaviour (#650, #655). +- Fix: honour fc/bg specs for icons (#663). + +## Version 0.45 (October, 2022) + +- New cairo/pango font drawing backend, substituting the direct X11/Xft one. + ## Version 0.44.2 (August, 2022) - Documentation improvements. diff --git a/doc/accordion.gif b/doc/accordion.gif Binary files differnew file mode 100644 index 0000000..c21d2b0 --- /dev/null +++ b/doc/accordion.gif diff --git a/doc/compiling.org b/doc/compiling.org index 4e0125e..977ef2b 100644 --- a/doc/compiling.org +++ b/doc/compiling.org @@ -85,7 +85,9 @@ enters a high-CPU regime right after starting. - =with_xrender= Enables the main bar background alpha parameter. Requires - the [[http://hackage.haskell.org/package/X11-xft/][X11-xft]] package. + the [[http://hackage.haskell.org/package/X11-xft/][X11-xft]] package. The Xrender extension is not compatible with 10-bit + colour modes, i.e., setting ~DefaultDepth~ to 30 in your Xorg + configuration. See discussion in [[https://codeberg.org/xmobar/xmobar/issues/651][issue 651]] for details. - =with_xpm= Support for xpm image file format. This will allow loading .xpm files in =<icon>=. Requires the [[http://cgit.freedesktop.org/xorg/lib/libXpm][libXpm]] C library. diff --git a/doc/plugins.org b/doc/plugins.org index 2199659..ecc9507 100644 --- a/doc/plugins.org +++ b/doc/plugins.org @@ -16,11 +16,11 @@ to a certain monitor. All Monitors accept a common set of arguments, described below in - [[Default Monitor Arguments]]. Some monitors also accept additional + [[#default-arguments][Default Monitor Arguments]]. Some monitors also accept additional options that are specific to them. When specifying the list of arguments in your configuration, the common options come first, followed by =--=, followed by any monitor-specific options. For - example, the following [[=Battery Args RefreshRate=][Battery]] configuration first sets the global + example, the following [[#batteryp-dirs-args-refreshrate][Battery]] configuration first sets the global =template= and =Low= arguments and then specifies the battery-specific =off= option. @@ -60,6 +60,9 @@ brightness value. ** Default monitor arguments + :PROPERTIES: + :CUSTOM_ID: default-arguments + :END: These are the options available for all monitors: @@ -303,8 +306,8 @@ "--", "-O", "<fc=green>On</fc> - ", "-i", "", "-L", "-15", "-H", "-5", "-l", "red", "-m", "blue", "-h", "green", - "-a", "notify-send -u critical 'Battery running out!!'", - "-A", "3"] + "-a", "notify-once \"xmobar\" -u critical 'Battery running out!!' \"$XMOBAR_BATT_LEFT% Remains\"", + "-A", "6"] 600 #+end_src @@ -313,7 +316,22 @@ separator affect how =<watts>= is displayed. For this monitor, neither the generic nor the specific options have any effect on =<timeleft>=. We are also telling the monitor to execute the unix command - =notify-send= when the percentage left in the battery reaches 6%. + =notify-once= when the percentage left in the battery reaches 6%. + + =XMOBAR_BATT_LEFT= environment variable is made available to the program being called. + You can use it to make the alert message more informative. It is an integer between 0 and 100. + + =notify-once= is a bash wrapper script provided with the =xmobar= package. + =xmobar= will run the notification command according to the refresh period, + this has the effect of spamming the notification tray if calling =notify-send= directly. + =notify-once= script deduplicates the notification by indicating to =notify-send= + the last notification we want to replace. + See [[https://codeberg.org/xmobar/xmobar/issues/746][#746]] for more information. + Note that your notification daemon should handle notification replacement + for this to work. Whether the replacement resets the timeout is also handled by the + notification daemon. + For example, the =wired-notify= implementation has =replacing_enabled= + and =replacing_resets_timeout= configuration options [[https://github.com/Toqozz/wired-notify/wiki/Config]]. It is also possible to specify template variables in the =-O= and =-o= switches, as in the following example: @@ -694,18 +712,37 @@ #+begin_src haskell Run Brightness ["-t", "<bar>"] 60 #+end_src + *** =Locks= - Displays the status of Caps Lock, Num Lock and Scroll Lock. - Aliases to =locks= + - Contructors: + + - =Locks= is nullary and uses the strings =CAPS=, =NUM=, =SCROLL= to signal + that a lock is enabled (and empty strings to signal it's disabled) + + - =Locks'= allow customizing the strings for the enabled/disabled states + of the 3 locks by accepting an assoc list of type =[(String, (String, String))]=, + which is expected to contain exactly 3 elements with keys + ="CAPS"=, ="NUM"=, ="SCROLL"=. + - Example: #+begin_src haskell + -- using default labels Run Locks #+end_src + #+begin_src haskell + -- using custom labels + Run $ Locks' [("CAPS" , ("<fc=#00ff00>\xf023</fc>", "<fc=#777777>\xf09c</fc>") ) + ,("NUM" , ("<fc=#777777>\xf047</fc>", "<fc=#00ff00>\xf047</fc>" ) ) + ,("SCROLL", ("SlOCK", "" ))] + #+end_src + ** Load and Process monitors *** =Load Args RefreshRate= @@ -814,6 +851,9 @@ ** Volume monitors *** =Volume Mixer Element Args RefreshRate= + :PROPERTIES: + :CUSTOM_ID: volume + :END: - Aliases to the mixer name and element name separated by a colon. Thus, =Volume "default" "Master" [] 10= can be used as @@ -889,7 +929,7 @@ *** =Alsa Mixer Element Args= - Like [[=Volume Mixer Element Args RefreshRate=][Volume]] but with the following differences: + Like [[#volume][Volume]] but with the following differences: - Uses event-based refreshing via =alsactl monitor= instead of polling, so it will refresh instantly when there's a volume change, and won't @@ -1052,8 +1092,8 @@ ** Music monitors *** =MPD Args RefreshRate= - - This monitor will only be compiled if you ask for it using the - =with_mpd= flag. It needs [[http://hackage.haskell.org/package/libmpd/][libmpd]] 5.0 or later (available on Hackage). + - This monitor will only be compiled if you ask for it using the =with_mpd= + flag. It needs [[http://hackage.haskell.org/package/libmpd/][libmpd]] 0.10.1 or later (available on Hackage). - Aliases to =mpd= @@ -1243,9 +1283,11 @@ - Works in the same way as =Weather=, but takes an additional argument, a list of pairs from sky conditions to their replacement (typically a unicode string or an icon specification). - - Use the variable =skyConditionS= to display the replacement of the - corresponding sky condition. All other =Weather= template variables - are available as well. + - Use the variable =skyConditionS= or =weatherS= to display the replacement of + the corresponding sky condition. =weatherS= uses the string returned by + =weather= to look up a replacement, and, if that one is not found, it + looks up the value of =skyConditionS=. All other =Weather= template + variables are available as well. For example: @@ -1296,7 +1338,7 @@ #+begin_src haskell Run UVMeter "Brisbane" ["-H", "3", "-L", "3", "--low", "green", "--high", "red"] 900 #+end_src -** Other monitors +** Other monitors and plugins *** =CatInt n filename= - Reads and displays an integer from the file whose path is =filename= @@ -1326,6 +1368,126 @@ the display of those numeric fields. - Default template: =Up: <days>d <hours>h <minutes>m= +*** =PacmanUpdates (Zero, One, Many, Error) Rate= + + - *This constructor is deprecated. Use =PacmanUpdatesK=, =PacmanUpdatesPredicateK=, or =PacmanUpdatesNoK= instead.* + - Aliases to =pacman= + - =Zero=: a =String= to use when the system is up to date. + - =One=: a =String= to use when only one update is available. + - =Many=: a =String= to use when several updates are available; it can contain + a =?= character as a placeholder for the number of updates. + - =Error=: a =String= to use when pacman fails for unkown reasons (e.g. + network error) + - Example: + #+begin_src haskell + PacmanUpdates ("<fc=green>up to date</fc>", + "<fc=yellow>1 update</fc>, + "<fc=red>? updates</fc>", + "<fc=red>!Pacman Error!</fc>") + 600 + #+end_src + +*** =PacmanUpdatesK Rate KernName (Bool -> Either String (Int, Bool) -> String)= + + - Aliases to =pacman= + - =KernName=: a =String= containing the name of the kernel package, e.g. `linux`, `linux-lts`, … + - =(Bool -> Either String (Int, Bool) -> String)=: a function producing the + string to be shown by the plugin; it is fed with a `Bool` telling whether + the running kernel is older than the installed kernel, and an `Int` and + `Bool` telling the number of available updates and whether one of them is a + kernel update (or an error message if `checkupdates` fails). + - Example: + #+begin_src haskell + PacmanUpdatesK + 600 + "linux" + $ \oldKern mayb -> (if oldKern then "Running old kernel!" else "") ++ + case mayb of + Left _ -> "Some error occurred!" + Right (0, False) -> "Up to date" + Right (n, pendingK) | n >= 1 -> show n ++ " updates available" + ++ if pendingK then " including a kernel update" else "" + _ -> error "This is impossible" + #+end_src + +*** =PacmanUpdatesPredicateK Rate IsKernelUpdate (Bool -> Either String (Int, Bool) -> String)= + + - Aliases to =pacman= + - =IsKernelUpdate=: a =String -> Bool= predicate that receives each available + package name and returns =True= for kernel packages. This is useful for + distributions with versioned kernel package names, such as Manjaro's + =linux618= or =linux70= packages. + - =(Bool -> Either String (Int, Bool) -> String)=: a function producing the + string to be shown by the plugin; it is fed with a =Bool= telling whether + the running kernel is older than the installed kernel, and an =Int= and + =Bool= telling the number of available updates and whether one of them + matches the kernel predicate (or an error message if =checkupdates= fails). + - Example: + This example requires =import Data.Char (isDigit)= and + =import Data.List (stripPrefix)= in your configuration. + #+begin_src haskell + PacmanUpdatesPredicateK + 600 + ( \packageName -> + case stripPrefix "linux" packageName of + Just n -> not (null n) && all isDigit n + Nothing -> False + ) + $ \oldKern mayb -> (if oldKern then "Running old kernel!" else "") ++ + case mayb of + Left _ -> "Some error occurred!" + Right (0, False) -> "Up to date" + Right (n, pendingK) | n >= 1 -> show n ++ " updates available" + ++ if pendingK then " including a kernel update" else "" + _ -> error "This is impossible" + #+end_src + +*** =PacmanUpdatesNoK Rate (Bool -> Either String Int -> String)= + + - Aliases to =pacman= + - =(Bool -> Either String Int -> String)=: a function producing the + string to be shown by the plugin; it is fed with a `Bool` telling whether + the running kernel is older than the installed kernel, and an `Int` telling + the number of available updates (or an error message if `checkupdates` fails). + - Example: + #+begin_src haskell + PacmanUpdatesNoK + 600 + $ \oldKern mayb -> (if oldKern then "Running old kernel!" else "") ++ + case mayb of + Left _ -> "Some error occurred!" + Right 0 -> "" + Right n | n >= 1 -> show n ++ " updates available" + _ -> error "impossible" + #+end_src + +*** =makeAccordion Tuning [Runnable]= + + - Wraps other =Runnable= plugins and makes them all collapsible to a single string: + + [[file:accordion.gif]] + + - Aliases to =alias' tuning=, being =tuning= of type =Tuning=, so one can use multiple such "accordions" + - **Disclaimer**: This only works for Haskell =xmobar.hs= + - =Tuning=: the "settings", for which a default value is provided, + #+begin_src haskell + defaultTuning = Tuning { + alias' = "accordion" + , initial = True + , expand = "<>" + , shrink = "><" + } + #+end_src + - =expand=: =String= shown when the accordion is contracted (defaults to ="<>"=). + - =shrink=: =String= shown when the accordion is expanded (defaults to ="><"=). + - =initial=: =Bool= to tell whether the accordion is initially expanded (defaults to =True=). + - =[Runnable]=: a list of =Runnable= plugins + +*** =makeAccordion' Tuning [Runnable] [Runnable]= + + - Like =makeAccordion=, but it accepts two distinct lists of runnables to be shown in the two states. + - One possible usage is to have a long-vs-short rather than an expanded-vs-collapsed policy, but it's up to you. + * Interfacing with window managers :PROPERTIES: :CUSTOM_ID: interfacing-with-window-managers @@ -1355,6 +1517,9 @@ (re)start xmobar outside xmonad. *** =UnsafeXMonadLog= + :PROPERTIES: + :CUSTOM_ID: UnsafeXMonadLog + :END: - Aliases to UnsafeXMonadLog - Displays any text received by xmobar on the =_XMONAD_LOG= atom. @@ -1385,7 +1550,7 @@ - Aliases to =PropName= - Reads the X property named by =PropName= (a string) and displays its - value. The [[../etc/xmonadpropwrite.hs][etc/xmonadpropwrite.hs script]] in xmobar's distribution can be + value. The [[https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmonadpropwrite.hs][etc/xmonadpropwrite.hs script]] in xmobar's distribution can be used to set the given property from the output of any other program or script. @@ -1421,7 +1586,7 @@ - Aliases to UnsafeStdinReader - Displays any text received by xmobar on its standard input. - - Similar to [[=UnsafeXMonadLog=][UnsafeXMonadLog]], in the sense that it does not strip any + - Similar to [[#UnsafeXMonadLog][UnsafeXMonadLog]], in the sense that it does not strip any actions from the received text, only using =stdin= and not a property atom of the root window. Please be equally carefully when using this as when using =UnsafeXMonadLog=! @@ -1480,7 +1645,7 @@ =/tmp/xmobar_status= will reveal xmonad for 1.5 seconds and temporarily overwrite the window titles. - - Take a look at [[../etc/status.sh][etc/status.sh]] + - Take a look at [[https://codeberg.org/xmobar/xmobar/src/branch/master/etc/status.sh][etc/status.sh]] - Expands environment variables for the pipe path @@ -1547,8 +1712,6 @@ xmonad configuration (=xmonad.hs=), e.g. by using a custom =~/.xmonad/build= script. - - * Executing external commands In order to execute an external command you can either write the diff --git a/doc/quick-start.org b/doc/quick-start.org index d711138..fecd953 100644 --- a/doc/quick-start.org +++ b/doc/quick-start.org @@ -1,12 +1,12 @@ #+title: Quick start: using xmobar -Xmobar can either be configured using the configuration language, or [[file:using-haskell.org][used as a +Xmobar can either be configured using the configuration language, or [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/using-haskell.org][used as a Haskell library]] (similar to xmonad) and compiled with your specific configuration. For an example of a configuration file using the plain -configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you can have a look at -[[../etc/xmobar.hs][etc/xmobar.hs]] for an example of how to write your own xmobar using Haskell. +configuration language, see [[https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.config][etc/xmobar.config]], and you can have a look at +[[https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.hs][etc/xmobar.hs]] for an example of how to write your own xmobar using Haskell. -* Command Line Options +* Command line options xmobar can be either configured with a configuration file or with command line options. In the second case, the command line options will @@ -20,7 +20,7 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you This is the list of command line options (the output of =xmobar --help=): - #+begin_src shell + #+begin_example Usage: xmobar [OPTION...] [FILE] Options: -h, -? --help This help @@ -38,187 +38,350 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you -o --top Place xmobar at the top of the screen -b --bottom Place xmobar at the bottom of the screen -d --dock Don't override redirect from WM and function as a dock - -a alignsep --alignsep=alignsep Separators for left, center and right text - alignment. Default: '}{' - -s char --sepchar=char Character used to separate commands in - the output template. Default '%' - -t template --template=template Output template - -c commands --commands=commands List of commands to be executed - -C command --add-command=command Add to the list of commands to be executed - -x screen --screen=screen On which X screen number to start - -p position --position=position Specify position of xmobar. Same syntax as in config file - -T [format] --text[=format] Write output to stdout - - Mail bug reports and suggestions to <mail@jao.io> - #+end_src -* Configuration Options + -a alignsep --alignsep=alignsep Separators for left, center and right text + alignment. Default: '}{' + -s char --sepchar=char Character used to separate commands in + the output template. Default '%' + -t template --template=template Output template + -c commands --commands=commands List of commands to be executed + -C command --add-command=command Add to the list of commands to be executed + -x screen --screen=screen On which X screen number to start + -p position --position=position Specify position of xmobar. Same syntax as in config file + -T [format] --text[=format] Write output to stdout + -D dpi --dpi=dpi The DPI scaling factor. Default 96.0 + + Mail bug reports and suggestions to <mail@jao.io> + #+end_example + +* Configuration options :PROPERTIES: :CUSTOM_ID: configuration-options :END: ** Global options - Here are all the global configuration options that you can set within - the =Config= block in your configuration. + Here are all the global options that you can set within the =Config= block in + your configuration and will define the overall behaviour and looks of your + bar. - - =font= Name of the font to be used. +*** Fonts + :PROPERTIES: + :CUSTOM_ID: fonts + :END: - - =additionalFonts= Haskell-style list of fonts to be used with the - =fn=-template. See also =textOffsets= below. For example: + The following configuration options control the fonts used by xmobar: - #+begin_src haskell - additionalFonts = [iconFont, altIconFont] - #+end_src + - =font= Name, as a string, of the default font to use. - - =bgColor= Background color. + - =additionalFonts= Haskell-style list of fonts to us with the + =fn=-template. See also =textOffsets= below. For example: - - =fgColor= Default font color. + #+begin_src haskell + additionalFonts = [iconFont, altIconFont] + #+end_src + + - =dpi= The DPI scaling factor, as a decimal, to use. If 0, negative, or not + given, the default of 96 will be used, which corresponds to an average + screen. A 10pt font will therefore scale to 10pt * (1/72 pt/inch) * (96 + pixel/inch) = 13.3 pixel. This is especially useful for HiDPI displays. + + The global font is used by default when none of the others is specified + using the ~<fn=n>...</fn>~ markup, with ~n~ a 1-based index in the + ~additionalFonts~ array. So, for instance + + #+begin_src + <fn=2>some text</fn> + #+end_src - - =alpha= The transparency. 0 is transparent, 255 is opaque. + will use, in the configuration above, ~altIconFont~ to display "some text". - - =position= Top, TopH, TopP, TopW, TopSize, Bottom, BottomH, - BottomP, BottomW, BottomSize or Static (with x, y, width and height). + Font names use the [[https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html][Pango format]]. Here are a few simple examples: - TopP and BottomP take 2 arguments: left padding and right padding. + #+begin_example + DejaVu Sans Mono 10 - TopW and BottomW take 2 arguments: an alignment parameter (L for left, - C for centered, R for Right) and an integer for the percentage width - xmobar window will have in respect to the screen width. + Iosevka Comfy Semi-Bold Italic 12 - TopSize and BottomSize take 3 arguments: an alignment parameter, an - integer for the percentage width, and an integer for the minimum pixel - height that the xmobar window will have. + Noto Color Emoji 10 + #+end_example - TopH and BottomH take one argument (Int) which adjusts the bar height. + We start with a family name (DejaVu Sans Mono, Iosevka Comfy, etc.), + followed by optional, space-separated /style options/ (Semi-Bold Italic in + the second example above), and ending with a size, in points. - For example: + There are many possible style options (if your font supports them). They + can be - #+begin_src haskell - position = TopH 30 - #+end_src + - *Plain styles*: Normal, Roman, Oblique, Italic. + - *Variants*: Small-Caps, All-Small-Caps, Petite-Caps, All-Petite-Caps, + Unicase, Title-Caps. + - *Weights*: Thin, Ultra-Light, Extra-Light, Light, Semi-Ligh, Demi-Light, + Book, Regular, Medium, Semi-Bold, Demi-Bold, Bold, Ultra-Bold, + Extra-Bold, Heavy, Black, Ultra-Black, Extra-Black. + - *Strectch values:* Thin, Ultra-Light, Extra-Light, Light, Semi-Light, + Demi-Light, Book, Regular, Medium, Semi-Bold, Demi-Bold, Bold, + Ultra-Bold, Extra-Bold, Heavy, Black, Ultra-Black, Extra-Black. + - *Gravity values*: Not-Rotated, South, Upside-Down, North, Rotated-Left, + East, Rotated-Right, West. - to make a 30 tall bar on the top, or + So you can add up to 5 style options per family: - #+begin_src haskell - position = BottomH 30 - #+end_src + #+begin_example + Monospace Italic All-Small-Caps Extra-Light Thin North 12 + #+end_example - to make a 30 tall bar on the bottom of the screen. + It's also possible to specify a list of fonts, separating them by commas, + so that they act as fallbacks when the preceding one is not able to display + a given glyph. A bit confusingly, the styles and sizes come in reverse + order after the families: - #+begin_src haskell - position = BottomW C 75 - #+end_src + #+begin_example + Family 1, Family 2 Styles 2 Size 2, Styles 1 Size 1 + #+end_example - to place xmobar at the bottom, centered with the 75% of the screen - width. Or + For instance you could have: - #+begin_src haskell - position = BottomP 120 0 - #+end_src + #+begin_example + Souce Code Pro, Noto Color Emoji Regular 12, Semi-Bold 10 + #+end_example - to place xmobar at the bottom, with 120 pixel indent of the left. Or + to use Source Code Pro Semi-Bold 10 when possible, and fall back to Noto + Color Emoji Regular 12 for characters that the former cannot display. - #+begin_src haskell - position = Static { xpos = 0 , ypos = 0, width = 1024, height = 15 } - #+end_src +**** X11 Bitmap fonts - or + If you want to use traditional, non-aliased X11 fonts, you can do so via + the [[https://www.cl.cam.ac.uk/~mgk25/ucs-fonts.html][Unicode fonts and tools for X11]] package, which provides bitmap + versions of them, with a specification of the stytle ~"Fixed 8"~ for what + in the old days would have been something like + ~-Misc-Fixed-Medium-R-Normal--13-120-75-75-C-70-ISO10646-1~. See also + discussion in [[https://codeberg.org/xmobar/xmobar/issues/658][issue #658]]. - #+begin_src haskell - position = Top - #+end_src +*** Colors - - =textOffset= The vertical offset, in pixels, for the text baseline. If - negative or not given, xmobar will try to center text vertically. + - =bgColor= Background color. - - =textOffsets= A list of vertical offsets, in pixels, for the text - baseline, to be used with the each of the fonts in =additionalFonts= - (if any). If negative or not given, xmobar will try to center text - vertically for that font. + - =fgColor= Default font color. - - =iconOffset= The vertical offset, in pixels, for icons bottom line. If - negative or not given, xmobar will try to center icons vertically. + - =alpha= The transparency. 0 is transparent, 255 is opaque. - - =lowerOnStart= When True the window is sent the bottom of the window - stack initially. +*** Vertical offsets - - =hideOnStart= When set to True the window is initially not mapped, - i.e. hidden. It then can be toggled manually (for example using the - dbus interface) or automatically (by a plugin) to make it reappear. + By default, all text and icons in the bar will be vertically centered + according to the configured height of the bar. You can override that + behaviour with the following options: - - =allDesktops= When set to True (the default), xmobar will tell the - window manager explicitly to be shown in all desktops, by setting - =_NET_WM_DESKTOP= to 0xffffffff. + - =textOffset= The vertical offset, in pixels, for the text baseline. If + negative or not given, xmobar will try to center text vertically. - - =overrideRedirect= If you're running xmobar in a tiling window - manager, you might need to set this option to =False= so that it - behaves as a docked application. Defaults to =True=. + - =textOffsets= A list of vertical offsets, in pixels, for the text + baseline, to be used with the each of the fonts in =additionalFonts= + (if any). If negative or not given, xmobar will try to center text + vertically for that font. - - =pickBroadest= When multiple displays are available, xmobar will - choose by default the first one to place itself. With this flag set to - =True= (the default is =False=) it will choose the broadest one - instead. + - =iconOffset= The vertical offset, in pixels, for icons bottom line. If + negative or not given, xmobar will try to center icons vertically. - - =persistent= When True the window status is fixed i.e. hiding or - revealing is not possible. This option can be toggled at runtime. - Defaults to False. +*** Borders + + - =border= TopB, TopBM, BottomB, BottomBM, FullB, FullBM or NoBorder + (default). + + TopB, BottomB, FullB take no arguments, and request drawing a border + at the top, bottom or around xmobar's window, respectively. + + TopBM, BottomBM, FullBM take an integer argument, which is the margin, + in pixels, between the border of the window and the drawn border. + + - =borderColor= Border color. + + - =borderWidth= Border width in pixels. + + - =iconRoot= Root folder where icons are stored. For =<icon=path/>= if + path start with =/=, =./= or =../= it is interpreted as it is. + Otherwise it will have + + #+begin_src haskell + iconRoot ++ "/" + #+end_src - - =border= TopB, TopBM, BottomB, BottomBM, FullB, FullBM or NoBorder - (default). + prepended to it. Default is =.=. - TopB, BottomB, FullB take no arguments, and request drawing a border - at the top, bottom or around xmobar's window, respectively. +*** Bar position - TopBM, BottomBM, FullBM take an integer argument, which is the margin, - in pixels, between the border of the window and the drawn border. + - =position= Top, TopH, TopHM, TopP, TopW, TopSize, Bottom, BottomH, BottomHM, + BottomP, BottomW, BottomSize or Static (with x, y, width and height). - - =borderColor= Border color. + TopP and BottomP take 2 arguments: left padding and right padding. - - =borderWidth= Border width in pixels. + TopW and BottomW take 2 arguments: an alignment parameter (L for left, + C for centered, R for Right) and an integer for the percentage width + xmobar window will have in respect to the screen width. - - =iconRoot= Root folder where icons are stored. For =<icon=path/>= if - path start with =/=, =./= or =../= it is interpreted as it is. - Otherwise it will have + TopSize and BottomSize take 3 arguments: an alignment parameter, an + integer for the percentage width, and an integer for the minimum pixel + height that the xmobar window will have. - #+begin_src haskell - iconRoot ++ "/" - #+end_src + TopH and BottomH take one argument (Int) which adjusts the bar height. - prepended to it. Default is =.=. + For example: - - =commands= For setting the options of the programs to run (optional). + #+begin_src haskell + position = TopH 30 + #+end_src + + to make a 30 tall bar on the top, or + + #+begin_src haskell + position = BottomH 30 + #+end_src + + to make a 30 tall bar on the bottom of the screen. The corresponding + variants ~TopHM~ and ~BottomHM~ allow you to specify, in addition to a + height, margins (in pixels) with the borders of the screen (left, right + top and bottom); so they take five integers as arguments. For instance, + if you one a margin of 2 pixels to the left of the top bar in the above + example and 4 to its right and top, you could use: + + #+begin_src haskell + position = TopHM 30 2 4 4 0 + #+end_src + + and similarly for ~BottomHM~. + + #+begin_src haskell + position = BottomW C 75 + #+end_src + + to place xmobar at the bottom, centered with the 75% of the screen + width. Or + + #+begin_src haskell + position = BottomP 120 0 + #+end_src + + to place xmobar at the bottom, with 120 pixel indent of the left. Or + + #+begin_src haskell + position = Static { xpos = 0 , ypos = 0, width = 1024, height = 15 } + #+end_src - - =sepChar= The character to be used for indicating commands in the - output template (default '%'). + or + + #+begin_src haskell + position = Top + #+end_src + + - =lowerOnStart= When True the window is sent the bottom of the window + stack initially. + + - =hideOnStart= When set to True the window is initially not mapped, + i.e. hidden. It then can be toggled manually (for example using the + dbus interface) or automatically (by a plugin) to make it reappear. + + - =allDesktops= When set to True (the default), xmobar will tell the + window manager explicitly to be shown in all desktops, by setting + =_NET_WM_DESKTOP= to 0xffffffff. + + - =overrideRedirect= If you're running xmobar in a tiling window + manager, you might need to set this option to =False= so that it + behaves as a docked application. Defaults to =True=. + + - =pickBroadest= When multiple displays are available, xmobar will + choose by default the first one to place itself. With this flag set to + =True= (the default is =False=) it will choose the broadest one + instead. + + - =persistent= When True the window status is fixed i.e. hiding or + revealing is not possible. This option can be toggled at runtime. + Defaults to False. + + - =wmClass= The value for the window's X11 ~WM_CLASS~ property. Defaults + to "xmobar". + + - =wmName= The value for the window's X11 ~WM_NAME~ property. Defaults to + "xmobar". + +*** Text output + + - =textOutput= When True, instead of running as an X11 application, + write output to stdout, with optional color escape sequences. In + this mode, icon and action specifications are ignored. Default is + False. + + - =textOutputFormat= Plain, Ansi or Pango, to emit, when in text + mode, escape color sequences using ANSI controls (for terminals) or + pango markup. Default is Plain. + +*** Commands and monitors + + - =commands= The list of monitors and plugins to run, together with their + individual configurations. The [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/plugins.org][plugin documentation]] details all the + available monitors, and you can also create new ones using Haskell. See + the [[#commands-list][commands list section]] below for more. + + - =sepChar= The character to be used for indicating commands in the + output template (defaults to '%'). + + - =alignSep= a 2-character string for aligning text in the output + template. See [[#bar-sections][this section]] for details. + + - =template= The output template: a string telling xmobar how to display the + outputs of all the =commands= above. See [[#output-template][the next section]] for a full + description. + +** The =commands= list + :PROPERTIES: + :CUSTOM_ID: commands-list + :END: + + The =commands= configuration option is a list of commands information + and arguments to be used by xmobar when parsing the output template. + Each member of the list consists in a command prefixed by the =Run= + keyword. Each command has arguments to control the way xmobar is going + to execute it. + + The options consist in a list of commands separated by a comma and enclosed + by square parenthesis. + + Example: + + #+begin_src haskell + [Run Memory ["-t","Mem: <usedratio>%"] 10, Run Swap [] 10] + #+end_src - - =alignSep= a 2 character string for aligning text in the output - template. The text before the first character will be align to left, - the text in between the 2 characters will be centered, and the text - after the second character will be align to the right. + to run the Memory monitor plugin with the specified template, and the + swap monitor plugin, with default options, every second. And here's an + example of a template for the commands above using an icon: - - =template= The output template. + #+begin_src haskell + template = "<icon=/home/jao/.xmobar/mem.xbm/><memory> <swap>" + #+end_src - - =wmClass= The value for the window's X11 ~WM_CLASS~ property. Defaults - to "xmobar". + This example will run "xclock" command when date is clicked: - - =wmName= The value for the window's X11 ~WM_NAME~ property. Defaults to - "xmobar". + #+begin_src haskell + template = "<action=`xclock`>%date%</action>" + #+end_src - - =textOutput= When True, instead of running as an X11 application, - write output to stdout, with optional color escape sequences. In - this mode, icon and action specifications are ignored. Default is - False. + The only internal available command is =Com= (see below Executing + External Commands). All other commands are provided by plugins. xmobar + comes with some plugins, providing a set of system monitors, a standard + input reader, an Unix named pipe reader, a configurable date plugin, and + much more: we list all available plugins below. - - =textOutputFormat= Plain, Ansi or Pango, to emit, when in text - mode, escape color sequences using ANSI controls (for terminals) or - pango markup. Default is Plain. + Other commands can be created as plugins with the Plugin infrastructure. + See below. ** The output =template= + :PROPERTIES: + :CUSTOM_ID: output-template + :END: The output template is how xmobar will end up printing all of your configured commands. It must contain at least one command. Xmobar will parse the template and search for the command to be executed in the =commands= configuration option. First an =alias= will be searched (some plugins, such as =Weather= or =Network=, have default - aliases, see the [[./plugins.org][plugin documentation]]). After that, the command + aliases, see the [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/plugins.org][plugin documentation]]). After that, the command name will be tried. If a command is found, the arguments specified in the =commands= list will be used. @@ -242,7 +405,8 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you opacity, with two additional hex digits (e.g. #FF00000aa). - =<fn=1>string</fn>= will print =string= with the first font from - =additionalFonts=. The index =0= corresponds to the standard font. + =additionalFonts=. The index =0= corresponds to the standard font. The + standard font is also used if the index is out of bounds. - =<hspace=X/>= will insert a blank horizontal space of =X= pixels. For example, to add a blank horizontal space of 123 pixels, @@ -284,6 +448,37 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you See the subsections below for more information on ~<box/>~, ~<icon/>~ and ~<action/>~. + - The special characters =}= and ={= are used to delimit up to three sections + in the bar that are drawn and aligned independently. See [[#bar-sections][this section]] + for more. + +*** Bar sections + :PROPERTIES: + :CUSTOM_ID: bar-sections + :END: + + You can use the special characters =}= and ={= are used to delimit up to three + sections in the bar, which are aligned and, if needed, overlapped + according to these rules: + + - If the template has the form =L}M{R=, with L, R, M arbitrary specs, the + monitors in =L= are drawn first, aligned to the left, then =R=, aligned to + the right, and finally =M= is drawn centered in the bar. =R= is trimmed to + the space left by =L=, and =M= is trimmed to the space left by =L= and =R=. As + a particular case, =}M{= will draw a single segment centered in the bar. + + - If the template has the form =L}{R=, =L= is drawn aligned to the left first + and then =R=, aligned to the right and trimmed if needed to fit in the + space left by =L=. + + - If the template has the form =}L{R=, =R= is drawn first, aligned to the + right, and then =L=, aligned to the left and trimmed to the space left by + =R=. + + When needed, sections are always trimmed on the right. The section + delimiters can be changed using the configuration option =alignSep,= a + two-character string. + *** Boxes around text - =<box>string</box>= will print string surrounded by a box in the @@ -319,7 +514,7 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you types, colors and widths are valid too, but margins and offsets are ignored. -*** Bitmap Icons +*** Bitmap icons It's possible to insert in the global templates icon directives of the form: @@ -344,7 +539,7 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you Icons are ignored when xmobar is run in text output mode. -*** Action Directives +*** Mouse actions It's also possible to use action directives of the form: @@ -359,46 +554,6 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you Actions work also when xmobar is run in text mode and used as the status command of swaybar. -** The =commands= configuration option - - The =commands= configuration option is a list of commands information - and arguments to be used by xmobar when parsing the output template. - Each member of the list consists in a command prefixed by the =Run= - keyword. Each command has arguments to control the way xmobar is going - to execute it. - - The option consists in a list of commands separated by a comma and - enclosed by square parenthesis. - - Example: - - #+begin_src haskell - [Run Memory ["-t","Mem: <usedratio>%"] 10, Run Swap [] 10] - #+end_src - - to run the Memory monitor plugin with the specified template, and the - swap monitor plugin, with default options, every second. And here's an - example of a template for the commands above using an icon: - - #+begin_src haskell - template = "<icon=/home/jao/.xmobar/mem.xbm/><memory> <swap>" - #+end_src - - This example will run "xclock" command when date is clicked: - - #+begin_src haskell - template = "<action=`xclock`>%date%</action>" - #+end_src - - The only internal available command is =Com= (see below Executing - External Commands). All other commands are provided by plugins. xmobar - comes with some plugins, providing a set of system monitors, a standard - input reader, an Unix named pipe reader, a configurable date plugin, and - much more: we list all available plugins below. - - Other commands can be created as plugins with the Plugin infrastructure. - See below. - * Runtime behaviour ** Running xmobar in text mode :PROPERTIES: @@ -430,7 +585,7 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you Other options are ~Ansi~, ~Pango~, and ~Swaybar~. ** Showing xmobar output in Emacs tab or mode line Using xmobar's ANSI color text ouput, one can plug it inside Emacs, and - display your monitors in the mode line or the tab bar. The [[../etc/xmobar.el][xmobar.el + display your monitors in the mode line or the tab bar. The [[https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.el][xmobar.el package]] provides a simple way of doing it. ** Using xmobar in wayland with swaybar or waybar :PROPERTIES: @@ -474,7 +629,7 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you dynamically xmobar's size and run it alongside a system tray widget such as trayer or stalonetray (although the idea is not limited to trays, really). For your convenience, there is a version of Jonas' script in - [[../etc/padding-icon.sh][etc/padding-icon.sh]]. + [[https://codeberg.org/xmobar/xmobar/src/branch/master/etc/padding-icon.sh][etc/padding-icon.sh]]. ** Signal handling @@ -484,10 +639,10 @@ configuration language, see [[../etc/xmobar.config][etc/xmobar.config]], and you - After receiving ~SIGUSR2~ xmobar repositions itself on the current screen. -* The DBus Interface +* The DBus interface When compiled with the optional =with_dbus= flag, xmobar can be controlled - over dbus. All signals defined in [[../src/Xmobar/System/Signal.hs][src/Signal.hs]] as =data SignalType= can now + over dbus. All signals defined in [[https://codeberg.org/xmobar/xmobar/src/branch/master/src/Xmobar/System/Signal.hs][src/Signal.hs]] as =data SignalType= can now be sent over dbus to xmobar. Due to current limitations of the implementation only one process of xmobar diff --git a/doc/using-haskell.org b/doc/using-haskell.org index 4020557..9fd3d88 100644 --- a/doc/using-haskell.org +++ b/doc/using-haskell.org @@ -26,7 +26,7 @@ config :: Config config = defaultConfig - { font = "xft:Terminus-8", + { font = "DejaVu Sans Mono 9", allDesktops = True, alpha = 200, commands = @@ -40,7 +40,7 @@ } main :: IO () - main = xmobar config + main = xmobar config -- or: configFromArgs config >>= xmobar #+end_src You can then for instance run =ghc --make xmobar.hs= to create a new xmobar @@ -49,6 +49,14 @@ system-wide xmobar, it will notice that you have your own implementation and (re)compile and run it as needed. +* Using dynamic linking + + Setting the =with_shared= flag, which is off by default, enables (re)building + of xmobar with shared libraries. By default, =xmobar= rebuilds itself with + static linking, as is the common practice with Haskell programs, but some + distributions use dynamic linking in their packages, or you might also be so + inclined. If so, this is your flag. + * Writing a plugin :PROPERTIES: :CUSTOM_ID: writing-a-plugin @@ -75,8 +83,8 @@ =run= can be used for simpler plugins. If you define only =run= the plugin will be run every second. To overwrite this default you just need to implement =rate=, which must return the number of tenth of seconds between - every successive runs. See [[../etc/xmobar.hs][etc/xmobar.hs]] for an example of a plugin - that runs just once, and [[../src/Xmobar/Plugins/Date.hs][src/Xmobar/Plugins/Date.hs]] for one that + every successive runs. See [[https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.hs][etc/xmobar.hs]] for an example of a plugin + that runs just once, and [[https://codeberg.org/xmobar/xmobar/src/branch/master/src/Xmobar/Plugins/Date.hs][src/Xmobar/Plugins/Date.hs]] for one that implements =rate=. Notice that Date could be implemented as: @@ -107,14 +115,14 @@ If your plugin only implements =alias= and =start=, then it is advisable to put it into the =Xmobar/Plugins/Monitors= directory and use one of the many - =run*= functions in [[../src/Xmobar/Plugins/Monitors/Common/Run.hs][Xmobar.Plugins.Monitors.Run]] in order to define - =start=. The =Exec= instance should then live in [[../src/Xmobar/Plugins/Monitors.hs][Xmobar.Plugins.Monitors]]. + =run*= functions in [[https://codeberg.org/xmobar/xmobar/src/branch/master/src/Xmobar/Plugins/Monitors/Common/Run.hs][Xmobar.Plugins.Monitors.Run]] in order to define + =start=. The =Exec= instance should then live in [[https://codeberg.org/xmobar/xmobar/src/branch/master/src/Xmobar/Plugins/Monitors.hs][Xmobar.Plugins.Monitors]]. * Using a plugin To use your new plugin, you just need to use a pure Haskell configuration for xmobar (as explained [[#xmobar-in-haskell][above]]) and load your definitions in your =xmobar.hs= - file. You can see an example in [[../etc/xmobar.hs][etc/xmobar.hs]] showing you how to write + file. You can see an example in [[https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.hs][etc/xmobar.hs]] showing you how to write a Haskell configuration that uses a new plugin, all in one file. When xmobar runs with the full path to that Haskell file as its argument diff --git a/etc/notify-once.sh b/etc/notify-once.sh new file mode 100644 index 0000000..1c46401 --- /dev/null +++ b/etc/notify-once.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -euo pipefail + +help() { + echo "notify-send: send deduplicated notifications" + echo "Usage: notify-once <name> [ARGS]" + echo " ARGS are arguments passed directly to notify-send" +} + +if [ $# -lt 1 ]; then + echo "Should get at least one argument, a name" >&2 + help + exit 1 +fi +APPNAME="$1" +shift + +ID_PATH="/tmp/notify-state-$APPNAME" + +if [ -e "$ID_PATH" ]; then + # Exists, replace + ID=$(cat "$ID_PATH") + ID=$(notify-send -r "$ID" -p "$@") +else + # Doesn't exist, create + ID=$(notify-send -p "$@") +fi + +# Store new ID +echo "$ID" >"$ID_PATH" diff --git a/flake.lock b/flake.lock deleted file mode 100644 index e60d10e..0000000 --- a/flake.lock +++ /dev/null @@ -1,75 +0,0 @@ -{ - "nodes": { - "flake-utils": { - "locked": { - "lastModified": 1653893745, - "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "git-ignore-nix": { - "inputs": { - "nixpkgs": "nixpkgs" - }, - "locked": { - "lastModified": 1646480205, - "narHash": "sha256-kekOlTlu45vuK2L9nq8iVN17V3sB0WWPqTTW3a2SQG0=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "bff2832ec341cf30acb3a4d3e2e7f1f7b590116a", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "ref": "master", - "repo": "gitignore.nix", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1632846328, - "narHash": "sha256-sFi6YtlGK30TBB9o6CW7LG9mYHkgtKeWbSLAjjrNTX0=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "2b71ddd869ad592510553d09fe89c9709fa26b2b", - "type": "github" - }, - "original": { - "id": "nixpkgs", - "type": "indirect" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1654398695, - "narHash": "sha256-Kw/KeoFXszNsF5mORP45mrxCP+k9Aq03hWcuWCL9sdI=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "c5d810f4c74c824ae0fb788103003c6c9d366a08", - "type": "github" - }, - "original": { - "id": "nixpkgs", - "type": "indirect" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "git-ignore-nix": "git-ignore-nix", - "nixpkgs": "nixpkgs_2" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 7471fb9..0000000 --- a/flake.nix +++ /dev/null @@ -1,45 +0,0 @@ -{ - inputs = { - git-ignore-nix.url = "github:hercules-ci/gitignore.nix/master"; - flake-utils.url = "github:numtide/flake-utils"; - }; - outputs = { self, nixpkgs, flake-utils, git-ignore-nix }: - let - overlay = final: prev: { - haskellPackages = prev.haskellPackages.override (old: { - overrides = prev.lib.composeExtensions (old.overrides or (_: _: { })) - (hself: hsuper: { - xmobar = prev.haskell.lib.compose.dontCheck (hself.callCabal2nix "xmobar" - (git-ignore-nix.lib.gitignoreSource ./.) { }); - }); - }); - }; - overlays = [ overlay ]; - in flake-utils.lib.eachDefaultSystem (system: - let pkgs = import nixpkgs { inherit system overlays; }; - dynamicLibraries = with pkgs; [ - xorg.libX11 - xorg.libXrandr - xorg.libXrender - xorg.libXScrnSaver - xorg.libXext - xorg.libXft - xorg.libXpm.out - xorg.libXrandr - xorg.libXrender - ]; - in rec { - devShell = pkgs.haskellPackages.shellFor { - packages = p: [ p.xmobar ]; - buildInputs = with pkgs; [ - haskellPackages.cabal-install - #haskellPackages.haskell-language-server - ] ++ dynamicLibraries; - - LD_LIBRARY_PATH = pkgs.lib.strings.makeLibraryPath dynamicLibraries; - }; - defaultPackage = pkgs.haskellPackages.xmobar; - }) // { - inherit overlay overlays; - }; -} diff --git a/nix/default.nix b/nix/default.nix new file mode 100644 index 0000000..aea52ed --- /dev/null +++ b/nix/default.nix @@ -0,0 +1,130 @@ +{ + config, + lib, + pkgs, + ... +}: +with lib; +let + cfg = config.programs.nixmobar; +in +{ + options.programs.nixmobar = { + enable = mkEnableOption (mdDoc "Xmobar, a minimalistic status bar"); + + font = mkOption { + type = types.str; + default = "Fira Code 13"; + description = mdDoc "Main font for Xmobar."; + }; + + additionalFonts = mkOption { + type = types.listOf types.str; + default = [ "Fira Code 22" ]; + description = mdDoc "Additional fonts for use in Xmobar."; + }; + + bgColor = mkOption { + type = types.str; + default = "#282a36"; + description = mdDoc "Background color of Xmobar."; + }; + + fgColor = mkOption { + type = types.str; + default = "#f8f8f2"; + description = mdDoc "Foreground (text) color of Xmobar."; + }; + + textOffset = mkOption { + type = types.int; + default = 2; + description = mdDoc "Offset of the text from the edge."; + }; + + verbose = mkOption { + type = types.bool; + default = true; + description = mdDoc "Enable verbose mode for Xmobar."; + }; + + allDesktops = mkOption { + type = types.bool; + default = true; + description = mdDoc "Show Xmobar on all desktops."; + }; + + lowerOnStart = mkOption { + type = types.bool; + default = true; + description = mdDoc "Whether Xmobar should be lowered on start."; + }; + + overrideRedirect = mkOption { + type = types.bool; + default = true; + description = mdDoc "If true, Xmobar will bypass window manager redirection."; + }; + + position = mkOption { + type = types.str; + default = "BottomH 26"; + description = mdDoc "Position of Xmobar on the screen."; + }; + + alpha = mkOption { + type = types.int; + default = 200; + description = mdDoc "Transparency level of Xmobar (0-255)."; + }; + + commands = mkOption { + type = types.lines; + default = '' + Run XMonadLog + Run DiskU ["/", "<fc=#bd93f9><fn=1>\\xf0a0</fn></fc> <free>"] [] 50 + Run DiskIO ["/", "<read><fc=#bd93f9> R</fc> <fc=#bd93f9>W</fc> <write>"] ["-t", "", "-w", "4"] 10 + Run Date "%a %_d %b %H:%M:%S" "date" 10 + # Add more commands here, one per line + ''; + description = mdDoc "List of commands to run in Xmobar, each on a new line."; + }; + + alignSep = mkOption { + type = types.str; + default = "}{"; + description = mdDoc "Separators for alignment left and right."; + }; + + template = mkOption { + type = types.str; + default = "<hspace=8/>%XMonadLog% }{ %load%|%disku%|%diskio%|<fc=#bd93f9><fn=1></fn></fc>%wifi_signal%|%dynnetwork%|<fc=#bd93f9><fn=1></fn></fc>%cat0%|%multicoretemp%|%cpufreq%|%multicpu%|<fc=#bd93f9><fn=1></fn></fc>%kbd%|%memory% %swap%|%battery%|%alsa:default:Master%|<fc=#bd93f9><fn=1></fn></fc>%kernel_version%|%date%|%_XMONAD_TRAYPAD%"; + description = mdDoc "Template string for Xmobar layout."; + }; + }; + + config = mkIf cfg.enable { + home.packages = [ pkgs.xmobar ]; + xdg.configFile."xmobar/.xmobarrc" = { + text = # haskell + '' + Config { + font = "${cfg.font}", + additionalFonts = [${lib.concatMapStringsSep ", " (s: "\"" + s + "\"") cfg.additionalFonts}], + bgColor = "${cfg.bgColor}", + fgColor = "${cfg.fgColor}", + textOffset = ${toString cfg.textOffset}, + verbose = ${if cfg.verbose then "True" else "False"}, + allDesktops = ${if cfg.allDesktops then "True" else "False"}, + lowerOnStart = ${if cfg.lowerOnStart then "True" else "False"}, + overrideRedirect = ${if cfg.overrideRedirect then "True" else "False"}, + position = ${cfg.position}, + alpha = ${toString cfg.alpha}, + commands = [${cfg.commands}], + alignSep = "${cfg.alignSep}", + template = "${cfg.template}" + } + ''; + }; + }; +} diff --git a/nix/flake.lock b/nix/flake.lock new file mode 100644 index 0000000..c4d98f4 --- /dev/null +++ b/nix/flake.lock @@ -0,0 +1,95 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "git-ignore-nix": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "ref": "master", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1666603677, + "narHash": "sha256-apAEIj+z1iwMaMJ4tB21r/VTetfGDLDzuhXRHJknIAU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "074da18a72269cc5a6cf444dce42daea5649b2fe", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1781074563, + "narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "git-ignore-nix": "git-ignore-nix", + "nixpkgs": "nixpkgs_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/nix/flake.nix b/nix/flake.nix new file mode 100644 index 0000000..2c0dbfb --- /dev/null +++ b/nix/flake.nix @@ -0,0 +1,64 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + git-ignore-nix.url = "github:hercules-ci/gitignore.nix/master"; + flake-utils.url = "github:numtide/flake-utils"; + }; + outputs = + { + self, + nixpkgs, + flake-utils, + git-ignore-nix, + }: + let + overlay = final: prev: { + haskellPackages = prev.haskellPackages.override (old: { + overrides = prev.lib.composeExtensions (old.overrides or (_: _: { })) ( + hself: hsuper: { + xmobar = prev.haskell.lib.compose.dontCheck ( + hself.callCabal2nix "xmobar" (git-ignore-nix.lib.gitignoreSource ../.) { } + ); + } + ); + }); + }; + overlays = [ overlay ]; + + homeModules.mainmodule = import ./default.nix; + in + flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { inherit system overlays; }; + dynamicLibraries = with pkgs; [ + libX11 + libXrandr + libXrender + libXScrnSaver + libXext + libXft + libXpm.out + libXrandr + libXrender + ]; + in + { + devShell = pkgs.haskellPackages.shellFor { + packages = p: [ p.xmobar ]; + buildInputs = + with pkgs; + [ + haskellPackages.cabal-install + ] + ++ dynamicLibraries; + + LD_LIBRARY_PATH = pkgs.lib.strings.makeLibraryPath dynamicLibraries; + }; + defaultPackage = pkgs.haskellPackages.xmobar; + } + ) + // { + inherit overlay overlays homeModules; + }; +} diff --git a/nix/readme.org b/nix/readme.org new file mode 100644 index 0000000..6a492e5 --- /dev/null +++ b/nix/readme.org @@ -0,0 +1,80 @@ +* Nix flake for Xmobar +This flake exposes a module to use with home-manager. +Here is how to set it up: + +1. Add the nixmobar flake as an input to your NiOS flake: + #+BEGIN_SRC nix + inputs = { + nixmobar.url = "git+https://codeberg.org/xmobar/xmobar.git/?dir=nix"; + }; + #+END_SRC + +2. And then, where you import your `home.nix`, add `extraSpecialArgs` like this: + #+BEGIN_SRC nix + home-manager = { + extraSpecialArgs = { + inherit inputs nixmobar; + }; + users.refaelsh = import ./home.nix; + }; + #+END_SRC + +3. Final step, use the module in `home.nix`: + #+BEGIN_SRC nix + { + inputs, + ... + }: + { + home.stateversion = "24.05"; + + imports = [ + inputs.nixmobar.homeModules.mainmodule + # Other imports go here. + ]; + + } + #+END_SRC + +* Example usage +#+BEGIN_SRC haskell +{ + programs.nixmobar = { + enable = true; + font = "Fira Code 13"; + additionalFonts = [ "Fira Code 22" ]; + bgColor = "#282A36"; + fgColor = "#F8F8F2"; + textOffset = 2; + verbose = true; + allDesktops = true; + lowerOnStart = true; + overrideRedirect = true; + position = "BottomH 26"; + alpha = 200; + alignSep = "}{"; + template = "<hspace=8/>%XMonadLog% }{ %load%|%disku%|%diskio%|<fc=#bd93f9><fn=1></fn></fc>%wifi_signal%|%dynnetwork%|<fc=#bd93f9><fn=1></fn></fc>%cat0%|%multicoretemp%|%cpufreq%|%multicpu%|<fc=#bd93f9><fn=1></fn></fc>%kbd%|%memory% %swap%|%battery%|%alsa:default:Master%|<fc=#bd93f9><fn=1></fn></fc>%kernel_version%|%date%|%_XMONAD_TRAYPAD%"; + commands = # haskell + '' + Run XMonadLog, + Run DiskU [("/", "<fc=#bd93f9><fn=1>\xf0a0</fn></fc> <free>")] [] 50, + Run DiskIO [("/", "<read><fc=#bd93f9> R</fc> <fc=#bd93f9>W</fc> <write>")] ["-t", "", "-w", "4"] 10, + Run DynNetwork ["-t", "<rx>KB/s<fc=#bd93f9><fn=1>\x1F89B</fn></fc><fc=#bd93f9><fn=1>\x1F899</fn></fc><tx>KB/s", "-w", "5"] 10, + Run Memory ["-t", "<fc=#bd93f9><fn=1>\xE266</fn></fc><usedratio>%"] 10, + Run Swap ["-t", "<fc=#bd93f9>S</fc><usedratio>%"] 10, + Run Kbd [], + Run CpuFreq ["-t", "<avg>GHz"] 50, + Run MultiCoreTemp ["-t", "<fc=#bd93f9><fn=1>\xf2c9</fn></fc><avg>°", "-L", "60", "-H", "95", "-l", "white", "-n", "white", "-h", "red"] 50, + Run CatInt 0 "/sys/class/hwmon/hwmon4/fan1_input" [] 50, + Run MultiCpu ["-t", "<fc=#bd93f9><fn=1>\xf4bc</fn></fc> <vbar0><vbar1><vbar2><vbar3><vbar4><vbar5><vbar6><vbar7>", "-w", "99", "-L", "3", "-H", "50", "--normal", "green", "--high", "red"] 10, + Run BatteryP ["BAT0"] ["-t", "<fc=#bd93f9><fn=1></fn></fc><left>%", "-L", "10", "-H", "80", "-p", "3", "--", "-O", "<fc=green>On</fc> - ", "-i", "", "-L", "-15", "-H", "-5", "-l", "red", "-m", "blue", "-h", "green", "-a", "notify-once \"xmobar\" -u critical 'Battery running out!!'", "-A", "3"] 600, + Run Alsa "default" "Master" ["-t", "<fc=#bd93f9><fn=1>\xf028</fn></fc> <volume>%"], + Run Date "%a %_d %b %H:%M:%S" "date" 10, + Run Load ["-t", "<fc=#bd93f9><fn=0>L</fn></fc><load1>", "-L", "1", "-H", "3", "-d", "2"] 300, + Run ComX "nmcli" ["-t", "-f", "SIGNAL", "dev", "wifi"] "N/A" "wifi_signal" 50, + Run Com "uname" ["-r"] "kernel_version" 3600, + Run XPropertyLog "_XMONAD_TRAYPAD" + ''; + }; +} +#+END_SRC @@ -5,8 +5,8 @@ <a href="http://hackage.haskell.org/package/xmobar"> <img src="https://img.shields.io/hackage/v/xmobar.svg" alt="hackage"/> </a> - <a href="https://ci.codeberg.org/xmobar/xmobar"> - <img src="https://ci.codeberg.org/api/badges/xmobar/xmobar/status.svg" alt="ci"/> + <a href="https://ci.codeberg.org/repos/17487" target="_blank"> + <img src="https://ci.codeberg.org/api/badges/17487/status.svg" alt="status-badge" /> </a> </p> #+end_export @@ -26,9 +26,25 @@ output, in a variety of formats. [[file:doc/screenshots/xmobar-exwm.png]] -Check [[./changelog.md][the change log]] for our release history. We also have an IRC +Check [[https://codeberg.org/xmobar/xmobar/src/branch/master/changelog.md][the change log]] for our release history. We also have an IRC channel, ~#xmobar~, at [[ircs://irc.libera.chat][Libera]]. +* Breaking news + + - Starting with version 0.51, we introduced a new GHC support policy: xmobar + officially supports only the GHC versions that upstream marks as /suitable + for use/ in the [[https://gitlab.haskell.org/ghc/ghc/-/wikis/GHC-status][official GHC status overview]]. + At the time of writing, this means GHC versions from 9.6 to 9.14. + + - Starting with version 0.45, we use cairo/pango as our drawing engine + (instead of plain X11/Xft). From a user's point of view, that change + should be mostly transparent, except for the facts that it's allowed + fixing quite a few bugs and that your /font names/ in your configuration, if + you used ~xft~ ones, might need to be adapted to Pango's syntax: please see + [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/quick-start.org#fonts][this section of the documentation]] for all the details. If you're + compiling your own xmobar, there's a new dependency on libpango (see + [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/compiling.org#c-libraries][C library dependencies]]). + * Installation :PROPERTIES: :CUSTOM_ID: installation @@ -64,10 +80,15 @@ channel, ~#xmobar~, at [[ircs://irc.libera.chat][Libera]]. emerge --ask xmobar #+end_src + - FreeBSD + #+begin_src shell + pkg install hs-xmobar + #+end_src + ** Using cabal or stack Xmobar is available from [[http://hackage.haskell.org/package/xmobar/][Hackage]], and you can compile and install it using - =cabal-install=, making sure the [[doc/compiling.org#c-libraries][required C libraries]] are in place. For a + =cabal-install=, making sure the [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/compiling.org#c-libraries][required C libraries]] are in place. For a full build with all available extensions: #+begin_src shell @@ -79,13 +100,10 @@ channel, ~#xmobar~, at [[ircs://irc.libera.chat][Libera]]. cabal install xmobar -fall_extensions #+end_src - Starting with version 0.35.1, xmobar requires at least GHC version - 8.4.x. to build. See [[https://codeberg.org/xmobar/xmobar/issues/461][this issue]] for more information. - - See [[file:doc/compiling.org#optional-features][here]] for a list of optional compilation flags that will enable some + See [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/compiling.org#optional-features][here]] for a list of optional compilation flags that will enable some optional plugins. - See [[file:doc/compiling.org][compiling]] for full compilation instructions starting from source. + See [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/compiling.org][compiling]] for full compilation instructions starting from source. * Running xmobar ** Running xmobar with a configuration file @@ -106,7 +124,7 @@ channel, ~#xmobar~, at [[ircs://irc.libera.chat][Libera]]. or =~/.xmobarrc=. All the available command line switches and configuration parameters are - described in [[file:quick-start.org][the quick start guide]] and [[file:doc/plugins.org][the plugins index]]. + described in [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/quick-start.org][the quick start guide]] and [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/plugins.org][the plugins index]]. ** Writing your own xmobar in Haskell @@ -116,7 +134,7 @@ channel, ~#xmobar~, at [[ircs://irc.libera.chat][Libera]]. Haskell instead of using a configuration file. (This is very similar to how [[http://xmonad.org][xmonad]] works.) That gives you the ability of using Haskell and its libraries to extend xmobar to your heart's content. If you are a - programmer, take a look [[file:doc/using-haskell.org][here]] to learn more. + programmer, take a look [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/using-haskell.org][here]] to learn more. ** Running xmobar in text mode @@ -124,63 +142,71 @@ channel, ~#xmobar~, at [[ircs://irc.libera.chat][Libera]]. it is possible to redirect xmobar's output to the standard output, optionally with color escape sequences. In this mode, xmobar can be run inside a terminal o console, or its output piped to other applications, and - there is no need for an X11 display. See [[./doc/quick-start.org#text-mode][Running xmobar in text mode]] for - details. Using this mode, you could [[file:doc/quick-start.org#wayland][pipe xmobar's output to, say, swaybar]], - and use it in wayland, or, with the [[./etc/xmobar.el][xmobar.el]] package, show it in Emacs's + there is no need for an X11 display. See [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/quick-start.org#text-mode][Running xmobar in text mode]] for + details. Using this mode, you could [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/quick-start.org#wayland][pipe xmobar's output to, say, swaybar]], + and use it in wayland, or, with the [[https://codeberg.org/xmobar/xmobar/src/branch/master/etc/xmobar.el][xmobar.el]] package, show it in Emacs's tab bar. * Configuration and further documentation - If you want to jump straight into running xmobar, head over to the - [[./doc/quick-start.org][quick start guide]]. + [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/quick-start.org][quick start guide]]. - If you want to get a detailed overview of all available plugins and - monitors, visit the [[./doc/plugins.org][plugins index]]. + monitors, visit the [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/plugins.org][plugins index]]. - For more information on how to use xmobar as a Haskell library see the - [[file:doc/using-haskell.org][using Haskell guide]]. + [[https://codeberg.org/xmobar/xmobar/src/branch/master/doc/using-haskell.org][using Haskell guide]]. - If you want to know how to contribute to the xmobar project, check out - [[contributing.org][contributing]]. + [[https://codeberg.org/xmobar/xmobar/src/branch/master/contributing.org][contributing]]. * Authors and credits - Andrea Rossato originally designed and implemented xmobar up to - version 0.11.1. Since then, it is maintained and developed by [[https://jao.io][jao]], - with the help of the greater xmobar and Haskell communities. - - In particular, xmobar incorporates patches by Mohammed Alshiekh, - Alex Ameen, Axel Angel, Dhananjay Balan, Claudio Bley, Dragos Boca, - Ben Boeckel, Ivan Brennan, Duncan Burke, Roman Cheplyaka, Patrick - Chilton, Antoine Eiche, Nathaniel Wesley Filardo, Guy Gastineau, - John Goerzen, Patrick Günther, Reto Hablützel, Juraj Hercek, Tomáš - Janoušek, Ada Joule, Spencer Janssen, Roman Joost, Pavel Kalugin, - Jochen Keil, Sam Kirby, Lennart Kolmodin, Krzysztof Kosciuszkiewicz, - Dmitry Kurochkin, Todd Lunter, Vanessa McHale, Robert J. Macomber, - Dmitry Malikov, David McLean, Joan MIlev, Marcin Mikołajczyk, Dino - Morelli, Tony Morris, Eric Mrak, Thiago Negri, Edward O'Callaghan, - Svein Ove, Martin Perner, Jens Petersen, Alexander Polakov, Sibi - Prabakaran, Pavan Rikhi, Petr Rockai, Andrew Emmanuel Rosa, - Sackville-West, Amir Saeid, Markus Scherer, Daniel Schüssler, - Olivier Schneider, Alexander Shabalin, Valentin Shirokov, Peter - Simons, Alexander Solovyov, Will Song, John Soo, John Soros, Felix - Springer, Travis Staton, Artem Tarasov, Samuli Thomasson, Edward - Tjörnhammar, Sergei Trofimovich, Thomas Tuegel, John Tyree, Jan - Vornberger, Anton Vorontsov, Daniel Wagner, Zev Weiss, Phil Xiaojun - Hu, Nikolay Yakimov, Edward Z. Yang, Leo Zhang, Norbert Zeh, and - Michał Zielonka. - - Andrea wants to thank Robert Manea and Spencer Janssen for their - help in understanding how X works. They gave him suggestions on how - to solve many problems with xmobar. He also thanks Claus Reinke for - making him understand existential types (or at least for letting him - think he grasps existential types...;-). + Andrea Rossato originally designed and implemented xmobar up to version + 0.11.1. Since then, it is maintained and developed by [[https://jao.io][jao]], with the help of + the greater xmobar and Haskell communities. + + In particular, xmobar incorporates patches by Kostas Agnantis, Mohammed + Alshiekh, Ashesh Ambasta, Alex Ameen, Axel Angel, Enrico Maria De Angelis, Dhananjay Balan, + Claudio Bley, Dragos Boca, Ben Boeckel, Ivan Brennan, Duncan Burke, Roman + Cheplyaka, Patrick Chilton, Antoine Eiche, Nathaniel Wesley Filardo, Guy + Gastineau, John Goerzen, Jonathan Grochowski, Patrick Günther, Reto + Hablützel, Corey Halpin, Juraj Hercek, Jaroslaw Jantura, Tomáš Janoušek, Ada + Joule, Spencer Janssen, Léana 江, Roman Joost, Pavel Kalugin, Jochen Keil, Sam Kirby, + Lennart Kolmodin, Krzysztof Kosciuszkiewicz, Dmitry Kurochkin, Todd Lunter, + Vanessa McHale, Robert J. Macomber, Dmitry Malikov, David McLean, Ulrik de + Muelenaere, Joan Milev, Marcin Mikołajczyk, Dino Morelli, Tony Morris, Eric + Mrak, Thiago Negri, Edward O'Callaghan, Svein Ove, Martin Perner, Alexander + Pankoff, Jens Petersen, Alexander Polakov, Sibi Prabakaran, Pavan Rikhi, + Petr Rockai, Andrew Emmanuel Rosa, Sackville-West, Amir Saeid, Markus + Scherer, Daniel Schüssler, Olivier Schneider, Alexander Shabalin, Valentin + Shirokov, Peter Simons, Alexander Solovyov, Will Song, John Soo, John Soros, + Felix Springer, Travis Staton, Artem Tarasov, Samuli Thomasson, Edward + Tjörnhammar, Sergei Trofimovich, Thomas Tuegel, John Tyree, Jan Vornberger, + Anton Vorontsov, Daniel Wagner, Zev Weiss, Phil Xiaojun Hu, Nikolay Yakimov, + Edward Z. Yang, Leo Zhang, Norbert Zeh, and Michał Zielonka. + + Andrea wants to thank Robert Manea and Spencer Janssen for their help in + understanding how X works. They gave him suggestions on how to solve many + problems with xmobar. He also thanks Claus Reinke for making him understand + existential types (or at least for letting him think he grasps existential + types...;-). + +* Contributing +** Generative AI (LLMs) + +Use of generative AI (LLMs, diffusion models and similar technology) is not +acceptable for contributions to or interactions with the xmobar project. + +This includes words, images and code, as well as bug reports, commit +messages, email (mailing list or private) and IRC messages. * License This software is released under a BSD-style license. See [[https://codeberg.org/xmobar/xmobar/src/branch/master/license][license]] for more details. - Copyright © 2010-2022 Jose Antonio Ortega Ruiz + Copyright © 2010-2026 Jose Antonio Ortega Ruiz Copyright © 2007-2010 Andrea Rossato diff --git a/src/Xmobar.hs b/src/Xmobar.hs index ced40a5..5a31d80 100644 --- a/src/Xmobar.hs +++ b/src/Xmobar.hs @@ -3,7 +3,7 @@ ----------------------------------------------------------------------------- -- | -- Module : Xmobar --- Copyright : (c) 2011, 2012, 2013, 2014, 2015, 2017, 2018, 2019, 2022 Jose Antonio Ortega Ruiz +-- Copyright : (c) 2011-2015, 2017-2019, 2022, 2025 Jose Antonio Ortega Ruiz -- (c) 2007 Andrea Rossato -- License : BSD-style (see LICENSE) -- @@ -19,13 +19,13 @@ module Xmobar (xmobar , xmobarMain , defaultConfig , configFromArgs - , tenthSeconds , Runnable (..) - , Exec (..) , Command (..) , SignalType (..) + , module Xmobar.Run.Exec , module Xmobar.Config.Types , module Xmobar.Config.Parse + , module Xmobar.Plugins.Accordion , module Xmobar.Plugins.BufferedPipeReader , module Xmobar.Plugins.CommandReader , module Xmobar.Plugins.Date @@ -43,6 +43,7 @@ module Xmobar (xmobar #endif , module Xmobar.Plugins.NotmuchMail , module Xmobar.Plugins.Monitors + , module Xmobar.Plugins.PacmanUpdates , module Xmobar.Plugins.PipeReader , module Xmobar.Plugins.MarqueePipeReader , module Xmobar.Plugins.StdinReader @@ -53,6 +54,7 @@ import Xmobar.Run.Runnable import Xmobar.Run.Exec import Xmobar.Config.Types import Xmobar.Config.Parse +import Xmobar.Plugins.Accordion import Xmobar.Plugins.Command import Xmobar.Plugins.BufferedPipeReader import Xmobar.Plugins.CommandReader @@ -70,6 +72,7 @@ import Xmobar.Plugins.Mail import Xmobar.Plugins.MBox #endif import Xmobar.Plugins.Monitors +import Xmobar.Plugins.PacmanUpdates import Xmobar.Plugins.PipeReader import Xmobar.Plugins.StdinReader import Xmobar.Plugins.MarqueePipeReader diff --git a/src/Xmobar/App/Compile.hs b/src/Xmobar/App/Compile.hs index 80dbac7..e5b08b7 100644 --- a/src/Xmobar/App/Compile.hs +++ b/src/Xmobar/App/Compile.hs @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------ -- | -- Module: Xmobar.App.Compile --- Copyright: (c) 2018 Jose Antonio Ortega Ruiz +-- Copyright: (c) 2018, 2026 Jose Antonio Ortega Ruiz -- License: BSD3-style (see LICENSE) -- -- Maintainer: jao@gnu.org @@ -20,20 +20,17 @@ module Xmobar.App.Compile(recompile, trace, xmessage) where import Control.Monad.IO.Class -import Control.Monad.Fix (fix) -import Control.Exception.Extensible (try, bracket, SomeException(..)) -import qualified Control.Exception.Extensible as E +import Control.Exception (SomeException(..)) +import qualified Control.Exception as E import Control.Monad (filterM, when) import Data.List ((\\)) -import Data.Maybe (isJust) import System.FilePath((</>), takeExtension) import System.IO import System.Directory import System.Process import System.Exit -import System.Posix.Process(executeFile, forkProcess, getAnyProcessStatus) +import System.Posix.Process(executeFile, forkProcess) import System.Posix.Types(ProcessID) -import System.Posix.Signals isExecutable :: FilePath -> IO Bool isExecutable f = @@ -144,14 +141,12 @@ recompile confDir dataDir execName force verb = liftIO $ do else shouldRecompile verb src bin lib if sc then do - uninstallSignalHandlers - status <- bracket (openFile err WriteMode) hClose $ + status <- withFile err WriteMode $ \errHandle -> waitForProcess =<< if useScript then runScript script bin confDir errHandle else runGHC bin confDir errHandle - installSignalHandlers if status == ExitSuccess then trace verb "Xmobar recompilation process exited with success!" else do @@ -168,24 +163,9 @@ recompile confDir dataDir execName force verb = liftIO $ do #ifdef RTSOPTS ++ ["-rtsopts", "-with-rtsopts", "-V0"] #endif +#ifdef SHARED_LIBRARIES + ++ ["-dynamic"] +#endif ++ ["-o", bin] runGHC bin = runProc "ghc" (opts bin) runScript script bin = runProc script [bin] - --- | Ignore SIGPIPE to avoid termination when a pipe is full, and SIGCHLD to --- avoid zombie processes, and clean up any extant zombie processes. -installSignalHandlers :: MonadIO m => m () -installSignalHandlers = liftIO $ do - installHandler openEndedPipe Ignore Nothing - installHandler sigCHLD Ignore Nothing - (try :: IO a -> IO (Either SomeException a)) - $ fix $ \more -> do - x <- getAnyProcessStatus False False - when (isJust x) more - return () - -uninstallSignalHandlers :: MonadIO m => m () -uninstallSignalHandlers = liftIO $ do - installHandler openEndedPipe Default Nothing - installHandler sigCHLD Default Nothing - return () diff --git a/src/Xmobar/App/Config.hs b/src/Xmobar/App/Config.hs index a284973..5c2f362 100644 --- a/src/Xmobar/App/Config.hs +++ b/src/Xmobar/App/Config.hs @@ -67,6 +67,7 @@ defaultConfig = , signal = SignalChan Nothing , textOutput = False , textOutputFormat = Plain + , dpi = 96.0 } -- | Return the path to the xmobar data directory. This directory is diff --git a/src/Xmobar/App/Opts.hs b/src/Xmobar/App/Opts.hs index 3a6b4e7..5196b3c 100644 --- a/src/Xmobar/App/Opts.hs +++ b/src/Xmobar/App/Opts.hs @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------ -- | -- Module: Xmobar.App.Opts --- Copyright: (c) 2018, 2019, 2020, 2022 Jose Antonio Ortega Ruiz +-- Copyright: (c) 2018, 2019, 2020, 2022, 2023-2026 Jose Antonio Ortega Ruiz -- License: BSD3-style (see LICENSE) -- -- Maintainer: jao@gnu.org @@ -52,6 +52,7 @@ data Opts = Help | Position String | WmClass String | WmName String + | Dpi String deriving (Show, Eq) options :: [OptDescr Opts] @@ -60,7 +61,7 @@ options = , Option "v" ["verbose"] (NoArg Verbose) "Emit verbose debugging messages" , Option "r" ["recompile"] (NoArg Recompile) "Force recompilation" , Option "V" ["version"] (NoArg Version) "Show version information" - , Option "T" ["text"] (OptArg TextOutput "color") + , Option "T" ["text"] (OptArg TextOutput "format") "Write text-only output to stdout. Plain/Ansi/Pango/Swaybar" , Option "f" ["font"] (ReqArg Font "font name") "Font name" , Option "N" ["add-font"] (ReqArg AddFont "font name") @@ -95,6 +96,8 @@ options = "On which X screen number to start" , Option "p" ["position"] (ReqArg Position "position") "Specify position of xmobar. Same syntax as in config file" + , Option "D" ["dpi"] (ReqArg Dpi "dpi") + "The DPI scaling factor. Default 96.0" ] getOpts :: [String] -> IO ([Opts], [String]) @@ -113,7 +116,7 @@ usage = usageInfo header options ++ footer info :: String info = "xmobar " ++ showVersion version - ++ "\n (C) 2010 - 2022 Jose A Ortega Ruiz" + ++ "\n (C) 2010 - 2026 Jose A Ortega Ruiz" ++ "\n (C) 2007 - 2010 Andrea Rossato\n " ++ mail ++ "\n" ++ license ++ "\n" @@ -161,6 +164,7 @@ doOpts conf (o:oo) = Right x -> doOpts' (conf {commands = commands conf ++ x}) Left e -> putStr (e ++ usage) >> exitWith (ExitFailure 1) Position s -> readPosition s + Dpi d -> doOpts' (conf {dpi = read d}) where readCom c str = case readStr str of [x] -> Right x diff --git a/src/Xmobar/Config/Parse.hs b/src/Xmobar/Config/Parse.hs index 16af3db..0b41267 100644 --- a/src/Xmobar/Config/Parse.hs +++ b/src/Xmobar/Config/Parse.hs @@ -19,7 +19,8 @@ module Xmobar.Config.Parse(readConfig , parseConfig , indexedFont - , indexedOffset) where + , indexedOffset + , colorComponents) where import Text.ParserCombinators.Parsec import Text.ParserCombinators.Parsec.Number (int) @@ -31,6 +32,14 @@ import Xmobar.Config.Types import qualified System.IO as S (readFile) +-- | Splits a colors string into its two components +colorComponents :: Config -> String -> (String, String) +colorComponents conf c = + case break (==',') c of + (f,',':b) -> (f, b) + (f, _) -> (f, bgColor conf) + + stripComments :: String -> String stripComments = unlines . map (drop 5 . strip False . (replicate 5 ' '++)) . lines @@ -63,7 +72,7 @@ parseConfig defaultConfig = <|?> pAllDesktops <|?> pOverrideRedirect <|?> pPickBroadest <|?> pLowerOnStart <|?> pPersistent <|?> pIconRoot <|?> pCommands <|?> pSepChar <|?> pAlignSep <|?> pTemplate - <|?> pVerbose <|?> pSignal + <|?> pVerbose <|?> pSignal <|?> pDpi fields = [ "font", "additionalFonts", "bgColor", "fgColor" , "wmClass", "wmName", "sepChar" @@ -72,7 +81,7 @@ parseConfig defaultConfig = , "allDesktops", "overrideRedirect", "pickBroadest" , "hideOnStart", "lowerOnStart", "persistent", "iconRoot" , "alpha", "commands", "verbose", "signal", "textOutput" - , "textOutputFormat" + , "textOutputFormat", "dpi" ] pTextOutput = readField textOutput "textOutput" @@ -103,6 +112,7 @@ parseConfig defaultConfig = pIconRoot = readField iconRoot "iconRoot" pAlpha = readField alpha "alpha" pVerbose = readField verbose "verbose" + pDpi = readField dpi "dpi" pSignal = field signal "signal" $ fail "signal is meant for use with Xmobar as a library.\n It is not meant for use in the configuration file." diff --git a/src/Xmobar/Config/Template.hs b/src/Xmobar/Config/Template.hs new file mode 100644 index 0000000..ad30c3d --- /dev/null +++ b/src/Xmobar/Config/Template.hs @@ -0,0 +1,188 @@ +------------------------------------------------------------------------------ +-- | +-- Module: Xmobar.Config.Template +-- Copyright: (c) 2022 jao +-- License: BSD3-style (see LICENSE) +-- +-- Maintainer: mail@jao.io +-- Stability: unstable +-- Portability: portable +-- Created: Fri Sep 30, 2022 06:33 +-- +-- +-- Parsing template strings +-- +------------------------------------------------------------------------------ + + +module Xmobar.Config.Template (parseString) where + +import Data.Maybe (fromMaybe) +import qualified Control.Monad as CM + +import Text.Parsec ((<|>)) +import Text.Read (readMaybe) + +import qualified Text.Parsec as P +import qualified Text.Parsec.Combinator as C + +import Text.ParserCombinators.Parsec (Parser) + +import qualified Xmobar.Config.Types as T + +type Context = (T.TextRenderInfo, T.FontIndex, Maybe [T.Action]) + +retSegment :: Context -> T.Widget -> Parser [T.Segment] +retSegment (i, idx, as) widget = return [(widget, i, idx, as)] + +-- | Run the template string parser for the given config, producing a list of +-- drawable segment specifications. +parseString :: T.Config -> String -> [T.Segment] +parseString c s = + case P.parse (stringParser ci) "" s of + Left _ -> [(T.Text $ "Could not parse string: " ++ s, ti, 0, Nothing)] + Right x -> concat x + where ci = (ti , 0, Nothing) + ti = T.TextRenderInfo (T.fgColor c) 0 0 [] + +-- Top level parser reading the full template string +stringParser :: Context -> Parser [[T.Segment]] +stringParser c = C.manyTill (allParsers c) C.eof + +allParsers :: Context -> Parser [T.Segment] +allParsers c = C.choice (textParser c:map (\f -> P.try (f c)) parsers) + where parsers = [ iconParser, hspaceParser, rawParser, actionParser + , fontParser, boxParser, colorParser ] + +-- Wrapper for notFollowedBy that returns the result of the first parser. +-- Also works around the issue that, at least in Parsec 3.0.0, notFollowedBy +-- accepts only parsers with return type Char. +notFollowedBy' :: Parser a -> Parser b -> Parser a +notFollowedBy' p e = do x <- p + C.notFollowedBy $ P.try (e >> return '*') + return x + +-- Parse a maximal string without markup +textParser :: Context -> Parser [T.Segment] +textParser c = + C.many1 (P.noneOf "<" <|> P.try (notFollowedBy' (P.char '<') suffixes)) + >>= retSegment c . T.Text + where suffixes = C.choice $ map (P.try . P.string) + [ "icon=" , "hspace=", "raw=" + , "action=", "/action>", "fn=", "/fn>" + , "box", "/box>", "fc=", "/fc>" ] + +-- Parse a "raw" tag, which we use to prevent other tags from creeping in. +-- The format here is net-string-esque: a literal "<raw=" followed by a string +-- of digits (base 10) denoting the length of the raw string, a literal ":" as +-- digit-string-terminator, the raw string itself, and then a literal "/>". +rawParser :: Context -> Parser [T.Segment] +rawParser c = do + P.string "<raw=" + lenstr <- C.many1 P.digit + P.char ':' + case reads lenstr of + [(len,[])] -> do + CM.guard ((len :: Integer) <= fromIntegral (maxBound :: Int)) + s <- C.count (fromIntegral len) P.anyChar + P.string "/>" + retSegment c (T.Text s) + _ -> CM.mzero + +iconParser :: Context -> Parser [T.Segment] +iconParser c = do + P.string "<icon=" + i <- C.manyTill (P.noneOf ">") (P.try (P.string "/>")) + retSegment c (T.Icon i) + +hspaceParser :: Context -> Parser [T.Segment] +hspaceParser c = do + P.string "<hspace=" + pVal <- C.manyTill P.digit (P.try (P.string "/>")) + retSegment c (T.Hspace (fromMaybe 0 $ readMaybe pVal)) + +actionParser :: Context -> Parser [T.Segment] +actionParser (ti, fi, act) = do + P.string "<action=" + command <- C.between (P.char '`') (P.char '`') (C.many1 (P.noneOf "`")) + <|> C.many1 (P.noneOf ">") + buttons <- (P.char '>' >> return "1") <|> (P.space >> P.spaces >> + C.between (P.string "button=") (P.string ">") (C.many1 (P.oneOf "12345"))) + let a = T.Spawn (toButtons buttons) command + a' = case act of + Nothing -> Just [a] + Just act' -> Just $ a : act' + s <- C.manyTill (allParsers (ti, fi, a')) (P.try $ P.string "</action>") + return (concat s) + +toButtons :: String -> [T.Button] +toButtons = map (\x -> read [x]) + +colorParser :: Context -> Parser [T.Segment] +colorParser (T.TextRenderInfo _ _ _ bs, fidx, a) = do + c <- C.between (P.string "<fc=") (P.string ">") (C.many1 colorc) + let colorParts = break (==':') c + let (ot,ob) = case break (==',') (drop 1 $ snd colorParts) of + (top,',':btm) -> (top, btm) + (top, _) -> (top, top) + tri = T.TextRenderInfo (fst colorParts) + (fromMaybe (-1) $ readMaybe ot) + (fromMaybe (-1) $ readMaybe ob) + bs + s <- C.manyTill (allParsers (tri, fidx, a)) (P.try $ P.string "</fc>") + return (concat s) + where colorc = P.alphaNum <|> P.oneOf ",:#" + +boxParser :: Context -> Parser [T.Segment] +boxParser (T.TextRenderInfo cs ot ob bs, f, a) = do + c <- C.between (P.string "<box") (P.string ">") + (C.option "" (C.many1 (P.alphaNum <|> P.oneOf "= #,"))) + let b = T.Box T.BBFull (T.BoxOffset T.C 0) 1 cs (T.BoxMargins 0 0 0 0) + let g = boxReader b (words c) + s <- C.manyTill + (allParsers (T.TextRenderInfo cs ot ob (g : bs), f, a)) + (P.try $ P.string "</box>") + return (concat s) + +boxReader :: T.Box -> [String] -> T.Box +boxReader b [] = b +boxReader b (x:xs) = boxReader (boxParamReader b param val) xs + where (param,val) = case break (=='=') x of + (p,'=':v) -> (p, v) + (p, _) -> (p, "") + +boxParamReader :: T.Box -> String -> String -> T.Box +boxParamReader b _ "" = b + +boxParamReader (T.Box bb off lw fc mgs) "type" val = + T.Box (fromMaybe bb $ readMaybe ("BB" ++ val)) off lw fc mgs + +boxParamReader (T.Box bb (T.BoxOffset alg off) lw fc mgs) "offset" (a:o) = + T.Box bb (T.BoxOffset align offset) lw fc mgs + where offset = fromMaybe off $ readMaybe o + align = fromMaybe alg $ readMaybe [a] + +boxParamReader (T.Box bb off lw fc mgs) "width" val = + T.Box bb off (fromMaybe lw $ readMaybe val) fc mgs + +boxParamReader (T.Box bb off lw _ mgs) "color" val = + T.Box bb off lw val mgs + +boxParamReader (T.Box bb off lw fc mgs@(T.BoxMargins mt mr mb ml)) ('m':pos) v = + let mgs' = case pos of + "t" -> T.BoxMargins (maybeVal mt) mr mb ml + "r" -> T.BoxMargins mt (maybeVal mr) mb ml + "b" -> T.BoxMargins mt mr (maybeVal mb) ml + "l" -> T.BoxMargins mt mr mb (maybeVal ml) + _ -> mgs + maybeVal d = fromMaybe d (readMaybe v) + in T.Box bb off lw fc mgs' + +boxParamReader b _ _ = b + +fontParser :: Context -> Parser [T.Segment] +fontParser (i, _, a) = do + f <- C.between (P.string "<fn=") (P.string ">") (C.many1 P.digit) + s <- C.manyTill (allParsers (i, fromMaybe 0 $ readMaybe f, a)) + (P.try $ P.string "</fn>") + return (concat s) diff --git a/src/Xmobar/Config/Types.hs b/src/Xmobar/Config/Types.hs index 4959aa1..785b55b 100644 --- a/src/Xmobar/Config/Types.hs +++ b/src/Xmobar/Config/Types.hs @@ -15,14 +15,28 @@ module Xmobar.Config.Types ( Config (..) , XPosition (..), Align (..), Border (..), TextOutputFormat (..) + , Segment , FontIndex + , Box(..) + , BoxBorder(..) + , BoxOffset(..) + , BoxMargins(..) + , TextRenderInfo(..) + , Widget(..) , SignalChan (..) + , Action (..) + , Button ) where import qualified Control.Concurrent.STM as STM import qualified Xmobar.Run.Runnable as R import qualified Xmobar.System.Signal as S +import Data.Int (Int32) +import Foreign.C.Types (CInt) + +import Xmobar.Run.Actions (Action (..), Button) + -- $config -- Configuration data type @@ -71,20 +85,94 @@ data Config = , template :: String -- ^ The output template , verbose :: Bool -- ^ Emit additional debug messages , signal :: SignalChan -- ^ Channel to send signals to xmobar + , dpi :: Double -- ^ DPI scaling factor for fonts } deriving (Read, Show) -data XPosition = Top - | TopH Int - | TopW Align Int - | TopSize Align Int Int - | TopP Int Int +-- | The position datatype +data XPosition = Top -- ^ Top of the screen, full width, auto height + + | TopH -- ^ Top of the screen, full width with + -- specific height + Int -- ^ Height (in pixels) + + -- | Top of the screen, full width with + -- specific height and margins + | TopHM + Int -- ^ Height (in pixels) + Int -- ^ Left margin (in pixels) + Int -- ^ Right margin (in pixels) + Int -- ^ Top margin (in pixels) + Int -- ^ Bottom margin (in pixels) + + -- | Top of the screen with specific width + -- (as screen percentage) and alignment + | TopW + Align -- ^ Alignement (L|C|R) + Int -- ^ Width as screen percentage (0-100) + + -- | Top of the screen with specific width + -- (as screen percentage), height and + -- alignment + | TopSize + Align -- ^ Alignement (L|C|R) + Int -- ^ Width as screen percentage (0-100) + Int -- ^ Height (in pixels) + + -- | Top of the screen with specific left/right + -- margins + | TopP + Int -- ^ Left margin (in pixels) + Int -- ^ Right margin (in pixels) + + -- | Bottom of the screen, full width, auto height | Bottom - | BottomH Int - | BottomP Int Int - | BottomW Align Int - | BottomSize Align Int Int - | Static {xpos, ypos, width, height :: Int} - | OnScreen Int XPosition + + | BottomH -- ^ Bottom of the screen, full width, with + -- specific height + Int -- ^ Height (in pixels) + + -- | Bottom of the screen with specific height + -- and margins + | BottomHM + Int -- ^ Height (in pixels) + Int -- ^ Left margin (in pixels) + Int -- ^ Right margin (in pixels) + Int -- ^ Top margin (in pixels) + Int -- ^ Bottom margin (in pixels) + + -- | Bottom of the screen with specific + -- left/right margins + | BottomP + Int -- ^ Left margin (in pixels) + Int -- ^ Bottom margin (in pixels) + + -- | Bottom of the screen with specific width + -- (as screen percentage) and alignment + -- and alignment + | BottomW + Align -- ^ Alignement (L|C|R) + Int -- ^ Width as screen percentage (0-100) + + -- | Bottom of the screen with specific width + -- (as screen percentage), height + -- and alignment + | BottomSize + Align -- ^ Alignement (L|C|R) + Int -- ^ Width as screen percentage (0-100) + Int -- ^ Height (in pixels) + + -- | Static position and specific size + | Static { xpos :: Int -- ^ Position X (in pixels) + , ypos :: Int -- ^ Position Y (in pixels) + , width :: Int -- ^ Width (in pixels) + , height :: Int -- ^ Height (in pixels) + } + + -- | Along with the position characteristics + -- specify the screen to display the bar + | OnScreen + Int -- ^ Screen id (primary is 0) + XPosition -- ^ Position deriving ( Read, Show, Eq ) data Align = L | R | C deriving ( Read, Show, Eq ) @@ -110,3 +198,34 @@ instance Read SignalChan where instance Show SignalChan where show (SignalChan (Just _)) = "SignalChan (Just <tmvar>)" show (SignalChan Nothing) = "SignalChan Nothing" + +data Widget = Icon String | Text String | Hspace Int32 deriving Show + +data BoxOffset = BoxOffset Align Int32 deriving (Eq, Show) + +-- margins: Top, Right, Bottom, Left +data BoxMargins = BoxMargins Int32 Int32 Int32 Int32 deriving (Eq, Show) + +data BoxBorder = BBTop + | BBBottom + | BBVBoth + | BBLeft + | BBRight + | BBHBoth + | BBFull + deriving (Read, Eq, Show) + +data Box = Box { bBorder :: BoxBorder + , bOffset :: BoxOffset + , bWidth :: CInt + , bColor :: String + , bMargins :: BoxMargins + } deriving (Eq, Show) + +data TextRenderInfo = TextRenderInfo { tColorsString :: String + , tBgTopOffset :: Int32 + , tBgBottomOffset :: Int32 + , tBoxes :: [Box] + } deriving Show + +type Segment = (Widget, TextRenderInfo, FontIndex, Maybe [Action]) diff --git a/src/Xmobar/Draw/Boxes.hs b/src/Xmobar/Draw/Boxes.hs index 1358805..ff55ab3 100644 --- a/src/Xmobar/Draw/Boxes.hs +++ b/src/Xmobar/Draw/Boxes.hs @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------ -- | -- Module: Xmobar.X11.Boxes --- Copyright: (c) 2022 Jose Antonio Ortega Ruiz +-- Copyright: (c) 2022, 2024 Jose Antonio Ortega Ruiz -- License: BSD3-style (see LICENSE) -- -- Maintainer: jao@gnu.org @@ -16,7 +16,6 @@ module Xmobar.Draw.Boxes (Line, boxLines, BoxRect, borderRect) where import qualified Xmobar.Config.Types as T -import qualified Xmobar.Run.Parsers as P type Line = (Double, Double, Double, Double) type BoxRect = (Double, Double, Double, Double) @@ -25,30 +24,29 @@ type BoxRect = (Double, Double, Double, Double) -- The Box is to be positioned between x0 and x1, with height ht, and drawn -- with line width lw. The returned lists are coordinates of the beginning -- and end of each line. -boxLines :: P.Box -> Double -> Double -> Double -> [Line] -boxLines (P.Box bd offset lw _ margins) ht x0 x1 = +boxLines :: T.Box -> Double -> Double -> Double -> [Line] +boxLines (T.Box bd offset lw _ margins) ht x0 x1 = case bd of - P.BBTop -> [rtop] - P.BBBottom -> [rbot] - P.BBVBoth -> [rtop, rbot] - P.BBLeft -> [rleft] - P.BBRight -> [rright] - P.BBHBoth -> [rleft, rright] - P.BBFull -> [rtop, rbot, rleft, rright] + T.BBTop -> [rtop] + T.BBBottom -> [rbot] + T.BBVBoth -> [rtop, rbot] + T.BBLeft -> [rleft] + T.BBRight -> [rright] + T.BBHBoth -> [rleft, rright] + T.BBFull -> [rtop, rbot, rleft, rright] where - (P.BoxMargins top right bot left) = margins - (P.BoxOffset align m) = offset + (T.BoxMargins top right bot left) = margins + (T.BoxOffset align m) = offset ma = fromIntegral m (p0, p1) = case align of T.L -> (0, -ma) T.C -> (ma, -ma) T.R -> (ma, 0) lc = fromIntegral lw / 2 - [mt, mr, mb, ml] = map fromIntegral [top, right, bot, left] - xmin = x0 - ml - lc - xmax = x1 + mr + lc - ymin = mt + lc - ymax = ht - mb - lc + xmin = x0 - fromIntegral left - lc + xmax = x1 + fromIntegral right + lc + ymin = fromIntegral top + lc + ymax = ht - fromIntegral bot - lc rtop = (xmin + p0, ymin, xmax + p1, ymin) rbot = (xmin + p0, ymax, xmax + p1, ymax) rleft = (xmin, ymin + p0, xmin, ymax + p1) diff --git a/src/Xmobar/Draw/Cairo.hs b/src/Xmobar/Draw/Cairo.hs index 7e22df4..2338b10 100644 --- a/src/Xmobar/Draw/Cairo.hs +++ b/src/Xmobar/Draw/Cairo.hs @@ -2,7 +2,7 @@ ------------------------------------------------------------------------------ -- | -- Module: Xmobar.X11.Cairo --- Copyright: (c) 2022 Jose Antonio Ortega Ruiz +-- Copyright: (c) 2022, 2023, 2024 Jose Antonio Ortega Ruiz -- License: BSD3-style (see LICENSE) -- -- Maintainer: jao@gnu.org @@ -29,14 +29,13 @@ import Graphics.Rendering.Cairo.Types(Surface) import qualified Xmobar.Config.Types as C import qualified Xmobar.Config.Parse as ConfigParse -import qualified Xmobar.Run.Parsers as P import qualified Xmobar.Text.Pango as TextPango import qualified Xmobar.Draw.Boxes as Boxes import qualified Xmobar.Draw.Types as T -type Renderinfo = (P.Segment, Surface -> Double -> Double -> IO (), Double) -type BoundedBox = (Double, Double, [P.Box]) +type Renderinfo = (C.Segment, Surface -> Double -> Double -> IO (), Double) +type BoundedBox = (Double, Double, [C.Box]) type Acc = (Double, T.Actions, [BoundedBox]) readColourName :: String -> (SRGB.Colour Double, Double) @@ -63,10 +62,10 @@ renderLines color wd lns = do mapM_ (\(x0, y0, x1, y1) -> Cairo.moveTo x0 y0 >> Cairo.lineTo x1 y1 >> Cairo.stroke) lns -segmentMarkup :: C.Config -> P.Segment -> String -segmentMarkup conf (P.Text txt, info, idx, _actions) = +segmentMarkup :: C.Config -> C.Segment -> String +segmentMarkup conf (C.Text txt, info, idx, _actions) = let fnt = TextPango.fixXft $ ConfigParse.indexedFont conf idx - (fg, bg) = P.colorComponents conf (P.tColorsString info) + (fg, bg) = ConfigParse.colorComponents conf (C.tColorsString info) attrs = [Pango.FontDescr fnt, Pango.FontForeground fg] attrs' = if bg == C.bgColor conf then attrs @@ -74,8 +73,8 @@ segmentMarkup conf (P.Text txt, info, idx, _actions) = in Pango.markSpan attrs' $ Pango.escapeMarkup txt segmentMarkup _ _ = "" -withRenderinfo :: Pango.PangoContext -> T.DrawContext -> P.Segment -> IO Renderinfo -withRenderinfo ctx dctx seg@(P.Text _, inf, idx, a) = do +withRenderinfo :: Pango.PangoContext -> T.DrawContext -> C.Segment -> IO Renderinfo +withRenderinfo ctx dctx seg@(C.Text _, inf, idx, a) = do let conf = T.dcConfig dctx lyt <- Pango.layoutEmpty ctx mk <- Pango.layoutSetMarkup lyt (segmentMarkup conf seg) :: IO String @@ -88,25 +87,28 @@ withRenderinfo ctx dctx seg@(P.Text _, inf, idx, a) = do Pango.layoutSetEllipsize lyt Pango.EllipsizeEnd Pango.layoutSetWidth lyt (Just $ mx - off) Cairo.renderWith s $ Cairo.moveTo off voff >> Pango.showLayout lyt - return ((P.Text mk, inf, idx, a), slyt, wd) + return ((C.Text mk, inf, idx, a), slyt, wd) -withRenderinfo _ _ seg@(P.Hspace w, _, _, _) = +withRenderinfo _ _ seg@(C.Hspace w, _, _, _) = return (seg, \_ _ _ -> return (), fromIntegral w) -withRenderinfo _ dctx seg@(P.Icon p, _, _, _) = do +withRenderinfo _ dctx seg@(C.Icon p, info, _, _) = do let (wd, _) = T.dcIconLookup dctx p ioff = C.iconOffset (T.dcConfig dctx) vpos = T.dcHeight dctx / 2 + fromIntegral ioff - render _ off mx = when (off + wd <= mx) $ T.dcIconDrawer dctx off vpos p + conf = T.dcConfig dctx + (fg, bg) = ConfigParse.colorComponents conf (C.tColorsString info) + render _ off mx = when (off + wd <= mx) $ + T.dcIconDrawer dctx off vpos p fg bg return (seg, render, wd) -drawBox :: T.DrawContext -> Surface -> Double -> Double -> P.Box -> IO () -drawBox dctx surf x0 x1 box@(P.Box _ _ w color _) = +drawBox :: T.DrawContext -> Surface -> Double -> Double -> C.Box -> IO () +drawBox dctx surf x0 x1 box@(C.Box _ _ w color _) = Cairo.renderWith surf $ renderLines color (fromIntegral w) (Boxes.boxLines box (T.dcHeight dctx) x0 x1) drawSegmentBackground :: - T.DrawContext -> Surface -> P.TextRenderInfo -> Double -> Double -> IO () + T.DrawContext -> Surface -> C.TextRenderInfo -> Double -> Double -> IO () drawSegmentBackground dctx surf info x0 x1 = when (bg /= C.bgColor conf && (top >= 0 || bot >= 0)) $ Cairo.renderWith surf $ do @@ -114,19 +116,20 @@ drawSegmentBackground dctx surf info x0 x1 = Cairo.rectangle x0 top (x1 - x0) (T.dcHeight dctx - bot - top) Cairo.fillPreserve where conf = T.dcConfig dctx - (_, bg) = P.colorComponents conf (P.tColorsString info) - top = fromIntegral $ P.tBgTopOffset info - bot = fromIntegral $ P.tBgBottomOffset info + (_, bg) = ConfigParse.colorComponents conf (C.tColorsString info) + top = fromIntegral $ C.tBgTopOffset info + bot = fromIntegral $ C.tBgBottomOffset info drawSegment :: T.DrawContext -> Surface -> Double -> Acc -> Renderinfo -> IO Acc drawSegment dctx surface maxoff (off, acts, boxs) (segment, render, lwidth) = do let end = min maxoff (off + lwidth) (_, info, _, a) = segment acts' = case a of Just as -> (as, off, end):acts; _ -> acts - bs = P.tBoxes info + bs = C.tBoxes info boxs' = if null bs then boxs else (off, end, bs):boxs - drawSegmentBackground dctx surface info off end - render surface off maxoff + when (end > off) $ do + drawSegmentBackground dctx surface info off end + render surface off maxoff return (off + lwidth, acts', boxs') renderOuterBorder :: C.Config -> Double -> Double -> Cairo.Render () @@ -166,25 +169,27 @@ drawCairoBackground dctx surf = do drawSegments :: T.DrawContext -> Surface -> IO T.Actions drawSegments dctx surf = do - let [left, center, right] = take 3 $ T.dcSegments dctx ++ repeat [] + let segs = take 3 $ T.dcSegments dctx ++ repeat [] dh = T.dcHeight dctx dw = T.dcWidth dctx conf = T.dcConfig dctx sWidth = foldl (\a (_,_,w) -> a + w) 0 ctx <- Pango.cairoCreateContext Nothing - llyts <- mapM (withRenderinfo ctx dctx) left - rlyts <- mapM (withRenderinfo ctx dctx) right - clyts <- mapM (withRenderinfo ctx dctx) center + Pango.cairoContextSetResolution ctx $ C.dpi conf + llyts <- mapM (withRenderinfo ctx dctx) (head segs) + rlyts <- mapM (withRenderinfo ctx dctx) (segs !! 2) + clyts <- mapM (withRenderinfo ctx dctx) (segs !! 1) #ifndef XRENDER drawCairoBackground dctx surf #endif (lend, as, bx) <- foldM (drawSegment dctx surf dw) (0, [], []) llyts let rw = sWidth rlyts - rstart = max (lend + 1) (dw - rw - 1) - cmax = rstart - 1 cw = sWidth clyts - cstart = lend + 1 + max 0 (dw - rw - lend - cw) / 2.0 - (_, as', bx') <- foldM (drawSegment dctx surf cmax) (cstart, as, bx) clyts + rstart = max lend (dw - rw) + cstart = if lend > 1 || rw == 0 then max lend ((dw - cw) / 2.0) else lend + (_, as', bx') <- if cw > 0 + then foldM (drawSegment dctx surf rstart) (cstart, as, bx) clyts + else return (0, as, bx) (_, as'', bx'') <- foldM (drawSegment dctx surf dw) (rstart, as', bx') rlyts drawBoxes dctx surf (reverse bx'') when (C.borderWidth conf > 0) (drawBorder conf dw dh surf) diff --git a/src/Xmobar/Draw/Types.hs b/src/Xmobar/Draw/Types.hs index 75dd714..1a076b3 100644 --- a/src/Xmobar/Draw/Types.hs +++ b/src/Xmobar/Draw/Types.hs @@ -17,16 +17,15 @@ module Xmobar.Draw.Types where -import Xmobar.Config.Types (Config) +import Xmobar.Config.Types (Config, Segment) import Xmobar.Run.Actions (Action) -import Xmobar.Run.Parsers (Segment) type Position = Double type ActionPos = ([Action], Position, Position) type Actions = [ActionPos] type IconLookup = String -> (Double, Double) -type IconDrawer = Double -> Double -> String -> IO () +type IconDrawer = Double -> Double -> String -> String -> String -> IO () data DrawContext = DC { dcIconDrawer :: IconDrawer , dcIconLookup :: IconLookup diff --git a/src/Xmobar/Plugins/Accordion.hs b/src/Xmobar/Plugins/Accordion.hs new file mode 100644 index 0000000..ce15cee --- /dev/null +++ b/src/Xmobar/Plugins/Accordion.hs @@ -0,0 +1,112 @@ +{-# LANGUAGE TupleSections, FlexibleContexts #-} +{-# LANGUAGE RecordWildCards #-} + +----------------------------------------------------------------------------- +-- | +-- Module : Plugins.Monitors.Accordion +-- Copyright : (c) 2024 Enrico Maria De Angelis +-- License : BSD-style (see LICENSE) +-- +-- Maintainer : Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com> +-- Stability : unstable +-- Portability : unportable +-- +-- A plugin to group adjacent plugins and make them, as a whole, shrinkable to +-- an alternate text upon clicking. +-- +----------------------------------------------------------------------------- + +module Xmobar.Plugins.Accordion (defaultTuning, makeAccordion, makeAccordion', Tuning(..)) where + +import Control.Concurrent.Async (concurrently_, mapConcurrently_) +import Control.Exception (finally) +import Control.Monad.Extra (whenM) +import Control.Monad (forever, join) +import Control.Monad.IO.Class (liftIO, MonadIO) +import Control.Monad.Reader (MonadReader, runReaderT, ask) +import Control.Monad.State.Strict (MonadState, evalStateT, get, modify') +import Data.IORef (atomicModifyIORef', newIORef, readIORef, IORef) +import GHC.IO.Handle.FD (withFileBlocking) +import System.Directory (removeFile) +import System.IO (IOMode(ReadMode), hGetContents') +import System.Process (readProcessWithExitCode) +import Xmobar.Run.Exec (Exec(..), tenthSeconds) + +-- TODO: Ideally, I'd have just `Accordion`, and not `Tuning`, but since +-- `Accordion` is polymorphic, I can't have a `defaultAccordion` constructor +-- with `plugins = []`, because that leaves `a` undetermined. +-- So I have move all non-polymorphic typed members in `Tuning`, allowing for +-- default values at least for those members. +data Accordion a = Accordion { + tuning :: Tuning + , plugins :: [a] + , shortPlugins :: [a] +} deriving (Show, Read) + +makeAccordion :: Exec a => Tuning -> [a] -> Accordion a +makeAccordion t rs = Accordion { tuning = t, plugins = rs, shortPlugins = [] } + +makeAccordion' :: Exec a => Tuning -> [a] -> [a] -> Accordion a +makeAccordion' t rs rs' = Accordion { tuning = t, plugins = rs, shortPlugins = rs' } + +data Tuning = Tuning { + alias' :: String + , initial :: Bool + , expand :: String + , shrink :: String +} deriving (Read, Show) + +defaultTuning :: Tuning +defaultTuning = Tuning { + alias' = "accordion" + , initial = True + , expand = "<>" + , shrink = "><" +} + +instance Exec a => Exec (Accordion a) where + alias (Accordion Tuning{..} _ _) = alias' + start (Accordion Tuning{..} runnables shortRunnables) cb = do + clicked <- newIORef False + (_, n, _) <- readProcessWithExitCode "uuidgen" [] "" + let pipe = "/tmp/accordion-" ++ removeLinebreak n + (_, _, _) <- readProcessWithExitCode "mkfifo" [pipe] "" + concurrently_ (forever $ do "" <- withFileBlocking pipe ReadMode hGetContents' + atomicModifyIORef' clicked (const (True, ()))) + (do + strRefs <- mapM (newIORef . const "") runnables + strRefs' <- mapM (newIORef . const "") shortRunnables + let processClick = forever (do liftIO (tenthSeconds 1) + whenM (liftIO $ readIORef clicked) + (do liftIO $ clear clicked + modify' not) + get >>= loop pipe) + `runReaderT` (strRefs, strRefs') + `evalStateT` initial + let startRunnables = zipWith start + (runnables ++ shortRunnables) + (map writeToRef $ strRefs ++ strRefs') + parallel_ $ processClick:startRunnables) + `finally` removeFile pipe + where + loop :: (MonadIO m, + MonadState Bool m, + MonadReader ([IORef String], [IORef String]) m) + => String -> Bool -> m () + loop pipe bool = do + (strRefs, strRefs') <- ask + text <- join <$> mapM (liftIO . readIORef) (if bool then strRefs else strRefs') + liftIO $ cb $ text ++ attachClick pipe (if bool then shrink else expand) + parallel_ = mapConcurrently_ id + +writeToRef :: IORef a -> a -> IO () +writeToRef strRef = atomicModifyIORef' strRef . const . (,()) + +clear :: IORef Bool -> IO () +clear = (`atomicModifyIORef'` const (False, ())) + +removeLinebreak :: [a] -> [a] +removeLinebreak = init + +attachClick :: String -> String -> String +attachClick file icon = "<action=`echo -n > " ++ file ++ "`>" ++ icon ++ "</action>" diff --git a/src/Xmobar/Plugins/EWMH.hs b/src/Xmobar/Plugins/EWMH.hs index 94fd7d7..9b583ea 100644 --- a/src/Xmobar/Plugins/EWMH.hs +++ b/src/Xmobar/Plugins/EWMH.hs @@ -19,6 +19,7 @@ module Xmobar.Plugins.EWMH (EWMH(..)) where import Control.Applicative (Applicative(..)) import Control.Monad.State +import Control.Monad import Control.Monad.Reader import Graphics.X11 hiding (Modifier, Color) import Graphics.X11.Xlib.Extras @@ -231,7 +232,7 @@ updateClientList _ = do where unmanage w = asks display >>= \d -> liftIO $ selectInput d w 0 listen w = asks display >>= \d -> liftIO $ selectInput d w propertyChangeMask - update w = mapM_ (($ w) . snd) clientHandlers + update w = mapM_ (`snd` w) clientHandlers modifyClient :: Window -> (Client -> Client) -> M () modifyClient w f = modify (\s -> s { clients = Map.alter f' w $ clients s }) diff --git a/src/Xmobar/Plugins/Kraken.hs b/src/Xmobar/Plugins/Kraken.hs index 2345b3d..5d565e0 100644 --- a/src/Xmobar/Plugins/Kraken.hs +++ b/src/Xmobar/Plugins/Kraken.hs @@ -36,7 +36,7 @@ instance Exec Kraken where cb (display g) loop mv g - loop mvar (Map.fromList $ zip ps (repeat 0.0)) + loop mvar (Map.fromList $ map (, 0.0) ps) where display :: Map.Map String Double -> String diff --git a/src/Xmobar/Plugins/Locks.hs b/src/Xmobar/Plugins/Locks.hs index 9176312..35a3f97 100644 --- a/src/Xmobar/Plugins/Locks.hs +++ b/src/Xmobar/Plugins/Locks.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE TupleSections #-} ----------------------------------------------------------------------------- -- | -- Module : Plugins.Locks @@ -16,45 +17,70 @@ module Xmobar.Plugins.Locks(Locks(..)) where import Graphics.X11 import Data.List +import Data.List.Extra (trim) import Data.Bits +import Data.Maybe (fromJust) import Control.Monad +import Control.Monad.Extra (ifM) import Graphics.X11.Xlib.Extras import Xmobar.Run.Exec import Xmobar.System.Kbd import Xmobar.X11.Events (nextEvent') -data Locks = Locks +data Locks = Locks | Locks' [(String, (String, String))] deriving (Read, Show) locks :: [ ( KeySym, String )] -locks = [ ( xK_Caps_Lock, "CAPS" ) - , ( xK_Num_Lock, "NUM" ) +locks = [ ( xK_Caps_Lock, "CAPS" ) + , ( xK_Num_Lock, "NUM" ) , ( xK_Scroll_Lock, "SCROLL" ) ] -run' :: Display -> Window -> IO String -run' d root = do +type Labels = [ ( String, (String, String) )] +defaultLabels :: Labels +defaultLabels = let nms = map snd locks + in zip nms (map (, mempty) nms) + +type LabelledLock = (KeySym, String, String, String) + +attach :: (KeySym, String) -> Labels -> LabelledLock +(key, lock) `attach` lbls = let (enb, dis) = fromJust $ lookup lock lbls + in (key, lock, enb, dis) + +enabled :: (a, b, c, d) -> c +enabled (_, _, c, _) = c +disabled :: (a, b, c, d) -> d +disabled (_, _, _, d) = d + +isEnabled :: (Bits a1, Foldable t, Foldable t1, Integral a) + => Display -> t (a, t1 KeyCode) -> a1 -> (KeySym, b, c, d) -> IO Bool +isEnabled d modMap m ( ks, _, _, _ ) = do + kc <- keysymToKeycode d ks + return $ case find (elem kc . snd) modMap of + Nothing -> False + Just ( i, _ ) -> testBit m (fromIntegral i) + +run' :: Display -> Window -> Labels -> IO String +run' d root labels = do modMap <- getModifierMapping d ( _, _, _, _, _, _, _, m ) <- queryPointer d root - ls <- filterM ( \( ks, _ ) -> do - kc <- keysymToKeycode d ks - return $ case find (elem kc . snd) modMap of - Nothing -> False - Just ( i, _ ) -> testBit m (fromIntegral i) - ) locks - - return $ unwords $ map snd ls + ls' <- forM (map (`attach` labels) locks) + (\l -> ifM (isEnabled d modMap m l) + (return (enabled l)) + (return (disabled l))) + return $ trim $ unwords ls' instance Exec Locks where - alias Locks = "locks" - start Locks cb = do + alias _ = "locks" + start Locks cb = start (Locks' defaultLabels) cb + start (Locks' labels) cb = do d <- openDisplay "" root <- rootWindow d (defaultScreen d) _ <- xkbSelectEventDetails d xkbUseCoreKbd xkbIndicatorStateNotify m m allocaXEvent $ \ep -> forever $ do - cb =<< run' d root + cb =<< run' d root labels nextEvent' d ep getEvent ep diff --git a/src/Xmobar/Plugins/MarqueePipeReader.hs b/src/Xmobar/Plugins/MarqueePipeReader.hs index 075503c..a6d590e 100644 --- a/src/Xmobar/Plugins/MarqueePipeReader.hs +++ b/src/Xmobar/Plugins/MarqueePipeReader.hs @@ -60,7 +60,7 @@ writer txt sep len rate chan cb = do Nothing -> tenthSeconds rate >> writer (drop 1 txt) sep len rate chan cb toInfTxt :: String -> String -> String -toInfTxt line sep = concat (repeat $ line ++ " " ++ sep ++ " ") +toInfTxt line sep = cycle (line ++ " " ++ sep ++ " ") checkPipe :: FilePath -> IO () checkPipe file = handle (\(SomeException _) -> waitForPipe) $ do diff --git a/src/Xmobar/Plugins/Monitors/Alsa.hs b/src/Xmobar/Plugins/Monitors/Alsa.hs index dfc7329..8d02931 100644 --- a/src/Xmobar/Plugins/Monitors/Alsa.hs +++ b/src/Xmobar/Plugins/Monitors/Alsa.hs @@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module : Plugins.Monitors.Alsa --- Copyright : (c) 2018 Daniel Schüssler +-- Copyright : (c) 2018, 2024 Daniel Schüssler -- License : BSD-style (see LICENSE) -- -- Maintainer : Jose A. Ortega Ruiz <jao@gnu.org> @@ -25,6 +25,7 @@ import Control.Concurrent.Async import Control.Exception import Control.Monad import Data.IORef +import Data.Maybe (fromJust) import Data.Time.Clock import Xmobar.Plugins.Monitors.Common import qualified Xmobar.Plugins.Monitors.Volume as Volume; @@ -129,7 +130,8 @@ alsaReaderThread mixerName alsaCtlPath outputCallback mvar = {std_out = CreatePipe} runAlsaOnce = - withCreateProcess createProc $ \_ (Just alsaOut) _ _ -> do + withCreateProcess createProc $ \_ out _ _ -> do + let alsaOut = fromJust out hSetBuffering alsaOut LineBuffering tryPutMVar mvar () -- Refresh immediately after restarting alsactl diff --git a/src/Xmobar/Plugins/Monitors/Batt/Common.hs b/src/Xmobar/Plugins/Monitors/Batt/Common.hs index 3262b78..31caabc 100644 --- a/src/Xmobar/Plugins/Monitors/Batt/Common.hs +++ b/src/Xmobar/Plugins/Monitors/Batt/Common.hs @@ -1,7 +1,8 @@ +{-# LANGUAGE TypeApplications #-} ----------------------------------------------------------------------------- -- | -- Module : Plugins.Monitors.Batt.Common --- Copyright : (c) 2010, 2011, 2012, 2013, 2015, 2016, 2018, 2019 Jose A Ortega +-- Copyright : (c) 2010-2016, 2018, 2019, 2024 Jose A Ortega -- (c) 2010 Andrea Rossato, Petr Rockai -- License : BSD-style (see LICENSE) -- @@ -18,7 +19,8 @@ module Xmobar.Plugins.Monitors.Batt.Common (BattOpts(..) , Status(..) , maybeAlert) where -import System.Process (system) +import System.Environment +import System.Process (waitForProcess, withCreateProcess, shell, CreateProcess(env)) import Control.Monad (unless, void) import Xmobar.Plugins.Monitors.Common @@ -54,4 +56,11 @@ maybeAlert opts left = case onLowAction opts of Nothing -> return () Just x -> unless (isNaN left || actionThreshold opts < 100 * left) - $ void $ system x + $ runCmd =<< mkShellCmd x + where + mkShellCmd command = do + selfEnv <- getEnvironment + pure (shell command) { env = Just $ ("XMOBAR_BATT_LEFT", show @Int $ round $ 100 * left) : selfEnv + } + runCmd c = withCreateProcess c $ \_ _ _ ph -> + void $ waitForProcess ph diff --git a/src/Xmobar/Plugins/Monitors/Batt/Linux.hs b/src/Xmobar/Plugins/Monitors/Batt/Linux.hs index 5389be0..d95b902 100644 --- a/src/Xmobar/Plugins/Monitors/Batt/Linux.hs +++ b/src/Xmobar/Plugins/Monitors/Batt/Linux.hs @@ -199,5 +199,5 @@ readBatteries opts bfs = | time == 0 = Idle | ac = Charging | otherwise = Discharging - unless ac (maybeAlert opts left) + unless (racst == Charging || racst == Idle) $ maybeAlert opts left return $ if isNaN left then NA else Result left watts time racst diff --git a/src/Xmobar/Plugins/Monitors/Bright.hs b/src/Xmobar/Plugins/Monitors/Bright.hs index 12790e1..9c61fb2 100644 --- a/src/Xmobar/Plugins/Monitors/Bright.hs +++ b/src/Xmobar/Plugins/Monitors/Bright.hs @@ -54,11 +54,12 @@ data Files = Files { fCurr :: String , fMax :: String } | NoFiles + deriving Show brightFiles :: BrightOpts -> IO Files brightFiles opts = do is_curr <- fileExist $ fCurr files - is_max <- fileExist $ fCurr files + is_max <- fileExist $ fMax files return (if is_curr && is_max then files else NoFiles) where prefix = sysDir </> subDir opts files = Files { fCurr = prefix </> currBright opts diff --git a/src/Xmobar/Plugins/Monitors/Common/Output.hs b/src/Xmobar/Plugins/Monitors/Common/Output.hs index bd60710..c0a00ab 100644 --- a/src/Xmobar/Plugins/Monitors/Common/Output.hs +++ b/src/Xmobar/Plugins/Monitors/Common/Output.hs @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------ -- | -- Module: Xmobar.Plugins.Monitors.Strings --- Copyright: (c) 2018, 2019, 2020, 2022 Jose Antonio Ortega Ruiz +-- Copyright: (c) 2018, 2019, 2020, 2022, 2024 Jose Antonio Ortega Ruiz -- License: BSD3-style (see LICENSE) -- -- Maintainer: jao@gnu.org @@ -88,9 +88,9 @@ pShowWithColors p f x = do pColorizeString :: (Num a, Ord a, MonadIO m) => MonitorConfig -> a -> String -> m String pColorizeString p x s = do let col = pSetColor p s - [ll,hh] = map fromIntegral $ sort [pLow p, pHigh p] -- consider high < low - pure $ head $ [col pHighColor | x > hh ] ++ - [col pNormalColor | x > ll ] ++ + cols = map fromIntegral $ sort [pLow p, pHigh p] -- consider high < low + pure $ head $ [col pHighColor | x > (cols !! 1) ] ++ + [col pNormalColor | x > head cols ] ++ [col pLowColor | True] pSetColor :: MonitorConfig -> String -> PSelector (Maybe String) -> String @@ -140,7 +140,7 @@ showWithUnits d n x padString :: Int -> Int -> String -> Bool -> String -> String -> String padString mnw mxw pad pr ellipsis s = let len = length s - rmin = if mnw < 0 then 0 else mnw + rmin = max mnw 0 rmax = if mxw <= 0 then max len rmin else mxw (rmn, rmx) = if rmin <= rmax then (rmin, rmax) else (rmax, rmin) rlen = min (max rmn len) rmx @@ -197,9 +197,9 @@ colorizeString x s = do h <- getConfigValue high l <- getConfigValue low let col = setColor s - [ll,hh] = map fromIntegral $ sort [l, h] -- consider high < low - head $ [col highColor | x > hh ] ++ - [col normalColor | x > ll ] ++ + cols = map fromIntegral $ sort [l, h] -- consider high < low + head $ [col highColor | x > cols !! 1 ] ++ + [col normalColor | x > head cols ] ++ [col lowColor | True] showWithColors :: (Num a, Ord a) => (a -> String) -> a -> Monitor String @@ -260,11 +260,11 @@ logScaling f v = do h <- fromIntegral `fmap` getConfigValue high l <- fromIntegral `fmap` getConfigValue low bw <- fromIntegral `fmap` getConfigValue barWidth - let [ll, hh] = sort [l, h] + let ws = sort [l, h] bw' = if bw > 0 then bw else 10 scaled x | x == 0.0 = 0 - | x <= ll = 1 / bw' - | otherwise = f + logBase 2 (x / hh) / bw' + | x <= head ws = 1 / bw' + | otherwise = f + logBase 2 (x / ws !! 1) / bw' return $ scaled v showLogBar :: Float -> Float -> Monitor String diff --git a/src/Xmobar/Plugins/Monitors/Disk.hs b/src/Xmobar/Plugins/Monitors/Disk.hs index 47d1eac..95bcff6 100644 --- a/src/Xmobar/Plugins/Monitors/Disk.hs +++ b/src/Xmobar/Plugins/Monitors/Disk.hs @@ -1,7 +1,7 @@ ----------------------------------------------------------------------------- -- | -- Module : Plugins.Monitors.Disk --- Copyright : (c) 2010, 2011, 2012, 2014, 2018, 2019 Jose A Ortega Ruiz +-- Copyright : (c) 2010-2012, 2014, 2018, 2019, 2024 Jose A Ortega Ruiz -- License : BSD-style (see LICENSE) -- -- Maintainer : Jose A Ortega Ruiz <jao@gnu.org> @@ -131,10 +131,9 @@ startDiskIO disks args rate cb = do runM args diskIOConfig (runDiskIO dref disks) rate cb runDiskU' :: DiskUOpts -> String -> [Integer] -> Monitor String -runDiskU' opts tmp stat = do +runDiskU' opts tmp (total:free:diff:_) = do setConfigValue tmp template - let [total, free, diff] = stat - strs = map sizeToStr [free, diff] + let strs = map sizeToStr [free, diff] freep = if total > 0 then free * 100 `div` total else 0 fr = fromIntegral freep / 100 s <- zipWithM showWithColors' strs [freep, 100 - freep] @@ -146,6 +145,7 @@ runDiskU' opts tmp stat = do uvb <- showVerticalBar (fromIntegral $ 100 - freep) (1 - fr) uipat <- showIconPattern (usedIconPattern opts) (1 - fr) parseTemplate $ [sizeToStr total] ++ s ++ sp ++ [fb,fvb,fipat,ub,uvb,uipat] +runDiskU' _ _ _ = return "" runDiskU :: [(String, String)] -> [String] -> Monitor String runDiskU disks argv = do diff --git a/src/Xmobar/Plugins/Monitors/Disk/FreeBSD.hsc b/src/Xmobar/Plugins/Monitors/Disk/FreeBSD.hsc index 296ba6c..b5530f1 100644 --- a/src/Xmobar/Plugins/Monitors/Disk/FreeBSD.hsc +++ b/src/Xmobar/Plugins/Monitors/Disk/FreeBSD.hsc @@ -39,7 +39,7 @@ import Xmobar.Plugins.Monitors.Disk.Common ( , Path ) -import qualified Control.Exception.Extensible as E +import qualified Control.Exception as E import qualified Data.List as DL import qualified Data.Map as DM import qualified Data.Set as DS diff --git a/src/Xmobar/Plugins/Monitors/MPD.hs b/src/Xmobar/Plugins/Monitors/MPD.hs index 7ecbc0c..b091147 100644 --- a/src/Xmobar/Plugins/Monitors/MPD.hs +++ b/src/Xmobar/Plugins/Monitors/MPD.hs @@ -109,8 +109,9 @@ parseMPD (Right st) song opts = do si = stateGlyph s opts vol = int2str $ fromMaybe 0 (M.stVolume st) (p, t) = fromMaybe (0, 0) (M.stTime st) - [lap, len, remain] = map showTime - [floor p, floor t, max 0 (floor t - floor p)] + lap = showTime $ floor p + len = showTime $ floor t + remain = showTime $ max 0 (floor t - floor p) b = if t > 0 then realToFrac $ p / t else 0 plen = int2str $ M.stPlaylistLength st ppos = maybe "" (int2str . (+1)) $ M.stSongPos st diff --git a/src/Xmobar/Plugins/Monitors/Mem/Linux.hs b/src/Xmobar/Plugins/Monitors/Mem/Linux.hs index 79dcc9d..7a81c6d 100644 --- a/src/Xmobar/Plugins/Monitors/Mem/Linux.hs +++ b/src/Xmobar/Plugins/Monitors/Mem/Linux.hs @@ -25,9 +25,13 @@ parseMEM = let content = map words $ take 8 $ lines file info = M.fromList $ map ( \line -> (head line, (read $ line !! 1 :: Float) / 1024)) content - [total, free, buffer, cache] = - map (info M.!) ["MemTotal:", "MemFree:", "Buffers:", "Cached:"] - available = M.findWithDefault (free + buffer + cache) "MemAvailable:" info + info' x = info M.! (x ++ ":") + total = info' "MemTotal" + free = info' "MemFree" + buffer = info' "Buffers" + cache = info' "Cached" + available = + M.findWithDefault (free + buffer + cache) "MemAvailable:" info used = total - available usedratio = used / total freeratio = free / total diff --git a/src/Xmobar/Plugins/Monitors/Mpris.hs b/src/Xmobar/Plugins/Monitors/Mpris.hs index ee30ad3..eb9595b 100644 --- a/src/Xmobar/Plugins/Monitors/Mpris.hs +++ b/src/Xmobar/Plugins/Monitors/Mpris.hs @@ -28,7 +28,7 @@ import qualified DBus.Client as DC import Control.Arrow ((***)) import Data.Maybe ( fromJust ) import Data.Int ( Int32, Int64 ) -import Data.Word ( Word32 ) +import Data.Word ( Word32, Word64 ) import System.IO.Unsafe ( unsafePerformIO ) import Control.Exception (try) @@ -136,17 +136,17 @@ makeList version md = map getStr (fieldsList version) where "xesam:trackNumber" -> printf "%02d" num _ -> (show::Int32 -> String) num pw32 v = printf "%02d" (fromVar v::Word32) - plen str v = let num = fromVar v in - case str of + plen str num = case str of "mpris:length" -> formatTime (num `div` 1000000) - _ -> (show::Int64 -> String) num + _ -> show num getStr str = case lookup str md of Nothing -> "" Just v -> case variantType v of TypeString -> fromVar v TypeInt32 -> pInt str v TypeWord32 -> pw32 v - TypeInt64 -> plen str v + TypeWord64 -> plen str (fromVar v :: Word64) + TypeInt64 -> plen str (fromVar v :: Int64) TypeArray TypeString -> let x = arrayItems (fromVar v) in if null x then "" else fromVar (head x) diff --git a/src/Xmobar/Plugins/Monitors/Net/Linux.hs b/src/Xmobar/Plugins/Monitors/Net/Linux.hs index 9306497..f9cbc28 100644 --- a/src/Xmobar/Plugins/Monitors/Net/Linux.hs +++ b/src/Xmobar/Plugins/Monitors/Net/Linux.hs @@ -47,7 +47,10 @@ isUp d = flip catchIOError (const $ return False) $ do return $! (head . B.lines) operstate `elem` ["up", "unknown"] readNetDev :: [String] -> IO NetDevRawTotal -readNetDev ~[d, x, y] = do +readNetDev ds = do + let (d, x, y) = case ds of + d':x':y':_ -> (d', x', y') + _ -> ("", "", "") up <- unsafeInterleaveIO $ isUp d return $ N d (if up then ND (r x) (r y) else NI) where r s | s == "" = 0 diff --git a/src/Xmobar/Plugins/Monitors/Swap/FreeBSD.hsc b/src/Xmobar/Plugins/Monitors/Swap/FreeBSD.hsc index 9c74e36..90c58c1 100644 --- a/src/Xmobar/Plugins/Monitors/Swap/FreeBSD.hsc +++ b/src/Xmobar/Plugins/Monitors/Swap/FreeBSD.hsc @@ -71,11 +71,10 @@ instance Storable SwapData where poke _ _ = pure () - isEnabled :: IO Bool isEnabled = do - enabled <- sysctlReadUInt "vm.swap_enabled" - return $ enabled == 1 + nswapdev <- sysctlReadUInt "vm.nswapdev" + return $ nswapdev > 0 parseMEM' :: Bool -> IO [Float] parseMEM' False = return [] diff --git a/src/Xmobar/Plugins/Monitors/Top.hs b/src/Xmobar/Plugins/Monitors/Top.hs index 3bfe6fd..b2e573b 100644 --- a/src/Xmobar/Plugins/Monitors/Top.hs +++ b/src/Xmobar/Plugins/Monitors/Top.hs @@ -3,7 +3,7 @@ ----------------------------------------------------------------------------- -- | -- Module : Plugins.Monitors.Top --- Copyright : (c) 2010, 2011, 2012, 2013, 2014, 2018, 2022 Jose A Ortega Ruiz +-- Copyright : (c) 2010-2014, 2018, 2022, 2025 Jose A Ortega Ruiz -- License : BSD-style (see LICENSE) -- -- Maintainer : Jose A Ortega Ruiz <jao@gnu.org> @@ -20,7 +20,7 @@ import Xmobar.Plugins.Monitors.Common import Data.IORef (newIORef, readIORef, writeIORef) import Data.List (sortBy) -import Data.Ord (comparing) +import Data.Ord (comparing, Down (..)) import Data.Time.Clock (getCurrentTime, diffUTCTime) import Xmobar.Plugins.Monitors.Top.Common ( @@ -66,7 +66,7 @@ showInfo nm sms mms = do sortTop :: [(String, Float)] -> [(String, Float)] -sortTop = sortBy (flip (comparing snd)) +sortTop = sortBy (comparing (Down . snd)) showMemInfo :: Float -> MemInfo -> Monitor [String] showMemInfo scale (nm, rss) = diff --git a/src/Xmobar/Plugins/Monitors/Weather.hs b/src/Xmobar/Plugins/Monitors/Weather.hs index e71de10..6b5c353 100644 --- a/src/Xmobar/Plugins/Monitors/Weather.hs +++ b/src/Xmobar/Plugins/Monitors/Weather.hs @@ -66,6 +66,7 @@ weatherConfig = mkMConfig , "skyCondition" , "skyConditionS" , "weather" + , "weatherS" , "tempC" , "tempF" , "dewPointC" @@ -221,23 +222,23 @@ getData station = CE.catch errHandler :: CE.SomeException -> IO String errHandler _ = return "<Could not retrieve data>" -formatSk :: Eq p => [(p, p)] -> p -> p -formatSk ((a,b):sks) sk = if a == sk then b else formatSk sks sk -formatSk [] sk = sk - formatWeather :: WeatherOpts -- ^ Formatting options from the cfg file -> [(String,String)] -- ^ 'SkyConditionS' for 'WeatherX' -> [WeatherInfo] -- ^ The actual weather info -> Monitor String -formatWeather opts sks [WI st ss y m d h (WindInfo wc wa wm wk wkh wms) v sk we tC tF dC dF r p] = - do cel <- showWithColors show tC +formatWeather opts sks [WI st ss y m d h wind v sk we tC tF dC dF r p] = + do let WindInfo wc wa wm wk wkh wms = wind + cel <- showWithColors show tC far <- showWithColors show tF - let sk' = formatSk sks (map toLower sk) - we' = showWeather (weatherString opts) we + let we' = showWeather (weatherString opts) we + sk' = findSk sks (map toLower sk) we' + we'' = findSk sks (map toLower we') sk' parseTemplate [st, ss, y, m, d, h, wc, wa, wm, wk, wkh - , wms, v, sk, sk', we', cel, far + , wms, v, sk, sk', we', we'', cel, far , show dC, show dF, show r , show p ] + where findSk ((a,b):xs) x df = if a == x then b else findSk xs x df + findSk [] _ df = df formatWeather _ _ _ = getConfigValue naString -- | Show the 'weather' field with a default string in case it was empty. diff --git a/src/Xmobar/Plugins/PacmanUpdates.hs b/src/Xmobar/Plugins/PacmanUpdates.hs new file mode 100644 index 0000000..ea14454 --- /dev/null +++ b/src/Xmobar/Plugins/PacmanUpdates.hs @@ -0,0 +1,165 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilyDependencies #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ViewPatterns #-} + +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- + +{- | +Module : Plugins.Monitors.PacmanUpdates +Copyright : (c) 2024, 2026 Enrico Maria De Angelis + , (c) 2025 Alexander Pankoff + , (c) 2026 Enrico Maria De Angelis +License : BSD-style (see LICENSE) + +Maintainer : Enrico Maria De Angelis <enricomaria.dean6elis@gmail.com> +Stability : unstable +Portability : unportable + +A Pacman updates availablility plugin for Xmobar. It also informs whether a kernel update is +available (provided the name of the kernel package), and whether the running kernel is older +than the installed one. +-} +module Xmobar.Plugins.PacmanUpdates ( +#if __GLASGOW_HASKELL__ >= 908 + {-# DEPRECATED "This ctor is DEPRECATED; please use `PacmanUpdates` type and `PacmanUpdatesK`, `PacmanUpdatesPredicateK` and `PacmanUpdatesNoK` constructors instead." #-} +#endif + pattern PacmanUpdates + , PacmanUpdates () + , PacmanUpdatesKernelCheck (..) + , pattern PacmanUpdatesK + , pattern PacmanUpdatesPredicateK + , pattern PacmanUpdatesNoK) where + +import System.Exit (ExitCode (..)) +import System.Process (readProcessWithExitCode) +import Xmobar.Plugins.Command (Rate) +import Xmobar.Run.Exec +import Data.Tuple.Extra (fst3) +import Data.Kind (Type) +import Data.Functor ((<&>)) +import Data.Void (Void) +import Control.Arrow ((&&&)) +import qualified Data.Vector as V + +-- | Deprecated plugin ctor (will be deleted in 2027). +-- Use `PacmanUpdatesK`, `PacmanUpdatesPredicateK`, or `PacmanUpdatesNoK` instead. +pattern PacmanUpdates :: (String, String, String, String) -- ^ `String`s to be shown for 0, 1, ≥ 2 updates, + -- and for error respectively (in the 3rd string, for + -- ≥ 2 updates, any occurrence of the '?' character + -- is a placeholder for the number of available updates). + -> Rate -- ^ `Rate` of update (see [Xmobar doc](https://codeberg.org/xmobar/xmobar/src/commit/39fd70308c3aef5402abe7152ade76ff7bb331bb/src/Xmobar/Plugins/Command.hs#L34)). + -> PacmanUpdates NoKernelCheck +pattern PacmanUpdates irrelevant <- (error "PacmanUpdates: PacmanUpdates is a build-only pattern synonym (a ctor synonym)." -> irrelevant) + where PacmanUpdates zome r + = let (z, o, m, e) = zome + printer = const + $ (++ deprecationNote) + . \case Left _ -> e + Right 0 -> z + Right 1 -> o + Right n -> m >>= \c -> if c == '?' + then show n + else pure c + deprecationNote = " <fc=#ff0000>(<action=`xdg-open https://codeberg.org/xmobar/xmobar/pulls/765`>" + ++ "deprecated plugin, click here</action>)</fc>" + in PacmanUpdatesNoK r printer + + +-- | Different types of kernel checks. +data PacmanUpdatesKernelCheck = NoKernelCheck | PredicateKernelCheck + +-- | PacmanUpdates plugin parametrized over the `PacmanUpdatesKernelCheck` kind. +data PacmanUpdates (b :: PacmanUpdatesKernelCheck) + = Make -- ^ Constructor. + Rate -- ^ `Rate` of update (see [Xmobar doc](https://codeberg.org/xmobar/xmobar/src/commit/39fd70308c3aef5402abe7152ade76ff7bb331bb/src/Xmobar/Plugins/Command.hs#L34)). + (Arg b) -- ^ Optional further argument. See instances of `Updates`. + (Printer b) -- ^ Printer. See instances of `Updates` for its signature. + +instance Show (PacmanUpdates b) where + show = error "PacmanUpdates: Show instance is stub" + +instance Read (PacmanUpdates b) where + readsPrec = error "PacmanUpdates: Read instance is stub" + +instance Updates b => Exec (PacmanUpdates (b :: PacmanUpdatesKernelCheck)) where + alias = const "pacman" + rate (Make r _ _) = r + run = Xmobar.Plugins.PacmanUpdates.run' + +class Updates (b :: PacmanUpdatesKernelCheck) where + -- | See `Updates`'s instances. + type Arg b = (a :: Type) | a -> b + -- | See `Updates`'s instances. + type Printer b = (p :: Type) | p -> b + -- | This is the implementation of `Xmobar.Run.Exec.run`. + run' :: PacmanUpdates b -> IO String + +-- | No additional argument required for constructing the plugin; +-- the user-provided printer is fed with a `Bool` telling whether +-- the system is running an outdated kernel, and an `Int` telling +-- the number of available updates (or `Left` if an error occurred +-- when calling `checkupdates`). +instance Updates NoKernelCheck where + type Arg NoKernelCheck = Void + type Printer NoKernelCheck = Bool -> Either String Int -> String + run' (Make _ _ printer) + = printer + <$> kernIsOld + <*> (fmap V.length <$> checkUpdates) + +-- | Constructing the plugin requires an additional `String -> Bool` predicate +-- that receives each available package name and returns `True` for kernel +-- packages; the user-provided printer is fed with a `Bool` telling whether the +-- system is running an outdated kernel, and an `(Int, Bool)` pair telling the +-- number of available updates and whether one of these is a kernel update (or +-- `Left` if an error occurred when calling `checkupdates`). +instance Updates PredicateKernelCheck where + type Arg PredicateKernelCheck = String -> Bool + type Printer PredicateKernelCheck = Bool -> Either String (Int, Bool) -> String + run' (Make _ checkKern printer) + = printer + <$> kernIsOld + <*> (fmap (V.length &&& V.any checkKern) <$> checkUpdates) + +-- | Pattern synonym to construct a `PacmanUpdates PredicateKernelCheck` that +-- detects updates for packages matched by the given @(String -> Bool)@ +-- predicate. This can be used to detect kernel updates for distributions +-- with versioned kernel package names (e.g. Manjaro's @linux618@) +pattern PacmanUpdatesPredicateK :: Rate -> Arg PredicateKernelCheck -> Printer PredicateKernelCheck -> PacmanUpdates PredicateKernelCheck +pattern PacmanUpdatesPredicateK r a p = Make r a p + +-- | A convenience wrapper around PacmanUpdatesPredicateK with the predicate @(== kernName)@ +-- Construction only: the kernel name cannot be recovered when matching. +pattern PacmanUpdatesK :: Rate -> String -> Printer PredicateKernelCheck -> PacmanUpdates PredicateKernelCheck +pattern PacmanUpdatesK r kernName p <- + (error "PacmanUpdatesK: build-only pattern synonym (a ctor synonym)." -> (r, kernName, p)) + where PacmanUpdatesK r kernName p = PacmanUpdatesPredicateK r (== kernName) p + +-- | Pattern synonym used to construct a `PacmanUpdates NoKernelCheck`. +pattern PacmanUpdatesNoK :: Rate -> Printer NoKernelCheck -> PacmanUpdates NoKernelCheck +pattern PacmanUpdatesNoK r p <- Make r _ p + where PacmanUpdatesNoK r p = Make r undefined p + +checkUpdates :: IO (Either String (V.Vector String)) +checkUpdates = readProcessWithExitCode "checkupdates" [] "" + <&> \case (ExitFailure 2, "", "") -> Right V.empty + (ExitSuccess, stdout, "") + -> let pkgName = takeWhile (/= ' ') + pkgs = V.fromList $ fmap pkgName $ lines stdout + in case V.length pkgs of + 0 -> impossible + _ -> Right pkgs + (ExitFailure 1, _, _) -> Left "checkupdates: unknown cause of failure." + _ -> impossible + +kernIsOld :: IO Bool +kernIsOld = (/= ExitSuccess) . exitCode <$> readProcessWithExitCode "modinfo" ["-n", "i915"] "" + where exitCode = fst3 + +impossible :: a +impossible = error "This is impossible, according to my knowledge." diff --git a/src/Xmobar/Run/Actions.hs b/src/Xmobar/Run/Actions.hs index 2a49312..a9afed9 100644 --- a/src/Xmobar/Run/Actions.hs +++ b/src/Xmobar/Run/Actions.hs @@ -16,7 +16,7 @@ module Xmobar.Run.Actions ( Button , runAction' , stripActions) where -import System.Process (system) +import System.Process (shell, withCreateProcess, waitForProcess) import Control.Monad (void) import Text.Regex (Regex, subRegex, mkRegex, matchRegex) import Data.Word (Word32) @@ -26,11 +26,12 @@ type Button = Word32 data Action = Spawn [Button] String deriving (Eq, Read, Show) runAction :: Action -> IO () -runAction (Spawn _ s) = void $ system (s ++ "&") +runAction (Spawn _ s) = withCreateProcess (shell s) $ \_ _ _ ph -> + void $ waitForProcess ph -- | Run action with stdout redirected to stderr runAction' :: Action -> IO () -runAction' (Spawn _ s) = void $ system (s ++ " 1>&2 &") +runAction' (Spawn btn s) = runAction (Spawn btn (s ++ " 1>&2")) stripActions :: String -> String stripActions s = case matchRegex actionRegex s of diff --git a/src/Xmobar/Run/Loop.hs b/src/Xmobar/Run/Loop.hs index bda41ff..6775961 100644 --- a/src/Xmobar/Run/Loop.hs +++ b/src/Xmobar/Run/Loop.hs @@ -57,7 +57,7 @@ type LoopFunction = TMVar SignalType -> TVar [String] -> IO () loop :: Config -> LoopFunction -> IO () loop conf looper = withDeferSignals $ do - cls <- mapM (parseTemplate (commands conf) (sepChar conf)) + let cls = map (parseTemplate (commands conf) (sepChar conf)) (splitTemplate (alignSep conf) (template conf)) let confSig = unSignalChan (signal conf) sig <- maybe newEmptyTMVarIO pure confSig diff --git a/src/Xmobar/Run/Parsers.hs b/src/Xmobar/Run/Parsers.hs deleted file mode 100644 index 9b36786..0000000 --- a/src/Xmobar/Run/Parsers.hs +++ /dev/null @@ -1,244 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} - ------------------------------------------------------------------------------ --- | --- Module : Xmobar.Run.Parsers --- Copyright : (c) Andrea Rossato --- License : BSD-style (see LICENSE) --- --- Maintainer : Jose A. Ortega Ruiz <jao@gnu.org> --- Stability : unstable --- Portability : portable --- --- Parsing for template substrings --- ------------------------------------------------------------------------------ - -module Xmobar.Run.Parsers ( parseString - , colorComponents - , Segment - , FontIndex - , Box(..) - , BoxBorder(..) - , BoxOffset(..) - , BoxMargins(..) - , TextRenderInfo(..) - , Widget(..)) where - -import Control.Monad (guard, mzero) -import Data.Maybe (fromMaybe) -import Data.Int (Int32) -import Text.ParserCombinators.Parsec -import Text.Read (readMaybe) -import Foreign.C.Types (CInt) - -import Xmobar.Config.Types -import Xmobar.Run.Actions - -data Widget = Icon String | Text String | Hspace Int32 deriving Show - -data BoxOffset = BoxOffset Align Int32 deriving (Eq, Show) - --- margins: Top, Right, Bottom, Left -data BoxMargins = BoxMargins Int32 Int32 Int32 Int32 deriving (Eq, Show) - -data BoxBorder = BBTop - | BBBottom - | BBVBoth - | BBLeft - | BBRight - | BBHBoth - | BBFull - deriving (Read, Eq, Show) - -data Box = Box { bBorder :: BoxBorder - , bOffset :: BoxOffset - , bWidth :: CInt - , bColor :: String - , bMargins :: BoxMargins - } deriving (Eq, Show) - -data TextRenderInfo = TextRenderInfo { tColorsString :: String - , tBgTopOffset :: Int32 - , tBgBottomOffset :: Int32 - , tBoxes :: [Box] - } deriving Show - -type Segment = (Widget, TextRenderInfo, FontIndex, Maybe [Action]) - --- | Runs the string parser -parseString :: Config -> String -> IO [Segment] -parseString c s = - case parse (stringParser ci 0 Nothing) "" s of - Left _ -> return [(Text $ "Could not parse string: " ++ s - , ci - , 0 - , Nothing)] - Right x -> return (concat x) - where ci = TextRenderInfo (fgColor c) 0 0 [] - --- | Splits a colors string into its two components -colorComponents :: Config -> String -> (String, String) -colorComponents conf c = - case break (==',') c of - (f,',':b) -> (f, b) - (f, _) -> (f, bgColor conf) - -allParsers :: TextRenderInfo -> FontIndex -> Maybe [Action] -> Parser [Segment] -allParsers c f a = textParser c f a - <|> try (iconParser c f a) - <|> try (hspaceParser c f a) - <|> try (rawParser c f a) - <|> try (actionParser c f a) - <|> try (fontParser c a) - <|> try (boxParser c f a) - <|> colorParser c f a - --- | Gets the string and combines the needed parsers -stringParser :: TextRenderInfo -> FontIndex -> Maybe [Action] -> Parser [[Segment]] -stringParser c f a = manyTill (allParsers c f a) eof - --- | Parses a maximal string without markup. -textParser :: TextRenderInfo -> FontIndex -> Maybe [Action] -> Parser [Segment] -textParser c f a = do s <- many1 $ - noneOf "<" <|> - try (notFollowedBy' (char '<') - (try (string "fc=") <|> - try (string "box") <|> - try (string "fn=") <|> - try (string "action=") <|> - try (string "/action>") <|> - try (string "icon=") <|> - try (string "hspace=") <|> - try (string "raw=") <|> - try (string "/fn>") <|> - try (string "/box>") <|> - string "/fc>")) - return [(Text s, c, f, a)] - --- | Parse a "raw" tag, which we use to prevent other tags from creeping in. --- The format here is net-string-esque: a literal "<raw=" followed by a --- string of digits (base 10) denoting the length of the raw string, --- a literal ":" as digit-string-terminator, the raw string itself, and --- then a literal "/>". -rawParser :: TextRenderInfo -> FontIndex -> Maybe [Action] -> Parser [Segment] -rawParser c f a = do - string "<raw=" - lenstr <- many1 digit - char ':' - case reads lenstr of - [(len,[])] -> do - guard ((len :: Integer) <= fromIntegral (maxBound :: Int)) - s <- count (fromIntegral len) anyChar - string "/>" - return [(Text s, c, f, a)] - _ -> mzero - --- | Wrapper for notFollowedBy that returns the result of the first parser. --- Also works around the issue that, at least in Parsec 3.0.0, notFollowedBy --- accepts only parsers with return type Char. -notFollowedBy' :: Parser a -> Parser b -> Parser a -notFollowedBy' p e = do x <- p - notFollowedBy $ try (e >> return '*') - return x - -iconParser :: TextRenderInfo -> FontIndex -> Maybe [Action] -> Parser [Segment] -iconParser c f a = do - string "<icon=" - i <- manyTill (noneOf ">") (try (string "/>")) - return [(Icon i, c, f, a)] - -hspaceParser :: TextRenderInfo -> FontIndex -> Maybe [Action] -> Parser [Segment] -hspaceParser c f a = do - string "<hspace=" - pVal <- manyTill digit (try (string "/>")) - return [(Hspace (fromMaybe 0 $ readMaybe pVal), c, f, a)] - -actionParser :: TextRenderInfo -> FontIndex -> Maybe [Action] -> Parser [Segment] -actionParser c f act = do - string "<action=" - command <- choice [between (char '`') (char '`') (many1 (noneOf "`")), - many1 (noneOf ">")] - buttons <- (char '>' >> return "1") <|> (space >> spaces >> - between (string "button=") (string ">") (many1 (oneOf "12345"))) - let a = Spawn (toButtons buttons) command - a' = case act of - Nothing -> Just [a] - Just act' -> Just $ a : act' - s <- manyTill (allParsers c f a') (try $ string "</action>") - return (concat s) - -toButtons :: String -> [Button] -toButtons = map (\x -> read [x]) - --- | Parsers a string wrapped in a color specification. -colorParser :: TextRenderInfo -> FontIndex -> Maybe [Action] -> Parser [Segment] -colorParser (TextRenderInfo _ _ _ bs) fidx a = do - c <- between (string "<fc=") (string ">") colors - let colorParts = break (==':') c - let (ot,ob) = case break (==',') (Prelude.drop 1 $ snd colorParts) of - (top,',':btm) -> (top, btm) - (top, _) -> (top, top) - tri = TextRenderInfo (fst colorParts) - (fromMaybe (-1) $ readMaybe ot) - (fromMaybe (-1) $ readMaybe ob) - bs - s <- manyTill (allParsers tri fidx a) (try $ string "</fc>") - return (concat s) - --- | Parses a string wrapped in a box specification. -boxParser :: TextRenderInfo -> FontIndex -> Maybe [Action] -> Parser [Segment] -boxParser (TextRenderInfo cs ot ob bs) f a = do - c <- between (string "<box") (string ">") - (option "" (many1 (alphaNum - <|> char '=' - <|> char ' ' - <|> char '#' - <|> char ','))) - let b = Box BBFull (BoxOffset C 0) 1 cs (BoxMargins 0 0 0 0) - let g = boxReader b (words c) - s <- manyTill - (allParsers (TextRenderInfo cs ot ob (g : bs)) f a) - (try $ string "</box>") - return (concat s) - -boxReader :: Box -> [String] -> Box -boxReader b [] = b -boxReader b (x:xs) = do - let (param,val) = case break (=='=') x of - (p,'=':v) -> (p, v) - (p, _) -> (p, "") - boxReader (boxParamReader b param val) xs - -boxParamReader :: Box -> String -> String -> Box -boxParamReader b _ "" = b -boxParamReader (Box bb off lw fc mgs) "type" val = - Box (fromMaybe bb $ readMaybe ("BB" ++ val)) off lw fc mgs -boxParamReader (Box bb (BoxOffset alg off) lw fc mgs) "offset" (a:o) = - Box bb (BoxOffset align offset) lw fc mgs - where offset = fromMaybe off $ readMaybe o - align = fromMaybe alg $ readMaybe [a] -boxParamReader (Box bb off lw fc mgs) "width" val = - Box bb off (fromMaybe lw $ readMaybe val) fc mgs -boxParamReader (Box bb off lw _ mgs) "color" val = - Box bb off lw val mgs -boxParamReader (Box bb off lw fc mgs@(BoxMargins mt mr mb ml)) ('m':pos) val = do - let mgs' = case pos of - "t" -> BoxMargins (fromMaybe mt $ readMaybe val) mr mb ml - "r" -> BoxMargins mt (fromMaybe mr $ readMaybe val) mb ml - "b" -> BoxMargins mt mr (fromMaybe mb $ readMaybe val) ml - "l" -> BoxMargins mt mr mb (fromMaybe ml $ readMaybe val) - _ -> mgs - Box bb off lw fc mgs' -boxParamReader b _ _ = b - --- | Parsers a string wrapped in a font specification. -fontParser :: TextRenderInfo -> Maybe [Action] -> Parser [Segment] -fontParser c a = do - f <- between (string "<fn=") (string ">") colors - s <- manyTill (allParsers c (fromMaybe 0 $ readMaybe f) a) (try $ string "</fn>") - return (concat s) - --- | Parses a color specification (hex or named) -colors :: Parser String -colors = many1 (alphaNum <|> char ',' <|> char ':' <|> char '#') diff --git a/src/Xmobar/Run/Template.hs b/src/Xmobar/Run/Template.hs index 87c84d3..0e066dc 100644 --- a/src/Xmobar/Run/Template.hs +++ b/src/Xmobar/Run/Template.hs @@ -50,21 +50,20 @@ templateParser s = many $ templateStringParser s -- | Actually runs the template parsers over a (segment of) a template -- string, returning a list of runnables with their prefix and suffix. -parseTemplate :: [Runnable] -> String -> String -> IO [(Runnable,String,String)] +parseTemplate :: [Runnable] -> String -> String -> [(Runnable,String,String)] parseTemplate c sepChar s = - do str <- case parse (templateParser sepChar) "" s of - Left _ -> return [("", s, "")] - Right x -> return x - let cl = map alias c - m = Map.fromList $ zip cl c - return $ combine c m str + let str = case parse (templateParser sepChar) "" s of + Left _ -> [("", s, "")] + Right x -> x + cl = map alias c + m = Map.fromList $ zip cl c + in combine m str -- | Given a finite "Map" and a parsed template produce the resulting -- output string. -combine :: [Runnable] -> Map.Map String Runnable -> [(String, String, String)] - -> [(Runnable,String,String)] -combine _ _ [] = [] -combine c m ((ts,s,ss):xs) = (com, s, ss) : combine c m xs +combine :: Map.Map String Runnable -> [(String, String, String)] -> [(Runnable,String,String)] +combine _ [] = [] +combine m ((ts,s,ss):xs) = (com, s, ss) : combine m xs where com = Map.findWithDefault dflt ts m dflt = Run $ Com ts [] [] 10 @@ -77,5 +76,6 @@ splitTemplate alignSep template = (ce,_:ri) -> [le, ce, ri] _ -> def _ -> def - where [l, r] = if length alignSep == 2 then alignSep else defaultAlign + where sep = if length alignSep == 2 then alignSep else defaultAlign + (l, r) = (head sep, sep !! 1) def = [template, "", ""] diff --git a/src/Xmobar/Run/Types.hs b/src/Xmobar/Run/Types.hs index 69406bb..8b9caa4 100644 --- a/src/Xmobar/Run/Types.hs +++ b/src/Xmobar/Run/Types.hs @@ -1,4 +1,5 @@ {-# LANGUAGE TypeOperators, CPP #-} +{-# LANGUAGE DataKinds #-} ------------------------------------------------------------------------------ -- | -- Module: Xmobar.Run.Types @@ -19,6 +20,7 @@ module Xmobar.Run.Types(runnableTypes) where import {-# SOURCE #-} Xmobar.Run.Runnable() +import Xmobar.Plugins.PacmanUpdates import Xmobar.Plugins.Command import Xmobar.Plugins.Monitors import Xmobar.Plugins.Date diff --git a/src/Xmobar/System/DBus.hs b/src/Xmobar/System/DBus.hs index 103a5a9..90bee2a 100644 --- a/src/Xmobar/System/DBus.hs +++ b/src/Xmobar/System/DBus.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE ScopedTypeVariables #-} ----------------------------------------------------------------------------- -- | -- Module : DBus @@ -17,9 +18,10 @@ module Xmobar.System.DBus (runIPC) where import DBus import DBus.Client hiding (interfaceName) import qualified DBus.Client as DC +import DBus.Socket import Data.Maybe (isNothing) import Control.Concurrent.STM -import Control.Exception (handle) +import Control.Exception import System.IO (stderr, hPutStrLn) import Control.Monad.IO.Class (liftIO) @@ -35,10 +37,10 @@ interfaceName :: InterfaceName interfaceName = interfaceName_ "org.Xmobar.Control" runIPC :: TMVar SignalType -> IO () -runIPC mvst = handle printException exportConnection +runIPC mvst = exportConnection `catches` [ + Handler(\ (ex :: ClientError) -> hPutStrLn stderr (clientErrorMessage ex)), + Handler(\ (ex :: SocketError) -> hPutStrLn stderr (socketErrorMessage ex))] where - printException :: ClientError -> IO () - printException = hPutStrLn stderr . clientErrorMessage exportConnection = do client <- connectSession requestName client busName [ nameDoNotQueue ] diff --git a/src/Xmobar/System/Environment.hs b/src/Xmobar/System/Environment.hs index 25802fe..0491bcc 100644 --- a/src/Xmobar/System/Environment.hs +++ b/src/Xmobar/System/Environment.hs @@ -13,14 +13,14 @@ ----------------------------------------------------------------------------- module Xmobar.System.Environment(expandEnv) where -import Data.Maybe (fromMaybe) -import System.Environment (lookupEnv) +import qualified Data.Maybe as M +import qualified System.Environment as E expandEnv :: String -> IO String expandEnv "" = return "" expandEnv (c:s) = case c of '$' -> do - envVar <- fromMaybe "" <$> lookupEnv e + envVar <- M.fromMaybe "" <$> E.lookupEnv e remainder <- expandEnv s' return $ envVar ++ remainder where (e, s') = getVar s @@ -36,12 +36,13 @@ expandEnv (c:s) = case c of False -> do remainder <- expandEnv $ drop 1 s return $ escString s ++ remainder - where escString s' = let (cc:_) = s' in + where escString (cc:_) = case cc of 't' -> "\t" 'n' -> "\n" '$' -> "$" _ -> [cc] + escString [] = "" _ -> do remainder <- expandEnv s diff --git a/src/Xmobar/Text/Loop.hs b/src/Xmobar/Text/Loop.hs index 05379cd..5d2c43f 100644 --- a/src/Xmobar/Text/Loop.hs +++ b/src/Xmobar/Text/Loop.hs @@ -45,4 +45,4 @@ eventLoop cfg signal tv = do updateString :: Config -> TVar [String] -> IO String updateString conf v = do s <- readTVarIO v - format conf (concat s) + return $ format conf (concat s) diff --git a/src/Xmobar/Text/Output.hs b/src/Xmobar/Text/Output.hs index 783a5bb..677b6d2 100644 --- a/src/Xmobar/Text/Output.hs +++ b/src/Xmobar/Text/Output.hs @@ -15,13 +15,15 @@ module Xmobar.Text.Output (initLoop, format) where -import Xmobar.Config.Types (Config(textOutputFormat, additionalFonts, font) - , TextOutputFormat(..)) -import Xmobar.Run.Parsers ( Segment - , Widget(..) - , parseString - , tColorsString - , colorComponents) +import Xmobar.Config.Types ( Config (..) + , TextOutputFormat (..) + , Segment + , Widget (..) + , tColorsString) + + +import Xmobar.Config.Parse (colorComponents) +import Xmobar.Config.Template (parseString) import Xmobar.Text.Ansi (withAnsiColor) import Xmobar.Text.Pango (withPangoMarkup) @@ -47,9 +49,9 @@ formatWithColor conf (Hspace n, i, x, y) = formatWithColor conf (Text $ replicate (fromIntegral n) ' ', i, x, y) formatWithColor _ _ = "" -format :: Config -> String -> IO String +format :: Config -> String -> String format conf s = do - segments <- parseString conf s + let segments = parseString conf s case textOutputFormat conf of - Swaybar -> return $ formatSwaybar conf segments - _ -> return (concatMap (formatWithColor conf) segments) + Swaybar -> formatSwaybar conf segments + _ -> concatMap (formatWithColor conf) segments diff --git a/src/Xmobar/Text/Swaybar.hs b/src/Xmobar/Text/Swaybar.hs index a2fc585..355de06 100644 --- a/src/Xmobar/Text/Swaybar.hs +++ b/src/Xmobar/Text/Swaybar.hs @@ -24,16 +24,16 @@ import Data.ByteString.Lazy.UTF8 (toString) import GHC.Generics -import Xmobar.Config.Types (Config (additionalFonts)) - -import Xmobar.Run.Parsers ( Segment - , Widget(..) - , Box(..) - , BoxBorder(..) - , FontIndex - , tBoxes - , tColorsString - , colorComponents) +import Xmobar.Config.Types ( Config (additionalFonts) + , Segment + , Widget(..) + , Box(..) + , BoxBorder(..) + , FontIndex + , tBoxes + , tColorsString) + +import Xmobar.Config.Parse (colorComponents) import Xmobar.Text.SwaybarClicks (startHandler) import Xmobar.Text.Pango (withPangoFont) diff --git a/src/Xmobar/X11/Bitmap.hs b/src/Xmobar/X11/Bitmap.hs index b14356f..c5304d9 100644 --- a/src/Xmobar/X11/Bitmap.hs +++ b/src/Xmobar/X11/Bitmap.hs @@ -2,7 +2,7 @@ ----------------------------------------------------------------------------- -- | -- Module : X11.Bitmap --- Copyright : (C) 2013, 2015, 2017, 2018, 2022 Alexander Polakov +-- Copyright : (C) 2013, 2015, 2017, 2018, 2022, 2024 Alexander Polakov -- License : BSD3 -- -- Maintainer : jao@gnu.org @@ -116,8 +116,9 @@ loadBitmap d w p = do drawBitmap :: Display -> Drawable -> GC -> String -> String -> Position -> Position -> Bitmap -> IO () drawBitmap d p gc fc bc x y i = - withColors d [fc, bc] $ \[fc', bc'] -> do - let w = width i + withColors d [fc, bc] $ \cs -> do + let (fc', bc') = (head cs, cs !! 1) + w = width i h = height i y' = 1 + y - fromIntegral h `div` 2 setForeground d gc fc' diff --git a/src/Xmobar/X11/Draw.hs b/src/Xmobar/X11/Draw.hs index a056136..a1ec901 100644 --- a/src/Xmobar/X11/Draw.hs +++ b/src/Xmobar/X11/Draw.hs @@ -25,7 +25,6 @@ import Foreign.C.Types as FT import qualified Graphics.X11.Xlib as X11 import qualified Xmobar.Config.Types as C -import qualified Xmobar.Run.Parsers as P import qualified Xmobar.Draw.Types as D import qualified Xmobar.Draw.Cairo as DC @@ -38,11 +37,8 @@ import qualified Xmobar.X11.XRender as XRender #endif drawXBitmap :: T.XConf -> X11.GC -> X11.Pixmap -> D.IconDrawer -drawXBitmap xconf gc p h v path = do +drawXBitmap xconf gc p h v path fc bc = do let disp = T.display xconf - conf = T.config xconf - fc = C.fgColor conf - bc = C.bgColor conf case M.lookup path (T.iconCache xconf) of Just bm -> liftIO $ B.drawBitmap disp p gc fc bc (round h) (round v) bm Nothing -> return () @@ -69,7 +65,7 @@ withPixmap disp win (X11.Rectangle _ _ w h) depth action = do X11.sync disp True return res -draw :: [[P.Segment]] -> T.X [D.ActionPos] +draw :: [[C.Segment]] -> T.X [D.ActionPos] draw segments = do xconf <- ask let disp = T.display xconf @@ -89,7 +85,7 @@ draw segments = do #ifdef XRENDER color = C.bgColor conf alph = C.alpha conf - XRender.drawBackground disp p color alph (X11.Rectangle 0 0 w h) + XRender.drawBackground disp p color alph rect #endif CS.withXlibSurface disp p vis (fromIntegral w) (fromIntegral h) render diff --git a/src/Xmobar/X11/Events.hs b/src/Xmobar/X11/Events.hs index 4334f6b..fbd2bd0 100644 --- a/src/Xmobar/X11/Events.hs +++ b/src/Xmobar/X11/Events.hs @@ -1,7 +1,7 @@ ------------------------------------------------------------------------------ -- | -- Module: Xmobar.X11.Events --- Copyright: (c) 2018 Jose Antonio Ortega Ruiz +-- Copyright: (c) 2018, 2022 Jose Antonio Ortega Ruiz -- License: BSD3-style (see LICENSE) -- -- Maintainer: jao@gnu.org @@ -17,20 +17,19 @@ module Xmobar.X11.Events(nextEvent') where -import Control.Concurrent -import System.Posix.Types (Fd(..)) +import qualified Control.Concurrent as C +import qualified System.Posix.Types as T -import Graphics.X11.Xlib ( - Display(..), XEventPtr, nextEvent, pending, connectionNumber) +import qualified Graphics.X11.Xlib as X -- | A version of nextEvent that does not block in foreign calls. -nextEvent' :: Display -> XEventPtr -> IO () +nextEvent' :: X.Display -> X.XEventPtr -> IO () nextEvent' d p = do - pend <- pending d + pend <- X.pending d if pend /= 0 - then nextEvent d p + then X.nextEvent d p else do - threadWaitRead (Fd fd) + C.threadWaitRead (T.Fd fd) nextEvent' d p where - fd = connectionNumber d + fd = X.connectionNumber d diff --git a/src/Xmobar/X11/Loop.hs b/src/Xmobar/X11/Loop.hs index 599e680..0425cff 100644 --- a/src/Xmobar/X11/Loop.hs +++ b/src/Xmobar/X11/Loop.hs @@ -3,7 +3,7 @@ ------------------------------------------------------------------------------ -- | -- Module: Xmobar.App.X11EventLoop --- Copyright: (c) 2018, 2020, 2022 Jose Antonio Ortega Ruiz +-- Copyright: (c) 2018, 2020, 2022, 2023, 2024 Jose Antonio Ortega Ruiz -- License: BSD3-style (see LICENSE) -- -- Maintainer: jao@gnu.org @@ -22,6 +22,7 @@ import Prelude hiding (lookup) import Control.Concurrent as Concurrent import Control.Concurrent.STM as STM +import Control.Monad as MR import Control.Monad.Reader as MR import Data.Bits (Bits((.|.))) @@ -35,10 +36,10 @@ import qualified Graphics.X11.Xinerama as Xinerama import qualified Graphics.X11.Xrandr as Xrandr import qualified Xmobar.Config.Types as C +import qualified Xmobar.Config.Template as CT import qualified Xmobar.Run.Actions as A import qualified Xmobar.Run.Loop as L -import qualified Xmobar.Run.Parsers as P import qualified Xmobar.System.Utils as U import qualified Xmobar.System.Signal as S @@ -145,15 +146,14 @@ signalLoop xc@(T.XConf d r w fs is cfg) actions signalv strs = do r' <- W.repositionWin d w (NE.head fs) rcfg signalLoop (T.XConf d r' w fs is rcfg) actions signalv strs -parseSegments :: C.Config -> STM.TVar [String] -> IO [[P.Segment]] +parseSegments :: C.Config -> STM.TVar [String] -> IO [[C.Segment]] parseSegments conf v = do s <- STM.readTVarIO v - let l:c:r:_ = s ++ repeat "" - MR.liftIO $ mapM (P.parseString conf) [l, c, r] + return $ map (CT.parseString conf) (take 3 $ s ++ repeat "") -updateIconCache :: T.XConf -> [[P.Segment]] -> IO T.XConf +updateIconCache :: T.XConf -> [[C.Segment]] -> IO T.XConf updateIconCache xc@(T.XConf d _ w _ c cfg) segs = do - let paths = [p | (P.Icon p, _, _, _) <- concat segs] + let paths = [p | (C.Icon p, _, _, _) <- concat segs] c' <- Bitmap.updateCache d w c (C.iconRoot cfg) paths return $ xc {T.iconCache = c'} @@ -170,7 +170,7 @@ updateConfigPosition disp cfg = runActions :: D.Actions -> A.Button -> X11.Position -> IO () runActions actions button pos = mapM_ A.runAction $ - filter (\(A.Spawn b _) -> button `elem` b) $ - concatMap (\(a,_,_) -> a) $ - filter (\(_, from, to) -> pos' >= from && pos' <= to) actions + concatMap + (filter (\ (A.Spawn b _) -> button `elem` b) . (\ (a, _, _) -> a)) + (filter (\ (_, from, to) -> pos' >= from && pos' <= to) actions) where pos' = fromIntegral pos diff --git a/src/Xmobar/X11/Window.hs b/src/Xmobar/X11/Window.hs index ad7ebf7..87d56f4 100644 --- a/src/Xmobar/X11/Window.hs +++ b/src/Xmobar/X11/Window.hs @@ -86,10 +86,14 @@ setPosition c p rs ht = T.Top -> X.Rectangle rx ry rw h T.TopP l r -> X.Rectangle (rx + fi l) ry (rw - fi l - fi r) h T.TopH ch -> X.Rectangle rx ry rw (mh ch) + T.TopHM ch l r t _ -> + X.Rectangle (rx + fi l) (ry + fi t) (rw - fi l - fi r) (mh ch) T.TopW a i -> X.Rectangle (ax a i) ry (nw i) h T.TopSize a i ch -> X.Rectangle (ax a i) ry (nw i) (mh ch) T.Bottom -> X.Rectangle rx ny rw h T.BottomH ch -> X.Rectangle rx (ny' ch) rw (mh ch) + T.BottomHM ch l r _ b -> + X.Rectangle (rx + fi l) (ry + fi rh - fi b - fi (mh ch)) (rw - fi l - fi r) (mh ch) T.BottomW a i -> X.Rectangle (ax a i) ny (nw i) h T.BottomP l r -> X.Rectangle (rx + fi l) ny (rw - fi l - fi r) h T.BottomSize a i ch -> X.Rectangle (ax a i) (ny' ch) (nw i) (mh ch) @@ -160,18 +164,20 @@ getRootWindowHeight srs = maximum (map getMaxScreenYCoord srs) getStrutValues :: X.Rectangle -> T.XPosition -> Int -> [Int] getStrutValues r@(X.Rectangle x y w h) p rwh = case p of - T.OnScreen _ p' -> getStrutValues r p' rwh - T.Top -> [0, 0, st, 0, 0, 0, 0, 0, nx, nw, 0, 0] - T.TopH _ -> [0, 0, st, 0, 0, 0, 0, 0, nx, nw, 0, 0] - T.TopP _ _ -> [0, 0, st, 0, 0, 0, 0, 0, nx, nw, 0, 0] - T.TopW _ _ -> [0, 0, st, 0, 0, 0, 0, 0, nx, nw, 0, 0] - T.TopSize {} -> [0, 0, st, 0, 0, 0, 0, 0, nx, nw, 0, 0] - T.Bottom -> [0, 0, 0, sb, 0, 0, 0, 0, 0, 0, nx, nw] - T.BottomH _ -> [0, 0, 0, sb, 0, 0, 0, 0, 0, 0, nx, nw] - T.BottomP _ _ -> [0, 0, 0, sb, 0, 0, 0, 0, 0, 0, nx, nw] - T.BottomW _ _ -> [0, 0, 0, sb, 0, 0, 0, 0, 0, 0, nx, nw] - T.BottomSize {} -> [0, 0, 0, sb, 0, 0, 0, 0, 0, 0, nx, nw] - T.Static {} -> getStaticStrutValues p rwh + T.OnScreen _ p' -> getStrutValues r p' rwh + T.Top -> [0, 0, st , 0 , 0, 0, 0, 0, nx, nw, 0 , 0 ] + T.TopH _ -> [0, 0, st , 0 , 0, 0, 0, 0, nx, nw, 0 , 0 ] + T.TopHM _ _ _ _ b -> [0, 0, st+b, 0 , 0, 0, 0, 0, nx, nw, 0 , 0 ] + T.TopP _ _ -> [0, 0, st , 0 , 0, 0, 0, 0, nx, nw, 0 , 0 ] + T.TopW _ _ -> [0, 0, st , 0 , 0, 0, 0, 0, nx, nw, 0 , 0 ] + T.TopSize {} -> [0, 0, st , 0 , 0, 0, 0, 0, nx, nw, 0 , 0 ] + T.Bottom -> [0, 0, 0 , sb , 0, 0, 0, 0, 0 , 0 , nx, nw] + T.BottomH _ -> [0, 0, 0 , sb , 0, 0, 0, 0, 0 , 0 , nx, nw] + T.BottomHM _ _ _ t _ -> [0, 0, 0 , sb+t, 0, 0, 0, 0, 0 , 0 , nx, nw] + T.BottomP _ _ -> [0, 0, 0 , sb , 0, 0, 0, 0, 0 , 0 , nx, nw] + T.BottomW _ _ -> [0, 0, 0 , sb , 0, 0, 0, 0, 0 , 0 , nx, nw] + T.BottomSize {} -> [0, 0, 0 , sb , 0, 0, 0, 0, 0 , 0 , nx, nw] + T.Static {} -> getStaticStrutValues p rwh where st = fi y + fi h sb = rwh - fi y nx = fi x diff --git a/src/Xmobar/X11/XRender.hsc b/src/Xmobar/X11/XRender.hsc index 5ad0391..4e4d448 100644 --- a/src/Xmobar/X11/XRender.hsc +++ b/src/Xmobar/X11/XRender.hsc @@ -91,22 +91,28 @@ drawBackground d p bgc alpha (Rectangle x y wid ht) = do withRenderFill d (XRenderColor 0 0 0 (257 * alpha)) (render pictOpSrc bgfill pic) - -- Handle transparency - internAtom d "_XROOTPMAP_ID" False >>= \xid -> - let xroot = defaultRootWindow d in - alloca $ \x1 -> - alloca $ \x2 -> - alloca $ \x3 -> - alloca $ \x4 -> - alloca $ \pprop -> do - xGetWindowProperty d xroot xid 0 1 False 20 x1 x2 x3 x4 pprop - prop <- peek pprop - when (prop /= nullPtr) $ do - rootbg <- peek (castPtr prop) :: IO Pixmap - xFree prop - withRenderPicture d rootbg $ \bgpic -> - withRenderFill d (XRenderColor 0 0 0 (0xFFFF - 257 * alpha)) - (render pictOpAdd bgpic pic) + -- Handle pseudo-transparency by compositing the root pixmap. + -- Skip entirely when alpha == 255 (fully opaque) since the blend + -- factor would be zero, making this a no-op. More importantly, + -- the root pixmap (_XROOTPMAP_ID) can be freed at any time by + -- wallpaper setters like feh (via XKillClient), causing a + -- BadDrawable crash in XRenderCreatePicture if we touch it. + when (alpha < 255) $ + internAtom d "_XROOTPMAP_ID" False >>= \xid -> + let xroot = defaultRootWindow d in + alloca $ \x1 -> + alloca $ \x2 -> + alloca $ \x3 -> + alloca $ \x4 -> + alloca $ \pprop -> do + xGetWindowProperty d xroot xid 0 1 False 20 x1 x2 x3 x4 pprop + prop <- peek pprop + when (prop /= nullPtr) $ do + rootbg <- peek (castPtr prop) :: IO Pixmap + xFree prop + withRenderPicture d rootbg $ \bgpic -> + withRenderFill d (XRenderColor 0 0 0 (0xFFFF - 257 * alpha)) + (render pictOpAdd bgpic pic) -- | Parses color into XRender color (allocation not necessary!) parseRenderColor :: Display -> String -> IO XRenderColor @@ -1,5 +1,5 @@ -# ghc 8.10.7 -resolver: lts-18.20 +# ghc-9.10.3 +resolver: lts-24.45 packages: - . @@ -11,7 +11,6 @@ flags: extra-deps: - netlink-1.1.1.0 - - libmpd-0.9.2.0 nix: packages: - alsaLib diff --git a/test/Xmobar/Plugins/Monitors/CpuSpec.hs b/test/Xmobar/Plugins/Monitors/CpuSpec.hs index 3d07dee..b415179 100644 --- a/test/Xmobar/Plugins/Monitors/CpuSpec.hs +++ b/test/Xmobar/Plugins/Monitors/CpuSpec.hs @@ -6,24 +6,38 @@ module Xmobar.Plugins.Monitors.CpuSpec import Test.Hspec import Xmobar.Plugins.Monitors.Common import Xmobar.Plugins.Monitors.Cpu + import Data.List +import Text.Regex.TDFA((=~)) main :: IO () main = hspec spec +withFc :: String -> String +withFc s = "((<fc=(green|red)>)?" ++ s ++ "(</fc>)?)" + +withFcDigits :: String -> String -> String +withFcDigits prefix suffix = prefix ++ digits ++ suffix + where digits = withFc "([0-9][0-9]?|-0)" -- in CI computers, -0 is a value + spec :: Spec spec = describe "CPU Spec" $ do + it "uses the correct regexps" $ + do "Cpu: -0%" `shouldSatisfy` (=~ withFcDigits "Cpu: " "%") + "Cpu: 12" `shouldSatisfy` (=~ withFcDigits "Cpu: " "") + "Cpu: <fc=red>12</fc>" `shouldSatisfy` (=~ withFcDigits "Cpu: " "") + "Cpu: <fc=green>12</fc> -0" `shouldSatisfy` (=~ withFcDigits "Cpu: (" ")+") it "works with total template" $ do let args = ["-L","3","-H","50","--normal","green","--high","red", "-t", "Cpu: <total>%"] cpuArgs <- getArguments args cpuValue <- runCpu cpuArgs - cpuValue `shouldSatisfy` (\item -> "Cpu:" `isPrefixOf` item) + cpuValue `shouldSatisfy` (=~ withFcDigits "Cpu: " "%") it "works with bar template" $ do let args = ["-L","3","-H","50","--normal","green","--high","red", "-t", "Cpu: <total>% <bar>"] cpuArgs <- getArguments args cpuValue <- runCpu cpuArgs - cpuValue `shouldSatisfy` (all (`elem` ":#") . last . words) + cpuValue `shouldSatisfy` ((=~ (withFc "#+" ++ "?:*")) . last . words) it "works with no icon pattern template" $ do let args = ["-L","3","-H","50","--normal","green","--high","red", "-t", "Cpu: <total>% <bar>", "--", "--load-icon-pattern", "<icon=bright_%%.xpm/>"] cpuArgs <- getArguments args @@ -38,4 +52,5 @@ spec = do let args = ["-L","3","-H","50","--normal","green","--high","red", "-t", "Cpu: <user> <nice> <iowait>"] cpuArgs <- getArguments args cpuValue <- runCpu cpuArgs - cpuValue `shouldSatisfy` (\item -> "Cpu:" `isPrefixOf` cpuValue) + cpuValue `shouldSatisfy` (=~ withFcDigits "Cpu:( " ")+") + diff --git a/xmobar.cabal b/xmobar.cabal index 8b331f5..e7ed69c 100644 --- a/xmobar.cabal +++ b/xmobar.cabal @@ -1,423 +1,471 @@ +cabal-version: >=1.10 name: xmobar -version: 0.45 -homepage: https://codeberg.org/xmobar/xmobar -synopsis: A Minimalistic Text Based Status Bar -description: Xmobar is a minimalistic text based status bar. - . - Inspired by the Ion3 status bar, it supports similar - features, like dynamic color management, output templates, - and extensibility through plugins. -category: System +version: 0.51.1 license: BSD3 license-file: license -author: Andrea Rossato and Jose A. Ortega Ruiz maintainer: Jose A. Ortega Ruiz <jao@gnu.org> +author: Andrea Rossato and Jose A. Ortega Ruiz +homepage: https://codeberg.org/xmobar/xmobar bug-reports: https://codeberg.org/xmobar/xmobar/issues -cabal-version: >= 1.10 -build-type: Simple +synopsis: A Minimalistic Text Based Status Bar +description: + Xmobar is a minimalistic text based status bar. + . + Inspired by the Ion3 status bar, it supports similar + features, like dynamic color management, output templates, + and extensibility through plugins. -extra-source-files: readme.org, changelog.md, - doc/quick-start.org, - doc/plugins.org, - doc/compiling.org, - doc/using-haskell.org, - etc/padding-icon.sh, - etc/xmobar.config, - etc/xmobar.hs, - etc/xmonadpropwrite.hs, - etc/xmobar.el +category: System +build-type: Simple +extra-source-files: + readme.org + changelog.md + doc/quick-start.org + doc/plugins.org + doc/compiling.org + doc/using-haskell.org + etc/padding-icon.sh + etc/xmobar.config + etc/xmobar.hs + etc/xmonadpropwrite.hs + etc/xmobar.el + etc/notify-once.sh source-repository head - type: git - location: git://codeberg.org/xmobar/xmobar.git - branch: master + type: git + location: https://codeberg.org/xmobar/xmobar.git + branch: master flag with_xrender - description: Use XRender for alpha background pseudo-transparency. - default: True + description: Use XRender for alpha background pseudo-transparency. flag with_inotify - description: inotify support (modern Linux only). Required for the Mail and MBox plugins. - default: False + description: + inotify support (modern Linux only). Required for the Mail and MBox plugins. + + default: False flag with_iwlib - description: Wireless info support via Wext ioctls (deprecated). Required for the Wireless plugin, needs iwlib installed. - default: False + description: + Wireless info support via Wext ioctls (deprecated). Required for the Wireless plugin, needs iwlib installed. + + default: False flag with_nl80211 - description: Wireless info support via nl80211. Required for the Wireless plugin on systems running Linux, the kernel. - default: False + description: + Wireless info support via nl80211. Required for the Wireless plugin on systems running Linux, the kernel. + + default: False flag with_mpd - description: MPD support. Needs libmpd installed. - default: False + description: MPD support. Needs libmpd installed. + default: False flag all_extensions - description: Includes all optional extensions. - default: False + description: Includes all optional extensions. + default: False flag with_alsa - description: Use alsa-mixer to get the volume from soundcards. - default: False + description: Use alsa-mixer to get the volume from soundcards. + default: False flag with_datezone - description: Enables localized date support. - default: False + description: Enables localized date support. + default: False flag with_mpris - description: MPRIS v1, v2 support. - default: False + description: MPRIS v1, v2 support. + default: False flag with_dbus - description: Publish a service on the session bus for controlling xmobar. - default: False + description: + Publish a service on the session bus for controlling xmobar. + + default: False flag with_xpm - description: Enable usage of xpm for icons. - default: False + description: Enable usage of xpm for icons. + default: False flag with_threaded - description: Use threaded runtime. Required for timer coalescing (less power usage). - default: False + description: + Use threaded runtime. Required for timer coalescing (less power usage). + + default: False flag with_rtsopts - description: Use -with-rtsopts=-V0 to reduce wakeups. - default: True + description: Use -with-rtsopts=-V0 to reduce wakeups. + +flag with_shared + description: + Use shared libraries. Required when dependencies are built as shared libraries. + + default: False flag with_weather - description: Enable weather plugin. - default: True + description: Enable weather plugin. flag with_uvmeter - description: UVMeter only useful to australians. - default: False + description: UVMeter only useful to australians. + default: False flag with_kraken - description: Enable Kraken plugin. - default: False + description: Enable Kraken plugin. + default: False library + exposed-modules: + Xmobar + Xmobar.Plugins.Accordion + Xmobar.Plugins.Monitors.Common.Types + Xmobar.Plugins.Monitors.Common.Run + Xmobar.Plugins.Monitors.Common + Xmobar.Plugins.Monitors.Cpu + + hs-source-dirs: src + other-modules: + Paths_xmobar + Xmobar.Config.Types + Xmobar.Config.Parse + Xmobar.Config.Template + Xmobar.Run.Types + Xmobar.Run.Timer + Xmobar.Run.Template + Xmobar.Run.Exec + Xmobar.Run.Runnable + Xmobar.Run.Actions + Xmobar.Run.Loop + Xmobar.Draw.Boxes + Xmobar.Draw.Cairo + Xmobar.Draw.Types + Xmobar.App.Config + Xmobar.App.Main + Xmobar.App.Opts + Xmobar.App.Compile + Xmobar.System.Utils + Xmobar.System.StatFS + Xmobar.System.Environment + Xmobar.System.Localize + Xmobar.System.Signal + Xmobar.System.Kbd + Xmobar.Text.Ansi + Xmobar.Text.Loop + Xmobar.Text.Pango + Xmobar.Text.Swaybar + Xmobar.Text.SwaybarClicks + Xmobar.Text.Output + Xmobar.X11.Bitmap + Xmobar.X11.CairoSurface + Xmobar.X11.ColorCache + Xmobar.X11.Draw + Xmobar.X11.Events + Xmobar.X11.Loop + Xmobar.X11.Text + Xmobar.X11.Types + Xmobar.X11.Window + Xmobar.Plugins.Command + Xmobar.Plugins.BufferedPipeReader + Xmobar.Plugins.CommandReader + Xmobar.Plugins.Date + Xmobar.Plugins.EWMH + Xmobar.Plugins.HandleReader + Xmobar.Plugins.QueueReader + Xmobar.Plugins.PacmanUpdates + Xmobar.Plugins.PipeReader + Xmobar.Plugins.MarqueePipeReader + Xmobar.Plugins.StdinReader + Xmobar.Plugins.XMonadLog + Xmobar.Plugins.Kbd + Xmobar.Plugins.Locks + Xmobar.Plugins.NotmuchMail + Xmobar.Plugins.Monitors + Xmobar.Plugins.Monitors.Batt + Xmobar.Plugins.Monitors.Batt.Common + Xmobar.Plugins.Monitors.Common.Output + Xmobar.Plugins.Monitors.Common.Parsers + Xmobar.Plugins.Monitors.Common.Files + Xmobar.Plugins.Monitors.CoreTemp + Xmobar.Plugins.Monitors.K10Temp + Xmobar.Plugins.Monitors.Cpu.Common + Xmobar.Plugins.Monitors.CpuFreq + Xmobar.Plugins.Monitors.Disk + Xmobar.Plugins.Monitors.Disk.Common + Xmobar.Plugins.Monitors.Load + Xmobar.Plugins.Monitors.Load.Common + Xmobar.Plugins.Monitors.Mem + Xmobar.Plugins.Monitors.MultiCoreTemp + Xmobar.Plugins.Monitors.MultiCpu + Xmobar.Plugins.Monitors.Net + Xmobar.Plugins.Monitors.Net.Common + Xmobar.Plugins.Monitors.Swap + Xmobar.Plugins.Monitors.Thermal + Xmobar.Plugins.Monitors.ThermalZone + Xmobar.Plugins.Monitors.Top + Xmobar.Plugins.Monitors.Top.Common + Xmobar.Plugins.Monitors.Uptime + Xmobar.Plugins.Monitors.Bright + Xmobar.Plugins.Monitors.CatInt + default-language: Haskell2010 - hs-source-dirs: src - - exposed-modules: Xmobar, - Xmobar.Plugins.Monitors.Common.Types, - Xmobar.Plugins.Monitors.Common.Run, - Xmobar.Plugins.Monitors.Common, - Xmobar.Plugins.Monitors.Cpu - - other-modules: Paths_xmobar, - Xmobar.Config.Types, - Xmobar.Config.Parse, - Xmobar.Run.Types, - Xmobar.Run.Timer, - Xmobar.Run.Template, - Xmobar.Run.Exec, - Xmobar.Run.Runnable - Xmobar.Run.Actions, - Xmobar.Run.Parsers, - Xmobar.Run.Loop, - Xmobar.Draw.Boxes, - Xmobar.Draw.Cairo, - Xmobar.Draw.Types, - Xmobar.App.Config, - Xmobar.App.Main, - Xmobar.App.Opts, - Xmobar.App.Compile, - Xmobar.System.Utils, - Xmobar.System.StatFS, - Xmobar.System.Environment, - Xmobar.System.Localize, - Xmobar.System.Signal, - Xmobar.System.Kbd, - Xmobar.Text.Ansi, - Xmobar.Text.Loop, - Xmobar.Text.Pango, - Xmobar.Text.Swaybar, - Xmobar.Text.SwaybarClicks, - Xmobar.Text.Output, - Xmobar.X11.Bitmap, - Xmobar.X11.CairoSurface, - Xmobar.X11.ColorCache, - Xmobar.X11.Draw, - Xmobar.X11.Events, - Xmobar.X11.Loop, - Xmobar.X11.Text, - Xmobar.X11.Types, - Xmobar.X11.Window, - Xmobar.Plugins.Command, - Xmobar.Plugins.BufferedPipeReader, - Xmobar.Plugins.CommandReader, - Xmobar.Plugins.Date, - Xmobar.Plugins.EWMH, - Xmobar.Plugins.HandleReader, - Xmobar.Plugins.QueueReader, - Xmobar.Plugins.PipeReader, - Xmobar.Plugins.MarqueePipeReader, - Xmobar.Plugins.StdinReader, - Xmobar.Plugins.XMonadLog, - Xmobar.Plugins.Kbd, - Xmobar.Plugins.Locks, - Xmobar.Plugins.NotmuchMail, - Xmobar.Plugins.Monitors, - Xmobar.Plugins.Monitors.Batt, - Xmobar.Plugins.Monitors.Batt.Common, - Xmobar.Plugins.Monitors.Common.Output, - Xmobar.Plugins.Monitors.Common.Parsers, - Xmobar.Plugins.Monitors.Common.Files, - Xmobar.Plugins.Monitors.CoreTemp, - Xmobar.Plugins.Monitors.K10Temp, - Xmobar.Plugins.Monitors.Cpu.Common, - Xmobar.Plugins.Monitors.CpuFreq, - Xmobar.Plugins.Monitors.Disk, - Xmobar.Plugins.Monitors.Disk.Common, - Xmobar.Plugins.Monitors.Load, - Xmobar.Plugins.Monitors.Load.Common, - Xmobar.Plugins.Monitors.Mem, - Xmobar.Plugins.Monitors.MultiCoreTemp, - Xmobar.Plugins.Monitors.MultiCpu, - Xmobar.Plugins.Monitors.Net, - Xmobar.Plugins.Monitors.Net.Common, - Xmobar.Plugins.Monitors.Swap, - Xmobar.Plugins.Monitors.Thermal, - Xmobar.Plugins.Monitors.ThermalZone, - Xmobar.Plugins.Monitors.Top, - Xmobar.Plugins.Monitors.Top.Common, - Xmobar.Plugins.Monitors.Uptime, - Xmobar.Plugins.Monitors.Bright, - Xmobar.Plugins.Monitors.CatInt - - extra-libraries: Xrandr Xrender - - ghc-options: -funbox-strict-fields -Wall -fno-warn-unused-do-bind + extra-libraries: + Xrandr + Xrender + ghc-options: -funbox-strict-fields -Wall -fno-warn-unused-do-bind build-depends: - aeson >= 1.4.7.1, - async, - base >= 4.11.0 && < 4.17, - bytestring >= 0.10.8.2, - cairo >= 0.13, - colour >= 2.3.6, - containers, - directory, - extensible-exceptions == 0.1.*, - filepath, - mtl >= 2.1 && < 2.3, - old-locale, - pango >= 0.13, - parsec == 3.1.*, - parsec-numbers >= 0.1.0, - process, - regex-compat, - stm >= 2.3 && < 2.6, - time, - transformers, - unix, - utf8-string >= 0.3 && < 1.1, - X11 >= 1.6.1 - - if impl(ghc < 8.0.2) - -- Disable building with GHC before 8.0.2. - -- Due to a cabal bug, do not use buildable: False, - -- but instead give it an impossible constraint. - -- See: https://github.com/haskell-infra/hackage-trustees/issues/165 - build-depends: unsupported-ghc-version > 1 && < 1 + aeson >=1.4.7.1 && <2.4, + async <2.3, + base >=4.18.0 && <4.22, + bytestring >=0.10.8.2 && <0.13, + cairo >=0.13 && <0.14, + colour >=2.3.6 && <2.4, + containers <0.9, + directory <1.4, + extra <1.9, + filepath <1.6, + mtl >=2.1 && <2.4, + old-locale <1.1, + pango >=0.13 && <0.14, + parsec >=3.1 && <3.2, + parsec-numbers >=0.1.0 && <0.2, + process <1.7, + regex-compat <0.96, + stm >=2.3 && <2.6, + time <1.17, + transformers <0.7, + unix <2.9, + utf8-string >=0.3 && <1.1, + vector <0.14, + X11 >=1.6.1 && <1.11 + + if impl(ghc <9.6.0) + build-depends: unsupported-ghc-version >=1 && <1 if flag(with_threaded) - -- -threaded is a workaround for 100% CPU busy loop - -- (http://hackage.haskell.org/trac/ghc/ticket/4934). - -- See also comments in https://codeberg.org/xmobar/xmobar/pulls/36 - cpp-options: -DTHREADED_RUNTIME + cpp-options: -DTHREADED_RUNTIME if flag(with_rtsopts) - cpp-options: -DRTSOPTS + cpp-options: -DRTSOPTS + + if flag(with_shared) + cpp-options: -DSHARED_LIBRARIES if flag(with_xrender) - build-depends: X11-xft >= 0.2 - other-modules: Xmobar.X11.XRender - cpp-options: -DXRENDER + cpp-options: -DXRENDER + other-modules: Xmobar.X11.XRender + build-depends: X11-xft >=0.2 && <0.4 + + if (flag(with_inotify) || flag(all_extensions)) + cpp-options: -DINOTIFY + other-modules: + Xmobar.Plugins.Mail + Xmobar.Plugins.MBox - if flag(with_inotify) || flag(all_extensions) - build-depends: hinotify >= 0.3 && < 0.5 - other-modules: Xmobar.Plugins.Mail, Xmobar.Plugins.MBox - cpp-options: -DINOTIFY + build-depends: hinotify >=0.3 && <0.5 - if flag(with_iwlib) || flag(with_nl80211) || flag(all_extensions) - other-modules: Xmobar.Plugins.Monitors.Wireless + if ((flag(with_iwlib) || flag(with_nl80211)) || flag(all_extensions)) + other-modules: Xmobar.Plugins.Monitors.Wireless if flag(with_iwlib) - extra-libraries: iw - build-depends: iwlib >= 0.1.0 && < 0.2 - cpp-options: -DIWLIB - - if !flag(with_iwlib) && (flag(with_nl80211) || flag(all_extensions)) - build-depends: netlink >= 1.1.1.0, - cereal >= 0.5.8.1 - cpp-options: -DUSE_NL80211 - - if flag(with_mpd) || flag(all_extensions) - build-depends: libmpd >= 0.9.2.0 - other-modules: Xmobar.Plugins.Monitors.MPD - cpp-options: -DLIBMPD - - if flag(with_alsa) || flag(all_extensions) - build-depends: alsa-mixer >= 0.3 && < 0.4 - build-depends: alsa-core == 0.5.*, - process >= 1.4.3.0 - other-modules: Xmobar.Plugins.Monitors.Volume, - Xmobar.Plugins.Monitors.Alsa - cpp-options: -DALSA - - if flag(with_datezone) || flag(all_extensions) - build-depends: timezone-olson >= 0.2 && < 0.3, timezone-series == 0.1.* - other-modules: Xmobar.Plugins.DateZone - cpp-options: -DDATEZONE - - if flag(with_mpris) || flag(all_extensions) - build-depends: dbus >= 1 - other-modules: Xmobar.Plugins.Monitors.Mpris - cpp-options: -DMPRIS - - if flag(with_dbus) || flag(all_extensions) - build-depends: dbus >= 1 - other-modules: Xmobar.System.DBus - cpp-options: -DDBUS - - if flag(with_xpm) || flag(all_extensions) - extra-libraries: Xpm - other-modules: Xmobar.X11.XPMFile - cpp-options: -DXPM - - if flag(with_weather) || flag(all_extensions) - other-modules: Xmobar.Plugins.Monitors.Weather - cpp-options: -DWEATHER - build-depends: http-conduit, http-types, http-client-tls + cpp-options: -DIWLIB + extra-libraries: iw + build-depends: iwlib >=0.1.0 && <0.2 + + if (!flag(with_iwlib) && (flag(with_nl80211) || flag(all_extensions))) + cpp-options: -DUSE_NL80211 + build-depends: + netlink >=1.1.1.0, + cereal >=0.5.8.1 + + if (flag(with_mpd) || flag(all_extensions)) + cpp-options: -DLIBMPD + other-modules: Xmobar.Plugins.Monitors.MPD + build-depends: libmpd >=0.10.0.1 && <0.11 + + if (flag(with_alsa) || flag(all_extensions)) + cpp-options: -DALSA + other-modules: + Xmobar.Plugins.Monitors.Volume + Xmobar.Plugins.Monitors.Alsa + + build-depends: + alsa-mixer >=0.3 && <0.4, + alsa-core >=0.5 && <0.6, + process >=1.4.3.0 && <1.7 + + if (flag(with_datezone) || flag(all_extensions)) + cpp-options: -DDATEZONE + other-modules: Xmobar.Plugins.DateZone + build-depends: + timezone-olson >=0.2 && <0.3, + timezone-series >=0.1 && <0.2 + + if (flag(with_mpris) || flag(all_extensions)) + cpp-options: -DMPRIS + other-modules: Xmobar.Plugins.Monitors.Mpris + build-depends: dbus >=1 && <1.5 + + if (flag(with_dbus) || flag(all_extensions)) + cpp-options: -DDBUS + other-modules: Xmobar.System.DBus + build-depends: dbus >=1 && <1.5 + + if (flag(with_xpm) || flag(all_extensions)) + cpp-options: -DXPM + other-modules: Xmobar.X11.XPMFile + extra-libraries: Xpm + + if (flag(with_weather) || flag(all_extensions)) + cpp-options: -DWEATHER + other-modules: Xmobar.Plugins.Monitors.Weather + build-depends: + http-conduit <2.4, + http-types <0.13, + http-client-tls <0.5 if flag(with_uvmeter) - other-modules: Xmobar.Plugins.Monitors.UVMeter - build-depends: http-conduit, http-types - cpp-options: -DUVMETER + cpp-options: -DUVMETER + other-modules: Xmobar.Plugins.Monitors.UVMeter + build-depends: + http-conduit, + http-types if flag(with_kraken) - other-modules: Xmobar.Plugins.Kraken - build-depends: aeson == 1.5.6.* - , text == 1.2.4.* - , unordered-containers == 0.2.14.* - , vector == 0.12.3.* - , wuss == 1.1.* - , websockets == 0.12.* - cpp-options: -DKRAKEN + cpp-options: -DKRAKEN + other-modules: Xmobar.Plugins.Kraken + build-depends: + aeson >=1.5.6 && <1.5.7, + text >=1.2.4 && <1.2.5, + unordered-containers >=0.2.14 && <0.2.15, + vector >=0.12.3 && <0.12.4, + wuss >=1.1 && <1.2, + websockets >=0.12 && <0.13 if os(freebsd) - -- enables freebsd specific code - extra-libraries: procstat - , kvm - , geom - build-depends: bsd-sysctl - other-modules: Xmobar.Plugins.Monitors.Batt.FreeBSD, - Xmobar.Plugins.Monitors.Cpu.FreeBSD, - Xmobar.Plugins.Monitors.Disk.FreeBSD, - Xmobar.Plugins.Monitors.Load.FreeBSD, - Xmobar.Plugins.Monitors.Mem.FreeBSD, - Xmobar.Plugins.Monitors.Net.FreeBSD, - Xmobar.Plugins.Monitors.Swap.FreeBSD, - Xmobar.Plugins.Monitors.Top.FreeBSD, - Xmobar.Plugins.Monitors.Uptime.FreeBSD + other-modules: + Xmobar.Plugins.Monitors.Batt.FreeBSD + Xmobar.Plugins.Monitors.Cpu.FreeBSD + Xmobar.Plugins.Monitors.Disk.FreeBSD + Xmobar.Plugins.Monitors.Load.FreeBSD + Xmobar.Plugins.Monitors.Mem.FreeBSD + Xmobar.Plugins.Monitors.Net.FreeBSD + Xmobar.Plugins.Monitors.Swap.FreeBSD + Xmobar.Plugins.Monitors.Top.FreeBSD + Xmobar.Plugins.Monitors.Uptime.FreeBSD + + extra-libraries: + procstat + kvm + geom + + build-depends: bsd-sysctl <1.1 + else - other-modules: Xmobar.Plugins.Monitors.Batt.Linux, - Xmobar.Plugins.Monitors.Cpu.Linux, - Xmobar.Plugins.Monitors.Disk.Linux, - Xmobar.Plugins.Monitors.Load.Linux, - Xmobar.Plugins.Monitors.Mem.Linux, - Xmobar.Plugins.Monitors.Net.Linux, - Xmobar.Plugins.Monitors.Swap.Linux, - Xmobar.Plugins.Monitors.Top.Linux, - Xmobar.Plugins.Monitors.Uptime.Linux + other-modules: + Xmobar.Plugins.Monitors.Batt.Linux + Xmobar.Plugins.Monitors.Cpu.Linux + Xmobar.Plugins.Monitors.Disk.Linux + Xmobar.Plugins.Monitors.Load.Linux + Xmobar.Plugins.Monitors.Mem.Linux + Xmobar.Plugins.Monitors.Net.Linux + Xmobar.Plugins.Monitors.Swap.Linux + Xmobar.Plugins.Monitors.Top.Linux + Xmobar.Plugins.Monitors.Uptime.Linux executable xmobar - default-language: Haskell2010 - hs-source-dirs: app - main-is: Main.hs - build-depends: X11, - async, - base, - containers, - directory, - filepath, - parsec, - unix, - xmobar - - ghc-options: -funbox-strict-fields -Wall -fno-warn-unused-do-bind + main-is: Main.hs + hs-source-dirs: app + default-language: Haskell2010 + ghc-options: -funbox-strict-fields -Wall -fno-warn-unused-do-bind + build-depends: + X11 <1.11, + async <2.3, + base, + containers <0.9, + directory <1.4, + filepath <1.6, + parsec, + unix <2.9, + xmobar if flag(with_rtsopts) - ghc-options: -with-rtsopts=-V0 + ghc-options: -with-rtsopts=-V0 if flag(with_threaded) - ghc-options: -threaded - cpp-options: -DTHREADED_RUNTIME + cpp-options: -DTHREADED_RUNTIME + ghc-options: -threaded test-suite XmobarTest - default-language: Haskell2010 - type: exitcode-stdio-1.0 - hs-source-dirs: src, test - main-is: Spec.hs - build-depends: X11, - async, - base, - bytestring, - containers, - directory, - filepath, - hspec == 2.*, - mtl, - old-locale, - parsec, - parsec-numbers, - process, - regex-compat, - stm, - temporary, - time, - transformers, - unix, - xmobar - - other-modules: Xmobar.Plugins.Monitors.CommonSpec - Xmobar.Plugins.Monitors.Common - Xmobar.Plugins.Monitors.Common.Parsers - Xmobar.Plugins.Monitors.Common.Types - Xmobar.Plugins.Monitors.Common.Output - Xmobar.Plugins.Monitors.Common.Files - Xmobar.Plugins.Monitors.Cpu - Xmobar.Plugins.Monitors.Cpu.Common - Xmobar.Plugins.Monitors.CpuSpec - Xmobar.Plugins.Monitors.Common.Run - Xmobar.Run.Exec - Xmobar.Run.Timer - Xmobar.System.Signal - - if flag(with_alsa) || flag(all_extensions) - build-depends: alsa-mixer, - alsa-core, - process >= 1.4.3.0 - other-modules: Xmobar.Plugins.Monitors.Volume - Xmobar.Plugins.Monitors.Alsa - Xmobar.Plugins.Monitors.AlsaSpec - - cpp-options: -DALSA - - if os(freebsd) - -- enables freebsd specific code - build-depends: bsd-sysctl - other-modules: Xmobar.Plugins.Monitors.Cpu.FreeBSD - else - other-modules: Xmobar.Plugins.Monitors.Cpu.Linux + type: exitcode-stdio-1.0 + main-is: Spec.hs + hs-source-dirs: src test + other-modules: + Xmobar.Plugins.Monitors.CommonSpec + Xmobar.Plugins.Monitors.Common + Xmobar.Plugins.Monitors.Common.Parsers + Xmobar.Plugins.Monitors.Common.Types + Xmobar.Plugins.Monitors.Common.Output + Xmobar.Plugins.Monitors.Common.Files + Xmobar.Plugins.Monitors.Cpu + Xmobar.Plugins.Monitors.Cpu.Common + Xmobar.Plugins.Monitors.CpuSpec + Xmobar.Plugins.Monitors.Common.Run + Xmobar.Run.Exec + Xmobar.Run.Timer + Xmobar.System.Signal + + default-language: Haskell2010 + build-depends: + X11 <1.11, + async <2.3, + base, + bytestring <0.13, + containers <0.9, + directory <1.4, + filepath <1.6, + hspec >=2 && <3, + mtl, + old-locale <1.1, + parsec, + parsec-numbers <0.2, + process <1.7, + regex-compat <0.96, + regex-tdfa <1.4, + stm, + temporary <1.4, + time <1.17, + transformers <0.7, + unix <2.9, + xmobar + + if (flag(with_alsa) || flag(all_extensions)) + cpp-options: -DALSA + other-modules: + Xmobar.Plugins.Monitors.Volume + Xmobar.Plugins.Monitors.Alsa + Xmobar.Plugins.Monitors.AlsaSpec + + build-depends: + alsa-mixer, + alsa-core, + process >=1.4.3.0 && <1.7 + + if os(freebsd) + other-modules: Xmobar.Plugins.Monitors.Cpu.FreeBSD + build-depends: bsd-sysctl + + else + other-modules: Xmobar.Plugins.Monitors.Cpu.Linux benchmark xmobarbench - type: exitcode-stdio-1.0 - main-is: main.hs - hs-source-dirs: bench - ghc-options: -funbox-strict-fields -Wall -fno-warn-unused-do-bind -O2 - build-depends: base, gauge, mtl, time, xmobar - default-language: Haskell2010 + type: exitcode-stdio-1.0 + main-is: main.hs + hs-source-dirs: bench + default-language: Haskell2010 + ghc-options: -funbox-strict-fields -Wall -fno-warn-unused-do-bind + build-depends: + base, + gauge, + mtl, + time <1.17, + xmobar |
