#!/bin/bash # I, Ian Kelling, follow the GNU license recommendations at # https://www.gnu.org/licenses/license-recommendations.en.html. They # recommend that small programs, < 300 lines, be licensed under the # Apache License 2.0. This file contains or is part of one or more small # programs. If a small program grows beyond 300 lines, I plan to change # to a recommended GPL license. # Copyright 2024 Ian Kelling # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e; . /usr/local/lib/bash-bear; set +e script_name="${0##*/}" usage() { cat <$xf m() { if $dry_run; then printf "$script_name: dry run: %s\n" "$*" else printf "$script_name: %s\n" "$*"; "$@" fi } secondary_out=$(awk '$2 == "connected" && $3 != "primary" {print $1}' $xf) primary_info=$(awk '$2 == "connected" && $3 == "primary" {print $1,$4}' $xf) # res_info eg: 3840x2160+1920+0 read -r primary_out primary_res_info <<<"$primary_info" primary_res=${primary_res_info%%+*} primary_x=${primary_res%x*} primary_y=${primary_res#*x} if [[ $secondary_out ]]; then x_offset=$primary_x left_right_arg=--right-of # dp-1 moves from left to right sometimes, i just move it out of the # conditional and comment it as needed. # [[ $secondary_out == DP-1 && $(edid card1-DP-1 ) == f3364bc6c1 ]] || \ if [[ $secondary_out == HDMI2 && $(edid card0-HDMI-A-2) == 192efbdcef ]] || \ [[ $secondary_out == HDMI-1 && $(edid card1-HDMI-A-1 ) == 7c58f9ac1e ]] || \ [[ $secondary_out == DP-2 && $(edid card1-DP-2 ) == 0c35564b67 ]]; then left_right_arg=--left-of x_offset=0 fi left_right_arg="$left_right_arg $primary_out" target_out=$secondary_out target_res=$(grep -A1 -E "^$secondary_out " $xf | tail -n1 | awk '{print $1}') target_x=${target_res%x*} target_y=${target_res#*x} else target_out=$primary_out x_offset=0 target_res=$primary_res target_x=$primary_x target_y=$primary_y fi if (( target_x <= 1280 )); then quarter=false tall=false elif (( target_x <= 1920 )); then quarter=false tall=true fi ##### begin command line parsing ######## temp=$(getopt -l help fnrs: "$@") || usage 1 eval set -- "$temp" while true; do case $1 in -f) downres=false quarter=false tall=false ;; -n) dry_run=true ;; -s) case $2 in tall) tall=true quarter=false ;; quarter) tall=false quarter=true ;; full) tall=false quarter=false ;; esac shift ;; -r) restart=true ;; -h|--help) usage 0 ;; --) shift; break ;; *) echo "$0: Internal error! unexpected args: $*" ; exit 1 ;; esac shift done ##### end command line parsing ######## if $downres && (( target_x > 2560 )); then target_x=2560 target_y=1440 target_res=${target_x}x$target_y fi half_x=$(( target_x / 2 )) half_y=$(( target_y / 2 )) for mon_suffix in TOP-LEFT BOTTOM-LEFT LEFT RIGHT; do if xrandr --listmonitors | awk '$2 == "MON-'$mon_suffix\" |grep . >/dev/null; then m xmod --delmonitor MON-$mon_suffix fi done if [[ ! $secondary_out ]] || grep -A1 disconnected $xf | grep '^[[:space:]]'; then # gets rid of leftover secondary m xmod --auto fi if [[ $secondary_out ]] && $restart; then m xmod --output $secondary_out --off m sleep 2 fi if [[ $secondary_out || $primary_res != "$target_res" ]]; then m xmod --output $target_out $left_right_arg --mode $target_res fi if $tall; then # 298 & 336 are millimeters. I took them from a monitor I was using. I # don't know if they are important, I assume not important enough to # change for different monitors. m xmod --setmonitor MON-LEFT $half_x/298x$target_y/336+$x_offset+0 $target_out m xmod --setmonitor MON-RIGHT $half_x/298x$target_y/336+$(( x_offset + half_x ))+0 none elif $quarter; then m xmod --setmonitor MON-LEFT $half_x/298x$half_y/336+$x_offset+0 $target_out # note: this bottom left is buggy when it comes to context menus, # including in web pages. In the tall configuration, I sometimes see # them in another "monitor" area, but with this bottom left monitor, # they aren't displayed at all, as if the window system is displaying # them off screen. When that happens, I just switch to the tall # configuration, use the dialog, and them maybe switch back. m xmod --setmonitor MON-BOTTOM-LEFT $half_x/298x$half_y/336+$x_offset+$half_y none m xmod --setmonitor MON-RIGHT $half_x/298x$target_y/336+$(( x_offset + half_x ))+0 none fi ## begin i3 config ## move_outputs=() ws_outputs=() if [[ $secondary_out ]]; then ws1_output=$primary_out move_outputs=($primary_out) elif $tall || $quarter; then ws1_output=MON-RIGHT fi ws_outputs=( $ws1_output ) if $tall; then ws_outputs+=( MON-LEFT MON-RIGHT ) move_outputs+=(MON-RIGHT) elif $quarter; then ws_outputs+=( MON-LEFT MON-BOTTOM-LEFT MON-RIGHT ) move_outputs+=(MON-BOTTOM-LEFT MON-RIGHT) elif [[ $secondary_out ]]; then move_outputs+=($secondary_out) ws_outputs+=($secondary_out) fi echo d1 ${ws_outputs[@]} rm -f ~/i3-myx.conf total_ws_count=11 for (( i=0; i>~/i3-myx.conf done if [[ $secondary_out ]]; then cat /a/bin/ds/i3-sway/bar.conf >> ~/i3-myx.conf fi echo "bindsym \$mod+Shift+t move workspace to output ${move_outputs[*]}" >>~/i3-myx.conf # give it some time to reload or adjust to new x settings. # i3-msg -t get_outputs | jq -C . # showed # { # "name": "HDMI-2", # "active": true, # "primary": false, # "rect": { # "x": 1920, # "y": 0, # "width": 3840, # "height": 2160 # }, # "current_workspace": "3" # }, # { # "name": "DP-1", # "active": false, # "primary": false, # "rect": { # "x": 1920, # "y": 0, # "width": 3840, # "height": 2160 # }, # "current_workspace": null # } # This was incorrect. The correct thing is that DP-1 is active, HDMI-2 is not active. # Restarting i3 did not help. sleep 1 m /a/bin/ds/i3-sway/gen $gen_arg if (( ${#ws_outputs[@]} )); then declare -A ws_expected_out for (( i=0; i 20 )); then cat <<'EOF' >&2 ERROR: moving workspaces is not working as expected I've seen it happen that an output should not exist anymore. Restarting i3 fixed it, reloading did not. This script did a restart, but it might need a sleep before doing so. EOF exit 1 fi refresh_count=$(( refresh_count + 1 )) refresh_workspaces=false tmps=$(i3 -t get_workspaces) tmps=$(jq -r '.[] | .num, .output' <<<"$tmps") while read -r ws; do read -r output || break if [[ $ws != [0-9] && $ws != 10 ]]; then echo "$0: error: unexpected workspace found: $ws. continuing" continue fi if [[ ${ws_expected_out[$ws]} != "$output" ]]; then m i3 '[workspace="'$ws'"]' move workspace to output ${ws_expected_out[$ws]} refresh_workspaces=true break fi done <<<"$tmps" done fi ## end i3 config ## m /a/exe/input-setup echo -n "myx end: " date "+%A, %B %d, %r, %S seconds" # myx -f # Tuesday, July 23, 05:54:37 PM, 37 seconds # myx: xmod --delmonitor MON-LEFT # myx: xmod --delmonitor MON-RIGHT # myx: xmod --output DP-1 --right-of eDP-1 --mode 3840x2160 # d1 eDP-1 DP-1 # myx: /a/bin/ds/i3-sway/gen -r # /a/bin/ds/i3-sway/gen: i3-msg restart # [{"success":true}] # myx: i3 [workspace="2"] move workspace to output DP-1 # ERROR: No output matched # [{"success":false,"error":"No output matched"}] # /a/exe/myx:63: `i3-msg "$@"' returned 2 # from /a/exe/myx:63:in `i3 [workspace="2"] move workspace to output DP-1' # from /a/exe/myx:88:in `m i3 [workspace="2"] move workspace to output DP-1' # from /a/exe/myx:330:in `main -f' # /a/exe/myx: exiting with status 2