Compare commits

..

22 commits
v2 ... main

Author SHA1 Message Date
0c59629b17 made brightnurrs work with freebsd 2024-08-03 17:25:32 +01:00
bebaaab101 fixed wallpapurr for when config file does not exist (gracefully exits) 2024-08-03 16:02:59 +01:00
f319a02bce fixed wallpapurr for when config file does not exist (gracefully exits) 2024-08-03 16:02:39 +01:00
fc9b01e77f edited install script 2024-05-07 15:17:12 +01:00
32b99c6818 edited install script 2024-05-07 15:16:48 +01:00
09e65aa651 better swaybg management in wl 2024-03-21 11:12:54 +00:00
2283a230d2 better swaybg management in wl 2024-03-21 10:41:40 +00:00
4303d16781 better swaybg management in wl 2024-03-21 10:19:50 +00:00
c96d39f05d better swaybg management in wl 2024-03-21 10:16:29 +00:00
ade0f07359 made screenshurrt semi-wayland compatible 2024-03-21 09:59:29 +00:00
6e31377f8a made wallpapurr wayland compatible 2024-03-20 21:28:55 +00:00
36fd258381 limit emojis to only smileys as there's too many now 2024-02-04 16:45:42 +00:00
f825d0ca59 now gets current emojis from unicode.org 2024-02-04 16:39:43 +00:00
e36842dcad parsing emoji list great, but now the list is too long! ha 2024-02-02 23:15:27 +00:00
c3fed850b2 Working on a way to refresh emojis 2024-02-02 23:03:19 +00:00
02a87fce2a clean up 2023-12-13 15:16:59 +00:00
50a87752ab notify query string for unsplash 2023-12-13 15:14:32 +00:00
de61baf816 wallpapuur working 2023-12-13 15:10:50 +00:00
d26f5d3ba6 adding wallpaper, can do last 2023-12-12 22:46:33 +00:00
16cf06d437 adding wallpaper, can do last 2023-12-12 22:46:15 +00:00
423831a401 starting to add wallpaper 2023-12-12 21:29:22 +00:00
f77e0edd6c added tideurrl 2023-11-23 20:23:25 +00:00
20 changed files with 419 additions and 3594 deletions

16
README.md Normal file
View file

@ -0,0 +1,16 @@
# Requirements
A non-exhaustive list of system requirements
General:
- rofi or
- rofi-lbonn-wayland for wayland support
Screenshurr:
- maim (for X)
- xsel (for X)
- grim (for wayland)
- slurp (for wayland)
- wl-clipboard (for wayland)
Brightnurrs:
- acpilight

1
emojis.json Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,5 @@
#!/bin/bash
#!/usr/bin/env bash
nimble install -y
mkdir -p "$HOME/.local/bin"
cp -v "wmtools" "$HOME/.local/bin/wmtools"

View file

@ -8,4 +8,3 @@ export info
var myConfig* = newConfig()

9
src/common/display.nim Normal file
View file

@ -0,0 +1,9 @@
import std/envvars
const XDG_SESSION_TYPE = "XDG_SESSION_TYPE"
const WAYLAND = "wayland"
proc isWayland*(): bool =
if existsEnv(XDG_SESSION_TYPE) and getEnv(XDG_SESSION_TYPE) == WAYLAND:
return true
return false

14
src/common/http.nim Normal file
View file

@ -0,0 +1,14 @@
import httpclient
proc download*(link: string): string =
var client = newHttpClient(timeout = 10000)
try:
let resp = client.get(link)
if resp.status == $Http200:
return resp.body
except:
echo getCurrentExceptionMsg()
return ""
proc save*(file: string, content: string) =
writeFile(file,content)

View file

@ -16,6 +16,7 @@ import util/screenshurrt
import util/calculaturr
import util/brightnurrs
import util/tideurrl
import util/wallpapurr
proc dispatch*(cfg: Config) =
case cfg.run
@ -51,5 +52,7 @@ proc dispatch*(cfg: Config) =
brightnurrs.go()
of Tideurrl:
tideurrl.go()
of Wallpapurr:
wallpapurr.go()
else:
echo "No valid run command given"

File diff suppressed because it is too large Load diff

55
src/lib/refresh_emoji.nim Normal file
View file

@ -0,0 +1,55 @@
import jsony
import strutils
import re
import httpclient
import emurrjilist
var emojis: seq[Emoji] = @[]
let e_re = re"E[0-9\.]+"
const group_line = "# group: "
const subgroup_line = "# subgroup: "
proc parse(body: string) =
var current_group = ""
var current_subgroup = ""
for line in body.split("\n"):
if line.startsWith(group_line):
let g = line.replace(group_line, "")
if current_group != g:
current_group = g
continue
if line.startsWith(subgroup_line):
let sub_g = line.replace(subgroup_line, "")
if current_subgroup != sub_g:
current_subgroup = sub_g
continue
if line == "" or (line.len > 0 and line[0] == '#'):
continue
try:
let parts = line.split("#")
let emo = parts[1].split(e_re)
var emoji = Emoji()
emoji.emoji = emo[0].strip(chars={' '})
emoji.name = emo[1].strip()
emoji.group = current_group
emoji.subgroup = current_subgroup
emojis.add(emoji)
except:
echo getCurrentExceptionMsg()
proc getUnicodeOrgEmoji() =
const url = "https://unicode.org/Public/emoji/latest/emoji-test.txt"
var client = newHttpClient()
let page = client.get(url)
parse(page.body)
proc save(emojis: seq[Emoji]) =
let file = "emojis.json"
writeFile(file, emojis.toJson())
proc getEmojis() =
getUnicodeOrgEmoji()
save(emojis)
when isMainModule:
getEmojis()

View file

@ -1,4 +1,5 @@
import os
import strutils
import parsetoml
import tool
@ -10,7 +11,10 @@ type
run*: Tool
max_lines*: int
prepend*: bool
to_stdout*: bool
screenshot_tool*: ScreenshotTool
unsplash_key*: string
bg_dir*: string
let config_dir* = getHomeDir() & ".config/wm_tools/"
let config_file* = config_dir & "config.toml"
@ -43,6 +47,10 @@ proc newConfig*(): Config =
cfg.max_lines = toml["max_lines"].getInt
if toml.hasKey("screenshot_tool"):
cfg.screenshot_tool = toml["screenshot_tool"].getStr.toScreenshotTool
if toml.hasKey("unsplash_key"):
cfg.unsplash_key = toml["unsplash_key"].getStr
if toml.hasKey("bg_dir"):
cfg.bg_dir = toml["bg_dir"].getStr.replace("$HOME",getHomeDir())
except:
echo "Error with Config File:"
echo getCurrentExceptionMsg()

View file

@ -14,6 +14,7 @@ type
ScreenshotTool* = enum
None = ""
Maim = "maim"
Grim = "grim"
proc newScreenshot*(): Screenshot =
var ss = Screenshot()
@ -41,6 +42,7 @@ proc ScreenshotSizes*(): seq[string] =
proc toScreenshotTool*(str: string): ScreenshotTool =
case str
of "maim": return Maim
of "grim": return Grim
else: return None
proc isScreenshotTool*(str: string): bool =
@ -49,6 +51,7 @@ proc isScreenshotTool*(str: string): bool =
proc command*(tool: ScreenshotTool): string =
case tool
of Maim: return "maim -u %s --format png %f"
of Grim: return "grim %s %f"
else: return ""
proc activeWindowCommand*(tool: ScreenshotTool): string =
@ -57,6 +60,9 @@ proc activeWindowCommand*(tool: ScreenshotTool): string =
case tool
of Maim:
cmd = cmd.replace("%s","-i $(xdotool getactivewindow)")
of Grim:
echo "Not currently Implemented"
quit(1)
else: return cmd
return cmd
@ -65,8 +71,11 @@ proc regionCommand*(tool: ScreenshotTool): string =
case tool
of Maim:
cmd = cmd.replace("%s","-s")
of Grim:
cmd = cmd.replace("%s","-g \"$(slurp)\"")
else: return cmd
return cmd
const CLIPBOARD_CMD* = "xclip -selection clipboard -t image/png"
const X_CLIPBOARD_CMD* = "xclip -selection clipboard -t image/png"
const WL_CLIPBOARD_CMD* = "wl-copy"

View file

@ -17,4 +17,5 @@ type
Screenshurrt,
Calculaturr,
Brightnurrs,
Tideurrl
Tideurrl,
Wallpapurr

6
src/model/wallpapurr.nim Normal file
View file

@ -0,0 +1,6 @@
type
WPArgs* = object
query*: string
last*: bool
from_unsplash*: bool

38
src/notify.nim Normal file
View file

@ -0,0 +1,38 @@
import strutils
import osproc
type
Note* = object
urgency*: Urgency
title*: string
content*: string
timeout*: int
Urgency* = enum
Normal = "normal"
Low = "low"
Urgent = "urgent"
Critical = "critical"
proc newNote*(): Note =
return Note(urgency: Normal, title: "Notification", content: "Hello, I am a notifications", timeout: 2000)
proc `$`*(n: Note): string =
let str = "notify-send -u $U $T $C -t $N"
.replace("$U", $n.urgency)
.replace("$T", n.title.escape)
.replace("$C", n.content.escape)
.replace("$N", $n.timeout)
return str
proc send*(n: Note) =
discard execCmdEx($n)
proc send*(s: varargs[string,`$`]) =
var n = newNote()
n.title = s[0]
if s.len > 1:
n.content = s[1..^1].join("")
else:
n.content = ""
send(n)

View file

@ -25,8 +25,18 @@ proc copyToClipboard*(str: string): bool {.discardable.} =
let ok = execCmd("echo -n " & quote(str) & " | xclip -selection clipboard")
return ok == 0
proc listify(data:Info, opts: varargs[string]): string =
var text = data.full_text
if opts.len > 0:
text &= "\n"
for opt in opts:
text &= markup text
text &= "\n"
return text
proc genMenuCmd(data: Info, opts: varargs[string]): string =
var cmd = ""
# length of the list of opts, plus the full text
var x_lines = len(opts) + 1
# if the text is empty, we don't want to create a menu item of it
if data.full_text != "":
@ -52,11 +62,14 @@ proc genMenuCmd(data: Info, opts: varargs[string]): string =
return cmd
proc runExec(data: Info, opts: varargs[string]): string =
let cmd = genMenuCmd(data, opts)
var output = execCmdEx(cmd)
echo "Output:\n" & $output
output.output.stripLineEnd()
return output.output
if not myConfig.to_stdout:
let cmd = genMenuCmd(data, opts)
var output = execCmdEx(cmd)
echo "Output:\n" & $output
output.output.stripLineEnd()
return output.output
else:
stdout.writeLine listify(data,opts)
proc outputData*(data: Info, args: varargs[string,`$`]): string {.discardable.} =
var output = runExec(data,args)

View file

@ -3,11 +3,13 @@ import os
import argparse
import common
import common/display
import model/pwgen
import model/volume
import model/brightness
import model/screenshot
import model/tides
import model/wallpapurr
proc parseArgs*() =
let params = commandLineParams()
@ -15,8 +17,11 @@ proc parseArgs*() =
help("WMTools : a set of tools to output option to your program of choice i.e. Rofi")
arg("input",help="the tool to run, i.e. furrytime, pingclock, volurrme, etc.")
arg("others", nargs = -1)
flag("-o","--output",help="outputs to stdout instead of something else")
try:
var opts = p.parse(params)
# TODO sort this but out, handle args for all modules, etc.
myConfig.to_stdout = opts.output
case opts.input
of "furrytime", "fuzzytime", "time":
myConfig.run = FurryTime
@ -50,6 +55,8 @@ proc parseArgs*() =
myConfig.run = Brightnurrs
of "tideurrl", "tides":
myConfig.run = Tideurrl
of "wallpapurr", "wallpaper":
myConfig.run = Wallpapurr
else:
echo p.help
quit(1)
@ -145,10 +152,15 @@ proc parseScreenshotArgs*(): Screenshot =
help("Args for screenshurrt")
arg("screenshurrt",help="can only ever be 'screenshurrt' as you won't have gotten this far otherwise")
option("-s","--size",help="size/region i.e. region, fullscreen or window")
option("-t","--tool",help="the tool to take the screenshot, i.e. maim or grim")
try:
var opts = p.parse(params)
if opts.size != "":
ss.size = opts.size.toScreenshotSize()
if opts.tool != "":
ss.tool = opts.tool.toScreenshotTool()
elif isWayland():
ss.tool = GRIM
except ShortCircuit as err:
if err.flag == "argparse_help":
echo err.help
@ -177,3 +189,29 @@ proc parseTideurrlArgs*(): TideList =
stderr.writeLine getCurrentExceptionMsg()
quit(1)
return t
proc parseWallpapurrArgs*(): WPArgs =
var args = WPArgs()
let params = commandLineParams()
var p = newParser:
help("Args for wallpapurr")
arg("wallpapurr",help="can only ever be 'wallpapurr' as you won't have gotten this far otherwise")
option("-q","--query",help="query name")
flag("-l","--last",help="last image")
flag("-n","--unsplash",help="get from unsplash")
try:
var opts = p.parse(params)
if opts.query != "":
args.query = opts.query
if opts.last:
args.last = true
if opts.unsplash:
args.from_unsplash = true
except ShortCircuit as err:
if err.flag == "argparse_help":
echo err.help
quit(1)
except UsageError:
stderr.writeLine getCurrentExceptionMsg()
quit(1)
return args

View file

@ -2,20 +2,20 @@ import os
import strutils
import osproc
import math
import tables
import ../common
import ../parser
import ../model/brightness
import ../output
#const backlight = "intel_backlight"
const BACKLIGHT_CMD = "xbacklight"
const UP = BACKLIGHT_CMD & " -inc %v" # %v is amount by
const DOWN = BACKLIGHT_CMD & " -dec %v" # %v is amount by
const SET = BACKLIGHT_CMD & " -set %v" # %v is amount by
const UP: Table[string,string] = {"linux": "xbacklight -inc %v", "freebsd": "backlight incr %v"}.toTable # %v is amount by
const DOWN: Table[string,string] = {"linux": "xbacklight -dec %v", "freebsd": "backlight decr %v"}.toTable() # %v is amount by
const SET: Table[string,string] = {"linux": "xbacklight -set %v", "freebsd": "backlight %v"}.toTable() # %v is amount by
const default_value = "5"
proc getBacklight(): string =
if hostOS == "freebsd": return ""
for dir in walkDir("/sys/class/backlight"):
echo dir.path
var bl = dir.path.replace("/sys/class/backlight/","")
@ -50,23 +50,29 @@ proc getDesign(pcnt: float): string =
return text
proc brightnessUp() =
let cmd = replace(UP,"%v",default_value)
let cmd = replace(UP[hostOS],"%v",default_value)
discard execCmd(cmd)
proc brightnessDown() =
let cmd = replace(DOWN,"%v",default_value)
let cmd = replace(DOWN[hostOS],"%v",default_value)
discard execCmd(cmd)
proc getBrightness*(backlight: string) =
var data = newInfo("Brightnurrs")
if backlight == "":
if hostOS == "freebsd":
let bl = execCmdEx("backlight")
let pcnt = bl.output.replace("brightness: ","").strip.parseFloat
let text = getDesign(pcnt)
data.full_text = text
elif backlight == "":
data.full_text = "No Backlight Found"
discard outputData(data)
quit(1)
let limit = getLimit(backlight)
let current = parseInt(strip(readFile("/sys/class/backlight/" & backlight & "/actual_brightness")))
let pcnt = (current/limit)*100
let text = getDesign(pcnt)
data.full_text = text
else:
let limit = getLimit(backlight)
let current = parseInt(strip(readFile("/sys/class/backlight/" & backlight & "/actual_brightness")))
let pcnt = (current/limit)*100
let text = getDesign(pcnt)
data.full_text = text
let args = @["up", "down"]
let option = outputData(data,args)
if option in args:
@ -80,7 +86,7 @@ proc getBrightness*(backlight: string) =
else:
try:
let i = parseInt(option)
let cmd = replace(SET,"%v",$i)
let cmd = replace(SET[hostOS],"%v",$i)
discard execCmd(cmd)
backlight.getBrightness()
except:

View file

@ -5,6 +5,7 @@ import strutils
import sequtils
import ../common
import ../common/display
import ../output
import ../parser
import ../model/screenshot
@ -14,6 +15,8 @@ const FILENAME = "Screenshot-%d.png"
const TEMP_DIR = "/tmp/"
let DATE_STR = now().format(DATE_FORMAT)
let CLIPBOARD_CMD = if isWayland(): WL_CLIPBOARD_CMD else: X_CLIPBOARD_CMD
proc saveToClipboard(filename: string) =
let cmd = "cat " & filename & " | " & CLIPBOARD_CMD
let status = execCmd(cmd)

140
src/util/wallpapurr.nim Normal file
View file

@ -0,0 +1,140 @@
import os
import json
import osproc
import random
import strutils
import sequtils
import httpclient
import ../common
import ../common/http
import ../common/display
import ../parser
import ../output
import ../notify
var UNSPLASH_KEY = ""
var BG_DIR = "/tmp/"
var LAST_FILE = ""
var LAST = ""
const UNSPLASH_URL = "https://api.unsplash.com/photos/random?query=$QUERY&orientation=landscape"
proc getFromUnsplash(q: var string): string =
let dir = BG_DIR & "/unsplash/" & q & "/"
createDir(dir)
notify.send("Getting from Unsplash",q)
q = q.replace(" ","%20")
let uri = UNSPLASH_URL.replace("$QUERY",q)
var client = newHttpClient(timeout = 10000)
let id = "Client-ID " & UNSPLASH_KEY
client.headers = newHttpHeaders({"Authorization": id})
try:
let resp = client.get(uri)
if resp.status == $Http200:
let j = parseJson(resp.body)
let link = j["links"]["download"].getStr
let filename = dir & j["slug"].getStr & ".jpg"
let img = download(link)
filename.save(img)
return filename
except:
echo getCurrentExceptionMsg()
proc getFiles(dir: string): seq[string] =
var files: seq[string] = @[]
for file in walkDir(dir):
if file.path.endsWith(".jpg"): files.add(file.path)
elif file.kind == pcDir:
files = files.concat(getFiles(file.path))
return files
proc getLast() =
LAST = readFile(LAST_FILE).strip()
proc setLastFileName(file: string) =
if file.len == 0:
echo "no file"
return
writeFile(LAST_FILE, file)
LAST = file
proc getImageFromDir(): string =
notify.send("Getting Random file from:",BG_DIR)
var img_files = getFiles(BG_DIR).filter(proc(f: string): bool = f != LAST)
randomize()
img_files.shuffle()
if img_files.len > 0:
let img_file = img_files[0]
notify.send("Found : ", img_file)
return img_file
notify.send("No Background Images Found")
return ""
proc getCurrSwayBGPID(): string =
let pid = execCmdEx("pgrep swaybg")
return pid.output
proc killCurrSwayBGPID(pid: string) =
sleep 2000
discard execCmd("kill " & pid)
proc setImage(img: string) =
notify.send("Setting Background to:",img)
if isWayland():
let pid = getCurrSwayBGPID()
let swaybg = "swaybg -m fill -i " & img.escape & " &"
discard execCmd(swaybg)
killCurrSwayBGPID(pid)
else:
let feh = "feh --bg-fill " & img.escape
discard execCmdEx(feh)
proc setLast() =
notify.send("Setting Background to Last", LAST)
if isWayland():
let pid = getCurrSwayBGPID()
let swaybg = "swaybg -m fill -i " & LAST.escape & " &"
discard execCmd(swaybg)
killCurrSwayBGPID(pid)
else:
let feh = "feh --bg-fill " & LAST.escape
discard execCmdEx(feh)
proc getDesign(): Info =
var data = newInfo("Wallpapurr")
return data
proc queryPrompt(): string =
let data = getDesign()
let output = data.outputData()
return output
proc go*() =
var args = parseWallpapurrArgs()
UNSPLASH_KEY = myConfig.unsplash_key
BG_DIR = myConfig.bg_dir
LAST_FILE = BG_DIR & "/last.txt"
if args.from_unsplash and args.query == "":
args.query = queryPrompt()
var img = ""
if args.query != "" or args.from_unsplash:
echo "Query: ", args.query
img = getFromUnsplash(args.query)
elif args.last:
getLast()
setLast()
quit(0)
else:
img = getImageFromDir()
setImage(img)
setLastFilename(img)
echo img

View file

@ -1,6 +1,6 @@
# Package
version = "2.0.1"
version = "2.0.8"
author = "Paul Wilde"
description = "A set of informational tools"
license = "AGPL-3.0-or-later"
@ -14,3 +14,7 @@ requires "nim >= 2.0.0"
requires "parsetoml >= 0.7.1"
requires "argparse"
requires "configparser"
requires "jsony"
task refresh_emoji, "Refresh Emoji Library file":
exec "nim c -r src/lib/refresh_emoji.nim"