Browse Source

Added

Signed-off-by: Marcin Woźniak <y0rune@aol.com>
master
Marcin Woźniak 1 year ago
commit
7b2650b646
Signed by: y0rune GPG Key ID: F204C385F57EB348
  1. 57
      CONTRIBUTING.md
  2. 20
      LICENSE
  3. 37
      Makefile
  4. 200
      README.md
  5. 19
      _gitignore
  6. 3
      _gitmodules
  7. BIN
      img/raw-body.png
  8. 20
      libshlist/LICENSE
  9. 111
      libshlist/README.md
  10. 1
      libshlist/_git
  11. 271
      libshlist/liblist.sh
  12. 286
      libshlist/liblist_unsafe.sh
  13. 85
      libshlist/test/test.sh
  14. 210
      libshlist/test/test_unsafe.sh
  15. 448
      mons
  16. BIN
      mons.1.gz
  17. 77
      test/test-args.sh
  18. 36
      test/test-common.sh
  19. 24
      test/test-select.sh

57
CONTRIBUTING.md

@ -0,0 +1,57 @@
Contributing
============
All kinds of contributions to Mons are greatly appreciated. For someone
unfamiliar with the code base, the most efficient way to contribute is usually
to submit a [feature request](#feature-requests) or [bug report](#bug-reports).
If you want to dive into the source code, you can submit a [patch](#patches) as
well, either working on your own ideas or [existing issues][issues].
Feature Requests
----------------
Do you have an idea for an awesome new feature for Mons? Please [submit a
feature request][issue]. It's great to hear about new ideas.
If you are inclined to do so, you're welcome to [fork][fork] Mons, work on
implementing the feature yourself, and submit a patch. In this case, it's
*highly recommended* that you first [open an issue][issue] describing your
enhancement to get early feedback on the new feature that you are implementing.
This will help avoid wasted efforts and ensure that your work is incorporated
into the code base.
Bug Reports
-----------
Did something go wrong with Mons? Sorry about that! Bug reports are greatly
appreciated!
When you [submit a bug report][issue], please include relevant information such
as Mons version, operating system, configuration file, error messages, and
steps to reproduce the bug. The more details you can include, the easier it is
to find and fix the bug.
Patches
-------
If there are [open issues][issues], you're more than welcome to work on those -
this is probably the best way to contribute to Mons. If you have your own
ideas, that's great too! In that case, before working on substantial changes to
the code base, it is *highly recommended* that you first [open an issue][issue]
describing what you intend to work on.
**Patches are generally submitted as pull requests.** Patches are also
[accepted over email][email].
The version history should be clean, and commit messages should be descriptive
and [properly formatted][commit-messages].
---
If you have any questions about anything, feel free to [ask][email]!
[issue]: https://github.com/ventto/mons/issues/new
[issues]: https://github.com/ventto/mons/issues
[fork]: https://github.com/ventto/mons/fork
[email]: mailto:thomas.venries@gmail.com
[commit-messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html

20
LICENSE

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2016-2017 Thomas "Ventto" Venriès <thomas.venries@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

37
Makefile

@ -0,0 +1,37 @@
PKGNAME = mons
PKGDESC = POSIX Shell script to quickly manage 2-monitors display.
LICENSEDIR = $(DESTDIR)/usr/share/licenses/$(PKGNAME)
MANDIR = $(DESTDIR)/usr/share/man/man1
BINDIR = $(DESTDIR)/usr/bin
LIBDIR = $(DESTDIR)/usr/lib/libshlist
LIB = libshlist/liblist.sh
install:
@if ! [ -r "$(LIB)" ]; then \
echo "$(LIB): missing file"; \
exit 1; \
fi
help2man -N -n "$(PKGDESC)" -h -h -v -v ./$(PKGNAME) | gzip - > $(PKGNAME).1.gz
@if ! [ -r "$(PKGNAME).1.gz" ]; then \
echo "$(PKGNAME).1.gz: missing manpage"; \
exit 1; \
fi
mkdir -p $(MANDIR)
mkdir -p $(LICENSEDIR)
mkdir -p $(LIBDIR)
mkdir -p $(BINDIR)
chmod 644 $(PKGNAME).1.gz
chmod 644 LICENSE
chmod 644 $(LIB)
chmod 755 mons
cp $(PKGNAME).1.gz $(MANDIR)/$(PKGNAME).1.gz
cp LICENSE $(LICENSEDIR)/LICENSE
cp $(LIB) $(LIBDIR)/liblist.sh
cp mons $(BINDIR)/mons
uninstall:
$(RM) -r $(LIBDIR)
$(RM) $(BINDIR)/mons
.PHONY: install uninstall

200
README.md

@ -0,0 +1,200 @@
Mons
===================
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/Ventto/mons/blob/master/LICENSE)
[![Language (XRandR)](https://img.shields.io/badge/powered_by-XRandR-brightgreen.svg)](https://www.x.org/archive/X11R7.5/doc/man/man1/xrandr.1.html)
[![Vote for mons](https://img.shields.io/badge/AUR-Vote_for-yellow.svg)](https://aur.archlinux.org/packages/mons/)
*"Mons is a Shell script to quickly manage 2-monitors display using xrandr."*
## Perks
* [x] **No requirement**: POSIX-compliant (minimal: *xorg-xrandr*)
* [x] **Useful**: Perfectly fit for laptops, quick and daily use
* [x] **Well known**: Laptop mode, projector mode, duplicate, mirror and extend
* [x] **More**: Select one or two monitors over several others
* [x] **Extra**: Cycle through every mode with only one shortcut
* [x] **Auto**: Deamon mode to automatically reset display
# Installation
* Package (AUR)
```
$ pacaur -S mons
```
* Manual
```
$ git clone --recursive https://github.com/Ventto/mons.git
$ cd mons
$ sudo make install
```
> Note: `--recursive` is needed for git submodule
# Usage
```
Without argument, it prints connected monitors list with their names and ids.
Options are exclusive and can be used in conjunction with extra options.
Information:
-h Prints this help and exits.
-v Prints version and exits.
Two monitors:
-o Primary monitor only.
-s Second monitor only.
-d Duplicates the primary monitor.
-m Mirrors the primary monitor.
-e <side>
Extends the primary monitor to the selected side
[ top | left | right | bottom ].
-n <side>
This mode selects the previous ones, one after another. The argument
sets the side for the extend mode.
More monitors:
-O <mon>
Only enables the monitor with a specified id.
-S <mon1>,<mon2>:<pos>
Only enables two monitors with specified ids. The specified position
places the second monitor on the right (R) or at the top (T).
Extra (in-conjunction options):
--dpi <dpi>
Set the DPI, a strictly positive value within the range [0 ; 27432].
--primary <mon_name>
Select a connected monitor as the primary output. Run the script
without argument to print monitors information, the names are in the
second column between ids and status. The primary monitor is marked
by an asterisk.
Daemon mode:
-a Performs an automatic display if it detects only one monitor.
```
# Examples
## Two monitors
Displays monitor list:
```
$ mons
0: LVDS-1 (enabled)
5: VGA-1
```
You have an enabled one, you want to extends the second one on the right:
```
$ mons -e right
```
You want to only display the second one:
```
$ mons -s
```
With the `-n` option, go through every 2-mons mode consecutively:
1. Primary monitor only
1. Second monitor only
1. Extend mode whose the side is set with `-n <side>`
1. Mirror
1. Duplicate
This mode is useful if you want to switch to every mode with only one shortcut.
![alt 2-monitors modes](img/raw-body.png)
```
# Now in 'Second monitor mode'
$ mons -n right # -> 'Extend mode'
# Now in 'Extend mode'
$ mons -n right # -> 'Mirror mode'
```
## Three monitors (selection mode)
Displays monitor list:
```
$ mons
Monitors: 3
Mode: Selection
0:* LVDS-1 (enabled)
1: DP-1 (enabled)
5: VGA-1
```
You may need to display only the third one:
```
$ mons -O 5
```
You may need to display the first and the third one on the right:
```
$ mons -S 0,5:R
```
Like above but you want to inverse the placement:
```
$ mons -S 5,0:R
```
## DPI value
You might want to switch mode and set the DPI value.
Use the `--dpi <dpi>` option in conjunction with all others options.
```
$ mons [OPTIONS] --dpi <dpi>
```
## Primary monitor
You might choose one of your monitors as the main one.
You can use the `--primary <mon_name>` option alone or in conjunction with all
others options.
`<mon_name>` refers to the monitor name that appears in the list of connected
monitors (ex: `LVDS-1` or `VGA-1`):
```
$ mons
Monitors: 3
Mode: Primary
0:* LVDS-1 (enabled)
5: VGA-1
```
The '*' character means that the monitor is the primary one:
```
$ mons --primary VGA-1
Monitors: 3
Mode: Primary
0: LVDS-1 (enabled)
5:* VGA-1
```
## Daemon mode
Especially for laptops, after unplugging the additional monitors, it might be
convenient to reset automatically the display for the remaining one.
Run *mons* in background as follow:
```
$ nohup mons -a > /dev/null 2>&1 & (all shells)
$ mons -a &! (zsh)
$ mons -a &; disown (bash)
```

19
_gitignore

@ -0,0 +1,19 @@
/*
!libshlist
!img
img/*
!img/raw-body.png
!test
test/*
!test/*.sh
!mons
!CONTRIBUTING.md
!LICENSE
!Makefile
!README.md
!.gitignore
!.gitmodules

3
_gitmodules

@ -0,0 +1,3 @@
[submodule "libshlist"]
path = libshlist
url = https://github.com/Ventto/libshlist.git

BIN
img/raw-body.png

After

Width: 750  |  Height: 70  |  Size: 9.8 KiB

20
libshlist/LICENSE

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2017-2018 Thomas "Ventto" Venriès <thomas.venries@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

111
libshlist/README.md

@ -0,0 +1,111 @@
POSIX Shell List
================
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/Ventto/posix-shell-list/blob/master/LICENSE)
*"This is a POSIX Shell list implementation."*
## Perks
* [x] **No requirement**: POSIX-compliant
* [x] **Lightweight**: ~100 lines
* [x] **Extra**: Additional functions to fit your needs
* [x] **Useful**: No internal independency
* [x] **Both**: Safe and unsafe (passing argument by reference) versions
# Installation
* Download it:
```bash
$ wget https://raw.githubusercontent.com/Ventto/posix-shell-list/master/liblist.sh
```
* Source the library for including the functions into your Shell script:
```bash
. liblist.sh (or)
. liblist_unsafe.sh
```
* Each function is independent. So you can copy some functions into your
script without sourcing
# Functions
The whole function documentation is in the following library scripts:
* `liblist.sh`: the variable assignation is required for conserving changes
* `liblist_unsafe.sh`: uses `eval` special shell builtin for passing argument
by reference and set the list variable
The following list enumerates all available functions:
```
SAFE | UNSAFE
____________________________________|_______________________________
list <elt> <elt> ... | list <elt> <elt> ...
list_back <lst> | list_back <lst>
list_contains <elt> <lst> | list_contains <lst> <elt>
list_count <elt> <lst> | list_count <lst> <elt>
list_empty <lst> | list_empty <lst>
list_erase <elt> <lst> | list_erase <lst> <elt>
list_erase_from <index> <lst> | list_erase_from <lst> <index>
list_erase_range <from> <to> <lst> | list_erase_range <lst> <from> <to>
list_eraseat <index> <lst> | list_eraseat <lst> <index>
list_extract <from> <to> <lst> | list_extract <lst> <from> <to>
list_front <lst> | list_front <lst>
list_get <index> <lst> | list_get <lst> <index>
list_indexof <elt> <lst> | list_indexof <lst> <elt>
list_insert <elt> <index> <lst> | list_insert <lst> <elt> <index>
list_maps <func> <lst> | list_maps <lst> <func>
list_pop_back <lst> | list_pop_back <lst>
list_pop_front <lst> | list_pop_front <lst>
list_push_back <elt> <lst> | list_push_back <lst> <elt>
list_push_front <elt> <lst> | list_push_front <lst> <elt>
list_remove <elt> <lst> | list_remove <lst> <elt>
list_replace <new> <old> <lst> | list_replace <lst> <new> <old>
list_reverse <lst> | list_reverse <lst>
list_set <elt> <index> <lst> | list_set <lst> <elt> <index>
list_size <lst> | list_size <lst>
list_sort <lst> | list_sort <lst>
list_sort_reverse <lst> | list_sort_reverse <lst>
```
# Example
Scripts in `test/` offer an exhaustive usage of both libraries.
* Quick start (safe version):
```bash
lst="$(list 'A' 'B' 'C')"
lst="$(list_sort "$lst")" # { C, B, A }
if list_empty "$lst"; then
echo 'The list is empty.'
fi
index="$(list_indexof 'D' "$lst")" # '', empty string
if [ "$?" -ne 0 ]; then
echo 'Element not found.'
fi
```
* Quick start (unsafe version):
```bash
lst="$(list 'A' 'B' 'C')"
list_sort lst # { C, B, A }
if list_empty lst; then
echo 'The list is empty.'
fi
index="$(list_indexof lst 'D')" # '', empty string
if [ "$?" -ne 0 ]; then
echo 'Element not found.'
fi
```

1
libshlist/_git

@ -0,0 +1 @@
gitdir: ../.git/modules/libshlist

271
libshlist/liblist.sh

@ -0,0 +1,271 @@
#!/bin/sh
#
# The MIT License (MIT)
#
# Copyright (c) 2017-2018 Thomas "Ventto" Venriès <thomas.venries@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
##
# @brief Return a list from argument strings
# @usage list <elt> <elt> ...
# @print The created list
#
list () {
for e; do [ -n "$e" ] && echo "$e"; done
}
##
# @brief Prints the number of elements in the list
# @usage list_size <lst>
# @print The size of the list as positive integer
#
list_size () {
if [ -z "$1" ]; then echo '0'; else echo "$@" | wc -l; fi
}
##
# @brief Returns whether the list is empty(1) or not(0)
# @usage list_empty <lst>
# @return The exit code of the command
#
list_empty () {
test -z "$1"
}
##
# @brief Adds a new element at the beginning of the list
# @usage list_push_front <elt> <lst>
# @print The list result
#
list_push_front () {
test "$#" -ne 2 && return 1
if [ "$2" = '' ]; then echo "$1"; else printf '%s\n%s' "$1" "$2"; fi
}
##
# @brief Adds a new element at the end of the list
# @usage list_push_back <elt> <lst>
# @print The list result
#
list_push_back () {
test "$#" -ne 2 && return 1
if [ "$2" = '' ]; then echo "$1"; else printf '%s\n%s' "$2" "$1"; fi
}
##
# @brief Inserts new elements in the list before a specified position
# @usage list_insert <elt> <index> <lst>
# @print The list result
#
list_insert () {
test "$#" -ne 3 && return 1
i="$2"; [ "$i" != '$' ] && i=$((i+1)); echo "$3" | sed "${i}i${1}"
}
##
# @brief Modifies an element from the list at a specified position
# @usage list_set <elt> <index> <lst>
# @print The list result
#
list_set () {
test "$#" -ne 3 && return 1
i="$2"; i=$((i+1)); echo "$3" | sed -e "${i}s/.*/$1/"
}
##
# @brief Extracts a range of elements from the list between two specified
# positions
# @usage list_extract <from_index> <to_index> <lst>
# @print The list result
#
list_extract () {
test "$#" -ne 3 && return 1
i="$1"; j="$2"; i=$((i+1)); j=$((j+1)); echo "$3" | sed -n "${i},${j}p"
}
##
# @brief Replaces all elements from the list with a specified element
# @usage list_replace <new_elt> <old_elt> <lst>
# @print The list result
#
list_replace () {
test "$#" -ne 3 && return 1; echo "$3" | sed -e "s/^$1$/$2/g"
}
##
# @brief Prints the element at a specified position
# @usage list_get <index> <lst>
# @print The element found
#
list_get () {
test "$#" -ne 2 && return 1; i="$1"; i=$((i+1)); echo "$2" | sed -n "${i}p"
}
##
# @brief Prints the head of the list
# @usage list_front <lst>
# @print The element found
#
list_front () {
test "$#" -ne 1 && return 1; echo "$@" | sed -n '1p'
}
##
# @brief Prints the queue of the list
# @usage list_back <lst>
# @print The element found
#
list_back () {
test "$#" -ne 1 && return 1; echo "$@" | sed -n '$p'
}
##
# @brief Removes the first-hit element from a list
# @usage list_erase <elt> <lst>
# @print The list result
#
list_erase () {
test "$#" -ne 2 && return 1; echo "$2" | sed -e "0,/^$1$/ s///" -e '/^$/d'
}
##
# @brief Removes a range of elements from a list between two specified
# positions
# @usage list_erase_range <from_index> <to_index> <lst>
# @print The list result
#
list_erase_range () {
test "$#" -ne 3 && return 1
i="$1"; j="$2"; i=$((i+1)); j=$((j+1)); echo "$3" | sed "${i},${j}d"
}
##
# @brief Removes all elements from a specified position
# @usage list_erase_from <index> <lst>
# @print The list result
#
list_erase_from () {
test "$#" -ne 2 && return 1; i="$1"; i=$((i+1)); echo "$2" | sed "${i},\$d"
}
##
# @brief Removes the element at a specified position
# @usage list_eraseat <index> <lst>
# @print The list result
#
list_eraseat () {
test "$#" -ne 2 && return 1; i="$1"; i=$((i+1)); echo "$2" | sed "${i}d"
}
##
# @brief Removes all the elements from the list, which are equal to given
# element
# @usage list_remove <elt> <lst>
# @print The list result
#
list_remove () {
test "$#" -ne 2 && return 1; echo "$2" | sed -e "/^$1$/d"
}
##
# @brief Removes the first element of the list
# @usage list_pop_front <lst>
# @print The list result
#
list_pop_front () {
test "$#" -ne 1 && return 1; echo "$1" | sed '1d'
}
##
# @brief Removes the last element of the list
# @usage list_pop_back <lst>
# @print The list result
#
list_pop_back () {
test "$#" -ne 1 && return 1; echo "$1" | sed '$d'
}
##
# @brief Prints the index of a specified element
# @usage list_indexof <elt> <lst>
# @print The index or empty string if the element is not found
#
list_indexof () {
test "$#" -ne 2 && return 1; i=0
for e in $2; do
[ "$e" = "$1" ] && { echo "$i"; return 0; }; i=$((i+1));
done
return 1
}
##
# @brief Returns whether the list contains a specified element(0) or not(1)
# @usage list_contains <elt> <lst>
# @return 0 or 1
#
list_contains () {
test "$#" -ne 2 && return 1
for e in $2; do [ "$e" = "$1" ] && return 0; done; return 1
}
##
# @brief Counts the number of a specified element in the list
# @usage list_count <elt> <lst>
# @print The number of elements as positive integer
#
list_count () {
test "$#" -ne 2 && return 1
i=0; for e in $2; do [ "$e" = "$1" ] && { i=$((i+1)); }; done; echo "$i"
}
##
# @brief Maps every element of the list
# @usage list_maps <func> <lst>
# @print The list result
#
list_map () {
test "$#" -ne 2 && return 1; for e in $2; do eval "$1 $e"; done
}
##
# @brief Reverses the list
# @usage list_reverse <lst>
# @print The list result
#
list_reverse() {
test "$#" -ne 1 && return 1; echo "$1" | sed '1!x;H;1h;$!d;g'
}
##
# @brief Sorts the list
# @usage list_sort <lst>
# @print The list result
#
list_sort () {
test "$#" -ne 1 && return 1; echo "$1" | sort -n
}
##
# @brief Sorts and reverses the sense of the list
# @usage list_sort_reverse <lst>
# @print The list result
#
list_sort_reverse () {
test "$#" -ne 1 && return 1; echo "$1" | sort -nr
}

286
libshlist/liblist_unsafe.sh

@ -0,0 +1,286 @@
#!/bin/sh
#
# The MIT License (MIT)
#
# Copyright (c) 2017-2018 Thomas "Ventto" Venriès <thomas.venries@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
##
# @brief Prints a list from arguments
# @usage list <elt> <elt> ...
# @print The list result
#
list () {
for e; do [ -n "$e" ] && echo "$e"; done
}
##
# @brief Prints the number of elements in the list
# @usage list_size <lst>
# @print The size of the list as positive integer
#
list_size () {
test "$#" -ne 1 && { echo "0"; return; }
eval "test -n \"\$$1\"" && { eval "echo \"\$$1\" | wc -l"; return; }
echo '0'
}
##
# @brief Returns whether the list is empty(1) or not(0)
# @usage list_empty <lst>
# @return 0 or 1
#
list_empty () {
eval "test -z \"\$$1\""
}
##
# @brief Adds a new element at the beginning of the list
# @usage list_push_front <lst> <elt>
# @out Set the variable passed by reference
#
list_push_front () {
test "$#" -ne 2 && return 1
eval "test -n \"\$$1\"" || { eval "$1='$2'"; return; }
eval "$1=\"\$(printf '%s\n%s' '$2' \"\$$1\")\"";
}
##
# @brief Adds a new element at the end of the list
# @usage list_push_back <lst> <elt>
# @out Set the variable passed by reference
#
list_push_back () {
test "$#" -ne 2 && return 1
eval "test -n \"\$$1\"" || { eval "$1='$2'"; return; }
eval "test -n \"\$$1\"" || { eval "$1='$2'"; return; }
eval "$1=\"\$(printf '%s\n%s' \"\$$1\" '$2')\"";
}
##
# @brief Inserts new elements in the list before a specified position
# @usage list_insert <lst> <elt> <index>
# @out Set the variable passed by reference
#
list_insert () {
test "$#" -ne 3 && return 1; i="$3"; [ "$i" != '$' ] && i=$((i+1))
eval "$1=\"\$(echo \"\$$1\" | sed \"${i}i${2}\")\""
}
##
# @brief Modifies an element from the list at a specified position
# @usage list_set <lst> <elt> <index>
# @out Set the variable passed by reference
#
list_set () {
test "$#" -ne 3 && return 1
i="$3"; i=$((i+1)); eval "$1=\"\$(echo \"\$$1\" | sed \"${i}s/.*/$2/\")\""
}
##
# @brief Extracts a range of elements from the list between two specified
# positions
# @usage list_extract <lst> <from_index> <to_index>
# @out Set the variable passed by reference
#
list_extract () {
test "$#" -ne 3 && return 1; i="$2"; j="$3"; i=$((i+1)); j=$((j+1))
eval "$1=\"\$(echo \"\$$1\" | sed -n \"${i},${j}p\")\""
}
##
# @brief Replaces all elements from the list with a specified element
# @usage list_replace <lst> <old_elt> <new_elt>
# @out Set the variable passed by reference
#
list_replace () {
test "$#" -ne 3 && return 1
eval "$1=\"\$(echo \"\$$1\" | sed -e \"s/^$2$/$3/g\")\""
}
##
# @brief Prints the element at a specified position
# @usage list_get <lst> <index>
# @print The element found
#
list_get () {
test "$#" -ne 2 && return 1; i="$2"; i=$((i+1));
eval "echo \"\$$1\" | sed -n \"${i}p\""
}
##
# @brief Prints the head of the list
# @usage list_front <lst>
# @print The element found
#
list_front () {
test "$#" -ne 1 && return 1; eval "echo \"\$$1\" | sed -n '1p'"
}
##
# @brief Prints the queue of the list
# @usage list_back <lst>
# @print The element found
#
list_back () {
test "$#" -ne 1 && return 1; eval "echo \"\$$1\" | sed -n '\$p'"
}
##
# @brief Removes the first-hit element from a list
# @usage list_erase <lst> <elt>
# @out Set the variable passed by reference
#
list_erase () {
test "$#" -ne 2 && return 1
eval "$1=\"\$(echo \"\$$1\" | sed -e \"0,/^$2$/ s///\" -e '/^$/d')\""
}
##
# @brief Removes a range of elements from a list between two specified
# positions
# @usage list_erase_range <lst> <from_index> <to_index>
# @out Set the variable passed by reference
#
list_erase_range () {
test "$#" -ne 3 && return 1
i="$2"; j="$3"; i=$((i+1)); j=$((j+1));
eval "$1=\"\$(echo \"\$$1\" | sed \"${i},${j}d\")\""
}
##
# @brief Removes all elements from a specified position
# @usage list_erase_from <lst> <index>
# @out Set the variable passed by reference
#
list_erase_from () {
test "$#" -ne 2 && return 1
i="$2"; i=$((i+1)); eval "$1=\"\$(echo \"\$$1\" | sed \"${i},\\\$d\")\""
}
##
# @brief Removes the element at a specified position
# @usage list_eraseat <lst> <index>
# @out Set the variable passed by reference
#
list_eraseat () {
test "$#" -ne 2 && return 1
i="$2"; i=$((i+1)); eval "$1=\"\$(echo \"\$$1\" | sed \"${i}d\")\""
}
##
# @brief Removes all the elements from the list, which are equal to given
# element
# @usage list_remove <lst> <elt>
# @out Set the variable passed by reference
#
list_remove () {
test "$#" -ne 2 && return 1
eval "$1=\"\$(echo \"\$$1\" | sed -e \"/^$2$/d\")\""
}
##
# @brief Removes the first element of the list
# @usage list_pop_front <lst>
# @out Set the variable passed by reference
#
list_pop_front () {
test "$#" -ne 1 && return 1; eval "$1=\"\$(echo \"\$$1\" | sed '1d')\""
}
##
# @brief Removes the last element of the list
# @usage list_pop_back <lst>
# @out Set the variable passed by reference
#
list_pop_back () {
test "$#" -ne 1 && return 1; eval "$1=\"\$(echo \"\$$1\" | sed '\$d')\""
}
##
# @brief Prints the index of a specified element
# @usage list_indexof <lst> <elt>
# @print positive integer or -1 if not found
#
list_indexof () {
test "$#" -ne 2 && return 1; i=0
eval "for e in \$$1; do
[ \"\$e\" = '$2' ] && { echo \"\$i\"; return 0; }; i=\$((i+1));
done"
return 1
}
##
# @brief Returns whether the list contains a specified element(0) or not(1)
# @usage list_contains <lst> <elt>
# @return 0 or 1
#
list_contains () {
test "$#" -ne 2 && return 1
eval "for e in \$$1; do [ \"\$e\" = '$2' ] && return 0; done; return 1"
}
##
# @brief Prints the number of a specified element in the list
# @usage list_count <lst> <elt>
# @print The number of elements as positive integer
#
list_count () {
test "$#" -ne 2 && { echo '0'; return; }
eval "i=0; for e in \$$1; do [ \"\$e\" = '$2' ] && { i=\$((i+1)); };done;"
echo "$i"
}
##
# @brief Maps every element of the list
# @usage list_maps <lst> <func>
# @out Set the variable passed by reference
#
list_map () {
test "$#" -ne 2 && return 1
eval "$1=\"\$(for e in \$$1; do eval \"$2 \$e\"; done)\""
}
##
# @brief Reverses the list
# @usage list_reverse <lst>
# @out Set the variable passed by reference
#
list_reverse() {
test "$#" -ne 1 && return 1
eval "$1=\"\$(echo \"\$$1\" | sed '1!x;H;1h;\$!d;g')\""
}
##
# @brief Sorts the list
# @usage list_sort <lst>
# @out Set the variable passed by reference
#
list_sort () {
test "$#" -ne 1 && return 1; eval "$1=\"\$(echo \"\$$1\" | sort -n)\""
}
##
# @brief Sorts and reverses the sense of the list
# @usage list_sort_reverse <lst>
# @out Set the variable passed by reference
#
list_sort_reverse () {
test "$#" -ne 1 && return 1; eval "$1=\"\$(echo \"\$$1\" | sort -nr)\""
}

85
libshlist/test/test.sh

@ -0,0 +1,85 @@
#!/bin/sh
. ./liblist.sh
print_list () {
echo '==========List============'
printf "%s\n" "$1"
echo '--------------------------'
printf "Size: %s\t\n\n" "$(list_size "$1")"
}
Test_Delete () {
lst="$(list '1' '12' '23' \
'33' '215' '-456' \
'1236' '1' '12' \
'3' '-3' '33' \
'1' '12' '-55' \
'123' '-1002' '-1' )"
printf "TEST: Deletion\n\n"
echo 'test: Initialization'; print_list "$lst"
lst="$(list_remove '1' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_erase '33' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_eraseat '3' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_pop_front "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_pop_back "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_erase_from '8' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_erase_range '1' '2' "$lst")"; echo 'test:' ; print_list "$lst"
lst="$(list_erase_range '0' '0' "$lst")"; echo 'test:' ; print_list "$lst"
lst="$(list_extract '1' '3' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_extract '1' '30' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_extract '1' '0' "$lst")" ; echo 'test:' ; print_list "$lst"
}
Test_Addition () {
lst=
printf "TEST: Addition\n\n"
echo 'test: Initialization'; print_list "$lst"
lst="$(list_push_front '12' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_push_back '33' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_push_front '1' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_insert '23' '2' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_push_back '215' "$lst")" ; echo 'test:' ; print_list "$lst"
}
inc () { i="$1"; i=$((i+1)); echo "${i}"; }
Test_Set() {
lst="$(list '1' '12' '23' \
'33' '215' '-456' \
'1236' '1' '12' )"
printf "TEST: Set\n\n"
echo 'test: Initialization'; print_list "$lst"
lst="$(list_reverse "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_sort "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_sort_reverse "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_map inc "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_replace '2' '999' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_set '999' '3' "$lst")" ; echo 'test:' ; print_list "$lst"
lst="$(list_set '999' '6' "$lst")" ; echo 'test:' ; print_list "$lst"
}
Test_Get() {
lst="$(list '1' '12' '23' \
'33' '215' '-456' \
'1236' '1' '12' )"
printf "TEST: Get\n\n"
echo 'test: Initialization'; print_list "$lst"
printf 'test: elt=' ; list_front "$lst"
printf 'test: elt=' ; list_back "$lst"
printf 'test: elt=' ; list_get '2' "$lst"
printf 'test: index=' ; list_indexof '1' "$lst" | grep -E '^[0-9]+$' || echo
printf 'test: index=' ; list_indexof '-456' "$lst" | grep -E '^[0-9]+$' || echo
printf 'test: contains='; list_contains '1236' "$lst" && echo 'yes' || echo 'no'
printf 'test: contains='; list_contains '999' "$lst" && echo 'yes' || echo 'no'
printf 'test: count=' ; list_count '1' "$lst"
printf 'test: count=' ; list_count '215' "$lst"
printf 'test: empty=' ; list_empty "$lst" && echo 'yes' || echo 'no'
}

210
libshlist/test/test_unsafe.sh

@ -0,0 +1,210 @@
#!/bin/sh
. ./liblist_unsafe.sh
print_list () {
echo '==========List============'
eval "printf \"%s\\n\" \"\$$1\""
echo '--------------------------'
eval "printf \"Size: %s\t\n\n\" \"\$(list_size $1)\""
}
Test_Delete () {
lst="$(list '1' '12' '23' \
'33' '215' '-456' \
'1236' '1' '12' \
'3' '-3' '33' \
'1' '12' '-55' \
'123' '-1002' '-1' )"
printf "TEST: Deletion\n\n"
echo 'test: Initialization'; print_list lst
list_remove lst '1' ; echo 'test:' ; print_list lst
list_erase lst '33' ; echo 'test:' ; print_list lst
list_eraseat lst 3 ; echo 'test:' ; print_list lst
list_pop_front lst ; echo 'test:' ; print_list lst
list_pop_back lst ; echo 'test:' ; print_list lst
list_erase_from lst 8 ; echo 'test:' ; print_list lst
list_erase_range lst 1 2 ; echo 'test:' ; print_list lst
list_erase_range lst 0 0 ; echo 'test:' ; print_list lst
list_extract lst 1 3 ; echo 'test:' ; print_list lst
list_extract lst 1 30 ; echo 'test:' ; print_list lst
list_extract lst 1 0 ; echo 'test:' ; print_list lst
}
Test_Addition () {
lst=
printf "TEST: Addition\n\n"
echo 'test: Initialization'; print_list lst
list_push_front lst '12' ; echo 'test:' ; print_list lst
list_push_back lst '33' ; echo 'test:' ; print_list lst
list_push_front lst '1' ; echo 'test:' ; print_list lst
list_insert lst '23' 2 ; echo 'test:' ; print_list lst
list_push_back lst '215' ; echo 'test:' ; print_list lst
}
inc () { i="$1"; i=$((i+1)); echo "${i}"; }
Test_Set() {
lst="$(list '1' '12' '23' \
'33' '215' '-456' \
'1236' '1' '12' )"
printf "TEST: Set\n\n"
echo 'test: Initialization'; print_list lst
list_reverse lst ; echo 'test:' ; print_list lst
list_sort lst ; echo 'test:' ; print_list lst
list_sort_reverse lst ; echo 'test:' ; print_list lst
list_map lst inc ; echo 'test:' ; print_list lst
list_replace lst '2' '999' ; echo 'test:' ; print_list lst
list_set lst '999' 3 ; echo 'test:' ; print_list lst
list_set lst '999' 6 ; echo 'test:' ; print_list lst
}
Test_Get() {
lst="$(list '1' '12' '23' \
'33' '215' '-456' \
'1236' '1' '12' )"
printf "TEST: Set\n\n"
echo 'test: Initialization'; print_list lst
printf 'test: elt=' ; list_front lst
printf 'test: elt=' ; list_back lst
printf 'test: elt=' ; list_get lst 2
printf 'test: index=' ; list_indexof lst '1' | grep -E '^[0-9]+$' || echo
printf 'test: index=' ; list_indexof lst '-456' | grep -E '^[0-9]+$' || echo
printf 'test: contains='; list_contains lst '1236' && echo 'yes' || echo 'no'
printf 'test: contains='; list_contains lst '2' && echo 'yes' || echo 'no'
printf 'test: count=' ; list_count lst '1'
printf 'test: count=' ; list_count lst '215'
printf 'test: empty=' ; list_empty lst && echo 'yes' || echo 'no'
}
Test_Void() {
lst="$(list '' '' '')"
printf "TEST: Void\n\n"
echo 'test: Initialization'; print_list lst
printf 'test (void): empty=' ; list_empty lst && echo 'yes' || echo 'no'
printf 'test (void): size=' ; list_size lst
list_reverse lst ; echo 'test:' ; print_list lst
list_sort lst ; echo 'test:' ; print_list lst
list_sort_reverse lst ; echo 'test:' ; print_list lst
list_replace lst '2' '999' ; echo 'test:' ; print_list lst
list_set lst '999' 3 ; echo 'test:' ; print_list lst
list_set lst '999' 6 ; echo 'test:' ; print_list lst
list_map lst inc ; echo 'test:' ; print_list lst
list_remove lst '1' ; echo 'test:' ; print_list lst
list_erase lst '33' ; echo 'test:' ; print_list lst
list_eraseat lst 3 ; echo 'test:' ; print_list lst
list_pop_front lst ; echo 'test:' ; print_list lst
list_pop_back lst ; echo 'test:' ; print_list lst
list_erase_from lst 8 ; echo 'test:' ; print_list lst
list_erase_range lst 1 2 ; echo 'test:' ; print_list lst
list_erase_range lst 0 0 ; echo 'test:' ; print_list lst
list_extract lst 1 3 ; echo 'test:' ; print_list lst
list_extract lst 1 30 ; echo 'test:' ; print_list lst
list_extract lst 1 0 ; echo 'test:' ; print_list lst
list_push_front lst '2' ; echo 'test:' ; print_list lst
list_push_back lst '4' ; echo 'test:' ; print_list lst
list_push_front lst '1' ; echo 'test:' ; print_list lst
list_insert lst '3' 2 ; echo 'test:' ; print_list lst
list_push_back lst '5' ; echo 'test:' ; print_list lst
}
Test_BadArg() {
lst="$1"
printf "TEST: BadArg\n\n"
echo 'test: Initialization'; print_list lst
list_push_front lst ; echo 'test:' ; print_list lst
list_push_front '' ; echo 'test:' ; print_list lst
list_push_front ; echo 'test:' ; print_list lst
list_push_back lst ; echo 'test:' ; print_list lst
list_push_back '' ; echo 'test:' ; print_list lst
list_push_back ; echo 'test:' ; print_list lst
list_insert lst '' ; echo 'test:' ; print_list lst
list_insert lst ; echo 'test:' ; print_list lst
list_insert 2 ; echo 'test:' ; print_list lst
list_reverse ; echo 'test:' ; print_list lst
list_sort ; echo 'test:' ; print_list lst
list_sort_reverse ; echo 'test:' ; print_list lst
list_replace lst '2' ; echo 'test:' ; print_list lst
list_replace lst ; echo 'test:' ; print_list lst
list_replace ; echo 'test:' ; print_list lst
list_set lst '999' ; echo 'test:' ; print_list lst
list_set lst ; echo 'test:' ; print_list lst
list_set ; echo 'test:' ; print_list lst
list_map lst ; echo 'test:' ; print_list lst
list_map inc ; echo 'test:' ; print_list lst
list_map ; echo 'test:' ; print_list lst
list_remove lst ; echo 'test:' ; print_list lst
list_remove '1' ; echo 'test:' ; print_list lst
list_remove ; echo 'test:' ; print_list lst
list_erase lst ; echo 'test:' ; print_list lst
list_erase '33' ; echo 'test:' ; print_list lst
list_erase ; echo 'test:' ; print_list lst
list_eraseat lst ; echo 'test:' ; print_list lst
list_eraseat 3 ; echo 'test:' ; print_list lst
list_eraseat ; echo 'test:' ; print_list lst
list_pop_front ; echo 'test:' ; print_list lst
list_pop_back ; echo 'test:' ; print_list lst
list_erase_from lst ; echo 'test:' ; print_list lst
list_erase_from 8 ; echo 'test:' ; print_list lst
list_erase_from ; echo 'test:' ; print_list lst
list_erase_range lst 0 ; echo 'test:' ; print_list lst
list_erase_range lst ; echo 'test:' ; print_list lst
list_erase_range ; echo 'test:' ; print_list lst
list_extract 0 1 ; echo 'test:' ; print_list lst
list_extract lst 1 ; echo 'test:' ; print_list lst
list_extract lst ; echo 'test:' ; print_list lst
list_extract ; echo 'test:' ; print_list lst
printf 'test: elt=' ; list_front || echo
printf 'test: elt=' ; list_back || echo
printf 'test: elt=' ; list_get lst || echo
printf 'test: elt=' ; list_get 2 || echo
printf 'test: elt=' ; list_get || echo
printf 'test: index=' ; list_indexof lst | grep -E '^[0-9]+$' || echo
printf 'test: index=' ; list_indexof '-456' | grep -E '^[0-9]+$' || echo
printf 'test: index=' ; list_indexof | grep -E '^[0-9]+$' || echo
printf 'test: contains='; list_contains lst && echo 'yes' || echo 'no'
printf 'test: contains='; list_contains '2' && echo 'yes' || echo 'no'
printf 'test: contains='; list_contains && echo 'yes' || echo 'no'
printf 'test: count=' ; list_count lst
printf 'test: count=' ; list_count '215'
printf 'test: count=' ; list_count
printf 'test: empty=' ; list_empty && echo 'yes' || echo 'no'
}
Test_VoidBadArg() {
Test_BadArg "$(list '' '' '' '')"
}
Test_WithBadArg() {
Test_BadArg "$(list 1 2 3)"
}

448
mons

@ -0,0 +1,448 @@
#!/bin/sh
#
# The MIT License (MIT)
#
# Copyright (c) 2017-2018 Thomas "Ventto" Venriès <thomas.venries@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
usage() {
echo 'Usage: mons [OPTION]...
Without argument, it prints connected monitors list with their names and ids.
Options are exclusive and can be used in conjunction with extra options.
Information:
-h Prints this help and exits.
-v Prints version and exits.
Two monitors:
-o Primary monitor only.
-s Second monitor only.
-d Duplicates the primary monitor.
-m Mirrors the primary monitor.
-e <side>
Extends the primary monitor to the selected side
[ top | left | right | bottom ].
-n <side>
This mode selects the previous ones, one after another. The argument
sets the side for the extend mode.
More monitors:
-O <mon>
Only enables the monitor with a specified id.
-S <mon1>,<mon2>:<pos>
Only enables two monitors with specified ids. The specified position
places the second monitor on the right (R) or at the top (T).
Extra (in-conjunction or alone):
--dpi <dpi>
Set the DPI, a strictly positive value within the range [0 ; 27432].
--primary <mon_name>
Select a connected monitor as the primary output. Run the script
without argument to print monitors information, the names are in the
second column between ids and status. The primary monitor is marked
by an asterisk.
Daemon mode:
-a Performs an automatic display if it detects only one monitor.
'
}
version() {
echo 'Mons 0.8.2
Copyright (C) 2017 Thomas "Ventto" Venries.
License MIT: <https://opensource.org/licenses/MIT>.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
'
}
# Helps to generate manpage with help2man before installing the library
[ "$1" = '-h' ] && { usage; exit; }
[ "$1" = '-v' ] && { version; exit; }
lib='/usr/lib/libshlist/liblist.sh'
[ ! -r "$lib" ] && { "$lib: library not found."; exit 1; }
. "${lib}"
arg_err() {
usage ; exit 2
}
enable_mon() {
"${XRANDR}" --output "${1}" --auto --dpi "${dpi}"
}
disable_mons() {
for mon in $@; do "${XRANDR}" --output "${mon}" --off ; done
}
arg2xrandr() {
case $1 in
left) echo '--left-of' ;;
right) echo '--right-of' ;;
bottom) echo '--below' ;;
top) echo '--above' ;;
esac
}
whichmode() {
if [ "$(list_size "${disp_mons}")" -eq 1 ]; then
if echo "${enabled_out}" | grep prima > /dev/null 2>&1; then
echo 'primary'
else
echo 'second'
fi
else
if [ "$(list_size "${plug_mons}")" -gt 2 ] ; then
echo 'selection'; return 0
fi
enabled_out="$(echo "${enabled_out}" | \
sed 's/^.*\( [0-9]\+\x[0-9]\++[0-9]\++[0-9]\+\).*/\1/')"
echo "${enabled_out}" | head -n1 | sed -e 's/+/ /g' | \
while read -r trash x1 y1; do
echo "${enabled_out}" | tail -n1 | sed -e 's/x/ /' -e 's/+/ /g' | \
while read -r w2 h2 x2 y2; do
echo "${xrandr_out}" | \
awk "/^$(list_get 1 "${plug_mons}")/{nr[NR+1]}; NR in nr" | \
awk '{print $1;}' | sed -e 's/x/ /' | \
while read -r wi2 hi2; do
if [ "$x1" = "$x2" ] && [ "$y1" = "$y2" ]; then
if [ "$w2" != "$wi2" ] || [ "$h2" != "$hi2" ]; then
echo 'mirror'
else
echo 'duplicate'
fi
else
echo 'extend'
fi
done
done
done
fi
}
main() {
aFlag=false
dFlag=false
eFlag=false
mFlag=false
nFlag=false
oFlag=false
sFlag=false
OFlag=false
SFlag=false
pFlag=false
iFlag=false
is_flag=false
# X has assumed 96 DPI and this is fine for many traditional monitors.
dpi=96
primary=
# getopts does not support long options. We convert them to short one.
for arg in "$@"; do
shift
case "$arg" in
--dpi) set -- "$@" '-i' ;;
--primary) set -- "$@" '-p' ;;
*) set -- "$@" "$arg"
esac
done
while getopts 'hvamosde:n:O:S:i:p:' opt; do
case $opt in
# Long options
i)
if ! echo "${OPTARG}" | \
grep -E '^[1-9][0-9]*$' > /dev/null 2>&1; then
arg_err
fi
iFlag=true; dpi="$OPTARG"
;;
p) if ! echo "${OPTARG}" | \
grep -E '^[a-zA-Z][a-zA-Z0-9\-]+' > /dev/null 2>&1; then
arg_err
fi
pFlag=true; primary="$OPTARG"
;;
# Short options
a) $is_flag && arg_err
aFlag=true ; is_flag=true
;;
m) $is_flag && arg_err
mFlag=true ; is_flag=true
;;
o) $is_flag && arg_err
oFlag=true ; is_flag=true
;;
s) $is_flag && arg_err
sFlag=true ; is_flag=true
;;
d) $is_flag && arg_err
dFlag=true ; is_flag=true
;;
e|n) $is_flag && arg_err
case ${OPTARG} in
left | right | bottom | top) ;;
*) arg_err ;;
esac
eArg=$OPTARG
[ "$opt" = "e" ] && eFlag=true || nFlag=true ; is_flag=true
;;
O) $is_flag && arg_err
! echo "${OPTARG}" | grep -E '^[0-9]+$' > /dev/null && arg_err
OArg=$OPTARG
OFlag=true ; is_flag=true
;;
S) $is_flag && arg_err
idx1="$(echo "${OPTARG}" | cut -d',' -f1)"
idx2="$(echo "${OPTARG}" | cut -d',' -f2)"
area="$(echo "${idx2}" | cut -d ':' -f2)"
idx2="$(echo "${idx2}" | cut -d ':' -f1)"
! echo "${idx1}" | grep -E '^[0-9]+$' > /dev/null && arg_err
! echo "${idx2}" | grep -E '^[0-9]+$' > /dev/null && arg_err
! echo "${area}" | grep -E '^[RT]$' > /dev/null && arg_err
[ "${idx1}" = "${idx2}" ] && arg_err
SFlag=true ; is_flag=true
;;
h) usage ; exit ;;
v) version ; exit ;;
\?) arg_err ;;
:) arg_err ;;
esac
done
[ -z "${DISPLAY}" ] && { echo 'DISPLAY: no variable set.'; exit 1; }
XRANDR="$(command -v xrandr)"
[ "$?" -ne 0 ] && { echo 'xrandr: command not found.'; exit 1; }
# DPI set
$iFlag && [ "$#" -eq 2 ] && { "${XRANDR}" --dpi "$dpi"; exit; }
# Daemon mode
if $aFlag ; then
prev=0; i=0
while true; do
for status in /sys/class/drm/*/status; do
[ "$(<"$status")" = 'connected' ] && i=$((i+1))
done
if [ "$i" -eq 1 ] && [ "$i" != "$prev" ]; then
"${XRANDR}" --auto --dpi "${dpi}"
fi
prev="$i"; i=0
sleep 2
done
fi
# List all outputs (except primary one)
xrandr_out="$("${XRANDR}")"
enabled_out="$(echo "${xrandr_out}" | grep 'connect')"
[ -z "${enabled_out}" ] && { echo 'No monitor output detected.'; exit; }
mons="$(echo "${enabled_out}" | cut -d' ' -f1)"
# List plugged-in and turned-on outputs
enabled_out="$(echo "${enabled_out}" | grep ' connect')"
[ -z "${enabled_out}" ] && { echo 'No plugged-in monitor detected.'; exit 1; }
plug_mons="$(echo "${enabled_out}" | cut -d' ' -f1)"
if [ "$(list_size "${plug_mons}")" -eq 0 ]; then
echo "No monitor plugged-in."
exit 0
fi
# Set primary output
if $pFlag; then
if ! list_contains "${primary}" "${plug_mons}"; then
echo "${primary}: output not connected."
exit 1
fi
"${XRANDR}" --output "${primary}" --primary
[ "$#" -eq 2 ] && exit
else
primary="$(echo "${enabled_out}" | grep 'primary' | cut -d' ' -f1)"
fi
# Move the primary monitor to the head if connected otherwise the first
# connected monitor that appears in the xrandr output is considerate as
# the primary one.
if [ -n "${primary}" ]; then
plug_mons="$(list_erase "${primary}" "${plug_mons}")"
plug_mons="$(list_insert "${primary}" 0 "${plug_mons}")"
fi
enabled_out="$(echo "${enabled_out}" | grep -E '\+[0-9]{1,4}\+[0-9]{1,4}')"
disp_mons="$(echo "${enabled_out}" | cut -d' ' -f1)"
if [ "$#" -eq 0 ]; then
echo "Monitors: $(list_size "${plug_mons}")"
echo "Mode: $(whichmode)"
i=0
for mon in ${mons}; do
if echo "${plug_mons}" | grep "^${mon}$" > /dev/null; then
if echo "${disp_mons}" | grep "^${mon}$" > /dev/null; then
state='(enabled)'
fi
if [ "${mon}" = "${primary}" ]; then
printf '%-4s %-8s %-8s %-8s\n' "${i}:*" "${mon}" "${state}"
else
printf '%-4s %-8s %-8s\n' "${i}:" "${mon}" "${state}"
fi
fi
i=$((i+1))
state=
done
exit
fi
if $nFlag ; then
case "$(whichmode)" in
primary) sFlag=true;;
second) eFlag=true;;
extend) mFlag=true;;
mirror) dFlag=true;;
duplicate) oFlag=true;;
esac
fi
if [ "$(list_size "${plug_mons}")" -eq 1 ] ; then
if $oFlag ; then
# After unplugging each monitor, the last preferred one might be
# still turned off or the window manager might need the monitor
# reset to cause the reconfiguration of the layout placement.
"${XRANDR}" --auto --dpi "${dpi}"
else
echo 'Only one monitor detected.'
fi
exit
fi
if $oFlag ; then
if [ "$(list_size "${disp_mons}")" -eq 1 ]; then
if [ "$(list_front "${disp_mons}")" = "$(list_front "${plug_mons}")" ]; then
exit
fi
fi
disp_mons="$(list_erase "$(list_front "${plug_mons}")" "$disp_mons")"
disable_mons "${disp_mons}"
enable_mon "$(list_front "${plug_mons}")"
exit
fi
if $OFlag ; then
if [ "${OArg}" -ge "$(list_size "${mons}")" ] ; then
echo "Monitor ID '${OArg}' does not exist."
echo 'Try without option to get monitor ID list.'
exit 2
fi
mons_elt="$(list_get "${OArg}" "${mons}")"
if ! list_contains "${mons_elt}" "${plug_mons}"; then
echo "Monitor ID '${OArg}' not plugged in."
echo 'Try without option to get monitor ID list.'
exit 2
fi
disp_mons="$(list_erase "${mons_elt}" "${disp_mons}")"
disable_mons "${disp_mons}"
enable_mon "${mons_elt}"
exit
fi
if $SFlag ; then
if [ "${idx1}" -ge "$(list_size "${mons}")" ] || \
[ "${idx2}" -ge "$(list_size "${mons}")" ]; then
echo 'One or both monitor IDs do not exist.'
echo 'Try without option to get monitor ID list.'
exit 2
fi
if ! list_contains "$(list_get "${idx1}" "${mons}")" "${plug_mons}" || \
! list_contains "$(list_get "${idx2}" "${mons}")" "${plug_mons}" ; then
echo 'One or both monitor IDs are not plugged in.'
echo 'Try without option to get monitor ID list.'
exit 2
fi
[ "${area}" = 'R' ] && area="--right-of" || area="--above"
mon1="$(list_get "${idx1}" "${mons}")"
mon2="$(list_get "${idx2}" "${mons}")"
disp_mons="$(list_erase "${mon1}" "${disp_mons}")"
disp_mons="$(list_erase "${mon2}" "${disp_mons}")"
disable_mons "${disp_mons}"
enable_mon "${mon1}"
enable_mon "${mon2}"
"${XRANDR}" --output "${mon2}" "${area}" "${mon1}"
exit
fi
if [ "$(list_size "${plug_mons}")" -eq 2 ]; then
if $sFlag ; then
if [ "$(list_size "${disp_mons}")" -eq 1 ] ; then
if [ "$(list_front "${disp_mons}")" = "$(list_get 1 "${plug_mons}")" ] ; then
enable_mon "$(list_get 1 "${plug_mons}")"
exit
fi
fi
enable_mon "$(list_get 1 "${plug_mons}")"
disable_mons "$(list_front "${disp_mons}")"
exit
fi
# Resets the screen configuration
disable_mons "$(list_get 1 "${plug_mons}")"
"${XRANDR}" --auto --dpi "${dpi}"
if $dFlag ; then
"${XRANDR}" --output "$(list_get 1 "${plug_mons}")" \
--same-as "$(list_front "${plug_mons}")"