8000 Set polybar on multiple screens. · Issue #763 · polybar/polybar · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Set polybar on multiple screens. #763

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jparsert opened this issue Sep 18, 2017 · 30 comments
Closed

Set polybar on multiple screens. #763

jparsert opened this issue Sep 18, 2017 · 30 comments
Labels

Comments

@jparsert
Copy link

I have a setup with 3 monitors and i was wondering if it was possible to display polybar on all 3 monitors.

@patrick96
Copy link
Member

Yes that's possible. The bar has a monitor option where you can set which monitor you want the bar to display on. You can start three bars with different settings for the monitor

@apetresc
Copy link

I have a launch_polybar.sh script which has a section like this:

if type "xrandr"; then
  for m in $(xrandr --query | grep " connected" | cut -d" " -f1); do
    MONITOR=$m polybar --reload example &
  done
else
  polybar --reload example &
fi

And then in my polybar config, I have:

[bar/example]
monitor = ${env:MONITOR:}
[..]

Works on every machine I've ever used it on, whether it was a multi-monitor setup or not :)

@mad0ba
Copy link
mad0ba commented Nov 1, 2017

Wonderfull!!

@patrick96
Copy link
Member

I think we can close this

@lhanson
Copy link
lhanson commented May 29, 2018

To golf that down a little bit, you can dispense with the xrandr conditional and let polybar enumerate the monitors:

for m in $(polybar --list-monitors | cut -d":" -f1); do
    MONITOR=$m polybar --reload example &
done

@TobiasKB
Copy link
TobiasKB commented Dec 7, 2018

I found this at ArchMergeD, credits to Erik for the basics, but enhanced it a bit, so that you can launch multible bars for different monitors. I just thought it could be useful for others. :)

killall -q polybar

# Wait until the processes have been shut down
while pgrep -u $UID -x polybar > /dev/null; do sleep 1; done

desktop=$(echo $DESKTOP_SESSION)

case $desktop in
    i3)
    if type "xrandr" > /dev/null; then
      for m in $(xrandr --query | grep " connected" | cut -d" " -f1); do
	if [ $m == 'DP-4' ] 
	then		
		MONITOR=$m polybar --reload mainbar-i3 -c ~/.config/polybar/config &	
	elif [ $m == 'HDMI-0' ]
	then
		MONITOR=$m polybar --reload sidebar-1-i3 -c ~/.config/polybar/config &
	else
		MONITOR=$m polybar --reload sidebar-2-i3 -c ~/.config/polybar/config &
	fi     
      done
    else
    polybar --reload mainbar-i3 -c ~/.config/polybar/config &
    fi
    ;;
    openbox)
    if type "xrandr" > /dev/null; then
      for m in $(xrandr --query | grep " connected" | cut -d" " -f1); do
        MONITOR=$m polybar --reload mainbar-openbox -c ~/.config/polybar/config &
      done
    else
    polybar --reload mainbar-openbox -c ~/.config/polybar/config &
    fi
#    if type "xrandr" > /dev/null; then
#      for m in $(xrandr --query | grep " connected" | cut -d" " -f1); do
#        MONITOR=$m polybar --reload mainbar-openbox-extra -c ~/.config/polybar/config &
#      done
#    else
#    polybar --reload mainbar-openbox-extra -c ~/.config/polybar/config &
#    fi

    ;;
    bspwm)
    if type "xrandr" > /dev/null; then
      for m in $(xrandr --query | grep " connected" | cut -d" " -f1); do
        MONITOR=$m polybar --reload mainbar-bspwm -c ~/.config/polybar/config &
      done
    else
    polybar --reload mainbar-bspwm -c ~/.config/polybar/config &
    fi
    ;;
esac

Then, of course, in my polybar config file, i add multiple bars just like that:

[bar/mainbar-i3] ... [bar/sidebar-1-i3] ....

@wouterh-dev
Copy link

Unfortunately it seems the tray can only be displayed on one polybar instance (and monitor) at a time. And when not specifying which monitor gets the tray it seems kind of random when using the above script. So, with thanks to @TobiasKB I'm now using the following script (executed by autorandr):

#!/bin/bash
(
  flock 200

  killall -q polybar

  while pgrep -u $UID -x polybar > /dev/null; do sleep 0.5; done

  outputs=$(xrandr --query | grep " connected" | cut -d" " -f1)
  tray_output=eDP1

  for m in $outputs; do
    if [[ $m == "HDMI1" ]]; then
        tray_output=$m
    fi
  done

  for m in $outputs; do
    export MONITOR=$m
    export TRAY_POSITION=none
    if [[ $m == $tray_output ]]; then
      TRAY_POSITION=right
    fi

    polybar --reload main </dev/null >/var/tmp/polybar-$m.log 2>&1 200>&- &
    disown
  done
) 200>/var/tmp/polybar-launch.lock

And relevant polybar/config lines:

[bar/main]
monitor = ${env:MONITOR:HDMI-1}
tray-position = ${env:TRAY_POSITION:right}

[module/i3]
pin-workspaces = true

My changes:

  • Consistently show tray on desired monitor (HDMI1 if present, otherwise eDP1). Could also use two different bar configs, but since I want my bars to otherwise be identical that is a hassle.

  • Assume xrandr & i3 are used and present. No need for extra complexity for use-cases that are irrelevant for me

  • Use flock to avoid race conditions

  • Disown polybar instance & close stdin to keep autorandr from getting confused

  • Preserve polybar output in logfile

I still think it would be great if Polybar had built-in multi-monitor support.

@husnaram
Copy link
husnaram commented Apr 6, 2019

And, what if i have two bar?

@michaelranaldo
Copy link

And, what if i have two bar?

Using @lhanson 's response:

To golf that down a little bit, you can dispense with the xrandr conditional and let polybar enumerate the monitors:

for m in $(polybar --list-monitors | cut -d":" -f1); do
    MONITOR=$m polybar --reload example &
done

Simply call MONITOR=$m polybar --reload example & for every bar you have i.e.:

for m in $(polybar --list-monitors | cut -d":" -f1); do
    MONITOR=$m polybar --reload bar1 &
    MONITOR=$m polybar --reload bar2 &
    ... and so on
done

edbizarro added a commit to edbizarro/dotfiles that referenced this issue Jul 26, 2019
@eagleusb
Copy link
eagleusb commented Aug 9, 2019

Hello,

inspired by what I read, a oneliner version with xargs and regexp:

if type "xrandr">/dev/null; then
  echo "Lanching polybar for each screen"
  xrandr --listactivemonitors | grep -oP '(HDMI\-\d+$|eDP\-\d+$)' | xargs -P1 -I{} bash -c "MONITOR={} polybar -q -r p00 &"
fi

@maxtimbo
Copy link
maxtimbo commented Dec 18, 2019

Thought I would contribute my python script that solves the issue for me:

#!/bin/python3

import gi
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk
import os

allmonitors = []
gdkdsp = Gdk.Display.get_default()

os.system('killall -q polybar')
os.system('while pgrep -u $UID -x polybar > /dev/null; do sleep 1; done')

for i in range(gdkdsp.get_n_monitors()):
    monitor = gdkdsp.get_monitor(i)
    primary = monitor.is_primary()
    allmonitors.append([monitor.get_model()] + [primary])

monitorint = len(allmonitors)

for i, x in zip(range(monitorint), allmonitors):
    if x[1]:
        os.system(f'MONITOR={x[0]} polybar -r main &')
    else:
        os.system(f'MONITOR={x[0]} polybar -r sidebar &') 

@wallace11
Copy link
wallace11 commented Jan 27, 2020

Instead if going around killing polybar, here's a more elegant solution that's also POSIX compliant:

#!/bin/sh

if [ -z "$(pgrep -x polybar)" ]; then
    BAR="bspwm"
    for m in $(polybar --list-monitors | cut -d":" -f1); do
        MONITOR=$m polybar --reload $BAR &
        sleep 1
    done
else
    polybar-msg cmd restart
fi

Since it uses polybar-msg, enable-ip option should be set to true, otherwise it won't work.

@Kabouik
Copy link
Kabouik commented Mar 11, 2020

I am not a developer so there might be issues with the below code, but here's my take on it based on others' work above:

~/.config/polybar/launch_multimonitor.sh

    #/!bin/bash
    # Detect if secondary monitor is connected, if so, add a specific bar and move tray to it
    # Else, keep tray on main monitor
    # see https://github.com/polybar/polybar/issues/763

    killall -q polybar

    # Wait until the processes have been shut down
    while pgrep -u $UID -x polybar > /dev/null; do sleep 1; done

    outputs=$(xrandr --query | grep " connected" | cut -d" " -f1)
    set -- $outputs
    tray_output=$1

    	for m in $outputs; do
    		if [ $m == $1 ] 
    		then		
    			MONITOR1=$m polybar --reload mainbar-i3 -c ~/.config/polybar/config &	
    		elif [ $m == $2 ]
    		then
    		    tray_output=$m
    			MONITOR2=$m polybar --reload sidebar-i3-right -c ~/.config/polybar/config &
    		else
    			MONITOR1=$m polybar --reload mainbar-i3 -c ~/.config/polybar/config &
    	  fi
    	done

      for m in $outputs; do
      	export MONITOR1=$1
      	export MONITOR2=$2
       	export TRAY_POSITION=none
        if [[ $m == $tray_output ]]; then
           TRAY_POSITION=right
      fi
    done

~/.config/polybar/bars/basic

    [bar/mainbar-i3]
    monitor = ${env:MONITOR1}
    # Tray
    tray-position = ${env:TRAY_POSITION:right}
…

    [bar/sidebar-i3-right]
    monitor = ${env:MONITOR2}
    # Tray
    tray-position = ${env:TRAY_POSITION:right}
…

~/.config/i3/config

…
exec_always --no-startup-id ~/.config/polybar/launch_multimonitor.sh &
…

It aims at:

  • making the script agnostic to monitor names (which should allow using it on different computers or with distinct home/office monitors),
  • displaying specific bars on each monitor (mainbar on primary screen and sidebar on secondary screen, if any),
  • moving the tray to the secondary bar (if present), else keep it on the main bar.

So far it works for me but I didn't have the opportunity to try it thoroughly on multiple computers yet, and there might be warnings that can be solved, or unnecessary variables that could be removed (if so, please don't hesitate to post a sanitized version).

@tsujp
Copy link
tsujp commented Mar 23, 2020

This is what I am using currently

#!/usr/bin/env bash

# define bars per monitors
declare -A ARRANGEMENTS=(["DP-2"]="mm-top,mm-bottom" ["HDMI-0"]="rm-top")

# each key
for MONITOR in "${!ARRANGEMENTS[@]}"; do
  # split at `,` into array
  while IFS=',' read -ra BARLIST; do
    # for each bar (seperated by `,`) at current key
    for BAR in "${BARLIST[@]}"; do
      MONITOR="$MONITOR" polybar --reload "$BAR" &
    done
  done <<< "${ARRANGEMENTS[$MONITOR]}"
done

@xtccc
Copy link
xtccc commented Apr 7, 2020

#763 (comment)
Thanks to your python3 code ,it runs good ,and I like the to python3 solve the problem.

@bbaserdem
Copy link
bbaserdem commented Apr 18, 2020

@wouterhund ; thank you for the elegent script. I have never seen flock being used before. I'm trying to develop my version of your script; for which i launch multiple polybars (one top, one bottom and a top one for all the non-primary monitors)

I understand the purpose of </dev/null (closes stdin to polybar), 2>&1 >/tmp/log (prints output to a log file) and & disown (releases the polybar from being a child process of the script). What does the 200>&- do? Doesn't flock close the descriptor itself after the block is run; or do I have to close it myself? I assume if I am launching multiple bars; I should refrain from closing 200 until I launch all the bars; and individually close it myself?

EDIT: I figured it out. You use it to close the file descriptor that the lock opened; since the lock would remain due to child process still is going on. (did not realize that detached children would still retain the lock) In my case; i spawn many child processes; and i don't know which is the final one to be. How I implemented the race lock was following (if anyone else is interested);

# Create directories for locks to avoid race conditions
lock="${XDG_CACHE_HOME}/polybar/bars.at.${DISPLAY}.lock"
if [ ! -d "$(dirname "${lock}")" ] ; then
    mkdir -p "$(dirname "${lock}")"
fi
( flock 200
    # There is code here that loops over possible polybars without the 200>&-
    # Close the file lock placed
    flock -u 200
) 200>"${lock}"

@ianmajkut
Copy link

And, what if i have two bar?

Using @lhanson 's response:

To golf that down a little bit, you can dispense with the xrandr conditional and let polybar enumerate the monitors:

for m in $(polybar --list-monitors | cut -d":" -f1); do
    MONITOR=$m polybar --reload example &
done

Simply call MONITOR=$m polybar --reload example & for every bar you have i.e.:

for m in $(polybar --list-monitors | cut -d":" -f1); do
    MONITOR=$m polybar --reload bar1 &
    MONITOR=$m polybar --reload bar2 &
    ... and so on
done

I have a question about this. I have a lot o bars for each monitor i have. Example:

;Top bar for my laptop
[bar/example2]
width = 110%
height = 20
fixed-center = true
bottom=false
enable-ipc= true
...

;Top Bar for monitor HDMI1
[bar/example3]
monitor = ${env:MONITOR:HDMI1}
width = 110%
height = 20
fixed-center = true
bottom=false
enable-ipc= true
...

;Top Bar for monitor DP1
[bar/example5]
monitor = ${env:MONITOR:DP1}
width = 110%
height = 20
fixed-center = true
bottom=false
enable-ipc= true
...

I tried to do the following, but it didn´t work:

for m in $(polybar --list-monitors | cut -d":" -f1); do
    MONITOR:HDMI1=$m polybar --reload example5 example6 &
    MONITOR:DP1=$m polybar --reload example3 example4 &

done

The errors are : Monitor:DP1=eDP1 order not found, Monitor:HDMI1=DP1 order not found, Monitor:DP1=DP1 order not found, Monitor:HDMI1=eDP1 order not found.

I appreciate any help you can give me.

@maxtimbo
Copy link

@ianmajkut

This python script works well for me.

#/bin/python3
import gi
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk
import os

allmonitors = []
gdkdsp = Gdk.Display.get_default()

os.system('killall -q polybar')
os.system('while pgrep -u $UID -x polybar > /dev/null; do sleep 1; done')

for i in range(gdkdsp.get_n_monitors()):
    monitor = gdkdsp.get_monitor(i)
    primary = monitor.is_primary()
    allmonitors.append([monitor.get_model()] + [primary])

monitorint = len(allmonitors)

for i, x in zip(range(monitorint), allmonitors):
    print(x[0])
    if x[1]:
        os.system(f'MONITOR={x[0]} polybar -r main &')
    elif x[0] == 'DVI-I-1':
        os.system(f'MONITOR={x[0]} polybar -r portrait &')
    else:
        os.system(f'MONITOR={x[0]} polybar -r sidebar &')

You can elif x[0] == 'HDMI1': os.system(f'MONITOR={x[0]} polybar -r exampleX &') to load the matching polybar on the matching screen.

@ianmajkut
Copy link

@ianmajkut

This python script works well for me.

#/bin/python3
import gi
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk
import os

allmonitors = []
gdkdsp = Gdk.Display.get_default()

os.system('killall -q polybar')
os.system('while pgrep -u $UID -x polybar > /dev/null; do sleep 1; done')

for i in range(gdkdsp.get_n_monitors()):
    monitor = gdkdsp.get_monitor(i)
    primary = monitor.is_primary()
    allmonitors.append([monitor.get_model()] + [primary])

monitorint = len(allmonitors)

for i, x in zip(range(monitorint), allmonitors):
    print(x[0])
    if x[1]:
        os.system(f'MONITOR={x[0]} polybar -r main &')
    elif x[0] == 'DVI-I-1':
        os.system(f'MONITOR={x[0]} polybar -r portrait &')
    else:
        os.system(f'MONITOR={x[0]} polybar -r sidebar &')

You can elif x[0] == 'HDMI1': os.system(f'MONITOR={x[0]} polybar -r exampleX &') to load the matching polybar on the matching screen.

I don't understand what It should do... It shows me a cursor then I click and I get a syntactic error about 'Gdk' and `gi.require_version (" Gdk "," 3.0 ") '. Also I do not understand how I could put my eDP1(example and example2), DP1(example3 and example4) and HDMI1(example5 and example6) monitors with their respective bars that I just mentioned

@patrick96
Copy link
Member

@ianmajkut The name of the environment variable isn't MONITOR:HDMI1, it's MONITOR, HDMI1 is the fallback value.
Also, you can only start a single bar per polybar command

@bbaserdem
Copy link

I have my own script that I have been using without issues as well; but it's a bit involved. Here is my launcher script that does all the locks and multiple screen setup. My config file is here.

I call the script on login; and on acpi monitor signals; hooked into by autorandr.

@viasux
Copy link
viasux commented Mar 14, 2021

I am confused. I have duplicated my entry in my bars.ini and put this code in my config.ini and it still only stays on one monitor.

  for m in $(polybar --list-monitors | cut -d":" -f1); do
    MONITOR=$m polybar --reload example &
    done
else
  polybar --reload example &
fi```

@stasys-hub
Copy link
stasys-hub commented Apr 6, 2021

I got a bit of similar question. I have a two monitor setup (laptop + external screen) running i3 and i want my polybar to run on the second (attached) monitor, if it is connected, else on my laptop. So I did this in my config:

[bar/main]
monitor = DP-2
monitor-fallback = eDP-1
monitor-strict = true

This works fine on startup and if i connect the second monitor manually and refresh i3/polybar. Nevertheless, if I plug my second monitor out, the polybar won't spawn on the laptop display. So i tried to track down why. The curious thing is, that if i use:
xrandr -q | grep " connected" | cut -d ' ' -f1
the output is only eDP-1, but if I use:
polybar -m | cut -d ':' -f 1
I get as output:

eDP-1
DP-2

So now I am sitting here, wondering and asking myself how can i solve this and why polybar is still displaying a detached monitor after i plugged it put but not xrandr?

Any help is appreciated!

rileyshahar added a commit to rileyshahar/dotfiles that referenced this issue Jul 10, 2021
This uses XMonad.Hooks.ManageDocks to automatically create space in
xmonad.

The part of the script to deal with monitors is from
polybar/polybar#763 (comment).
@huyhoang8398
Copy link

My better solution for people who are using autorandr:

  • Setup basic autorandr as you like
  • Use postswitch feature to reload the polybar
  • For polybar setting:
[bar/main]

monitor = DP-2
monitor-fallback = eDP-1

@zeorin
Copy link
zeorin commented Aug 3, 2023

I'm using the following in home manager atm:

{ pkgs, ... }: {
  services.polybar.script = ''
    # Launch bar on each monitor, tray on primary
    polybar --list-monitors | while IFS=$'\n' read line; do
      monitor=$(echo $line | ${pkgs.coreutils}/bin/cut -d':' -f1)
      primary=$(echo $line | ${pkgs.coreutils}/bin/cut -d' ' -f3)
      tray_position=$([ -n "$primary" ] && echo "right" || echo "none")
      MONITOR=$monitor TRAY_POSITION=$tray_position polybar --reload top &
    done
  '';
}

@OriLiMu
Copy link
OriLiMu commented Nov 11, 2023

xrandr output

xrandr --query | grep " connected" | cut -d" " -f1                    9.1m  Nov-11 04:03
HDMI-0
DP-0

My test script to start

MONITOR=HDMI-0 polybar --reload example &
MONITOR=DP-0 polybar --reload example &

Error msg:

warn: No monitor specified, using "DP-0"                                         Nov-11 04:06
notice: Loading module 'xworkspaces' of type 'internal/xworkspaces'
warn: No monitor specified, using "DP-0"

But the result is all polybar in one monitor

image

@zeorin
Copy link
zeorin commented Jan 13, 2024

I've just updated to the new tray module, I've adjusted my setup as follows:

Startup script:

# Launch bar on each monitor, tray on primary
polybar --list-monitors | while IFS=$'\n' read line; do
  monitor=$(echo $line | cut -d':' -f1)
  primary=$(echo $line | cut -d' ' -f3)
  MONITOR=$monitor polybar --reload "top${primary:+"-primary"}" &
done

Polybar config (irrelevant bits elided):

[bar/top]
monitor=${env:MONITOR:}
modules-right=moduleA moduleB moduleC

[bar/top-primary]
inherit=bar/top
modules-right=moduleA moduleB moduleC tray

[module/tray]
type=internal/tray

Essentially, I'm starting a different bar for the primary monitor, on which I want the tray to be placed. That bar inherits from the original bar's config, and then I overwrite modules-right to include the tray module. Unfortunately I do need to duplicate the original modules: #954.

@moshpirit
Copy link
moshpirit commented Nov 1, 2024

I've just updated to the new tray module, I've adjusted my setup as follows:

Startup script:

# Launch bar on each monitor, tray on primary
polybar --list-monitors | while IFS=$'\n' read line; do
  monitor=$(echo $line | cut -d':' -f1)
  primary=$(echo $line | cut -d' ' -f3)
  MONITOR=$monitor polybar --reload "top${primary:+"-primary"}" &
done

Polybar config (irrelevant bits elided):

[bar/top]
monitor=${env:MONITOR:}
modules-right=moduleA moduleB moduleC

[bar/top-primary]
inherit=bar/top
modules-right=moduleA moduleB moduleC tray

[module/tray]
type=internal/tray

Essentially, I'm starting a different bar for the primary monitor, on which I want the tray to be placed. That bar inherits from the original bar's config, and then I overwrite modules-right to include the tray module. Unfortunately I do need to duplicate the original modules: #954.

I tried this, but I'm getting this warning:

notice: Parsing config file: ~/.config/polybar/config.ini
error: Uncaught exception, shutting down: Undefined bar: top-primary. Available bars: monitor, mybar
notice: Parsing config file: ~/.config/polybar/config.ini
error: Uncaught exception, shutting down: Undefined bar: top. Available bars: monitor, mybar

@dakeryas
Copy link
dakeryas commented Dec 23, 2024

For those who like quick one-liners relying on GNU parallel to start the bars in parallel, and provided you've already defined monitor=${env:MONITOR:} in your polybar config.ini, then the following works:

polybar-msg cmd quit; polybar -m | cut -d":" -f1 | parallel "MONITOR={} polybar &!"

and as an i3wm command:

exec_always --no-startup-id zsh -c 'polybar-msg cmd quit; polybar -m | cut -d":" -f1 | parallel "MONITOR={} polybar&!"'

@davidmreed
Copy link

I've been frustrated with the fragility of the shell scripts I use for polybar in a multi-monitor setup, especially because I tend to switch which monitors are plugged in to which machine.

I wrote a tool, monitor-affinity, to allow routing bars based on criteria like "the largest monitor" or "every monitor except the topmost". One does, e.g.,

monitor-affinity -a largest polybar main-bar
monitor-affinity -a not-largest --allow-multiple polybar secondary-bar

to put the main bar on the largest monitor and one instance of the secondary bar on each other connected monitor. Also supports using a TOML config file for an arbitrary number of commands in a single invocation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

0