|
- #!/usr/bin/perl
-
- ########################################################################
- # Usage
- ########################################################################
- #
- # $ displays.pl [layout] [-w]
- #
- # layout Name of desired layout, as configured starting at line 100
- # If missing, last known layout is used (logged to file $last)
- # -w (Re)load waybar only. Skip display configuration. Only kill
- # existing waybars and start new ones for desired layout
-
- ########################################################################
- # Dependencies
- ########################################################################
- #
- # Depends on JSON::XS and Proc::ProcessTable
- #
- # Debian:
- # apt install libjson-xs-perl libproc-processtable-perl
-
- ########################################################################
- # Directories and Files
- ########################################################################
-
- # Template is a normal config with the following in place of values:
- # "output": __OUTPUT__
- # "position": __POSITION__
- # "width": __WIDTH__ (optional)
- my $waybar_template = "$ENV{'HOME'}/.config/waybar/config.template";
-
- # Temporary directory to save generated waybar config(s)
- my $waybar_temporary = '/tmp';
-
- # File to log and recover last used layout name
- my $last = "$ENV{'HOME'}/.config/last_display";
-
- ########################################################################
- # Display Serials and Names
- ########################################################################
-
- # Hash to match display Serial with a friendly name.
- # You can get the serial from (quoted with *):
- # $ swaymsg -t get_outputs
- # Output eDP-1 'Unknown 0x057D *0x00000000*'
- my %outputs = (
- '0x00000101' => 'Sam',
- '3CQ4342S6W' => 'HP-1',
- '3CQ3310Q1Q' => 'HP-2',
- '0x00000000' => 'LVDS'
- );
-
- ########################################################################
- # Display Configurations
- ########################################################################
-
- # First-level key of hash is the name of each configuration
- # Second-level keys are the display friendly-names, above
- # Third-level are the actual settings for that display
- my %configs = (
- 'detached' => {
- 'HP-1' => {
- 'on' => 0
- },
- 'HP-2' => {
- 'on' => 0
- },
- 'Sam' => {
- 'on' => 0
- },
- 'LVDS' => {
- 'on' => 1,
- 'width' => 1920,
- 'height' => 1080,
- 'x' => 0,
- 'y' => 0,
- 'rotate' => 0,
- 'waybar' => 'bottom'
- }
- },
- 'stacked' => {
- 'Sam' => {
- 'on' => 1,
- 'width' => 1920,
- 'height' => 1080,
- 'x' => 960,
- 'y' => 0,
- 'rotate' => 0,
- 'waybar' => 'bottom'
- },
- 'HP-1' => {
- 'on' => 1,
- 'width' => 1920,
- 'height' => 1080,
- 'x' => 0,
- 'y' => 1200,
- 'rotate' => 0,
- 'waybar' => 'top'
- },
- 'HP-2' => {
- 'on' => 1,
- 'width' => 1920,
- 'height' => 1080,
- 'x' => 1920,
- 'y' => 1200,
- 'rotate' => 0,
- 'waybar' => 'top'
- },
- 'LVDS' => {
- 'on' => 0
- }
- },
- 'sidebyside' => {
- 'HP-1' => {
- 'on' => 1,
- 'width' => 1080,
- 'height' => 1920,
- 'x' => 0,
- 'y' => 225,
- 'rotate' => 270,
- 'waybar' => 'top'
- },
- 'Sam' => {
- 'on' => 1,
- 'width' => 1200,
- 'height' => 1920,
- 'x' => 1080,
- 'y' => 0,
- 'rotate' => 90,
- 'waybar' => 'top'
- },
- 'HP-2' => {
- 'on' => 1,
- 'width' => 1080,
- 'height' => 1920,
- 'x' => 2280,
- 'y' => 225,
- 'rotate' => 90,
- 'waybar' => 'top'
- },
- 'LVDS' => {
- 'on' => 0,
- }
- }
- );
-
- ########################################################################
- # Program (do not edit below)
- ########################################################################
-
- use strict;
- use warnings;
-
- my $waybar_only = 0;
- my $config;
-
- if (scalar(@ARGV)) {
- while (@ARGV) {
- my $arg = shift;
- if ($arg eq "-w") {
- $waybar_only = 1;
- } else {
- if (defined $config) {
- die "Too many arguments.\n";
- }
- $config = $arg;
- }
- }
- }
-
- # Get previous config if one is not provided
- unless (defined $config) {
- open(my $fh, '<', $last) ||
- die "Config name not provided and failed to open $last\n";
- $config = <$fh>;
- close($fh);
- chomp $config;
- }
-
- # Bail if requested config doesn't exist
- unless (defined $configs{$config}) {
- die "$config is not a defined configuration: "
- . join(', ', keys %configs) . "\n";
- }
-
- # Write config that is to be used so that it can be recovered as default
- open(my $fh, '>', $last) ||
- print STDERR "Config name cannot be written to $last\n";
- print $fh $config;
- close($fh);
-
- # Fetch connected displays
- use JSON::XS;
- my $json = JSON::XS->new();
- my $displays_raw = `swaymsg -t get_outputs --raw`;
- my $displays = $json->decode($displays_raw);
-
- # For each connected display, collect the desired settings for enabled
- # displays and a list of displays to disable
- my $on;
- my @off;
- for (my $i = 0; $i < scalar(@$displays); $i++) {
-
- # If a display is found without any settings print error and turn it off
- unless (defined $configs{$config}{$outputs{$displays->[$i]->{serial}}}){
- print STDERR "Output $displays->[$i]->{name} ("
- . $displays->[$i]->{serial}
- . ") found without defined function. Disabling.\n";
- push @off, $displays->[$i]->{name};
- next;
- }
-
- # If display is enabled, copy all of the desired settings
- if ($configs{$config}{$outputs{$displays->[$i]->{serial}}}{on}) {
- $on->{$outputs{$displays->[$i]->{serial}}} =
- $configs{$config}{$outputs{$displays->[$i]->{serial}}};
- $on->{$outputs{$displays->[$i]->{serial}}}{output} =
- $displays->[$i]->{name};
- # Otherwise simply list it for disabling
- } else {
- push @off, $displays->[$i]->{name};
- }
-
- }
-
- # Skip enabling/disabling displays if only running waybar (re)start
- unless ($waybar_only) {
- # Number of simultaneous outputs is limited by gpu, so disabled displays
- # first
- foreach (@off) {
-
- # Sway returns status as JSON
- my $res_raw = `sway output $_ disable`;
- my $res = $json->decode($res_raw)->[0];
-
- # If failed, print to STDERR
- unless ($res->{success}) {
- print STDERR "Error ($res->{error}) in command 'sway"
- . "output $_ disable'\n";
- }
-
- }
- }
-
- # Kill existing Waybars
- require Proc::ProcessTable;
- my $t = new Proc::ProcessTable;
- foreach my $p ( @{ $t->table } ) {
- my $cmndline = $p->{'cmndline'};
- $cmndline =~ s/\s*$//g;
- if ($cmndline =~ /^waybar.*/) {
- # Never kill this process
- if ($p->{'pid'} == $$) {
- next;
- } else {
- $p->kill(9);
- }
- }
- }
-
- # Load in config template
- my $template;
- if (open (my $fh, '<', $waybar_template)) {
- while (<$fh>) {
- $template .= $_;
- }
- close $fh;
- chomp $template;
- } else {
- print STDERR "Failed to load template $waybar_template\n";
- }
-
- # If template is already set up as an array, remove the square brackets so that
- # we can concatenate multiple displays
- $template =~ s/^[^\[\{]*\[(.*)\]$/$1/s;
-
- # Setup waybar config file
- my $waybar = '';
-
- # Configure each enabled display
- foreach my $out (keys %$on) {
-
- unless ($waybar_only) {
- # Build command, starting by enabling and powering on
- my $cmd = "sway output $on->{$out}->{output} enable dpms on";
-
- # If additional options are provided, add them to command
- if (defined $on->{$out}->{rotate}) {
- $cmd .= " transform $on->{$out}->{rotate}";
- }
- if (defined $on->{$out}->{x} && defined $on->{$out}->{y}) {
- $cmd .= " position $on->{$out}->{x} $on->{$out}->{y}";
- }
- if (defined $on->{$out}->{width} &&
- defined $on->{$out}->{height} )
- {
- $cmd .= " mode $on->{$out}->{width}x$on->{$out}->{height}";
- }
-
- # Sway returns status as JSON
- my $res_raw = `$cmd`;
- my $res = $json->decode($res_raw)->[0];
-
- # If failed, print to STDERR
- unless ($res->{success}) {
- print STDERR "Error ($res->{error}) in command '$cmd'\n";
- }
- }
-
- # Skip waybar setup if template failed to be loaded
- if ( (defined $template) &&
- (defined $on->{$out}->{waybar}) &&
- ($on->{$out}->{waybar} =~ m/(top|bottom|left|right)/) )
- {
-
- # If there's already a display set up, add a comma
- unless ($waybar eq '') {
- $waybar .= ',';
- }
-
- $waybar .= $template;
-
- # Replace basic preferences
- $waybar =~ s/__OUTPUT__/"$on->{$out}->{output}"/;
- $waybar =~ s/__POSITION__/"$on->{$out}->{waybar}"/;
- if (defined $on->{$out}->{width}) {
- $waybar =~ s/__WIDTH__/$on->{$out}->{width}/;
- # If width is not set, comment that line out to use default
- } else {
- $waybar =~ s/([^\s]*\s*)__WIDTH__/\/\/ $1__WIDTH__/gg;
- }
-
- }
- }
-
- # Restore array formatting
- $waybar = '[' . $waybar . ']';
-
- # Start Waybar as fork
- my $pid = fork;
- unless ($pid) {
- open STDIN, '/dev/null';
- open STDOUT, '>>/dev/null';
- open STDERR, '>>/dev/null';
- # Write config to a temporary file
- my $tmp = $waybar_temporary . "/waybar-" . time() .".config";
- open ($fh, '>', $tmp);
- print $fh $waybar;
- close $fh;
- `nohup waybar --config=$tmp`;
-
- # Remove config
- unlink $tmp;
- }
|