Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add color picker #35

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
| :---: | :---: |
| autoname-workspaces.py | Adds icons to the workspace name for each open window |
| firefox-focus-monitor.py | Utility to selectively disable keypresses to specific windows |
| grimpicker | A simple color picker for wlroots |
| grimshot | A helper for screenshots within sway |
| inactive-windows-transparency.py | Makes inactive windows transparent |
| layout-per-window.py | A script keeps track of the active layout for each window |
Expand Down
15 changes: 15 additions & 0 deletions grimpicker/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
PKGNAME = "grimpicker"
DESTDIR ?= ""
PREFIX ?= "/usr"

.PHONY: build install

build:
scdoc <"${PKGNAME}.1.scd" >"${PKGNAME}.1"

install:
# Not installing zsh completion here as its destination depends on the distribution
install -D -m 755 "${PKGNAME}" "${DESTDIR}${PREFIX}/bin/${PKGNAME}"
install -D -m 644 "completion.bash" "${DESTDIR}${PREFIX}/share/bash-completion/completions/${PKGNAME}"
install -D -m 644 "completion.fish" "${DESTDIR}${PREFIX}/share/fish/vendor_completions.d/${PKGNAME}.fish"
install -D -m 644 "${PKGNAME}.1" "${DESTDIR}${PREFIX}/share/man/man1/${PKGNAME}.1"
15 changes: 15 additions & 0 deletions grimpicker/completion.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
_grimpicker() {
local cur="${COMP_WORDS[COMP_CWORD]}"

short=(-p -d -e -c -n -h -v)
long=(--print --draw --escape --copy --notify --help --version)

if [[ $cur == --* ]]; then
COMPREPLY=($(compgen -W "${long[*]}" -- "$cur"))
else
COMPREPLY=($(compgen -W "${short[*]}" -- "$cur"))
COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur"))
fi
}

complete -F _grimpicker grimpicker
8 changes: 8 additions & 0 deletions grimpicker/completion.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
complete -c grimpicker -f
complete -c grimpicker -s p -l print -d "Print to stdout"
complete -c grimpicker -s d -l draw -d "Draw a colored block"
complete -c grimpicker -s e -l escape -d "Print shell escape sequences"
complete -c grimpicker -s c -l copy -d "Copy to clipboard"
complete -c grimpicker -s n -l notify -d "Send a notification"
complete -c grimpicker -s h -l help -d "Show help message and quit"
complete -c grimpicker -s v -l version -d "Show version number and quit"
10 changes: 10 additions & 0 deletions grimpicker/completion.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#compdef grimpicker

_arguments -s \
{-d,--print}'[Print to stdout]' \
{-d,--draw}'[Draw a colored block]' \
{-e,--escape}'[Print shell escape sequences]' \
{-c,--copy}'[Copy to clipboard]' \
{-n,--notify}'[Send a notification]' \
{-h,--help}'[Show help message and quit]' \
{-v,--version}'[Show version number and exit]' \
109 changes: 109 additions & 0 deletions grimpicker/grimpicker
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env python3

'''
grimpicker: a simple color picker for wlroots

Dependencies:
`slurp`: utility to select a region
`grim`: utility to make screenshots

Recommendations:
`wl-copy`: clipboard utility
`notify-send`: desktop notifications sender
'''

__pkgname__ = 'grimpicker'
__version__ = '1.0.0'

import argparse
import subprocess
import sys


class Color:
escape = b'\x1b'
reset_fg = escape + b'[39m'
reset_bg = escape + b'[49m'
reset_all = escape + b'[0m'

escape_str = '\\e'
reset_fg_str = escape_str + '[39m'
reset_bg_str = escape_str + '[49m'
reset_all_str = escape_str + '[0m'

def __init__(self, r: int, g: int, b: int):
(self.r, self.g, self.b) = (r, g, b)

@classmethod
def decode_ppm_pixel(cls, ppm: bytes):
ppm_lines = ppm.splitlines()
if not (len(ppm_lines) == 4 and ppm_lines[:3] == [b'P6', b'1 1', b'255'] and len(ppm_lines[3]) == 3):
raise ValueError('only 1x1 pixel ppm P6 format without comments is supported, no HDR')
return cls(*ppm_lines[3])

def to_hex(self) -> str:
return '#{:0>2X}{:0>2X}{:0>2X}'.format(self.r, self.g, self.b)

def to_escape_fg(self) -> bytes:
return b'%b[38;2;%d;%d;%dm' % (self.escape, self.r, self.g, self.b)

def to_escape_bg(self) -> bytes:
return b'%b[48;2;%d;%d;%dm' % (self.escape, self.r, self.g, self.b)

def to_escape_fg_str(self) -> str:
return '{}[38;2;{};{};{}m'.format(self.escape_str, self.r, self.g, self.b)

def to_escape_bg_str(self) -> str:
return '{}[48;2;{};{};{}m'.format(self.escape_str, self.r, self.g, self.b)


def run(args) -> None:
slurp = subprocess.check_output(('slurp', '-p'))
grim = subprocess.check_output(('grim', '-g', '-', '-t', 'ppm', '-'), input=slurp)
color = Color.decode_ppm_pixel(grim)

if not (args.print or args.draw or args.escape or args.copy or args.notify):
args.print = True
args.draw = True

if args.print:
print(color.to_hex())
if args.draw:
sys.stdout.buffer.write(color.to_escape_bg() + b' ' * 7 + color.reset_bg + b'\n')
if args.escape:
sys.stdout.buffer.write(
b'Truecolor terminal shell escape sequences:\n' +

b'%bTo change foreground:%b ' % (color.to_escape_fg(), color.reset_fg) +
b'echo -e "%b", to reset: ' % color.to_escape_fg_str().encode() +
b'echo -e "%b"\n' % color.reset_fg_str.encode() +

b'%bTo change background:%b ' % (color.to_escape_bg(), color.reset_bg) +
b'echo -e "%b", to reset: ' % color.to_escape_bg_str().encode() +
b'echo -e "%b"\n' % color.reset_bg_str.encode() +

b'To reset all attributes: echo -e "%b"\n' % color.reset_all_str.encode()
)
if args.copy:
subprocess.run(('wl-copy', color.to_hex()), check=True)
if args.notify:
subprocess.run(('notify-send', color.to_hex()), check=True)


def parse_args() -> argparse.Namespace:
usage = '{} [OPTIONS]'.format(__pkgname__)
version = '{} {}'.format(__pkgname__, __version__)
epilog = 'See `man 1 grimpicker` for further details'
parser = argparse.ArgumentParser(usage=usage, add_help=False, epilog=epilog)
parser.add_argument('-p', '--print', dest='print', action='store_true', help='Print to stdout')
parser.add_argument('-d', '--draw', dest='draw', action='store_true', help='Draw a colored block')
parser.add_argument('-e', '--escape', dest='escape', action='store_true', help='Print shell escape sequences')
parser.add_argument('-c', '--copy', dest='copy', action='store_true', help='Copy to clipboard')
parser.add_argument('-n', '--notify', dest='notify', action='store_true', help='Send a notification')
parser.add_argument('-h', '--help', action='help', help='Show help message and quit')
parser.add_argument('-v', '--version', action='version', version=version, help='Show version number and quit')
return parser.parse_args()


if __name__ == '__main__':
run(parse_args())
65 changes: 65 additions & 0 deletions grimpicker/grimpicker.1.scd
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
GRIMPICKER(1)

# NAME

grimpicker - a simple color picker for wlroots

# SYNOPSIS

*grimpicker* [_OPTIONS_]

# OPTIONS

*-p*, *--print*
Print to stdout

*-d*, *--draw*
Draw a colored block

*-e*, *--escape*
Print shell escape sequences

*-c*, *--copy*
Copy to clipboard

*-n*, *--notify*
Send a notification

*-h*, *--help*
Show help message and quit

*-v*, *--version*
Show version number and quit

# DESCRIPTION

*grimpicker* is a color picker that uses *slurp* and *grim*.
These programs rely on _zwlr_layer_shell_v1_ and _wlr-screencopy-unstable-v1_
(maybe be replaced with _ext-image-capture-source-v1_ and
_ext-image-copy-capture-v1_ in the future) wayland protocols
(implemented in wlroots-based compositors, e.g. *sway*).

It has several output options, they can be combined.

_--copy_ needs *wl-clipboard* to be installed.

_--draw_ and _--escape_ need a terminal with truecolor support (e.g. *foot*).

_--notify_ needs *libnotify* to be installed
and a notification daemon (e.g. *mako* or *fnott*) to be running.

_--print_ and _--draw_ are selected by default if no arguments are provided.

# EXAMPLES

An example usage pattern is to add this binding to your sway config:

```
# Super+Print: color picker
bindsym --to-code $mod+Print exec grimpicker --notify

```

# SEE ALSO

*slurp*(1), *grim*(1), *grimshot*(1)
Loading