308 lines
8.5 KiB
Perl
308 lines
8.5 KiB
Perl
|
#!/usr/bin/perl
|
||
|
|
||
|
########################################################################
|
||
|
# 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 = '/home/jpm/.config/waybar/config.template';
|
||
|
|
||
|
# Temporary directory to save generated waybar config(s)
|
||
|
my $waybar_temporary = '/tmp';
|
||
|
|
||
|
########################################################################
|
||
|
# 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;
|
||
|
|
||
|
# Bail if zero or >1 args
|
||
|
my $last = "/home/jpm/.config/last_display";
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unless (defined $config) {
|
||
|
open(my $fh, '<', $last);
|
||
|
$config = <$fh>;
|
||
|
close($fh);
|
||
|
chomp $config;
|
||
|
print "$config\n";
|
||
|
}
|
||
|
|
||
|
# Bail if requested config doesn't exist
|
||
|
unless (defined $configs{$config}) {
|
||
|
die "$config is not a defined configuration: " . join(', ', keys %configs) . "\n";
|
||
|
}
|
||
|
|
||
|
# Import and setup JSON object
|
||
|
use JSON::XS;
|
||
|
my $json = JSON::XS->new();
|
||
|
|
||
|
# Fetch connected displays
|
||
|
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};
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
unless ($waybar_only) {
|
||
|
# Number of simultaneous outputs is limited by possible, 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.*/) {
|
||
|
if ($p->{'pid'} == $$) {
|
||
|
next;
|
||
|
} else {
|
||
|
$p->kill(9);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# 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";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# If waybar position is set, fork, generate config and execute it
|
||
|
if (defined $on->{$out}->{waybar}) {
|
||
|
my $pid = fork;
|
||
|
unless ($pid) {
|
||
|
open STDIN, '/dev/null';
|
||
|
open STDOUT, '>>/dev/null';
|
||
|
open STDERR, '>>/dev/null';
|
||
|
|
||
|
# Load in config template
|
||
|
my $waybar;
|
||
|
open (my $fh, '<', $waybar_template);
|
||
|
while (<$fh>) {
|
||
|
$waybar .= $_;
|
||
|
}
|
||
|
close $fh;
|
||
|
chomp $waybar;
|
||
|
|
||
|
# 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;
|
||
|
}
|
||
|
|
||
|
# Write config to a temporary file
|
||
|
my $tmp = $waybar_temporary . "/" . $on->{$out}->{output} . ".tmp";
|
||
|
open ($fh, '>', $tmp);
|
||
|
print $fh $waybar;
|
||
|
close $fh;
|
||
|
`nohup waybar --config=$tmp`;
|
||
|
|
||
|
# Remove config
|
||
|
unlink $tmp;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
open(my $fh, '>', $last);
|
||
|
print $fh $config;
|
||
|
close($fh);
|