A variety of simple automation scripts and enhancements to Sway and i3.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

309 lines
8.5KB

  1. #!/usr/bin/perl
  2. ########################################################################
  3. # Dependencies
  4. ########################################################################
  5. #
  6. # Depends on JSON::XS and Proc::ProcessTable
  7. #
  8. # Debian:
  9. # apt install libjson-xs-perl libproc-processtable-perl
  10. ########################################################################
  11. # Directories and Files
  12. ########################################################################
  13. # Template is a normal config with the following in place of values:
  14. # "output": __OUTPUT__
  15. # "position": __POSITION__
  16. # "width": __WIDTH__ (optional)
  17. my $waybar_template = '/home/jpm/.config/waybar/config.template';
  18. # Temporary directory to save generated waybar config(s)
  19. my $waybar_temporary = '/tmp';
  20. ########################################################################
  21. # Display Serials and Names
  22. ########################################################################
  23. # Hash to match display Serial with a friendly name.
  24. # You can get the serial from (quoted with *):
  25. # $ swaymsg -t get_outputs
  26. # Output eDP-1 'Unknown 0x057D *0x00000000*'
  27. my %outputs = (
  28. '0x00000101' => 'Sam',
  29. '3CQ4342S6W' => 'HP-1',
  30. '3CQ3310Q1Q' => 'HP-2',
  31. '0x00000000' => 'LVDS'
  32. );
  33. ########################################################################
  34. # Display Configurations
  35. ########################################################################
  36. # First-level key of hash is the name of each configuration
  37. # Second-level keys are the display friendly-names, above
  38. # Third-level are the actual settings for that display
  39. my %configs = (
  40. 'detached' => {
  41. 'HP-1' => {
  42. 'on' => 0
  43. },
  44. 'HP-2' => {
  45. 'on' => 0
  46. },
  47. 'Sam' => {
  48. 'on' => 0
  49. },
  50. 'LVDS' => {
  51. 'on' => 1,
  52. 'width' => 1920,
  53. 'height' => 1080,
  54. 'x' => 0,
  55. 'y' => 0,
  56. 'rotate' => 0,
  57. 'waybar' => 'bottom'
  58. }
  59. },
  60. 'stacked' => {
  61. 'Sam' => {
  62. 'on' => 1,
  63. 'width' => 1920,
  64. 'height' => 1080,
  65. 'x' => 960,
  66. 'y' => 0,
  67. 'rotate' => 0,
  68. 'waybar' => 'bottom'
  69. },
  70. 'HP-1' => {
  71. 'on' => 1,
  72. 'width' => 1920,
  73. 'height' => 1080,
  74. 'x' => 0,
  75. 'y' => 1200,
  76. 'rotate' => 0,
  77. 'waybar' => 'top'
  78. },
  79. 'HP-2' => {
  80. 'on' => 1,
  81. 'width' => 1920,
  82. 'height' => 1080,
  83. 'x' => 1920,
  84. 'y' => 1200,
  85. 'rotate' => 0,
  86. 'waybar' => 'top'
  87. },
  88. 'LVDS' => {
  89. 'on' => 0
  90. }
  91. },
  92. 'sidebyside' => {
  93. 'HP-1' => {
  94. 'on' => 1,
  95. 'width' => 1080,
  96. 'height' => 1920,
  97. 'x' => 0,
  98. 'y' => 225,
  99. 'rotate' => 270,
  100. 'waybar' => 'top'
  101. },
  102. 'Sam' => {
  103. 'on' => 1,
  104. 'width' => 1200,
  105. 'height' => 1920,
  106. 'x' => 1080,
  107. 'y' => 0,
  108. 'rotate' => 90,
  109. 'waybar' => 'top'
  110. },
  111. 'HP-2' => {
  112. 'on' => 1,
  113. 'width' => 1080,
  114. 'height' => 1920,
  115. 'x' => 2280,
  116. 'y' => 225,
  117. 'rotate' => 90,
  118. 'waybar' => 'top'
  119. },
  120. 'LVDS' => {
  121. 'on' => 0,
  122. }
  123. }
  124. );
  125. ########################################################################
  126. # Program (do not edit below)
  127. ########################################################################
  128. use strict;
  129. use warnings;
  130. # Bail if zero or >1 args
  131. my $last = "/home/jpm/.config/last_display";
  132. my $waybar_only = 0;
  133. my $config;
  134. if (scalar(@ARGV)) {
  135. while (@ARGV) {
  136. my $arg = shift;
  137. if ($arg eq "-w") {
  138. $waybar_only = 1;
  139. } else {
  140. if (defined $config) {
  141. die "Too many arguments.\n";
  142. }
  143. $config = $arg;
  144. }
  145. }
  146. }
  147. unless (defined $config) {
  148. open(my $fh, '<', $last);
  149. $config = <$fh>;
  150. close($fh);
  151. chomp $config;
  152. print "$config\n";
  153. }
  154. # Bail if requested config doesn't exist
  155. unless (defined $configs{$config}) {
  156. die "$config is not a defined configuration: " . join(', ', keys %configs) . "\n";
  157. }
  158. # Import and setup JSON object
  159. use JSON::XS;
  160. my $json = JSON::XS->new();
  161. # Fetch connected displays
  162. my $displays_raw = `swaymsg -t get_outputs --raw`;
  163. my $displays = $json->decode($displays_raw);
  164. # For each connected display, collect the desired settings for enabled
  165. # displays and a list of displays to disable
  166. my $on;
  167. my @off;
  168. for (my $i = 0; $i < scalar(@$displays); $i++) {
  169. # If a display is found without any settings, print error and turn it off
  170. unless (defined $configs{$config}{$outputs{$displays->[$i]->{serial}}}) {
  171. print STDERR "Output $displays->[$i]->{name} ($displays->[$i]->{serial}) found without defined function. Disabling.\n";
  172. push @off, $displays->[$i]->{name};
  173. next;
  174. }
  175. # If display is enabled, copy all of the desired settings
  176. if ($configs{$config}{$outputs{$displays->[$i]->{serial}}}{on}) {
  177. $on->{$outputs{$displays->[$i]->{serial}}} = $configs{$config}{$outputs{$displays->[$i]->{serial}}};
  178. $on->{$outputs{$displays->[$i]->{serial}}}{output} = $displays->[$i]->{name};
  179. # Otherwise simply list it for disabling
  180. } else {
  181. push @off, $displays->[$i]->{name};
  182. }
  183. }
  184. unless ($waybar_only) {
  185. # Number of simultaneous outputs is limited by possible, so disabled displays first
  186. foreach (@off) {
  187. # Sway returns status as JSON
  188. my $res_raw = `sway output $_ disable`;
  189. my $res = $json->decode($res_raw)->[0];
  190. # If failed, print to STDERR
  191. unless ($res->{success}) {
  192. print STDERR "Error ($res->{error}) in command 'sway output $_ disable'\n";
  193. }
  194. }
  195. }
  196. # Kill existing Waybars
  197. require Proc::ProcessTable;
  198. my $t = new Proc::ProcessTable;
  199. foreach my $p ( @{ $t->table } ) {
  200. my $cmndline = $p->{'cmndline'};
  201. $cmndline =~ s/\s*$//g;
  202. if ($cmndline =~ /^waybar.*/) {
  203. if ($p->{'pid'} == $$) {
  204. next;
  205. } else {
  206. $p->kill(9);
  207. }
  208. }
  209. }
  210. # Configure each enabled display
  211. foreach my $out (keys %$on) {
  212. unless ($waybar_only) {
  213. # Build command, starting by enabling and powering on
  214. my $cmd = "sway output $on->{$out}->{output} enable dpms on";
  215. # If additional options are provided, add them to command
  216. if (defined $on->{$out}->{rotate}) {
  217. $cmd .= " transform $on->{$out}->{rotate}";
  218. }
  219. if (defined $on->{$out}->{x} && defined $on->{$out}->{y}) {
  220. $cmd .= " position $on->{$out}->{x} $on->{$out}->{y}";
  221. }
  222. if (defined $on->{$out}->{width} && defined $on->{$out}->{height}) {
  223. $cmd .= " mode $on->{$out}->{width}x$on->{$out}->{height}";
  224. }
  225. # Sway returns status as JSON
  226. my $res_raw = `$cmd`;
  227. my $res = $json->decode($res_raw)->[0];
  228. # If failed, print to STDERR
  229. unless ($res->{success}) {
  230. print STDERR "Error ($res->{error}) in command '$cmd'\n";
  231. }
  232. }
  233. # If waybar position is set, fork, generate config and execute it
  234. if (defined $on->{$out}->{waybar}) {
  235. my $pid = fork;
  236. unless ($pid) {
  237. open STDIN, '/dev/null';
  238. open STDOUT, '>>/dev/null';
  239. open STDERR, '>>/dev/null';
  240. # Load in config template
  241. my $waybar;
  242. open (my $fh, '<', $waybar_template);
  243. while (<$fh>) {
  244. $waybar .= $_;
  245. }
  246. close $fh;
  247. chomp $waybar;
  248. # Replace basic preferences
  249. $waybar =~ s/__OUTPUT__/"$on->{$out}->{output}"/;
  250. $waybar =~ s/__POSITION__/"$on->{$out}->{waybar}"/;
  251. if (defined $on->{$out}->{width}) {
  252. $waybar =~ s/__WIDTH__/$on->{$out}->{width}/;
  253. # If width is not set, comment that line out to use default
  254. } else {
  255. $waybar =~ s/([^\s]*\s*)__WIDTH__/\/\/ $1__WIDTH__/gg;
  256. }
  257. # Write config to a temporary file
  258. my $tmp = $waybar_temporary . "/" . $on->{$out}->{output} . ".tmp";
  259. open ($fh, '>', $tmp);
  260. print $fh $waybar;
  261. close $fh;
  262. `nohup waybar --config=$tmp`;
  263. print $tmp . "\n";
  264. # Remove config
  265. unlink $tmp;
  266. }
  267. }
  268. }
  269. open(my $fh, '>', $last);
  270. print $fh $config;
  271. close($fh);