Signed-off-by: Marcin Woźniak <y0rune@aol.com>
This commit is contained in:
Marcin Woźniak 2020-09-14 11:00:29 +02:00
commit 7b2650b646
Signed by: y0rune
GPG Key ID: F204C385F57EB348
19 changed files with 1905 additions and 0 deletions

57
CONTRIBUTING.md Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

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

BIN
img/raw-body.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

20
libshlist/LICENSE Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

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

271
libshlist/liblist.sh Normal file
View File

@ -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 Normal file
View File

@ -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 Normal file
View File

@ -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'
}

View File

@ -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 Normal file
View File

@ -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}")"
exit $?
fi
if $mFlag ; then
xrandr_out="$(echo "${xrandr_out}" | \
awk "/primary/{nr[NR]; nr[NR+1]}; NR in nr")"
if echo "${xrandr_out}" | \
grep -E 'primary [0-9]+x[0-9]+' >/dev/null 2>&1; then
size="$(echo "${xrandr_out}" | head -n1 | cut -d' ' -f4 | \
cut -d'+' -f1)"
else
size="$(echo "${xrandr_out}" | tail -n1 | awk '{ print $1 }')"
fi
"${XRANDR}" --output "$(list_get 1 "${plug_mons}")" \
--auto --scale-from "${size}" \
--output "$(list_front "${plug_mons}")"
exit $?
fi
if $eFlag ; then
"${XRANDR}" --output "$(list_get 1 "${plug_mons}")" \
"$(arg2xrandr "$eArg")" "$(list_front "${plug_mons}")"
exit $?
fi
else
echo 'At most two plugged monitors for this option.'
fi
}
main "$@"

BIN
mons.1.gz Normal file

Binary file not shown.

77
test/test-args.sh Normal file
View File

@ -0,0 +1,77 @@
#!/bin/sh
if [ "$#" -ne 3 ]; then
echo 'Usage: test-args.sh MON_ID1 MON_ID2 MON_ID3'
echo
echo 'Three monitor ids are required. Ids are given by `mons`.'
exit 1
fi
echo 'TEST: Bad Arguments'
echo '#=========================#'
./mons
echo '#=========================#'
echo 'test: common'
n=1
./mons --s -o >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --o >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -e fekl56 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -e 2 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -e pot >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -d 2 >/dev/null 2>&1 || echo "failed: $n" ; n=$((n+1))
./mons -m 2 >/dev/null 2>&1 || echo "failed: $n" ; n=$((n+1))
echo 'test: single selection'
n=1
./mons -O 5 "$1" >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -O abc "$2" >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --O "$3" >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -O >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
echo 'test: multi selection'
n=1
./mons -S >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -S "$1,$2:P" >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -S "$3,$2:5" >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -S "$2:R" >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -S "$3,$1" >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -S "A,B:R" >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
echo 'test: dpi'
n=1
./mons --dpi >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --dpi A >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --dpi 0 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --dpi -0 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --dpi -5 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --dpi -3000 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --dpi -s 1 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
# Weird configuration
./mons -e --dpi 96 left >/dev/null 2>&1 || echo "failed: $n" ; n=$((n+1))
./mons -n --dpi 72 right >/dev/null 2>&1 || echo "failed: $n" ; n=$((n+1))
echo 'test: primary'
n=1
./mons --primary >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary -HDMI1 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary -HDMI1 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary 1HDMI >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary -s 1 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -n --primary HDMI1 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -n --primary HDMI1 left >/dev/null 2>&1 || echo "failed: $n" ; n=$((n+1))
./mons -n --primary HDMI1 left -s >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
echo 'test: dpi + primary'
n=1
./mons --primary --dpi >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary --dpi 75 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary HDMI1 --dpi >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary HDMI1 --dpi 0 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary 1HDMI --dpi 0 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary HDMI1 --dpi HDMI1 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons --primary HDMI1 --dpi 75 >/dev/null 2>&1 && echo "failed: $n" ; n=$((n+1))
./mons -n --primary HDMI1 --dpi 75 left >/dev/null 2>&1 || echo "failed: $n" ; n=$((n+1))

36
test/test-common.sh Normal file
View File

@ -0,0 +1,36 @@
#!/bin/sh
if [ "$#" -ne 2 ]; then
echo 'Usage: test-common.sh CURR_MON_NAME MON_NAME'
echo
echo 'Monitor names are required. Names are given by "mons".
CURR_MON_NAME is your current monitor and the second one is reserved
for primary test.'
exit 1
fi
Test_Common () {
echo 'TEST: Common Modes'
echo '#=========================#'
./mons
echo '#=========================#'
./mons -o ; echo "test: primary" ; sleep 8
./mons -s ; echo "test: second" ; sleep 8
./mons -e left ; echo "test: extend left" ; sleep 8
./mons -d ; echo "test: duplicate" ; sleep 8
./mons -m ; echo "test: mirror" ; sleep 8
./mons -e right ; echo "test: extend right" ; sleep 8
./mons -e top ; echo "test: extend top" ; sleep 8
./mons -e bottom ; echo "test: extend bottom" ; sleep 8
./mons -o ; echo "test: primary" ; sleep 8
./mons -s ; echo "test: second" ; sleep 8
./mons -o ; echo "test: primary"
}
./mons -o --primary "$1"
Test_Common
./mons -o --primary "$2"
echo "[ Primary has been set: $1 ]"
Test_Common
./mons -o --primary "$1"

24
test/test-select.sh Normal file
View File

@ -0,0 +1,24 @@
#!/bin/sh
if [ "$#" -ne 3 ]; then
echo 'Usage: test-select.sh MON_ID1 MON_ID2 MON_ID3'
echo
echo 'Three monitor ids are required. Ids are given by `mons`.'
exit 1
fi
echo 'TEST: Selections'
echo '#=========================#'
./mons
echo '#=========================#'
./mons -O "$1" ; echo "test: single select $1" ; sleep 8
./mons -O "$2" ; echo "test: single select $2" ; sleep 8
./mons -O "$3" ; echo "test: single select $3" ; sleep 8
./mons -O "$1" ; echo "test: single select $1" ; sleep 8
./mons -S "$1,$2:R" ; echo "test: single select $1,$2:R" ; sleep 8
./mons -S "$1,$2:T" ; echo "test: multi select $1,$2:T" ; sleep 8
./mons -S "$3,$2:T" ; echo "test: multi select $3,$2:T" ; sleep 8
./mons -S "$2,$3:R" ; echo "test: multi select $2,$3:R" ; sleep 8
./mons -S "$3,$1:T" ; echo "test: multi select $3,$1:T" ; sleep 8
./mons -O "$1" ; echo "test: single select $1"