Compare commits

...

25 commits

Author SHA1 Message Date
92d1eea678 added tideurrl 2023-11-23 20:10:24 +00:00
7f4bf7bb0f added tideurrl 2023-11-23 20:10:04 +00:00
e6a72669ba added wirelurrs 2023-11-23 19:40:20 +00:00
8010714091 added brightnurrs - needs testing on laptop 2023-11-23 12:50:54 +00:00
528cbb6200 added brightnurrs - needs testing on laptop 2023-11-23 12:49:06 +00:00
ff21191f2d tidy 2023-11-23 12:31:23 +00:00
69918d3dfe added calc 2023-11-23 12:29:08 +00:00
9570461b13 editing install.sh 2023-11-23 12:23:20 +00:00
bd5b314cf1 screenshurrt working 2023-11-23 12:14:24 +00:00
6f5f255b66 added output to clipboard 2023-11-23 11:14:44 +00:00
669707f14a tiody 2023-11-23 11:10:25 +00:00
e018c8f985 added pw_generaturr 2023-11-23 11:09:41 +00:00
3074f55973 added temperaturr 2023-11-23 10:13:59 +00:00
1ff912edff tdy and add passwurrd 2023-11-23 10:07:37 +00:00
079c5d9526 added reminurr 2023-11-23 10:02:06 +00:00
ae102073b8 added calendurr 2023-11-23 09:50:15 +00:00
c341c9242a added emurrji 2023-11-23 09:41:16 +00:00
d0142a966b added network 2023-11-23 09:29:59 +00:00
f17a160450 added network 2023-11-23 09:27:54 +00:00
ae35c221fe volurrme now working 2023-11-22 22:03:03 +00:00
deb43144aa volurrme now working 2023-11-22 22:02:52 +00:00
0c03850eb5 batturry now working 2023-11-22 21:42:13 +00:00
c2b14eb8ef removed older furrytime and pingclurrk 2023-11-22 21:20:08 +00:00
83171db72e furrytime and pingclock working 2023-11-22 21:19:37 +00:00
1a478fca9e start work on v2 2023-11-22 19:16:47 +00:00
75 changed files with 1072 additions and 1633 deletions

View file

@ -1,70 +0,0 @@
# A selection of information output tools for dmenu
These are a selection of independant tools for displaying various information
about system status in dmenu. Some of them i.e. `volurrme` have options (up, down, mute...)
which are selectable options in dmenu.
## Tools
- `pingclurrk` performs a single `ping` to a server and returns the response time
- `batturry` shows the current battery level
- `brightnurrs` shows the current backlight level and gives options to adjust it
- `volurrme` shows the current volume level and gives options to adjust and manage it
- `calendurr` shows the date
- `furrytime` shows the fuzzytime clock
- `wirelurrs` shows the state of the wireless network interface. SSID connected to and signal level.
- `netwurrk` shows the status and/or the ip address of the network interface card
- `temperaturr` shows the current CPU temperature
- `noteurr` a simple one liner note taking tool, displaying notes in `dmenu`/`rofi`
- `calculaturr` a calculator, utilising `qalculate` - inspired by [@fedops](https://codeberg.org/fedops/scripts)
- `emurrji` an emoji picker
- `remmina_choosurr` reads the files in your remmina config directory and allows you to connect to and edit them
- `translaturr` utilises libretranslate (you'll need and API key or your own instance) to translate test. Prefix the text with `en>de`, `de>en`, `en>fr`, etc. as you need. Must be compiled with `-d:ssl`
- `clipurr` clipboard manager
- `passwuurd` a passmenu clone, that works in rofi too
- `cmd_wrappurr` a basic tool to run other `dmenu` related tools with uniform styling.
- For example: `dmenu_run`, `clipmenu`, `passmenu` etc.
### Why do all the tools have "urr" in them?
This is something I was inspired to do after writing `clipurr`… "purr", like a cat... see?
So I thought I'd rename everything else to conform to this amazing naming convention… cool eh‽
## How to compile
There are some configuration variables explicit to me, you'll need to change them for you for them to be useful I imagine.
Configuration variables are compile - there are no config files or runtime parameters
Each tool is compiled separately, for example:
```sh
nimble install
or
nim c pingclurrk
```
and then run with
```sh
./pingclurrk
or
./pingclurrk rofi
```
## How to use
Personally, I have these bound to key combinations in i3 and sway.
In fact, I have a seperate `bindsym` mode in which all these
tools are accessible i.e. `$mod+i` to get to "info" mode then `p` to show pingclock.
It's completely up to you how to run them, they're just simple CLI tools.
### You can also set the volume and brightness levels by typing a numeric figure into the dmenu/rofi input box
## Dependencies
- `dmenu` or `rofi`
- `yad` for calendar
- basically any tool that's used to gather the information.
- "tools" for audio etc. (`pamixer`, `ncpamixer`, etc.) can be set in the source
## Full disclosure
I'm aware my code is messy.
I'm aware my code is mostly undocumented.
But hopefully these things are simple enough to work out.
<a href="https://notnull.click/users/paul" rel="me">Fediverse</a>

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Clipboard manager for X11 or Wayland"
license = "MIT"
srcDir = "src"
bin = @["clipurr"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,184 +0,0 @@
import ../../globurrl
import std/[strutils,os,db_sqlite,osproc]
const CLIP_DB = WM_TOOLS_DIR & "clipurr_cache.sqlite"
const KEEP_ITEMS = 15
proc openDBConn(): DBConn
proc addClip(str: var string)
proc killOldRunningProcesses() =
let x = execCmdEx("killall wl-paste clipnotify")
echo x
proc runDaemon() =
echo "Starting Daemon..."
if wayland:
echo "Using Wl-paste"
let cwd = getAppDir()
let outp = execProcess("wl-paste -n -w " & cwd & "/clipurr set")
else:
var run = true
while run:
# TODO;
# Check if WM is running otherwise the TTY will be spammed with "Using Clipnotify" text
if XisRunning():
echo "Using Clipnotify"
let outp = execCmdEx("clipnotify")
if outp.exitcode == 0:
var content = getCurrentClipboardContent()
addClip(content)
echo "Exiting Daemon..."
proc openDBConn(): DBConn =
let db: DBconn = open(CLIP_DB,"","","")
try:
db.exec(sql"""create table if not exists
clip_items (
timestamp DATETIME NOT NULL,
clip NVARCHAR(500) NOT NULL
)
""")
except:
echo getCurrentExceptionMsg()
return db
proc clearHistory() =
let db = openDBConn()
try:
db.exec(sql"drop table if exists clip_items")
except:
echo getCurrentExceptionMsg()
proc maintainDB() =
return # order by and offset doesn't work unless certain sqlite compile time options set
# will create a different way to do this
try:
let db = openDBConn()
defer: db.close()
db.exec(sql"""BEGIN""")
db.exec(sql"delete from clip_items order by timestamp desc offset ?", KEEP_ITEMS)
db.exec(sql"""COMMIT""")
except:
echo "Error cleaning DB : " & getCurrentExceptionMsg()
proc escapeClip(str: string): string =
var clip = str
clip = clip.replace("`","\\`")
clip = clip.replace("\\n`","\\\\n`")
clip = clip.replace("\x0A","\\x0A")
clip = escape(clip)
echo "CLIP : ", clip
return strip(clip)
proc unescapeClip(str: string): string =
var clip = str
try:
clip = unescape(clip)
if contains(clip,"\\x0A"):
echo "NEWLINE FOUND"
let idx = find(clip, "\\x0A") - 1
clip = clip[0 .. idx] & " ... more ..."
except:
echo getCurrentExceptionMsg()
return strip(clip)
proc readClipFile(): seq[string] =
var clips: seq[string] = @[]
# let db = openDBConn()
try:
let db = openDBConn()
defer: db.close()
for row in db.fastRows(sql"select distinct(clip) from clip_items order by timestamp desc LIMIT ?", KEEP_ITEMS):
var str = unescapeClip(row[0])
clips.add(str)
except:
echo "Error Reading Clip File : " & getCurrentExceptionMsg()
return clips
proc addClip(str: var string) =
if str == "":
return
elif str[0] == '\x89':
var t = str[1..3]
echo "Is a ", $t, " file , not storing"
str = "[" & t & " Image] (not stored)"
try:
str = escapeClip(str)
echo "clipboard content : ", str
let db = openDBConn()
defer: db.close()
db.exec(sql"""BEGIN""")
db.exec(sql"""insert into clip_items (timestamp, clip)
values (CURRENT_TIMESTAMP, ?)
""", str)
db.exec(sql"""COMMIT""")
except:
echo getCurrentExceptionMsg()
return
proc getFullClipboardContent(str: string): string =
var full = ""
try:
let db = openDBConn()
defer: db.close()
let text = "\"" & replace(str," ... more ...", "%") & "\""
let stmt = """
select clip
from clip_items
where clip like ?
order by timestamp desc
LIMIT 1"""
var prep = db.prepare(stmt)
prep.bindParams(text)
let res = db.getAllRows(prep)
for r in res:
# may need to switch to a getRow or getValue method here as this is messy
full = unescape(r[0])
full = replace(full, "\\x0A","\x0A")
break
finalize(prep)
except:
echo "Error Reading Clip File : " & getCurrentExceptionMsg()
return full
proc showClips() =
let clips = readClipFile()
let info = newInfo("Clipurr")
let option = outputData(info, clips)
if option != "":
if contains(option, "... more ..."):
let full = getFullClipboardContent(option)
copyToClipboard(full)
else:
copyToClipboard(option)
return
proc main() =
for idx, arg in args:
if arg == "daemon":
killOldRunningProcesses()
runDaemon()
return
if arg == "set":
var content = getCurrentClipboardContent()
addClip(content)
return
if arg == "clear":
clearHistory()
return
showClips()
return
block start:
if isMainModule:
main()
maintainDB()

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Displays open windows in i3 workspaces"
license = "MIT"
srcDir = "src"
bin = @["i3_wurrkspaces"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,173 +0,0 @@
import ../../globurrl
import std/[osproc,json,strutils]
const I3_WORKSPACES = "i3-msg -t get_workspaces"
const SWAY_WORKSPACES = "swaymsg -t get_workspaces"
let WORKSPACES = if wayland: SWAY_WORKSPACES else: I3_WORKSPACES
const I3_TREE = "i3-msg -t get_tree"
const SWAY_TREE = "swaymsg -t get_tree"
let TREE = if wayland: SWAY_TREE else: I3_TREE
const VISIBLE = "#"
const URGENT = "!"
const FOCUSED = "%"
type
Workspace = object
num: int
name: string
focused: bool
visible: bool
output: string
urgent: bool
display_string: string
apps: seq[string]
applications: seq[Application]
application: Application
Application = object
title: string
class: string
focused: bool
urgent: bool
var my_workspaces: seq[Workspace]
var current_workspace: int = 0
proc showWorkspaces()
proc buildString(ws: Workspace): string =
var str = $ws.num & " |"
# if ws.urgent or ws.application.urgent:
if ws.application.urgent:
str &= URGENT
else:
str &= " "
if ws.focused or ws.application.focused:
current_workspace = ws.num
str &= FOCUSED
# elif ws.visible:
# str &= VISIBLE
else:
str &= " "
str &= "| " & ws.output & " | "
if ws.application.class != "":
str &= ws.application.class & " " & ws.application.title & " | "
else:
str = ""
# for app in ws.applications:
# str &= app.class & " " & app.title & " | "
return str
proc findWorkspace(workspace: string): Workspace =
for ws in my_workspaces:
if workspace == ws.display_string:
return ws
proc switchWorkspace(workspace: string) =
if workspace.contains("%"):
return
let ws = findWorkspace(workspace)
if ws.num == current_workspace:
return
if wayland:
let cmd = "swaymsg workspace " & $ws.num
discard execCmd(cmd)
else:
let cmd = "i3-msg workspace " & $ws.num
discard execCmd(cmd)
showWorkspaces()
proc getApplication(node: JsonNode, ws: Workspace = Workspace()): Application =
var app = Application()
let window = node["window_properties"]
app.title = window["title"].getStr()
app.class = window["class"].getStr()
app.focused = node["focused"].getBool()
app.urgent = node["urgent"].getBool()
#echo ws.num
#echo app.title & " " & app.class
return app
proc newWorkspace(node: JsonNode): Workspace =
return Workspace(
num: node["num"].getInt(),
name: node["name"].getStr(),
focused: node["focused"].getBool(),
#visible: w["visible"].getBool(),
urgent: node["urgent"].getBool(),
output: node["output"].getStr(),
)
proc findWorkspacesTree(node: JsonNode, parent: Workspace = Workspace()) =
for channel in node["nodes"].getElems():
### move this into for loop if want separate entry per window
var ws: Workspace = Workspace()
if parent.num > 0:
ws = parent
elif node{"type"}.getStr() == "workspace":
if node["output"].getStr() == "__i3":
return
ws = newWorkspace(node)
echo ws
###
echo channel
if channel{"window_properties"} != nil:
#or (wayland and `something that is the same as window_properties'):
let app = getApplication(channel,ws)
echo app
if ws.name != "":
#if app.focused:
# ws.focused = true
ws.applications.add(app)
ws.application = app
elif ws.num > 0 and len(channel{"nodes"}) > 0:
findWorkspacesTree(channel,ws)
else:
findWorkspacesTree(channel)
### move this into for loop if want separate entry per window
if ws.name != "":
ws.display_string = ws.buildString()
if ws.display_string != "":
my_workspaces.add(ws)
###
return
proc getTree() =
let cur_workspaces = execCmdEx(TREE)
if cur_workspaces.output != "":
let root = parseJson(cur_workspaces.output)
findWorkspacesTree(root)
return
proc getWorkspaces(): seq[Workspace] =
let cur_workspaces = execCmdEx(WORKSPACES)
if cur_workspaces.output != "":
let ws = parseJson(cur_workspaces.output)
for w in ws:
var space = Workspace(
num: w["num"].getInt(),
name: w["name"].getStr(),
focused: w["focused"].getBool(),
visible: w["visible"].getBool(),
urgent: w["urgent"].getBool(),
output: w["output"].getStr()
)
space.display_string = buildString(space)
my_workspaces.add(space)
return my_workspaces
proc showWorkspaces() =
my_workspaces = @[]
getTree()
var info = newInfo("Wurrkspaces")
var args: seq[string] = @[]
for ws in my_workspaces:
args.add(ws.display_string)
let output = outputData(info,args)
if output in args:
switchWorkspace(output)
proc main() =
showWorkspaces()
if isMainModule:
main()

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Shows battery percentage using dmenu"
license = "MIT"
srcDir = "src"
bin = @["batturry"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Shows and controls laptop brightness"
license = "MIT"
srcDir = "src"
bin = @["brightnurrs"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,116 +0,0 @@
import ../../globurrl
import std/[os,strutils,osproc,math]
const backlight = "intel_backlight"
const default_bg = yellow
const default_fg = black
const BACKLIGHT_CMD = "xbacklight"
const UP_X = BACKLIGHT_CMD & " -inc %v" # %v is amount by
const DOWN_X = BACKLIGHT_CMD & " -dec %v" # %v is amount by
const SET_X = BACKLIGHT_CMD & " -set %v" # %v is amount by
const BACKLIGHT_CMD_WL = "brightnessctl"
const UP_WL = BACKLIGHT_CMD_WL & " set %v%+"
const DOWN_WL = BACKLIGHT_CMD_WL & " set %v%-"
const SET_WL = BACKLIGHT_CMD_WL & " set %v%"
var CMD = BACKLIGHT_CMD
var UP = UP_X
var DOWN = DOWN_X
var SET = SET_X
const default_value = "5"
proc getLimit(): int =
try:
let back_l = readFile("/sys/class/backlight/" & backlight & "/max_brightness")
return parseInt(strip(back_l))
except:
echo "Error getting backlight max : ", getCurrentExceptionMsg()
return -1
let limit = getLimit()
proc getDesign(pcnt: float): string =
var icon = "🌑"
case pcnt:
of 85..100:
icon = "🌕"
of 75..85:
icon = "🌖"
of 50..75:
icon = "🌗"
of 35..50:
icon = "🌘"
else:
icon = "🌑"
let percent = toInt(round(pcnt,0))
let text = icon & " " & $percent & "%"
return text
proc brightnessUp() =
let cmd = replace(UP,"%v",default_value)
discard execCmd(cmd)
proc brightnessDown() =
let cmd = replace(DOWN,"%v",default_value)
discard execCmd(cmd)
proc getBrightness*(run_once: bool = false) =
var last_pcnt: float = 0
while true:
let current = parseInt(strip(readFile("/sys/class/backlight/" & backlight & "/actual_brightness")))
let pcnt = (current/limit)*100
if pcnt != last_pcnt:
let text = getDesign(pcnt)
var data = newInfo("Brightnurrs")
data.full_text = text
data.selected_bg = default_fg
data.selected_fg = default_bg
# i3bar stuff
data.border = default_fg
let args = @["up", "down"]
let option = outputData(data,args)
if option in args:
case option:
of "up":
brightnessUp()
getBrightness(true)
of "down":
brightnessDown()
getBrightness(true)
else:
try:
let i = parseInt(option)
let cmd = replace(SET,"%v",$i)
discard execCmd(cmd)
getBrightness(true)
except:
echo getCurrentExceptionMsg()
if run_once:
break
if stoploop:
break
last_pcnt = pcnt
sleep(1000)
proc main() =
if limit == -1:
switchTwmMode()
return
getBrightness()
if isMainModule:
block start:
if wayland:
CMD = BACKLIGHT_CMD_WL
UP = UP_WL
DOWN = DOWN_WL
SET = SET_WL
for arg in args:
case arg:
of "up":
brightnessUp()
break start
of "down":
brightnessDown()
break start
main()

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "A simple dmenu calculator"
license = "MIT"
srcDir = "src"
bin = @["calculaturr"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Displays the date in dmenu"
license = "MIT"
srcDir = "src"
bin = @["calendurr"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "A Command wrapper thing - i.e. run clipmenu/passmenu into dmenu with styling, sucks really"
license = "MIT"
srcDir = "src"
bin = @["cmd_wrappurr"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,22 +0,0 @@
import ../../globurrl
import std/[strutils,osproc]
# Basically just a wrapper to style passmenu nicely
proc main() =
var info = newInfo(capitalizeAscii(run_command))
let cmd = genMenuCmd(info)
discard execCmd(cmd)
return
if isMainModule:
globurrl.wrappurr = true
for idx, arg in args:
case arg:
of "-r", "--run":
run_command = args[idx + 1]
break
else:
echo "No command given, please run again with `[-r|--run] __cmd__`"
if run_command != "":
main()

View file

@ -1,26 +0,0 @@
#!/bin/bash
build () {
dir="$1"
i="$2"
cd "./$dir" || exit
f=$(echo "$dir" | sed 's/\.\///')
nimble install -y
if [[ $i == "install" ]]; then
cp -v "$f" "$HOME/.local/bin/$f"
fi
cd ../
}
if [[ $2 != "" ]]; then
build "$2" "$1"
exit
fi
for dir in ./*; do
if [ -d "$dir" ]; then
if [[ "$dir" == "./" ]]; then
continue
fi
build "$dir" "$1"
fi
done

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "An emoji picker for dmenu/rofi"
license = "MIT"
srcDir = "src"
bin = @["emurrji"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Displays the fuzzy time in dmenu"
license = "MIT"
srcDir = "src"
bin = @["furrytime"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,278 +0,0 @@
import std/[os,osproc,strutils,json,rdstdin,marshal]
type
Info* = object
title*: string
selected_fg*: string
selected_bg*: string
unselected_fg*: string
unselected_bg*: string
full_text*: string
# next few are for i3bar use
border*: string
background*: string
color*: string
html_text*: string
short_text*: string
args*: seq[string]
Menu = object
command: string
bottom: string
grab_kb: string
i_case: string
lines_shown: string
monitor: string
prompt: string
font: string
norm_bg: string
norm_fg: string
sel_bg: string
sel_fg: string
extra_cmd: string
Tool* = enum
ROFI = "rofi", DMENU = "dmenu"
const WM_TOOLS_DIR* = getHomeDir() & ".wm_tools/"
const WM_TOOLS_SYNC_DIR = getHomeDir() & "/Nextcloud/.wm_tools_sync/"
const background* = "#000000"
const backgroundalt* = "#bb222222"
const backgroundalt2* = "#bb333333"
const foreground* = "#dfdfdf"
const foregroundalt* = "#777"
const foregroundalt2* = "#ccc"
const black* = "#000000"
const white* = "#FFFFFF"
const yellow* = "#ffb52a"
const red* = "#e60053"
const purple* = "#9f78e1"
const blue* = "#0a6cf5"
const lightblue* = "#7296EF"
const lighterblue* = "#B5DDF7"
const green* = "#4b9901"
const lightgreen* = "#00ff00"
const grey* = "#dfdfdf"
const darkgrey* = "#444"
const primary* = yellow
const secondary* = red
const alert* = "#bd2c40"
const font = "Hermit-12"
const MAX_LINES = 20
var loop* = false
var stoploop* = true
var tool* = ROFI
var wrappurr* = false
var run_command* = ""
var wayland* = false
proc newInfo*(str: string = "Info"): Info =
var title = str
if tool == ROFI:
title = title & " : "
return Info(
title: title,
selected_fg: black,
selected_bg: white,
unselected_fg: white,
unselected_bg: black,
# next few are for i3bar use
border: white,
background: black,
color: foreground,
)
proc newMenuConfig(cmd: Tool = ROFI): Menu =
var run = $cmd
var menu = Menu()
menu.command = run
if cmd == ROFI:
menu.command &= " -dmenu"
menu.prompt = "-p"
menu.i_case = "-i"
menu.lines_shown = "-l"
return menu
proc newRofiConfig(cmd: Tool = ROFI): Menu =
var run = cmd
var menu = newMenuConfig(run)
#menu.extra_cmd = "-markup-rows" #-kb-row-select \"Tab\" -kb-row-tab \"\""
return menu
proc newDmenuConfig(cmd: Tool = DMENU): Menu =
var run = cmd
var menu = newMenuConfig(run)
menu.bottom = "-b"
menu.grabkb = "-f"
menu.monitor = "-m"
menu.font = "-fn"
menu.norm_bg = "-nb"
menu.norm_fg = "-nf"
menu.sel_bg = "-sb"
menu.sel_fg = "-sf"
return menu
proc newMenu(): Menu =
#if wrappurr:
# return newDmenuConfig(run_command)
case tool:
of ROFI:
return newRofiConfig()
of DMENU:
return newDmenuConfig()
return newMenuConfig()
proc debugLog*(str: string) =
let f = open("/tmp/debug.txt",fmAppend)
defer: f.close()
f.writeLine(str)
proc checkWayland() =
if getEnv("XDG_SESSION_TYPE") == "wayland":
wayland = true
proc XisRunning*(): bool =
if getEnv("XAUTHORITY") != "":
echo "X IS RUNNING"
echo getEnv("XAUTHORITY")
return true
return false
proc clearInput*(count: int = 1) =
for x in countup(1, count):
discard readLineFromStdin("")
proc getArguments*(): seq[string] =
let args = commandLineParams()
return args
proc stripQuotes*(str: string): string =
return replace(str,"\"","&quot;")
proc quote*(str: string): string =
var text = str
# May need to put some further work to escape some special chars here
text = stripQuotes(text)
# Put leading and ending quote marks in
return " \"" & text & "\" "
# ^ Add a spaces ^ so the previous flag isn't touching
proc markup(str: string): string =
var text = str
return text
proc genMenuCmd*(data: Info, opts: varargs[string], rofi: bool = false): string =
# Build dmenu/rofi command
var cmd = ""
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 != "":
let text = markup(data.full_text)
cmd &= text & "\n"
else:
x_lines -= 1
for opt in opts:
let text = markup(opt)
cmd = cmd & text & "\n"
cmd.removeSuffix("\n")
if x_lines > MAX_LINES:
x_lines = MAX_LINES
cmd = "echo -e" & quote(cmd) & " | "
var menu = newMenu()
cmd = cmd & menu.command & " "
cmd = cmd & menu.extra_cmd & " "
cmd = cmd & menu.i_case & " "
cmd = cmd & menu.lines_shown & " " & $x_lines & " "
cmd = cmd & menu.prompt & quote(data.title)
cmd = cmd & menu.norm_bg & quote(data.unselected_bg)
cmd = cmd & menu.norm_fg & quote(data.unselected_fg)
cmd = cmd & menu.sel_bg & quote(data.selected_bg)
cmd = cmd & menu.sel_fg & quote(data.selected_fg)
cmd = cmd & menu.font & quote(font)
echo cmd
return cmd
proc runMenu*(data: Info, opts: varargs[string], dmenu: bool = false): string =
let cmd = genMenuCmd(data, opts, dmenu)
#echo cmd
#
# Run command and get output
var output = execCmdEx(cmd)
output.output.stripLineEnd()
return output.output
proc copyToClipboard*(str: string) =
if wayland:
discard execCmd("wl-copy " & str)
else:
discard execCmd("echo -n " & quote(str) & " | xclip -selection clipboard")
proc getCurrentClipboardContent*(): string =
var str = ""
if wayland:
let cur = execCmdEx("wl-paste")
if cur.exitcode == 0:
str = cur[0]
else:
echo cur
else:
let cur = execCmdEx("xsel -o -b")
if cur.exitcode == 0:
str = cur[0]
else:
echo cur
return strip(str)
proc outputData*(data: Info, args: varargs[string]): string {.discardable.} =
var output = ""
if tool == DMENU:
output = runMenu(data,args, dmenu = true)
elif loop:
# mainly for i3bar/i3blocks compatible output
var j_data = data
if j_data.html_text != "":
j_data.full_text = j_data.html_text
echo $$j_data
else:
# if all else fails, use dmenu (default)
output = runMenu(data,args)
return output
proc getSyncDir*(): string =
if existsOrCreateDir(WM_TOOLS_SYNC_DIR):
echo "Sync Dir already exists"
return WM_TOOLS_SYNC_DIR
return WM_TOOLS_SYNC_DIR
proc checkCacheDir() =
if not dirExists(WM_TOOLS_DIR):
createDir(WM_TOOLS_DIR)
# At Start up:
checkCacheDir()
let args* = getArguments()
for idx, arg in args:
case arg:
of "noloop":
stoploop = true
of "i3bar":
# I've kind of changed from using an i3bar to using #nobar so i3bar
# isn't really supported any more but this is here for backwards compatibility
loop = true
stoploop = false
of "dmenu":
stoploop = true
tool = DMENU
of "rofi":
stoploop = true
tool = ROFI

4
install.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
nimble install -y
cp -v "wmtools" "$HOME/.local/bin/wmtools"

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Display NIC IP address and link to manage NICs"
license = "GPL-3.0-or-later"
srcDir = "src"
bin = @["netwurrk"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "src"
bin = @["passwurrd"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "A ping clock - display current ping time"
license = "GPL-3.0-or-later"
srcDir = "src"
bin = @["pingclurrk"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,4 +0,0 @@
{
"version": 1,
"reverseDeps": {}
}

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "A new awesome nimble package"
license = "AGPL-3.0-or-later"
srcDir = "src"
bin = @["pw_generaturr"]
# Dependencies
requires "nim >= 2.0.0"

View file

@ -1,65 +0,0 @@
#import ../../globurrl
import httpclient
import json
import strutils
import random
import parseopt
import os
var length = 4
var number = 10
var word_len = 5
proc getNumber(size: int = 4): string =
var num = ""
for _ in countup(1,size):
randomize()
let roll = rand(0..9)
num &= $roll
return num
proc parsePw(body: string) =
let j = body.parseJson
for pass in j.getElems:
var p = pass.getStr.capitalizeAscii
p &= getNumber(length)
echo p
proc getPW() =
var c = newHttpClient()
try:
let url = "https://random-word-api.herokuapp.com/word?number=" & $number & "&length=" & $word_len
echo url
let resp = c.get(url)
if resp.status == $Http200:
parsePw(resp.body)
except:
echo getCurrentExceptionMsg()
proc parseArgs() =
var p = initOptParser(commandLineParams())
while true:
p.next()
case p.kind
of cmdEnd: break
of cmdShortOption, cmdLongOption:
if p.val == "":
#echo "Option: ", p.key
discard
else:
#echo "Option and value: ", p.key, ", ", p.val
case p.key
of "length":
word_len = parseInt(p.val)
of "number":
number = parseInt(p.val)
of cmdArgument:
#echo "Argument: ", p.key
discard
if isMainModule:
parseArgs()
getPW()

View file

@ -1,14 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "A Remmina client tool and opener"
license = "GPL-3.0-or-later"
srcDir = "src"
bin = @["remmina_choosurr"]
# Dependencies
requires "nim >= 1.6.6"
requires "configparser >= 0.1.0"

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "A screenshot client, works on both X11 and Wayland"
license = "GPL-3.0-or-later"
srcDir = "src"
bin = @["screenshurrt"]
# Dependencies
requires "nim >= 1.6.6"

11
src/common.nim Normal file
View file

@ -0,0 +1,11 @@
import model/config
import model/tool
import model/info
export config
export tool
export info
var myConfig* = newConfig()

22
src/common/colours.nim Normal file
View file

@ -0,0 +1,22 @@
const background* = "#000000"
const backgroundalt* = "#bb222222"
const backgroundalt2* = "#bb333333"
const foreground* = "#dfdfdf"
const foregroundalt* = "#777"
const foregroundalt2* = "#ccc"
const black* = "#000000"
const white* = "#FFFFFF"
const yellow* = "#ffb52a"
const red* = "#e60053"
const purple* = "#9f78e1"
const blue* = "#0a6cf5"
const lightblue* = "#7296EF"
const lighterblue* = "#B5DDF7"
const green* = "#4b9901"
const lightgreen* = "#00ff00"
const grey* = "#dfdfdf"
const darkgrey* = "#444"
const primary* = yellow
const secondary* = red
const alert* = "#bd2c40"

55
src/dispatcher.nim Normal file
View file

@ -0,0 +1,55 @@
import common
import util/furrytime
import util/pingclock
import util/batturry
import util/volurrme
import util/netwurrk
import util/wirelurrs
import util/emurrji
import util/calendurr
import util/remminurr
import util/passwurrd
import util/pw_generaturr
import util/temperaturr
import util/screenshurrt
import util/calculaturr
import util/brightnurrs
import util/tideurrl
proc dispatch*(cfg: Config) =
case cfg.run
of FurryTime:
furrytime.go()
of PingClock:
pingclock.go()
of Batturry:
batturry.go()
of Volurrme:
volurrme.go()
of Netwurrk:
netwurrk.go()
of Wirelurrs:
wirelurrs.go()
of Emurrji:
emurrji.go()
of Calendurr:
calendurr.go()
of Remminurr:
remminurr.go()
of Passwurrd:
passwurrd.go()
of PasswurrdGeneraturr:
pw_generaturr.go()
of Temperaturr:
temperaturr.go()
of Screenshurrt:
screenshurrt.go()
of Calculaturr:
calculaturr.go()
of Brightnurrs:
brightnurrs.go()
of Tideurrl:
tideurrl.go()
else:
echo "No valid run command given"

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

@ -0,0 +1,6 @@
type
BrightnessArg* = enum
None,
BrightUp,
BrightDown

49
src/model/config.nim Normal file
View file

@ -0,0 +1,49 @@
import os
import parsetoml
import tool
import screenshot
type
Config* = ref object
exec*: string
run*: Tool
max_lines*: int
prepend*: bool
screenshot_tool*: ScreenshotTool
let config_dir* = getHomeDir() & ".config/wm_tools/"
let config_file* = config_dir & "config.toml"
proc `$`(c: Config): string =
var str = "exec = \"" & c.exec & "\"\n"
str &= "prepend = " & $c.prepend & "\n"
str &= "screenshot_tool = \"" & $c.screenshot_tool & "\"\n"
str &= "max_lines = " & $c.max_lines
str &= "\n"
return str
proc newConfig*(): Config =
var cfg = Config()
cfg.exec = "rofi -dmenu"
cfg.prepend = true
cfg.screenshot_tool = Maim
cfg.max_lines = 20
discard existsOrCreateDir(config_dir)
if not fileExists(config_file):
writeFile(config_file,$cfg)
else:
let content = readFile(config_file)
try:
let toml = parseString(content)
if toml.hasKey("exec"):
cfg.exec = toml["exec"].getStr
if toml.hasKey("max_lines"):
cfg.max_lines = toml["max_lines"].getInt
if toml.hasKey("screenshot_tool"):
cfg.screenshot_tool = toml["screenshot_tool"].getStr.toScreenshotTool
except:
echo "Error with Config File:"
echo getCurrentExceptionMsg()
return cfg

11
src/model/info.nim Normal file
View file

@ -0,0 +1,11 @@
type
Info* = object
title*: string
full_text*: string
html_text*: string
short_text*: string
args*: seq[string]
proc newInfo*(str: string = "Info"): Info =
var title = str
return Info(title: title)

14
src/model/pwgen.nim Normal file
View file

@ -0,0 +1,14 @@
type
PWGen* = object
to_terminal*: bool
word_len*: int
digits*: int
number*: int
proc newPWGen*(): PWGen =
var pw = PWGen()
pw.word_len = 5
pw.digits = 4
pw.number = 10
return pw

72
src/model/screenshot.nim Normal file
View file

@ -0,0 +1,72 @@
import sequtils
import strutils
type
Screenshot* = object
size*: ScreenshotSize
tool*: ScreenshotTool
ScreenshotSize* = enum
None = ""
Region = "region",
Full = "fullscreen",
Window = "window"
ScreenshotTool* = enum
None = ""
Maim = "maim"
proc newScreenshot*(): Screenshot =
var ss = Screenshot()
ss.tool = Maim
ss.size = None
return ss
proc toScreenshotSize*(str: string): ScreenshotSize =
case str
of "region": return Region
of "fullscreen": return Full
of "window": return Window
else: return None
proc isScreenshotSize*(str: string): bool =
return str.toScreenshotSize != None
proc ScreenshotSizes*(): seq[string] =
var sizes: seq[string] = @[]
for item in ScreenshotSize.toSeq:
if item != None:
sizes.add($item)
return sizes
proc toScreenshotTool*(str: string): ScreenshotTool =
case str
of "maim": return Maim
else: return None
proc isScreenshotTool*(str: string): bool =
return str.toScreenshotTool != None
proc command*(tool: ScreenshotTool): string =
case tool
of Maim: return "maim -u %s --format png %f"
else: return ""
proc activeWindowCommand*(tool: ScreenshotTool): string =
var cmd = tool.command()
# where %s is an extra flag or process, i.e. xdotool for getting active window
case tool
of Maim:
cmd = cmd.replace("%s","-i $(xdotool getactivewindow)")
else: return cmd
return cmd
proc regionCommand*(tool: ScreenshotTool): string =
var cmd = tool.command()
case tool
of Maim:
cmd = cmd.replace("%s","-s")
else: return cmd
return cmd
const CLIPBOARD_CMD* = "xclip -selection clipboard -t image/png"

22
src/model/tides.nim Normal file
View file

@ -0,0 +1,22 @@
import times
const TIDE_URL* = "https://www.tidetimes.org.uk/%LOC-tide-times"
const DEFAULT_LOC* = "exmouth-dock"
type
Tide* = ref object
state*: string
time*: string
height*: string
tomorrow*: bool
TideList* = ref object
tides*: seq[Tide]
url*: string
last_updated*: DateTime
location*: string
proc newTideList*(): TideList =
var tl = TideList()
tl.url = TIDE_URL
tl.location = DEFAULT_LOC
return tl

20
src/model/tool.nim Normal file
View file

@ -0,0 +1,20 @@
type
Tool* = enum
None,
FurryTime,
PingClock,
Batturry,
Volurrme,
Netwurrk,
Wirelurrs,
Emurrji,
Calendurr,
Remminurr,
Passwurrd,
PasswurrdGeneraturr,
Temperaturr,
Screenshurrt,
Calculaturr,
Brightnurrs,
Tideurrl

4
src/model/volume.nim Normal file
View file

@ -0,0 +1,4 @@
type
VolArg* = enum
None, VolUp, VolDown, VolMute

63
src/output.nim Normal file
View file

@ -0,0 +1,63 @@
import osproc
import strutils
import common
proc stripQuotes(str: string): string =
var text = replace(str,"\"","&quot;")
return text
proc quote*(str: string): string =
var text = str
# May need to put some further work to escape some special chars here
text = stripQuotes(text)
# Put leading and ending quote marks in
return " \"" & text & "\" "
# ^ Add a spaces ^ so the previous flag isn't touching
proc markup(str: string): string =
# Placeholder proc for future use
var text = stripQuotes(str)
return text
proc copyToClipboard*(str: string): bool {.discardable.} =
let ok = execCmd("echo -n " & quote(str) & " | xclip -selection clipboard")
return ok == 0
proc genMenuCmd(data: Info, opts: varargs[string]): string =
var cmd = ""
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 != "":
let text = markup data.full_text
cmd &= text & "\n"
else:
x_lines -= 1
for opt in opts:
let text = markup opt
cmd &= text & "\n"
cmd.removeSuffix("\n")
if x_lines > myConfig.max_lines: x_lines = myConfig.max_lines
if myConfig.prepend:
cmd = "echo -e" & quote(cmd) & "| "
cmd &= myConfig.exec
cmd &= " -i" # set case insensitive
cmd &= " -p" & quote(data.title)
cmd &= "-l " & $x_lines
echo "Sending command:\n" & cmd
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
proc outputData*(data: Info, args: varargs[string,`$`]): string {.discardable.} =
var output = runExec(data,args)
return output

179
src/parser.nim Normal file
View file

@ -0,0 +1,179 @@
import os
import argparse
import common
import model/pwgen
import model/volume
import model/brightness
import model/screenshot
import model/tides
proc parseArgs*() =
let params = commandLineParams()
var p = newParser:
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)
try:
var opts = p.parse(params)
case opts.input
of "furrytime", "fuzzytime", "time":
myConfig.run = FurryTime
of "pingclock", "pingclurrk", "ping":
myConfig.run = PingClock
of "batturry", "battery", "bat":
myConfig.run = Batturry
of "volurrme", "volume", "vol":
myConfig.run = Volurrme
of "netwurrk", "network", "net":
myConfig.run = Netwurrk
of "wirelurrs", "wireless", "wifi":
myConfig.run = Wirelurrs
of "emurrji", "emoji":
myConfig.run = Emurrji
of "calendurr", "calender", "cal":
myConfig.run = Calendurr
of "remminurr", "remmina", "rdp", "rem":
myConfig.run = Remminurr
of "passwurrd", "password", "pw":
myConfig.run = Passwurrd
of "passwurrdgeneraturr", "passwordgenerator", "pwgen":
myConfig.run = PasswurrdGeneraturr
of "temperaturr", "temperature", "temp":
myConfig.run = Temperaturr
of "screenshurrt", "screenshot", "screeny":
myConfig.run = Screenshurrt
of "calculaturr", "calculator", "calc":
myConfig.run = Calculaturr
of "brightnurrs", "brightness", "bright":
myConfig.run = Brightnurrs
of "tideurrl", "tides":
myConfig.run = Tideurrl
else:
echo p.help
quit(1)
except ShortCircuit as err:
if err.flag == "argparse_help":
echo err.help
quit(1)
except UsageError:
stderr.writeLine getCurrentExceptionMsg()
quit(1)
proc parseVolArgs*(): VolArg =
var arg: VolArg
let params = commandLineParams()
var p = newParser:
help("Args for volurrme")
arg("volurrme",help="can only ever be 'volurrme' as you won't have gotten this far otherwise")
arg("adjust",help="up, down, or mute",default=some(""))
try:
var opts = p.parse(params)
case opts.adjust
of "volup", "up":
arg = VolUp
of "voldown", "down":
arg = VolDown
of "mute","volmute":
arg = VolMute
except ShortCircuit as err:
if err.flag == "argparse_help":
echo err.help
quit(1)
except UsageError:
stderr.writeLine getCurrentExceptionMsg()
quit(1)
return arg
proc parseBrightnessArgs*(): BrightnessArg =
var arg: BrightnessArg
let params = commandLineParams()
var p = newParser:
help("Args for volurrme")
arg("brightnurrs",help="can only ever be 'brightnurrs' as you won't have gotten this far otherwise")
arg("adjust",help="up, down, or mute",default=some(""))
try:
var opts = p.parse(params)
case opts.adjust
of "up":
arg = BrightUp
of "down":
arg = BrightDown
except ShortCircuit as err:
if err.flag == "argparse_help":
echo err.help
quit(1)
except UsageError:
stderr.writeLine getCurrentExceptionMsg()
quit(1)
return arg
proc parsePWGenArgs*(): PWGen =
var gen = newPWGen()
let params = commandLineParams()
var p = newParser:
help("Args for pw_generaturr")
arg("pwgen",help="can only ever be 'pwgen' as you won't have gotten this far otherwise")
flag("-o","--output",help="outputs to terminal instead of something else")
option("-l","--length",help="Length of the word part of the password")
option("-d","--digits",help="Length of the number part of the password",default=some(""))
option("-n","--number",help="Number of passwords to return")
try:
var opts = p.parse(params)
if opts.length != "":
gen.word_len = parseInt(opts.length)
if opts.digits != "":
gen.digits = parseInt(opts.digits)
if opts.number != "":
gen.number = parseInt(opts.number)
gen.to_terminal = not opts.output
except ShortCircuit as err:
if err.flag == "argparse_help":
echo err.help
quit(1)
except UsageError:
stderr.writeLine getCurrentExceptionMsg()
quit(1)
return gen
proc parseScreenshotArgs*(): Screenshot =
var ss = newScreenshot()
ss.tool = myConfig.screenshot_tool
let params = commandLineParams()
var p = newParser:
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")
try:
var opts = p.parse(params)
if opts.size != "":
ss.size = opts.size.toScreenshotSize()
except ShortCircuit as err:
if err.flag == "argparse_help":
echo err.help
quit(1)
except UsageError:
stderr.writeLine getCurrentExceptionMsg()
quit(1)
return ss
proc parseTideurrlArgs*(): TideList =
var t = newTideList()
let params = commandLineParams()
var p = newParser:
help("Args for tideurrl")
arg("tideurrl",help="can only ever be 'tideurrl' as you won't have gotten this far otherwise")
option("-l","--loc",help="location name")
try:
var opts = p.parse(params)
if opts.loc != "":
t.location = opts.loc
except ShortCircuit as err:
if err.flag == "argparse_help":
echo err.help
quit(1)
except UsageError:
stderr.writeLine getCurrentExceptionMsg()
quit(1)
return t

View file

@ -1,5 +1,8 @@
import ../../globurrl import strutils
import std/[strutils,os]
import ../common
import ../common/colours
import ../output
const battery = "BAT0" const battery = "BAT0"
const ok_fg = lightgreen const ok_fg = lightgreen
@ -43,7 +46,7 @@ proc getCharge(): int =
echo "Error getting battery level : " & getCurrentExceptionMsg() echo "Error getting battery level : " & getCurrentExceptionMsg()
return charge return charge
proc getDesign(charge: int, state: bool): (string, string, string, string, string) = proc getDesign(charge: int, state: bool): string =
var icon = "" var icon = ""
var icon_colour = ok_fg var icon_colour = ok_fg
var col = default_fg var col = default_fg
@ -81,46 +84,25 @@ proc getDesign(charge: int, state: bool): (string, string, string, string, strin
icon = "x " icon = "x "
let main_text = icon & " " & $charge & "%" let main_text = icon & " " & $charge & "%"
# This next line is here for i3bar purposes
let html_text = "<span foreground=\"" & icon_colour & "\">" & icon & "</span>" & $charge & "%"
return (html_text,main_text, col, bg, border) return main_text
proc getOutput(charge: int, state: bool): Info = proc getOutput(charge: int, state: bool): Info =
let (html_text,main_text,col,bg_col,highlight_col) = get_design(charge, state) let main_text = get_design(charge, state)
var data = newInfo("Batturry") var data = newInfo("Batturry")
# TODO check if html text works with rofi
data.full_text = main_text data.full_text = main_text
data.selected_bg = highlight_col
data.selected_fg = col # may just want `black` here
# i3bar stuff
data.html_text = html_text
data.color = col
data.border = highlight_col
data.background = bg_col
return data return data
proc getBatteryInfo() = proc getBatteryInfo() =
var last_charge = -1 let charge = getCharge()
var last_state = false let state = isCharging()
while true: let data = getoutput(charge, state)
let charge = getCharge() outputData(data)
let state = isCharging()
if charge != last_charge or state != last_state:
let data = getoutput(charge, state)
outputData(data)
last_charge = charge
last_state = state
if stoploop:
break
sleep(1000)
proc main() = proc go*() =
if batteryExists(): if batteryExists():
getBatteryInfo() getBatteryInfo()
else:
switchTwmMode()
if isMainModule:
main()

98
src/util/brightnurrs.nim Normal file
View file

@ -0,0 +1,98 @@
import os
import strutils
import osproc
import math
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 default_value = "5"
proc getBacklight(): string =
for dir in walkDir("/sys/class/backlight"):
echo dir.path
var bl = dir.path.replace("/sys/class/backlight/","")
echo bl
return bl
proc getLimit(backlight: string): int =
try:
if backlight != "":
let back_l = readFile("/sys/class/backlight/" & backlight & "/max_brightness")
return parseInt(strip(back_l))
except:
echo "Error getting backlight max : ", getCurrentExceptionMsg()
return -1
proc getDesign(pcnt: float): string =
var icon = "🌑"
case pcnt:
of 85..100:
icon = "🌕"
of 75..85:
icon = "🌖"
of 50..75:
icon = "🌗"
of 35..50:
icon = "🌘"
else:
icon = "🌑"
let percent = toInt(round(pcnt,0))
let text = icon & " " & $percent & "%"
return text
proc brightnessUp() =
let cmd = replace(UP,"%v",default_value)
discard execCmd(cmd)
proc brightnessDown() =
let cmd = replace(DOWN,"%v",default_value)
discard execCmd(cmd)
proc getBrightness*(backlight: string) =
var data = newInfo("Brightnurrs")
if 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
let args = @["up", "down"]
let option = outputData(data,args)
if option in args:
case option:
of "up":
brightnessUp()
backlight.getBrightness()
of "down":
brightnessDown()
backlight.getBrightness()
else:
try:
let i = parseInt(option)
let cmd = replace(SET,"%v",$i)
discard execCmd(cmd)
backlight.getBrightness()
except:
echo getCurrentExceptionMsg()
proc go*() =
let backlight = getBacklight()
let barg = parseBrightnessArgs()
case barg:
of BrightUp:
brightnessUp()
of BrightDown:
brightnessDown()
else:
backlight.getBrightness()

View file

@ -1,9 +1,14 @@
import ../../globurrl import osproc
import std/[osproc,strutils,sequtils] import strutils
import sequtils
import ../common
import ../output
# using qalc as it has a lot of nice inbuilt features # using qalc as it has a lot of nice inbuilt features
# may be nice to make it totally non-dependant on other things though # may be nice to make it totally non-dependant on other things though
const exitSeq = @["---","exit"]
proc doCalculation(calc: string) = proc doCalculation(calc: string) =
var info = newInfo("Calculaturr") var info = newInfo("Calculaturr")
let ans_cmd_terse = "echo \"" & calc & "\" | qalc +u8 -terse -color=never | awk '!/^>/ && !/^$/ {gsub(/^[ \\t]+|[ \\t]+$/, \"\", $0); print}'" let ans_cmd_terse = "echo \"" & calc & "\" | qalc +u8 -terse -color=never | awk '!/^>/ && !/^$/ {gsub(/^[ \\t]+|[ \\t]+$/, \"\", $0); print}'"
@ -14,17 +19,16 @@ proc doCalculation(calc: string) =
ans_terse.output.stripLineEnd() ans_terse.output.stripLineEnd()
let answers = @[strip(ans_full.output), let answers = @[strip(ans_full.output),
strip(ans_terse.output)] strip(ans_terse.output)]
let args = concat(answers,@["exit"]) let args = concat(answers,exitSeq)
var cmd = outputData(info, args) var cmd = outputData(info, args)
cmd.stripLineEnd()
if cmd in answers: if cmd in answers:
copyToClipboard(cmd) copyToClipboard(cmd)
elif cmd == "exit" or cmd == "": elif cmd in exitSeq or cmd == "":
return return
else: else:
doCalculation(cmd) doCalculation(cmd)
proc main() = proc go*() =
var info = newInfo("Calculaturr") var info = newInfo("Calculaturr")
let args = @["exit"] let args = @["exit"]
let cmd = outputData(info, args) let cmd = outputData(info, args)
@ -32,5 +36,3 @@ proc main() =
doCalculation(cmd) doCalculation(cmd)
return return
if isMainModule:
main()

View file

@ -1,8 +1,10 @@
import ../../globurrl import times
import std/[times,osproc,re] import osproc
import re
import ../common
import ../output
const default_bg = blue
const default_fg = white
const default_format = "yyyy-MM-dd" const default_format = "yyyy-MM-dd"
const cal_pos_x = "20" const cal_pos_x = "20"
const cal_pos_y = "20" const cal_pos_y = "20"
@ -10,9 +12,6 @@ const cal_pos_y = "20"
proc getObject(date: string): Info = proc getObject(date: string): Info =
var data = newInfo("Calendurr") var data = newInfo("Calendurr")
data.full_text = date data.full_text = date
data.border = default_bg
data.selected_bg = default_bg
data.selected_fg = default_fg
return data return data
proc newCalendar(): string = proc newCalendar(): string =
@ -24,13 +23,7 @@ proc newCalendar(): string =
""" """
return c return c
proc openCalendar*(input: i3barInput) = proc openCalendar() =
var c = newCalendar()
c = replace(c,re"%pos_x", $(input.x - 111))
c = replace(c,re"%pos_y", $input.y)
discard execCmd(c)
proc dmenuCalendar() =
var c = newCalendar() var c = newCalendar()
c = replace(c,re"%pos_x", cal_pos_x) c = replace(c,re"%pos_x", cal_pos_x)
c = replace(c,re"%pos_y", cal_pos_y) c = replace(c,re"%pos_y", cal_pos_y)
@ -41,12 +34,9 @@ proc getDate*() =
let data = getObject(date_today) let data = getObject(date_today)
let output = outputData(data) let output = outputData(data)
if output == date_today: if output == date_today:
dmenuCalendar() openCalendar()
proc main() = proc go*() =
getDate() getDate()
if isMainModule:
main()

View file

@ -1,8 +1,10 @@
import ../../globurrl import ../lib/emurrjilist
import lib/emurrjilist import ../common
import std/[re] import ../output
proc main() = import re
proc go*() =
var info = newInfo("Emurrji") var info = newInfo("Emurrji")
var args = getEmoji() var args = getEmoji()
args.add("exit") args.add("exit")
@ -16,5 +18,3 @@ proc main() =
copyToClipboard(emoji) copyToClipboard(emoji)
return return
if isMainModule:
main()

View file

@ -1,28 +1,7 @@
import ../../globurrl import times
import std/[times]
const default_bg = lightblue import ../model/info
const default_fg = black import ../output
proc getHour(hr: int): string
proc getMinute(min: int): string
proc getFuzzyTime(): string =
let tm = now()
var hr = tm.hour()
let min = tm.minute()
var link = "past"
if min > 32 :
link = "to"
case hr:
of 23:
hr = 0
else:
hr = hr + 1
if min >= 58 or min <= 02:
return getHour(hr) & " " & getMinute(min)
else:
return getMinute(min) & " " & link & " " & getHour(hr)
proc getHour(hr: int): string = proc getHour(hr: int): string =
case hr: case hr:
@ -56,7 +35,7 @@ proc getHour(hr: int): string =
proc getMinute(min: int): string = proc getMinute(min: int): string =
case min: case min:
of 58,59,0,1,2: of 58,59,0,1,2:
return "o'clock" return "oclock"
of 3,4,5,6,7,53,54,55,56,57: of 3,4,5,6,7,53,54,55,56,57:
return "five" return "five"
of 8,9,10,11,12,48,49,50,51,52: of 8,9,10,11,12,48,49,50,51,52:
@ -72,14 +51,26 @@ proc getMinute(min: int): string =
else: else:
return "error" return "error"
proc getFuzzyTime(): string =
let tm = now()
var hr = tm.hour()
let min = tm.minute()
var link = "past"
if min > 32 :
link = "to"
case hr:
of 23:
hr = 0
else:
hr = hr + 1
if min >= 58 or min <= 02:
return getHour(hr) & " " & getMinute(min)
else:
return getMinute(min) & " " & link & " " & getHour(hr)
proc getObject(time: string): Info = proc getObject(time: string): Info =
var data = newInfo("Furry Time") var data = newInfo("Furry Time")
data.full_text = time data.full_text = time
data.selected_bg = default_bg
data.selected_fg = default_fg
#i3bar stuff
data.color = default_fg
data.border = default_fg
return data return data
proc show(time: string, next_fuzzy: bool = false) = proc show(time: string, next_fuzzy: bool = false) =
@ -94,10 +85,7 @@ proc show(time: string, next_fuzzy: bool = false) =
let t = now().format("HH:mm:ss") let t = now().format("HH:mm:ss")
show(t, true) show(t, true)
proc go*() =
proc main() =
let time = getFuzzyTime() let time = getFuzzyTime()
show(time) show(time)
if isMainModule:
main()

View file

@ -1,10 +1,12 @@
import ../../globurrl import os
import std/[os,osproc,strutils,sequtils] import osproc
import strutils
import sequtils
import ../common
import ../output
const default_fg = black
const default_bg = purple
const mng_cmd = "alacritty -e nmtui-connect" const mng_cmd = "alacritty -e nmtui-connect"
const nics: seq[string] = @["wlan0","bridge0", "enp3s0","wlp2s0","enp0s20f0u3"]
proc getIP(nic: string): string = proc getIP(nic: string): string =
let cmd = "ifconfig " & nic & " | grep inet | awk -F\" \" '{print $2}' | head -1 | awk '{print $1}'" let cmd = "ifconfig " & nic & " | grep inet | awk -F\" \" '{print $2}' | head -1 | awk '{print $1}'"
@ -29,10 +31,6 @@ proc getConnState(nic: string): (string, string) =
proc getObject(): Info = proc getObject(): Info =
var data = newInfo("Netwurrk") var data = newInfo("Netwurrk")
data.selected_bg = default_bg
data.selected_fg = default_fg
# i3bar stuff
data.border = default_bg
return data return data
proc getNetInfo*(my_nics: seq[string]) = proc getNetInfo*(my_nics: seq[string]) =
@ -59,8 +57,6 @@ proc getNics*(): seq[string] =
return my_nics return my_nics
return @["no-nic"] return @["no-nic"]
proc main() = proc go*() =
getNetInfo(getNics()) getNetInfo(getNics())
if isMainModule:
main()

View file

@ -1,5 +1,10 @@
import ../../globurrl.nim import os
import std/[os,osproc,re,strutils] import osproc
import re
import strutils
import ../common
import ../output
const pw_store = getHomeDir() & ".password-store/" const pw_store = getHomeDir() & ".password-store/"
var passwords: seq[string] = @[] var passwords: seq[string] = @[]
@ -20,12 +25,9 @@ proc getPasswords(): seq[string] =
proc passwordToClipboard(password: string) = proc passwordToClipboard(password: string) =
discard execCmd("pass show -c " & password) discard execCmd("pass show -c " & password)
proc main() = proc go*() =
var info = newInfo("Passwurrd") var info = newInfo("Passwurrd")
var pws = getPasswords() var pws = getPasswords()
let output = outputData(info,pws) let output = outputData(info,pws)
if output in passwords: if output in passwords:
passwordToClipboard(output) passwordToClipboard(output)
when isMainModule:
main()

View file

@ -1,15 +1,11 @@
import ../../globurrl import osproc
import std/[osproc, re, strutils] import re
import strutils
import ../common
import ../output
const host: string = "9.9.9.9" const host: string = "9.9.9.9"
const default_bg = blue
const default_fg = white
const medium_bg = alert
const medium_fg = black
const alert_bg = alert
const alert_fg = black
const warning_bg = red
const warning_fg = white
let ping_re = re(r"time=[0-9.]+") let ping_re = re(r"time=[0-9.]+")
const ping_cmd: string = "ping -4 -c 1 " & host const ping_cmd: string = "ping -4 -c 1 " & host
@ -47,39 +43,13 @@ proc getObject(ping: float): Info =
var data = newInfo("Ping Clurrk") var data = newInfo("Ping Clurrk")
data.full_text = text data.full_text = text
# i3bar stuff # i3bar stuff
data.color = default_fg
data.border = default_bg
data.background = black
data.selected_bg = default_bg
data.selected_fg = default_fg
case state:
of 1:
data.selected_bg = medium_bg
data.selected_fg = medium_fg
# i3bar stuff
data.color = medium_bg
of 2:
data.selected_bg = alert_bg
data.selected_fg = alert_fg
# i3bar stuff
data.color = alert_bg
of 9:
data.selected_bg = warning_bg
data.selected_fg = warning_fg
# i3bar stuff
data.color = warning_bg
else:
#default options already set
let ok = true
return data return data
proc main() = proc go*() =
let ping = get_ping() let ping = get_ping()
let data = getObject(ping) let data = getObject(ping)
let output = outputData(data) let output = outputData(data)
if output == data.full_text: if output == data.full_text:
main() go()
if isMainModule:
main()

View file

@ -0,0 +1,65 @@
import httpclient
import json
import strutils
import random
import ../parser
import ../model/pwgen
import ../common
import ../output
var passwords: seq[string]
proc getNumber(size: int = 4): string =
var num = ""
for _ in countup(1,size):
randomize()
let roll = rand(0..9)
num &= $roll
return num
proc parsePasswords(body: string, digits: int) =
passwords = @[]
let j = body.parseJson
for pass in j.getElems:
var p = pass.getStr.capitalizeAscii
p &= getNumber(digits)
passwords.add(p)
proc getPasswords(pwgen: PWGen) =
var c = newHttpClient()
try:
let url = "https://random-word-api.herokuapp.com/word?number=" & $pwgen.number & "&length=" & $pwgen.word_len
let resp = c.get(url)
if resp.status == $Http200:
parsePasswords(resp.body, pwgen.digits)
except:
stderr.writeLine getCurrentExceptionMsg()
proc getOutput(): Info =
var data = newInfo("PW Generaturr")
data.full_text = "Refresh"
return data
proc goOutput(args: PWGen) =
let data = getoutput()
let selected = outputData(data,passwords)
if selected in passwords:
copyToClipboard(selected)
elif selected == "Refresh":
getPasswords(args)
goOutput(args)
proc go*() =
echo "Getting passwords..."
let args = parsePWGenArgs()
getPasswords(args)
if args.to_terminal:
for pw in passwords:
echo pw
else:
echo "Outputting to app"
goOutput(args)
if isMainModule:
go()

View file

@ -1,20 +1,18 @@
import ../../globurrl import os
import std/[os,osproc,tables,algorithm] import osproc
import tables
import algorithm
import configparser import configparser
import ../common
import ../output
const REMMINA_DIR = getHomeDir() & ".local/share/remmina" const REMMINA_DIR = getHomeDir() & ".local/share/remmina"
const SWITCH_WS = false
const REMMINA_WS = "4"
var sessions = initTable[string,string]() var sessions = initTable[string,string]()
var names: seq[string] = @[] var names: seq[string] = @[]
proc main()
proc switchWorkspace() =
if SWITCH_WS and REMMINA_WS != "":
discard execCmd("i3-msg workspace number " & REMMINA_WS)
proc getRemminaFiles(): seq[string] = proc getRemminaFiles(): seq[string] =
if len(names) < 1: if len(names) < 1:
for file in walkFiles(REMMINA_DIR & "/*.remmina"): for file in walkFiles(REMMINA_DIR & "/*.remmina"):
@ -38,6 +36,8 @@ proc startRemmina(conn: string) =
let session = sessions[conn] let session = sessions[conn]
discard execCmd("remmina -c " & quote(session)) discard execCmd("remmina -c " & quote(session))
proc go*() # adding as need to refer back to it in selectRemmina
proc selectRemmina(conn: string) = proc selectRemmina(conn: string) =
var info = newInfo("Remmina Choosurr : " & conn) var info = newInfo("Remmina Choosurr : " & conn)
let args = @["connect", "edit", "back"] let args = @["connect", "edit", "back"]
@ -51,11 +51,9 @@ proc selectRemmina(conn: string) =
editRemmina(conn) editRemmina(conn)
#switchWorkspace() #switchWorkspace()
of "back": of "back":
main() go()
proc go*() =
proc main() =
var info = newInfo("Remmina Choosurr") var info = newInfo("Remmina Choosurr")
var args: seq[string] = getRemminaFiles() var args: seq[string] = getRemminaFiles()
args.add("new") args.add("new")
@ -68,7 +66,3 @@ proc main() =
elif output in names: elif output in names:
selectRemmina(output) selectRemmina(output)
return return
return
if isMainModule:
main()

View file

@ -1,23 +1,21 @@
import ../../globurrl import times
import std/[times,os,osproc,strutils,sequtils] import os
import osproc
import strutils
import sequtils
import ../common
import ../output
import ../parser
import ../model/screenshot
var screenshot_type = ""
const TYPES = @["region", "fullscreen", "window"]
const DATE_FORMAT = "yyyyMMdd-hhmmss" const DATE_FORMAT = "yyyyMMdd-hhmmss"
const FILENAME = "Screenshot-%d.png" const FILENAME = "Screenshot-%d.png"
const TEMP_DIR = "/tmp/" const TEMP_DIR = "/tmp/"
const SCREENSHOT_CMD = "maim -u %s --format png %f"
var RUN_CMD = SCREENSHOT_CMD
let DATE_STR = now().format(DATE_FORMAT) let DATE_STR = now().format(DATE_FORMAT)
# where %s is an extra flag or process, i.e. xdotool for getting active window
const ACTIVE_WINDOW_CMD = "-i $(xdotool getactivewindow)"
const REGION_FLAG = "-s"
var REGION_CMD = REGION_FLAG
const CLIPBOARD_CMD = "xclip -selection clipboard -t image/png"
var CLIP_CMD = CLIPBOARD_CMD
proc saveToClipboard(filename: string) = proc saveToClipboard(filename: string) =
let cmd = "cat " & filename & " | " & CLIP_CMD let cmd = "cat " & filename & " | " & CLIPBOARD_CMD
let status = execCmd(cmd) let status = execCmd(cmd)
if status == 0 and fileExists(filename): if status == 0 and fileExists(filename):
removeFile(filename) removeFile(filename)
@ -54,42 +52,44 @@ proc showScreenshotSaveSel(filename: string) =
openFile(filename) openFile(filename)
return return
proc showScreenshotTypeSel() = proc showScreenshotSizeSel(): ScreenshotSize =
let info = newInfo("Screenshurrt type") let info = newInfo("Screenshurrt type")
let args = concat(TYPES,@["---","exit"]) let args = concat(ScreenshotSizes(),@["---","exit"])
let choice = outputData(info,args) let choice = outputData(info,args)
if choice in TYPES: if choice.isScreenshotSize():
screenshot_type = choice return choice.toScreenshotSize()
elif choice == "---": elif choice == "---":
showScreenshotTypeSel() return showScreenshotSizeSel()
elif choice == "exit": elif choice == "exit":
return quit(0)
return else:
quit(0)
proc takeScreenshot() = proc takeScreenshot(ss: Screenshot) =
let filename = TEMP_DIR & FILENAME.replace("%d",DATE_STR) let filename = TEMP_DIR & FILENAME.replace("%d",DATE_STR)
var cmd = RUN_CMD.replace("%f",filename) var cmd = ss.tool.command
case screenshot_type: case ss.size:
of "window": of Window:
cmd = cmd.replace("%s",ACTIVE_WINDOW_CMD) cmd = ss.tool.activeWindowCommand()
of "region": of Region:
cmd = cmd.replace("%s",REGION_CMD) cmd = ss.tool.regionCommand()
else: #fullscreen else: #fullscreen
cmd = cmd.replace("%s","") cmd = cmd.replace("%s","")
# sleep for a bit otherwise the screen shot grabs dmenu as well # sleep for a bit otherwise the screen shot could grabs dmenu as well
sleep(1*500) sleep(1*500)
cmd = cmd.replace("%f",filename)
echo "Running command:\n" & cmd
let status = execCmd(cmd) let status = execCmd(cmd)
if status == 0: if status == 0:
showScreenshotSaveSel(filename) showScreenshotSaveSel(filename)
return return
proc go*() =
var ss = parseScreenshotArgs()
if ss.size == None:
ss.size = showScreenshotSizeSel()
if ss.size != None:
ss.takeScreenshot()
if isMainModule: if isMainModule:
for arg in args: go()
if arg in TYPES:
screenshot_type = arg
break
if screenshot_type == "":
showScreenshotTypeSel()
if screenshot_type != "":
takeScreenshot()

54
src/util/temperaturr.nim Normal file
View file

@ -0,0 +1,54 @@
import os
import re
import math
import strutils
import ../common
import ../output
proc getThermalZones(): seq[string] =
var zones: seq[string] = @[]
let dirname_re = re("thermal_zone[\\d]+")
let enabled_re = re("enabled")
for file in walkDir("/sys/class/thermal/"):
if file.path.contains(dirname_re):
let state = readFile(file.path & "/mode")
if state.contains(enabled_re):
zones.add(file.path)
return zones
proc getTemp(zone: string): int =
let temp = strip(readFile(zone & "/temp"))
return parseInt(temp)
proc getAverageTemp(zones: seq[string]): float =
var temps: int = 0
for zone in zones:
let temp = getTemp(zone)
temps += temp
let avgtemp = ceil((temps / len(zones))/1000)
return round(avgtemp,2)
proc getObject(temp: float): Info =
var icon = ""
case temp:
of 40..59:
icon = ""
of 60.. 200:
icon = ""
else:
icon = ""
let main_text = icon & " " & $temp & "°C"
var data = newInfo("Temperaturr")
data.full_text = main_text
return data
proc go*() =
let zones = getThermalZones()
let temp = getAverageTemp(zones)
let data = getObject(temp)
let option = outputData(data)
if option == data.full_text:
# Refresh
go()

110
src/util/tideurrl.nim Normal file
View file

@ -0,0 +1,110 @@
#curl https://www.tidetimes.org.uk/exmouth-dock-tide-times-20190101 | grep -E -o ">((High|Low)|([0-9]+:[0-9]+)|([0-9]+\.[0-9]+m))"
import re
import httpclient
import times
import osproc
import sequtils
import ../common
import ../parser
import ../output
import ../model/tides
const icon: string = "🌊 "
proc sortTides(tides: seq[Tide], is_tomorrow: bool = false): seq[Tide] =
let timenow = now()
var reltides: seq[Tide]
for tide in tides:
if timenow.format("HH:MM") <= tide.time and not is_tomorrow:
reltides.add(tide)
elif is_tomorrow:
reltides.add(tide)
return reltides
proc getTideData(mytides: TideList, get_tomorrow: bool = false): seq[Tide] =
var tides: seq[Tide]
let fnd = re">((High|Low)|([0-9]+:[0-9]+)|([0-9]+\.[0-9]+m))"
var client = newHttpClient(timeout = 10000)
var link = replace(mytides.url,re"\%LOC",mytides.location)
if get_tomorrow:
let tomdate = now() + initTimeInterval(days = 1)
link &= "-" & tomdate.format("yyyyMMdd")
try:
# Remember to compile with -d:ssl else this won't work
let resp = client.request(link)
if resp.status != $Http200 or resp.body == "":
var data = newInfo("Tideurrl")
data.full_text = "Error Response: " & resp.status & ":\nBody:" & resp.body
discard outputData(data)
return @[]
let data = resp.body
let times = findAll(data,fnd)
var tide: Tide
var column = 0
for idx,time in times:
if idx == 12:
break
let l = len(time) - 1
if time == ">High" or time == ">Low":
tide = Tide()
tide.state = time[1..l]
column = 1
continue
elif column == 1:
tide.time = time[1..l]
column = 2
continue
elif column == 2:
tide.height = time[1..l]
tides.add(tide)
column = 0
continue
except:
var data = newInfo("Tideurrl")
data.full_text = "Unable to get Tide Data : " & getCurrentExceptionMsg()
discard outputData(data)
return tides
proc getDesign(tl: TideList): Info =
var my_tides: seq[string] = @[]
my_tides.add(tl.location)
let tides = tl.tides
for tide in tides:
let str = icon & tide.state[0] & " " & tide.time & " " & tide.height
my_tides.add(str)
var data = newInfo("Tideurrl")
data.args = my_tides
return data
proc getTides*(mytides: var TideList, get_tomorrow: bool = false) =
mytides.tides = mytides.getTideData(get_tomorrow)
mytides.tides = sortTides(mytides.tides, get_tomorrow)
if len(mytides.tides) == 0:
return
let data = getDesign(mytides)
var opt_tomorrow = "tomorrow"
if get_tomorrow:
opt_tomorrow = "back"
let args = concat(data.args,@["---",opt_tomorrow])
let output = outputData(data,args)
if output == "tomorrow":
getTides(mytides,true)
elif output == "back":
getTides(mytides)
elif output == "---" or output == "":
return
elif output in args:
let url = replace(mytides.url,re"\%LOC",mytides.location)
discard execCmd("xdg-open " & url)
else:
mytides.location = output
getTides(mytides)
proc go*() =
var mytides = parseTideurrlArgs()
getTides(mytides)

View file

@ -1,5 +1,12 @@
import ../../globurrl import os
import std/[os,strutils,sequtils,osproc] import strutils
import sequtils
import osproc
import ../common
import ../output
import ../parser
import ../model/volume
const audio_tools = @["ncpamixer", "pavucontrol"] const audio_tools = @["ncpamixer", "pavucontrol"]
const vol_cmd = "pamixer" const vol_cmd = "pamixer"
@ -10,11 +17,14 @@ const vol_mute = vol_cmd & " -t"
const vol_default_by = "5" const vol_default_by = "5"
const vol_get = vol_cmd & " --get-volume" const vol_get = vol_cmd & " --get-volume"
const vol_get_mute = vol_cmd & " --get-mute" const vol_get_mute = vol_cmd & " --get-mute"
const default_bg = green
const default_fg = black
proc getCurrentVolume(): string {.gcsafe.} proc getCurrentVolume(): string =
let mute = execCmdEx(vol_get_mute)
if strip(mute.output) == "true":
return "muted"
let vol = execCmdEx(vol_get)
return vol.output
proc checkVolume(volume: string): string = proc checkVolume(volume: string): string =
var vol = volume var vol = volume
@ -24,11 +34,11 @@ proc checkVolume(volume: string): string =
vol = checkVolume(vol) vol = checkVolume(vol)
return vol return vol
proc getDesign(volume: string): (string,string) = proc getDesign(volume: string): string =
let vol = checkVolume(volume) let vol = checkVolume(volume)
var icon = "" var icon = ""
if vol == "muted": if vol == "muted":
return (icon & "muted", icon & "muted") return icon & "muted"
let pcnt = parseInt(strip(vol)) let pcnt = parseInt(strip(vol))
case pcnt: case pcnt:
of 85..100: of 85..100:
@ -42,15 +52,7 @@ proc getDesign(volume: string): (string,string) =
else: else:
icon = "" icon = ""
let main_text = icon & $pcnt & "%" let main_text = icon & $pcnt & "%"
let text = "<span size=\"x-large\">" & icon & "</span>" & $pcnt & "%" return main_text
return (text, main_text)
proc getCurrentVolume(): string =
let mute = execCmdEx(vol_get_mute)
if strip(mute.output) == "true":
return "muted"
let vol = execCmdEx(vol_get)
return vol.output
proc volumeUp() = proc volumeUp() =
let cmd = replace(vol_up, "%v", vol_default_by) let cmd = replace(vol_up, "%v", vol_default_by)
@ -63,18 +65,11 @@ proc volumeDown() =
proc volumeMute() = proc volumeMute() =
discard execCmd(vol_mute) discard execCmd(vol_mute)
proc getVolume*(run_once: bool = false) = proc getVolume*() =
let vol = getCurrentVolume() let vol = getCurrentVolume()
let (text, main_text) = getDesign(vol) let main_text = getDesign(vol)
var data = newInfo("Volurrme") var data = newInfo("Volurrme")
data.full_text = main_text data.full_text = main_text
data.selected_bg = default_bg
data.selected_fg = default_fg
# i3bar stuff
data.html_text = text
data.color = foreground
data.border = green
data.background = black
let args = concat(@["up", "down", "mute", "---", "exit", "---"],audio_tools) let args = concat(@["up", "down", "mute", "---", "exit", "---"],audio_tools)
let option = outputData(data,args) let option = outputData(data,args)
if option == "": if option == "":
@ -101,26 +96,20 @@ proc getVolume*(run_once: bool = false) =
try: try:
let vol = parseInt(option) let vol = parseInt(option)
let cmd = replace(vol_set, "%v", $vol) let cmd = replace(vol_set, "%v", $vol)
let x = execCmd(cmd) discard execCmd(cmd)
getVolume() getVolume()
except: except:
echo getCurrentExceptionMsg() echo getCurrentExceptionMsg()
getVolume() getVolume()
proc main() = proc go*() =
getVolume() let arg = parseVolArgs()
case arg
if isMainModule: of VolUp:
block start: volumeUp()
for arg in args: of VolDown:
case arg: volumeDown()
of "up": of VolMute:
volumeUp() volumeMute()
break start else:
of "down": getVolume()
volumeDown()
break start
of "mute":
volumeMute()
break start
main()

View file

@ -1,9 +1,11 @@
import ../../globurrl import os
import std/[os,osproc,strutils,sequtils] import osproc
import strutils
import sequtils
import ../common
import ../output
const default_bg = purple
const default_fg = white
const wlan_nics: seq[string] = @["wlan0"]
const get_ssid_cmd = "iwgetid -r" const get_ssid_cmd = "iwgetid -r"
const mng_cmd = "alacritty -e nmtui-connect" const mng_cmd = "alacritty -e nmtui-connect"
@ -28,9 +30,6 @@ proc getWifi(nic: string): (string, string) =
proc getObject(): Info = proc getObject(): Info =
var data = newInfo("Wirelurrs") var data = newInfo("Wirelurrs")
data.border = purple
data.selected_bg = default_bg
data.selected_fg = default_fg
return data return data
proc getWifiInfo*(nics: seq[string]) = proc getWifiInfo*(nics: seq[string]) =
@ -41,6 +40,8 @@ proc getWifiInfo*(nics: seq[string]) =
let data = getObject() let data = getObject()
let args = concat(lst,@["---", "manage","exit"]) let args = concat(lst,@["---", "manage","exit"])
let output = outputData(data, args) let output = outputData(data, args)
if output in lst:
discard execCmd(mng_cmd)
case output: case output:
of "manage": of "manage":
discard execCmd(mng_cmd) discard execCmd(mng_cmd)
@ -49,16 +50,20 @@ proc getWifiInfo*(nics: seq[string]) =
of "---": of "---":
return return
proc main() = proc getWiFiNICs(): seq[string] =
var my_nics: seq[string] = @[] var my_nics: seq[string] = @[]
for nic in wlan_nics: for path in walkDir("/sys/class/net/"):
if dirExists("/sys/class/net/" & nic): if dirExists(path.path & "/wireless"):
let nic = path.path.replace("/sys/class/net/","")
my_nics.add(nic) my_nics.add(nic)
return my_nics
proc go*() =
let my_nics = getWiFiNICs()
if len(my_nics) > 0: if len(my_nics) > 0:
getWifiInfo(my_nics) getWifiInfo(my_nics)
else: else:
switchTwmMode() var data = getObject()
echo "No WLAN" data.full_text = "No WLAN"
discard outputData(data)
if isMainModule:
main()

11
src/wmtools.nim Normal file
View file

@ -0,0 +1,11 @@
# This is just an example to get you started. A typical binary package
# uses this file as the main entry point of the application.
#
import common
import parser
import dispatcher
when isMainModule:
parseArgs()
dispatch myConfig

View file

@ -1,72 +0,0 @@
import ../../globurrl
import std/[os,re,math,strutils]
const default_bg = green
const default_fg = black
const alert_bg = yellow
const alert_fg = black
const warning_bg = red
const warning_fg = black
proc getThermalZones(): seq[string] =
var zones: seq[string] = @[]
let dirname = re("thermal_zone[\\d]+")
for file in walkDir("/sys/class/thermal/"):
if contains(file.path,dirname):
let state = readFile(file.path & "/mode")
if contains(state,re"enabled"):
zones.add(file.path)
return zones
proc getTemp(zone: string): int =
let temp = strip(readFile(zone & "/temp"))
return parseInt(temp)
proc getAverageTemp(zones: seq[string]): int =
var temps: int = 0
for zone in zones:
let temp = getTemp(zone)
temps += temp
let avgtemp = ceil((temps / len(zones))/1000)
return toInt(round(avgtemp,0))
proc getObject(temp: int): Info =
var icon = ""
var bg_col = default_bg
var fg_col = default_fg
case temp:
of 40..59:
bg_col = alert_bg
fg_col = alert_fg
icon = ""
of 60.. 200:
bg_col = warning_bg
fg_col = warning_fg
icon = ""
else:
bg_col = default_bg
fg_col = default_fg
icon = ""
let text = "<span foreground='" & bg_col & "'>" & icon & "</span> " & $temp & "°C"
let main_text = icon & " " & $temp & "°C"
var data = newInfo("Temperaturr")
data.full_text = main_text
data.selected_bg = bg_col
data.selected_fg = fg_col
# i3bar stuff
data.html_text = text
data.color = bg_col
data.border = bg_col
return data
proc main() =
let zones = getThermalZones()
let temp = getAverageTemp(zones)
let data = getObject(temp)
let option = outputData(data)
if option == data.full_text:
# Refresh
main()
if isMainModule:
main()

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Display temp in dmenu"
license = "GPL-3.0-or-later"
srcDir = "src"
bin = @["temperaturr"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,106 +0,0 @@
#curl https://www.tidetimes.org.uk/exmouth-dock-tide-times-20190101 | grep -E -o ">((High|Low)|([0-9]+:[0-9]+)|([0-9]+\.[0-9]+m))"
import ../../globurrl
import std/[re,httpclient,times,osproc,sequtils]
# TODO:
# Pass location in as variable
const url* = "https://www.tidetimes.org.uk/%LOC-tide-times"
const loc* = "exmouth-dock"
const icon: string = "🌊 "
type
Tide = ref object
State: string
Time: string
Height: string
Tomorrow: bool
TideList = ref object
Tides: seq[Tide]
LastUpdated: DateTime
proc sortTides(tides: seq[Tide], is_tomorrow: bool = false): seq[Tide] =
let timenow = now()
var reltides: seq[Tide]
for tide in tides:
if timenow.format("HH:MM") <= tide.Time and not is_tomorrow:
reltides.add(tide)
elif is_tomorrow:
reltides.add(tide)
return reltides
proc getTideData(get_tomorrow: bool = false): seq[Tide] =
var tides: seq[Tide]
let fnd = re">((High|Low)|([0-9]+:[0-9]+)|([0-9]+\.[0-9]+m))"
var client = newHttpClient()
var link = replace(url,re"\%LOC",loc)
if get_tomorrow:
let tomdate = now() + initTimeInterval(days = 1)
link &= "-" & tomdate.format("yyyyMMdd")
try:
# Remember to compile with -d:ssl else this won't work
let data = client.getContent(link)
let times = findAll(data,fnd)
var tide: Tide
var column = 0
for idx,time in times:
if idx == 12:
break
let l = len(time) - 1
if time == ">High" or time == ">Low":
tide = Tide()
tide.State = time[1..l]
column = 1
continue
elif column == 1:
tide.Time = time[1..l]
column = 2
continue
elif column == 2:
tide.Height = time[1..l]
tides.add(tide)
column = 0
continue
except:
echo "Unable to get Tide Data : " & getCurrentExceptionMsg()
return tides
proc getDesign(tides: seq[Tide]): Info =
var my_tides: seq[string] = @[]
for tide in tides:
let str = icon & tide.State[0] & " " & tide.Time & " " & tide.Height
my_tides.add(str)
var data = newInfo("Tideurrl")
data.border = black
data.args = my_tides
return data
proc getTides*(get_tomorrow: bool = false) =
var mytides = TideList()
mytides.Tides = getTideData(get_tomorrow)
mytides.Tides = sortTides(mytides.Tides, get_tomorrow)
if len(mytides.Tides) == 0:
getTides(true)
return
let data = getDesign(mytides.Tides)
var opt_tomorrow = "tomorrow"
if get_tomorrow:
opt_tomorrow = "back"
let args = concat(data.args,@["---",opt_tomorrow])
let output = outputData(data,args)
if output == "tomorrow":
getTides(true)
elif output == "back":
getTides()
elif output == "---":
return
elif output in args:
let url = replace(url,re"\%LOC",loc)
discard execCmd("xdg-open " & url)
proc main() =
getTides()
if isMainModule:
main()

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Display local tides in dmenu"
license = "GPL-3.0-or-later"
srcDir = "src"
bin = @["tideurrl"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1 +0,0 @@
-d:ssl

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Display and control volume with dmenu"
license = "GPL-3.0-or-later"
srcDir = "src"
bin = @["volurrme"]
# Dependencies
requires "nim >= 1.6.6"

View file

@ -1,13 +0,0 @@
# Package
version = "0.1.0"
author = "Paul Wilde"
description = "Display and control WLAN state with dmenu"
license = "GPL-3.0-or-later"
srcDir = "src"
bin = @["wirelurrs"]
# Dependencies
requires "nim >= 1.6.6"

16
wm_tools.nimble Normal file
View file

@ -0,0 +1,16 @@
# Package
version = "2.0.1"
author = "Paul Wilde"
description = "A set of informational tools"
license = "AGPL-3.0-or-later"
srcDir = "src"
bin = @["wmtools"]
# Dependencies
requires "nim >= 2.0.0"
requires "parsetoml >= 0.7.1"
requires "argparse"
requires "configparser"