destroyed repo, recreated with new nimble packaging
This commit is contained in:
commit
f69b202da8
46 changed files with 5995 additions and 0 deletions
8
.gitignore
vendored
Executable file
8
.gitignore
vendored
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
# ignore all
|
||||||
|
*
|
||||||
|
|
||||||
|
# unignore all with extensions
|
||||||
|
!*.*
|
||||||
|
|
||||||
|
# unignore directories
|
||||||
|
!*/
|
70
README.MD
Executable file
70
README.MD
Executable file
|
@ -0,0 +1,70 @@
|
||||||
|
# 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. `volume` have options (up, down, mute...)
|
||||||
|
which are selectable options in dmenu.
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
- `pingclock` performs a single `ping` to a server and returns the response time
|
||||||
|
- `battery` shows the current battery level
|
||||||
|
- `brightness` shows the current backlight level and gives options to adjust it
|
||||||
|
- `volume` shows the current volume level and gives options to adjust and manage it
|
||||||
|
- `date` shows the date
|
||||||
|
- `fuzzytime` shows the fuzzytime clock
|
||||||
|
- `wlan` shows the state of the wireless network interface. SSID connected to and signal level.
|
||||||
|
- `nic` shows the status and/or the ip address of the network interface card
|
||||||
|
- `temperature` shows the current CPU temperature
|
||||||
|
- `notes` a simple one liner note taking tool, displaying notes in `dmenu`/`rofi`
|
||||||
|
- `calculate` a calculator, utilising `qalculate` - inspired by [@fedops](https://codeberg.org/fedops/scripts)
|
||||||
|
- `emoji` an emoji picker
|
||||||
|
- `remmina` reads the files in your remmina config directory and allows you to connect to and edit them
|
||||||
|
- `translate` 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`
|
||||||
|
|
||||||
|
The next two do not work with `rofi` unless you have `alias dmenu=rofi` set, but they're pretty nice tools
|
||||||
|
|
||||||
|
- `passmenu_wrapper` a wrapper for passmenu. It basically just styles `passmenu` with no other features
|
||||||
|
- `command_wrapper` inspired by passmenu_wrapper, a basic tool to run other `dmenu` related tools with uniform styling.
|
||||||
|
- For example: `dmenu_run`, `clipmenu`, `passmenu` etc.
|
||||||
|
|
||||||
|
### Example in `dmenu`:
|
||||||
|
|
||||||
|
![dmenu_tools](https://user-images.githubusercontent.com/31094984/167123173-ee8092a2-d5ab-47b4-b207-ced328072cc0.gif)
|
||||||
|
|
||||||
|
### Example of `command_wrapper` with `clipmenu`
|
||||||
|
|
||||||
|
![command_wrap](https://user-images.githubusercontent.com/31094984/167122436-eea0be88-a929-46e8-9b4d-cb677dcb129c.gif)
|
||||||
|
|
||||||
|
## 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 (except for "rofi")
|
||||||
|
|
||||||
|
Each tool is compiled separately, for example:
|
||||||
|
```nim
|
||||||
|
nim c pingclock
|
||||||
|
```
|
||||||
|
and then run with
|
||||||
|
```sh
|
||||||
|
./pingclock
|
||||||
|
or
|
||||||
|
./pingclock rofi
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
Personally, I have these bound to key combinations in i3.
|
||||||
|
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 really.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
- `passmenu` for passmenu_wrapper
|
||||||
|
- 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.
|
293
base.nim
Normal file
293
base.nim
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
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
|
||||||
|
i3BarInput* = object
|
||||||
|
button*: int
|
||||||
|
x*: int
|
||||||
|
y*: int
|
||||||
|
|
||||||
|
const WM_TOOLS_DIR* = getHomeDir() & "Nextcloud/.wm_tools/"
|
||||||
|
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 MAX_LINES = 20
|
||||||
|
const font = "Hermit-12"
|
||||||
|
const WL_DMENU = "dmenu"
|
||||||
|
const WL_ROFI = "wofi --dmenu"
|
||||||
|
var loop* = false
|
||||||
|
var stoploop* = true
|
||||||
|
var tool* = "dmenu"
|
||||||
|
var passmenu* = false
|
||||||
|
var command_wrapper* = 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: string = "dmenu"): Menu =
|
||||||
|
var run = cmd
|
||||||
|
if wayland and cmd == "dmenu":
|
||||||
|
run = WL_DMENU
|
||||||
|
var menu = Menu()
|
||||||
|
menu.command = run
|
||||||
|
menu.prompt = "-p"
|
||||||
|
menu.i_case = "-i"
|
||||||
|
menu.lines_shown = "-l"
|
||||||
|
return menu
|
||||||
|
|
||||||
|
proc newRofiConfig(cmd: string = "rofi -dmenu"): Menu =
|
||||||
|
var run = cmd
|
||||||
|
if wayland and cmd == "rofi -dmenu":
|
||||||
|
run = WL_ROFI
|
||||||
|
var menu = newMenuConfig(run)
|
||||||
|
menu.extra_cmd = "-markup-rows -kb-row-select \"Tab\" -kb-row-tab \"\""
|
||||||
|
return menu
|
||||||
|
|
||||||
|
proc newDmenuConfig(cmd: string = "dmenu"): Menu =
|
||||||
|
var run = cmd
|
||||||
|
if wayland and cmd == "dmenu":
|
||||||
|
run = WL_DMENU
|
||||||
|
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 passmenu:
|
||||||
|
return newDmenuConfig("passmenu")
|
||||||
|
elif command_wrapper:
|
||||||
|
return newDmenuConfig(run_command)
|
||||||
|
elif tool == "rofi":
|
||||||
|
return newRofiConfig()
|
||||||
|
elif tool == "dmenu":
|
||||||
|
return newDmenuConfig()
|
||||||
|
return newMenuConfig()
|
||||||
|
|
||||||
|
proc debugLog*(str: string) =
|
||||||
|
let f = open("/tmp/debug.txt",fmAppend)
|
||||||
|
defer: f.close()
|
||||||
|
f.writeLine(str)
|
||||||
|
|
||||||
|
proc switchTwmMode*(mode: string = "default") =
|
||||||
|
# I intend to add support for more twm as time goes on (I switch around a lot)
|
||||||
|
# Switch out of an i3 bindsym mode if set
|
||||||
|
if wayland:
|
||||||
|
discard execCmd("sway mode \"" & mode & "\"")
|
||||||
|
else:
|
||||||
|
discard execCmd("i3-msg mode \"" & mode & "\"")
|
||||||
|
|
||||||
|
proc checkWayland() =
|
||||||
|
if getEnv("XDG_SESSION_TYPE") == "wayland":
|
||||||
|
wayland = true
|
||||||
|
|
||||||
|
proc parseInput*(): i3BarInput =
|
||||||
|
let input = readLineFromStdin("")
|
||||||
|
try:
|
||||||
|
let jsonNode = parseJson(input)
|
||||||
|
let i3input = to(jsonNode, i3BarInput)
|
||||||
|
return i3input
|
||||||
|
except:
|
||||||
|
return i3BarInput()
|
||||||
|
|
||||||
|
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,"\"",""")
|
||||||
|
|
||||||
|
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 = ""
|
||||||
|
# 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"
|
||||||
|
for opt in opts:
|
||||||
|
let text = markup(opt)
|
||||||
|
cmd = cmd & text & "\n"
|
||||||
|
cmd.removeSuffix("\n")
|
||||||
|
|
||||||
|
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 & " " & $MAX_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], rofi: bool = false): string =
|
||||||
|
let cmd = genMenuCmd(data, opts, rofi)
|
||||||
|
#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 == "rofi":
|
||||||
|
output = runMenu(data,args, rofi = 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 checkCacheDir() =
|
||||||
|
if not dirExists(WM_TOOLS_DIR):
|
||||||
|
createDir(WM_TOOLS_DIR)
|
||||||
|
|
||||||
|
# At Start up:
|
||||||
|
checkCacheDir()
|
||||||
|
checkWayland()
|
||||||
|
|
||||||
|
# Switch bindsym mode back to default as it could be being used.
|
||||||
|
switchTwmMode()
|
||||||
|
|
||||||
|
|
||||||
|
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"
|
||||||
|
of ["pass","passmenu"]:
|
||||||
|
passmenu = true
|
||||||
|
break
|
||||||
|
|
||||||
|
|
13
battery/battery.nimble
Normal file
13
battery/battery.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "Shows battery percentage using dmenu"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["battery"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
126
battery/src/battery.nim
Normal file
126
battery/src/battery.nim
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[strutils,os]
|
||||||
|
|
||||||
|
const battery = "BAT0"
|
||||||
|
const ok_fg = lightgreen
|
||||||
|
const default_fg = white
|
||||||
|
const default_bg = black
|
||||||
|
const warning_fg = black
|
||||||
|
const warning_bg = red
|
||||||
|
const low_bg = black
|
||||||
|
const low_fg = red
|
||||||
|
const alert_fg = black
|
||||||
|
const alert_bg = yellow
|
||||||
|
const med_fg = green
|
||||||
|
const med_bg = black
|
||||||
|
|
||||||
|
|
||||||
|
proc batteryExists(): bool =
|
||||||
|
try:
|
||||||
|
let state = strip(readFile("/sys/class/power_supply/" & battery & "/present"))
|
||||||
|
if state == "1":
|
||||||
|
return true
|
||||||
|
except:
|
||||||
|
echo "Error getting battery : " & getCurrentExceptionMsg()
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc isCharging(): bool =
|
||||||
|
try:
|
||||||
|
let state = strip(readFile("/sys/class/power_supply/" & battery & "/status"))
|
||||||
|
if state == "Charging":
|
||||||
|
return true
|
||||||
|
except:
|
||||||
|
echo "Error getting charging status : " & getCurrentExceptionMsg()
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc getCharge(): int =
|
||||||
|
var charge = 0
|
||||||
|
try:
|
||||||
|
let chg = strip(readFile("/sys/class/power_supply/" & battery & "/capacity"))
|
||||||
|
if chg != "":
|
||||||
|
charge = parseInt(chg)
|
||||||
|
except:
|
||||||
|
echo "Error getting battery level : " & getCurrentExceptionMsg()
|
||||||
|
return charge
|
||||||
|
|
||||||
|
proc getDesign(charge: int, state: bool): (string, string, string, string, string) =
|
||||||
|
var icon = " "
|
||||||
|
var icon_colour = ok_fg
|
||||||
|
var col = default_fg
|
||||||
|
var bg = default_bg
|
||||||
|
var border = ok_fg
|
||||||
|
if isCharging():
|
||||||
|
icon = " "
|
||||||
|
else:
|
||||||
|
case charge:
|
||||||
|
of 0..5:
|
||||||
|
icon_colour = warning_fg
|
||||||
|
col = warning_fg
|
||||||
|
bg = warning_bg
|
||||||
|
of 6..19:
|
||||||
|
icon_colour = low_fg
|
||||||
|
border = low_bg
|
||||||
|
bg = default_bg
|
||||||
|
of 20..39:
|
||||||
|
icon_colour = alert_fg
|
||||||
|
border = alert_bg
|
||||||
|
icon = " "
|
||||||
|
of 40..59:
|
||||||
|
icon_colour = med_fg
|
||||||
|
border= med_fg
|
||||||
|
icon = " "
|
||||||
|
of 60..79:
|
||||||
|
icon_colour = med_fg
|
||||||
|
border = med_fg
|
||||||
|
icon = " "
|
||||||
|
of 80..100:
|
||||||
|
icon_colour = ok_fg
|
||||||
|
border = ok_fg
|
||||||
|
icon = " "
|
||||||
|
else:
|
||||||
|
icon = "x "
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
proc getOutput(charge: int, state: bool): Info =
|
||||||
|
let (html_text,main_text,col,bg_col,highlight_col) = get_design(charge, state)
|
||||||
|
var data = newInfo("Battery")
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
proc getBatteryInfo() =
|
||||||
|
var last_charge = -1
|
||||||
|
var last_state = false
|
||||||
|
while true:
|
||||||
|
let charge = getCharge()
|
||||||
|
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() =
|
||||||
|
if batteryExists():
|
||||||
|
getBatteryInfo()
|
||||||
|
else:
|
||||||
|
switchTwmMode()
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
||||||
|
|
13
brightness/brightness.nimble
Normal file
13
brightness/brightness.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "Shows and controls laptop brightness"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["brightness"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
116
brightness/src/brightness.nim
Normal file
116
brightness/src/brightness.nim
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import ../../base
|
||||||
|
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("Brightness")
|
||||||
|
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()
|
5
brightness/src/src/thingy.nim
Normal file
5
brightness/src/src/thingy.nim
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# This is just an example to get you started. A typical binary package
|
||||||
|
# uses this file as the main entry point of the application.
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
echo("Hello, World!")
|
13
calculate/calculate.nimble
Normal file
13
calculate/calculate.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "A simple dmenu calculator"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["calculate"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
35
calculate/src/calculate.nim
Normal file
35
calculate/src/calculate.nim
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[osproc,strutils,sequtils]
|
||||||
|
|
||||||
|
#using qalc as it has a lot of nice inbuilt features
|
||||||
|
#
|
||||||
|
proc doCalculation(calc: string) =
|
||||||
|
var info = newInfo("Calculator")
|
||||||
|
let ans_cmd_terse = "echo \"" & calc & "\" | qalc +u8 -terse -color=never | awk '!/^>/ && !/^$/ {gsub(/^[ \\t]+|[ \\t]+$/, \"\", $0); print}'"
|
||||||
|
let ans_cmd_full = "echo \"" & calc & "\" | qalc +u8 -color=never | awk '!/^>/ && !/^$/ {gsub(/^[ \\t]+|[ \\t]+$/, \"\", $0); print}'"
|
||||||
|
var ans_full = execCmdEx(ans_cmd_full)
|
||||||
|
ans_full.output.stripLineEnd()
|
||||||
|
var ans_terse = execCmdEx(ans_cmd_terse)
|
||||||
|
ans_terse.output.stripLineEnd()
|
||||||
|
let answers = @[strip(ans_full.output),
|
||||||
|
strip(ans_terse.output)]
|
||||||
|
let args = concat(answers,@["exit"])
|
||||||
|
var cmd = outputData(info, args)
|
||||||
|
cmd.stripLineEnd()
|
||||||
|
if cmd in answers:
|
||||||
|
copyToClipboard(cmd)
|
||||||
|
elif cmd == "exit" or cmd == "":
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
doCalculation(cmd)
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
var info = newInfo("Calculator")
|
||||||
|
let args = @["exit"]
|
||||||
|
let cmd = outputData(info, args)
|
||||||
|
if cmd != "":
|
||||||
|
doCalculation(cmd)
|
||||||
|
return
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
13
clipurr/clipurr.nimble
Normal file
13
clipurr/clipurr.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# 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"
|
135
clipurr/src/clipurr.nim
Normal file
135
clipurr/src/clipurr.nim
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[strutils,os,db_sqlite,osproc]
|
||||||
|
|
||||||
|
const CLIP_DB = WM_TOOLS_DIR & ".clipurr_cache.sqlite"
|
||||||
|
const KEEP_ITEMS = 20
|
||||||
|
|
||||||
|
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:
|
||||||
|
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()
|
||||||
|
defer: db.close()
|
||||||
|
try:
|
||||||
|
db.exec(sql"drop table if exists clip_items")
|
||||||
|
except:
|
||||||
|
echo getCurrentExceptionMsg()
|
||||||
|
|
||||||
|
proc maintainDB() =
|
||||||
|
let db = openDBConn()
|
||||||
|
try:
|
||||||
|
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 = escape(clip)
|
||||||
|
return strip(clip)
|
||||||
|
|
||||||
|
proc unescapeClip(str: string): string =
|
||||||
|
var clip = str
|
||||||
|
try:
|
||||||
|
clip = unescape(clip)
|
||||||
|
except:
|
||||||
|
echo getCurrentExceptionMsg()
|
||||||
|
|
||||||
|
return strip(clip)
|
||||||
|
|
||||||
|
proc readClipFile(): seq[string] =
|
||||||
|
var clips: seq[string] = @[]
|
||||||
|
let db = openDBConn()
|
||||||
|
defer: db.close()
|
||||||
|
try:
|
||||||
|
for row in db.fastRows(sql"select distinct(clip) from clip_items order by timestamp desc"):
|
||||||
|
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)"
|
||||||
|
let db = openDBConn()
|
||||||
|
defer: db.close()
|
||||||
|
try:
|
||||||
|
db.exec(sql"""BEGIN""")
|
||||||
|
db.exec(sql"""insert into clip_items (timestamp, clip)
|
||||||
|
values (CURRENT_TIMESTAMP, ?)
|
||||||
|
""", escapeClip(str))
|
||||||
|
db.exec(sql"""COMMIT""")
|
||||||
|
except:
|
||||||
|
echo getCurrentExceptionMsg()
|
||||||
|
return
|
||||||
|
|
||||||
|
proc showClips() =
|
||||||
|
let clips = readClipFile()
|
||||||
|
let info = newInfo("Clipurr")
|
||||||
|
let option = outputData(info, clips)
|
||||||
|
if option != "":
|
||||||
|
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()
|
13
command_wrapper/command_wrapper.nimble
Normal file
13
command_wrapper/command_wrapper.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# 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 = @["command_wrapper"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
22
command_wrapper/src/command_wrapper.nim
Normal file
22
command_wrapper/src/command_wrapper.nim
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import ../../base
|
||||||
|
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:
|
||||||
|
base.command_wrapper = 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()
|
11
compile_all.sh
Executable file
11
compile_all.sh
Executable file
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
for dir in ./*; do
|
||||||
|
if [ -d $dir ]; then
|
||||||
|
cd $dir
|
||||||
|
f=$(echo $dir | sed 's/\.\///')
|
||||||
|
nimble install -y
|
||||||
|
sudo cp -v $f /usr/local/bin/$f
|
||||||
|
cd ../
|
||||||
|
fi
|
||||||
|
done
|
13
emoji/emoji.nimble
Normal file
13
emoji/emoji.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "An emoji picker for dmenu/rofi"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["emoji"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
20
emoji/src/emoji.nim
Normal file
20
emoji/src/emoji.nim
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import ../../base
|
||||||
|
import lib/emojilist
|
||||||
|
import std/[re]
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
var info = newInfo("Emoji Picker")
|
||||||
|
var args = getEmoji()
|
||||||
|
args.add("exit")
|
||||||
|
let output = outputData(info,args)
|
||||||
|
if output == "exit" or output == "":
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
let e = re.findAll(output,re(".+ :"))
|
||||||
|
if len(e) > 0:
|
||||||
|
let emoji = re.replace(e[0], re(" :"),"")
|
||||||
|
copyToClipboard(emoji)
|
||||||
|
return
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
3572
emoji/src/lib/emojilist.nim
Normal file
3572
emoji/src/lib/emojilist.nim
Normal file
File diff suppressed because it is too large
Load diff
13
fuzzytime/fuzzytime.nimble
Normal file
13
fuzzytime/fuzzytime.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "Displays the fuzzy time in dmenu"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["fuzzytime"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
92
fuzzytime/src/fuzzytime.nim
Normal file
92
fuzzytime/src/fuzzytime.nim
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[times]
|
||||||
|
|
||||||
|
const default_bg = lightblue
|
||||||
|
const default_fg = black
|
||||||
|
|
||||||
|
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 =
|
||||||
|
case hr:
|
||||||
|
of 1, 13:
|
||||||
|
return "one"
|
||||||
|
of 2, 14:
|
||||||
|
return "two"
|
||||||
|
of 3, 15:
|
||||||
|
return "three"
|
||||||
|
of 4, 16:
|
||||||
|
return "four"
|
||||||
|
of 5, 17:
|
||||||
|
return "five"
|
||||||
|
of 6, 18:
|
||||||
|
return "six"
|
||||||
|
of 7, 19:
|
||||||
|
return "seven"
|
||||||
|
of 8, 20:
|
||||||
|
return "eight"
|
||||||
|
of 9, 21:
|
||||||
|
return "nine"
|
||||||
|
of 10, 22:
|
||||||
|
return "ten"
|
||||||
|
of 11, 23:
|
||||||
|
return "eleven"
|
||||||
|
of 0, 12, 24:
|
||||||
|
return "twelve"
|
||||||
|
else:
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
proc getMinute(min: int): string =
|
||||||
|
case min:
|
||||||
|
of 58,59,0,1,2:
|
||||||
|
return "o'clock"
|
||||||
|
of 3,4,5,6,7,53,54,55,56,57:
|
||||||
|
return "five"
|
||||||
|
of 8,9,10,11,12,48,49,50,51,52:
|
||||||
|
return "ten"
|
||||||
|
of 13,14,15,16,17,43,44,45,46,47:
|
||||||
|
return "quarter"
|
||||||
|
of 18,19,20,21,22,38,39,40,41,42:
|
||||||
|
return "twenty"
|
||||||
|
of 23,24,25,26,27,33,34,35,36,37:
|
||||||
|
return "twenty-five"
|
||||||
|
of 28,29,30,31,32:
|
||||||
|
return "half"
|
||||||
|
else:
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
proc getObject(time: string): Info =
|
||||||
|
var data = newInfo("Fuzzy 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
|
||||||
|
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
let time = getFuzzyTime()
|
||||||
|
let data = getObject(time)
|
||||||
|
outputData(data)
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
13
i3_workspaces/i3_workspaces.nimble
Normal file
13
i3_workspaces/i3_workspaces.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "Displays open windows in i3 workspaces"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["i3_workspaces"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
200
i3_workspaces/src/i3_workspaces.nim
Normal file
200
i3_workspaces/src/i3_workspaces.nim
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[osproc,json,strutils]
|
||||||
|
|
||||||
|
const I3_WORKSPACES = "i3-msg -t get_workspaces"
|
||||||
|
const I3_TREE = "i3-msg -t get_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
|
||||||
|
let cmd = "i3-msg workspace " & $ws.num
|
||||||
|
discard execCmd(cmd)
|
||||||
|
showWorkspaces()
|
||||||
|
|
||||||
|
# proc getApplications(node: JsonNode): (seq[string],bool) =
|
||||||
|
# var apps: seq[string] = @[]
|
||||||
|
# var focused = false
|
||||||
|
# for item in node["nodes"].getElems():
|
||||||
|
# var name = ""
|
||||||
|
# if item["focused"].getBool():
|
||||||
|
# focused = true
|
||||||
|
# if item{"window_properties"} != nil :
|
||||||
|
# let prop = item["window_properties"]
|
||||||
|
# name = prop["class"].getStr()
|
||||||
|
# name &= " " & prop["title"].getStr()
|
||||||
|
# echo "Adding application : " & name
|
||||||
|
# apps.add(name)
|
||||||
|
# return (apps, focused)
|
||||||
|
#
|
||||||
|
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 findWorkspaces(node: JsonNode) =
|
||||||
|
# for channel in node["nodes"].getElems():
|
||||||
|
# case channel["type"].getStr():
|
||||||
|
# of "workspace":
|
||||||
|
# if channel["output"].getStr() == "__i3":
|
||||||
|
# continue
|
||||||
|
# let w = channel
|
||||||
|
# let apps = getApplications(channel)
|
||||||
|
# var space = Workspace(
|
||||||
|
# num: w["num"].getInt(),
|
||||||
|
# name: w["name"].getStr(),
|
||||||
|
# focused: apps[1],
|
||||||
|
# #visible: w["visible"].getBool(),
|
||||||
|
# urgent: w["urgent"].getBool(),
|
||||||
|
# output: w["output"].getStr(),
|
||||||
|
# apps: apps[0]
|
||||||
|
# )
|
||||||
|
# space.display_string = buildString(space)
|
||||||
|
# my_workspaces.add(space)
|
||||||
|
# else:
|
||||||
|
# echo "finding workspaces..."
|
||||||
|
# findWorkspaces(channel)
|
||||||
|
# return
|
||||||
|
|
||||||
|
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)
|
||||||
|
###
|
||||||
|
if channel{"window_properties"} != nil:
|
||||||
|
let app = getApplication(channel,ws)
|
||||||
|
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(I3_TREE)
|
||||||
|
if cur_workspaces.output != "":
|
||||||
|
let root = parseJson(cur_workspaces.output)
|
||||||
|
findWorkspacesTree(root)
|
||||||
|
return
|
||||||
|
|
||||||
|
proc getWorkspaces(): seq[Workspace] =
|
||||||
|
let cur_workspaces = execCmdEx(I3_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("Workspaces")
|
||||||
|
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()
|
13
nic/nic.nimble
Normal file
13
nic/nic.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# 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 = @["nic"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
66
nic/src/nic.nim
Normal file
66
nic/src/nic.nim
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[os,osproc,strutils,sequtils]
|
||||||
|
|
||||||
|
const default_fg = black
|
||||||
|
const default_bg = purple
|
||||||
|
const mng_cmd = "alacritty -e nmtui-connect"
|
||||||
|
const nics: seq[string] = @["wlan0","bridge0", "enp3s0","wlp2s0","enp0s20f0u3"]
|
||||||
|
|
||||||
|
proc getIP(nic: string): string =
|
||||||
|
let cmd = "ifconfig " & nic & " | grep inet | awk -F\" \" '{print $2}' | head -1 | awk '{print $1}'"
|
||||||
|
let ip = execCmdEx(cmd)
|
||||||
|
return strip(ip.output)
|
||||||
|
|
||||||
|
proc getOnlineState(nic: string): string =
|
||||||
|
try:
|
||||||
|
let oper = readFile("/sys/class/net/" & nic & "/operstate")
|
||||||
|
let state = strip(oper)
|
||||||
|
return "[" & state & "]"
|
||||||
|
except:
|
||||||
|
echo "Error getting operstate for " & nic & " : ", getCurrentExceptionMsg()
|
||||||
|
return ""
|
||||||
|
|
||||||
|
proc getConnState(nic: string): (string, string) =
|
||||||
|
let state = getOnlineState(nic)
|
||||||
|
let ip = getIP(nic)
|
||||||
|
if state == "[down]" or ip == "":
|
||||||
|
return ("disconnected", state)
|
||||||
|
return (ip, state)
|
||||||
|
|
||||||
|
proc getObject(): Info =
|
||||||
|
var data = newInfo("IP")
|
||||||
|
data.selected_bg = default_bg
|
||||||
|
data.selected_fg = default_fg
|
||||||
|
# i3bar stuff
|
||||||
|
data.border = default_bg
|
||||||
|
return data
|
||||||
|
|
||||||
|
proc getNetInfo*(my_nics: seq[string]) =
|
||||||
|
var my_nic_states: seq[string] = @[]
|
||||||
|
for nic in my_nics:
|
||||||
|
let (ip, state) = getConnState(nic)
|
||||||
|
my_nic_states.add(nic & ":" & state & " " & ip)
|
||||||
|
let data = getObject()
|
||||||
|
let args = concat(my_nic_states,@["manage"])
|
||||||
|
let option = outputData(data, args)
|
||||||
|
if option in my_nic_states:
|
||||||
|
discard execCmd(mng_cmd)
|
||||||
|
case option:
|
||||||
|
of "manage":
|
||||||
|
discard execCmd(mng_cmd)
|
||||||
|
|
||||||
|
proc getNics*(): seq[string] =
|
||||||
|
var my_nics: seq[string] = @[]
|
||||||
|
for nic in os.walkDir("/sys/class/net/"):
|
||||||
|
let n = replace(nic.path, "/sys/class/net/", "")
|
||||||
|
my_nics.add(n)
|
||||||
|
|
||||||
|
if len(my_nics) > 0:
|
||||||
|
return my_nics
|
||||||
|
return @["no-nic"]
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
getNetInfo(getNics())
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
13
notes/notes.nimble
Normal file
13
notes/notes.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "A notes app for dmenu"
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["notes"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
167
notes/src/notes.nim
Normal file
167
notes/src/notes.nim
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[os,strutils,sequtils,times]
|
||||||
|
|
||||||
|
const note_dir = WM_TOOLS_DIR & ".notes.dmenu/" # Putting it in Nextcloud so it can sync :-)
|
||||||
|
const default_bg = white
|
||||||
|
const default_fg = black
|
||||||
|
|
||||||
|
type
|
||||||
|
Note = object
|
||||||
|
id: string
|
||||||
|
text: string
|
||||||
|
|
||||||
|
proc startNotes()
|
||||||
|
|
||||||
|
proc displayOptionMenu(option: string)
|
||||||
|
|
||||||
|
var notes: seq[Note] = @[]
|
||||||
|
|
||||||
|
proc readNotes(): seq[Note] =
|
||||||
|
if len(notes) == 0:
|
||||||
|
if existsOrCreateDir(note_dir):
|
||||||
|
for note_file in walkDir(note_dir):
|
||||||
|
try:
|
||||||
|
var note = Note()
|
||||||
|
note.id = note_file[1]
|
||||||
|
note.text = stripQuotes(readFile(note_file[1]))
|
||||||
|
notes.add(note)
|
||||||
|
except:
|
||||||
|
echo "Unable to read note : ", note_file, " : ", getCurrentExceptionMsg()
|
||||||
|
return notes
|
||||||
|
|
||||||
|
proc removeNote(note: Note) =
|
||||||
|
try:
|
||||||
|
removeFile(note.id)
|
||||||
|
for idx, a_note in notes:
|
||||||
|
if note.id == a_note.id:
|
||||||
|
notes.delete(idx)
|
||||||
|
except:
|
||||||
|
echo "Unable to delete file : ", note.id, " : ", getCurrentExceptionMsg()
|
||||||
|
|
||||||
|
proc getNoteStrings(): seq[string] =
|
||||||
|
var note_list: seq[string] = @[]
|
||||||
|
if len(notes) == 0:
|
||||||
|
discard readNotes()
|
||||||
|
for note in notes:
|
||||||
|
note_list.add(note.text)
|
||||||
|
return note_list
|
||||||
|
|
||||||
|
proc writeNote(note: Note, is_new: bool = false) =
|
||||||
|
if note.text == "":
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
|
||||||
|
writeFile(note.id, stripQuotes(note.text))
|
||||||
|
if is_new:
|
||||||
|
var new_note = note
|
||||||
|
new_note.text = stripQuotes(new_note.text)
|
||||||
|
notes.add(note)
|
||||||
|
else:
|
||||||
|
for idx, a_note in notes:
|
||||||
|
if a_note.id == note.id:
|
||||||
|
notes[idx].text = stripQuotes(note.text)
|
||||||
|
except:
|
||||||
|
echo "write_note, Unable to write note : ", note.id, " : ", getCurrentExceptionMsg()
|
||||||
|
|
||||||
|
proc getNotes(): (Info, seq[string]) =
|
||||||
|
var info = newInfo("Notes")
|
||||||
|
info.selected_bg = default_bg
|
||||||
|
info.selected_fg = default_fg
|
||||||
|
let notes = getNoteStrings()
|
||||||
|
return (info,notes)
|
||||||
|
|
||||||
|
proc getNote(text: string): Note =
|
||||||
|
for note in notes:
|
||||||
|
if note.text == text:
|
||||||
|
return note
|
||||||
|
return Note()
|
||||||
|
|
||||||
|
proc displayDeleteConfirmationMenu(note: string) =
|
||||||
|
var yes_no = newInfo("Confirm Delete note : " & note)
|
||||||
|
yes_no.selected_bg = default_bg
|
||||||
|
yes_no.selected_fg = default_fg
|
||||||
|
let args = @["yes", "no"]
|
||||||
|
let choice = outputData(yes_no, args)
|
||||||
|
case choice:
|
||||||
|
of "yes":
|
||||||
|
let rm_note = getNote(note)
|
||||||
|
if rm_note.text == note:
|
||||||
|
removeNote(rm_note)
|
||||||
|
of "no":
|
||||||
|
displayOptionMenu(note)
|
||||||
|
|
||||||
|
proc replaceNoteConfirmationMenu(note_idx: int, new_text: string): bool =
|
||||||
|
let old_note = notes[note_idx]
|
||||||
|
var diag = newInfo("Replace Note : '" & old_note.text & "', with '" & new_text & "' ?")
|
||||||
|
diag.selected_bg = default_bg
|
||||||
|
diag.selected_fg = default_fg
|
||||||
|
let args = @["yes", "no"]
|
||||||
|
let choice = outputData(diag,args)
|
||||||
|
case choice:
|
||||||
|
of "yes":
|
||||||
|
echo "here"
|
||||||
|
return true
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc replaceNote(new_text: string, old_text: string) =
|
||||||
|
for idx, note in notes:
|
||||||
|
if note.text == old_text:
|
||||||
|
if replaceNoteConfirmationMenu(idx,new_text):
|
||||||
|
var new_note = note
|
||||||
|
new_note.text = new_text
|
||||||
|
notes[idx] = new_note
|
||||||
|
writeNote(new_note)
|
||||||
|
return
|
||||||
|
|
||||||
|
proc displayOptionMenu(option: string) =
|
||||||
|
var select = newInfo("Note")
|
||||||
|
select.selected_bg = default_bg
|
||||||
|
select.selected_fg = default_fg
|
||||||
|
let note_choices = @["rm","back"]
|
||||||
|
let args = concat(@[option],note_choices)
|
||||||
|
let chosen = outputData(select,args)
|
||||||
|
if chosen in note_choices:
|
||||||
|
case chosen:
|
||||||
|
of "rm":
|
||||||
|
displayDeleteConfirmationMenu(option)
|
||||||
|
startNotes()
|
||||||
|
of "back":
|
||||||
|
startNotes()
|
||||||
|
elif chosen != "" and chosen != option:
|
||||||
|
replaceNote(chosen, option)
|
||||||
|
displayOptionMenu(chosen)
|
||||||
|
else:
|
||||||
|
displayOptionMenu(option)
|
||||||
|
|
||||||
|
proc newNote(text: string) =
|
||||||
|
var new_note = Note()
|
||||||
|
let note_id = times.now().format("'{'yyyyMMdd-HHmmss-ffffff'}'")
|
||||||
|
new_note.id = note_dir & note_id
|
||||||
|
new_note.text = text
|
||||||
|
writeNote(new_note, is_new = true)
|
||||||
|
|
||||||
|
proc startNotes() =
|
||||||
|
let (info,all_notes) = getNotes()
|
||||||
|
let all_choices = @["exit"]
|
||||||
|
let args = concat(all_notes, all_choices)
|
||||||
|
let option = outputData(info, args)
|
||||||
|
if option in all_choices:
|
||||||
|
case option:
|
||||||
|
of "exit":
|
||||||
|
return
|
||||||
|
if option in all_notes:
|
||||||
|
displayOptionMenu(option)
|
||||||
|
elif option != "":
|
||||||
|
newNote(option)
|
||||||
|
startNotes()
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
echo "Note dir : ", note_dir
|
||||||
|
if tool != "dmenu" and tool != "rofi":
|
||||||
|
echo "Can only be run in dmenu or rofi mode. Exiting..."
|
||||||
|
return
|
||||||
|
startNotes()
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
13
pingclock/pingclock.nimble
Normal file
13
pingclock/pingclock.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# 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 = @["pingclock"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
85
pingclock/src/pingclock.nim
Normal file
85
pingclock/src/pingclock.nim
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[osproc, re, strutils]
|
||||||
|
|
||||||
|
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.]+")
|
||||||
|
const ping_cmd: string = "ping -4 -c 1 " & host
|
||||||
|
|
||||||
|
proc getPing(): float =
|
||||||
|
var ping: float = -1
|
||||||
|
let cmdOut = execCmdEx(ping_cmd)
|
||||||
|
let lines = splitLines(cmdOut.output)
|
||||||
|
let ping_line = lines[1]
|
||||||
|
let bounds = findBounds(ping_line, ping_re)
|
||||||
|
if bounds.first > 0:
|
||||||
|
let png = ping_line[bounds.first+5..bounds.last]
|
||||||
|
ping = parseFloat(png)
|
||||||
|
return ping
|
||||||
|
|
||||||
|
proc getObject(ping: float): Info =
|
||||||
|
let pingstr = split($ping,".")
|
||||||
|
let niceping = pingstr[0] & "." & pingstr[1][0]
|
||||||
|
var text = "🏓 " & niceping & " ms"
|
||||||
|
var state = 0
|
||||||
|
if ping < 0:
|
||||||
|
text = "❌ No Pong"
|
||||||
|
state = 1
|
||||||
|
else:
|
||||||
|
case ping:
|
||||||
|
of 0..100:
|
||||||
|
state = 0
|
||||||
|
of 101..400:
|
||||||
|
state = 1
|
||||||
|
of 401..1000:
|
||||||
|
state = 2
|
||||||
|
else:
|
||||||
|
state = 9
|
||||||
|
|
||||||
|
var data = newInfo("Ping Clock")
|
||||||
|
data.full_text = text
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
let ping = get_ping()
|
||||||
|
let data = getObject(ping)
|
||||||
|
let output = outputData(data)
|
||||||
|
if output == data.full_text:
|
||||||
|
main()
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
14
remmina/remmina.nimble
Normal file
14
remmina/remmina.nimble
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# 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"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
||||||
|
requires "configparser >= 0.1.0"
|
74
remmina/src/remmina.nim
Normal file
74
remmina/src/remmina.nim
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[os,osproc,tables,algorithm]
|
||||||
|
import configparser
|
||||||
|
|
||||||
|
const REMMINA_DIR = getHomeDir() & ".local/share/remmina"
|
||||||
|
const REMMINA_WS = "4"
|
||||||
|
|
||||||
|
var sessions = initTable[string,string]()
|
||||||
|
var names: seq[string] = @[]
|
||||||
|
|
||||||
|
proc main()
|
||||||
|
|
||||||
|
proc switchWorkspace() =
|
||||||
|
if REMMINA_WS != "":
|
||||||
|
discard execCmd("i3-msg workspace number " & REMMINA_WS)
|
||||||
|
discard execCmd("swaymsg workspace number " & REMMINA_WS)
|
||||||
|
|
||||||
|
proc getRemminaFiles(): seq[string] =
|
||||||
|
if len(names) < 1:
|
||||||
|
for file in walkFiles(REMMINA_DIR & "/*.remmina"):
|
||||||
|
let content = readFile(file)
|
||||||
|
let ini = parseIni(content)
|
||||||
|
let group = ini.getProperty("remmina","group")
|
||||||
|
let name = ini.getProperty("remmina","name")
|
||||||
|
let server = ini.getProperty("remmina","server")
|
||||||
|
if name != "" and server != "":
|
||||||
|
let slug = group & " : " & name & " : (" & server & ")"
|
||||||
|
sessions[slug] = file
|
||||||
|
names.add(slug)
|
||||||
|
names.sort()
|
||||||
|
return names
|
||||||
|
|
||||||
|
proc editRemmina(conn: string) =
|
||||||
|
let session = sessions[conn]
|
||||||
|
discard execCmd("remmina -e " & quote(session))
|
||||||
|
|
||||||
|
proc startRemmina(conn: string) =
|
||||||
|
let session = sessions[conn]
|
||||||
|
discard execCmd("remmina -c " & quote(session))
|
||||||
|
|
||||||
|
proc selectRemmina(conn: string) =
|
||||||
|
var info = newInfo("Remmina : " & conn)
|
||||||
|
let args = @["connect", "edit", "back"]
|
||||||
|
let output = outputData(info,args)
|
||||||
|
if output in args:
|
||||||
|
case output:
|
||||||
|
of "connect":
|
||||||
|
startRemmina(conn)
|
||||||
|
switchWorkspace()
|
||||||
|
of "edit":
|
||||||
|
editRemmina(conn)
|
||||||
|
switchWorkspace()
|
||||||
|
of "back":
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
var info = newInfo("Remmina")
|
||||||
|
var args: seq[string] = getRemminaFiles()
|
||||||
|
args.add("new")
|
||||||
|
args.add("exit")
|
||||||
|
let output = outputData(info,args)
|
||||||
|
if output == "exit" or output == "":
|
||||||
|
return
|
||||||
|
elif output == "new":
|
||||||
|
discard execCmd("remmina --new")
|
||||||
|
elif output in names:
|
||||||
|
selectRemmina(output)
|
||||||
|
return
|
||||||
|
return
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
13
screenshot/screenshot.nimble
Normal file
13
screenshot/screenshot.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# 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 = @["screenshot"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
102
screenshot/src/screenshot.nim
Normal file
102
screenshot/src/screenshot.nim
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[times,os,osproc,strutils,sequtils]
|
||||||
|
|
||||||
|
var screenshot_type = ""
|
||||||
|
const TYPES = @["region", "fullscreen", "window"]
|
||||||
|
const DATE_FORMAT = "yyyyMMdd-hhmmss"
|
||||||
|
const FILENAME = "Screenshot-%d.png"
|
||||||
|
const TEMP_DIR = "/tmp/"
|
||||||
|
const SCREENSHOT_CMD = "maim -u %s --format png %f"
|
||||||
|
const SCREENSHOT_CMD_WL = "grim %s %f"
|
||||||
|
var RUN_CMD = SCREENSHOT_CMD
|
||||||
|
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"
|
||||||
|
const REGION_FLAG_WL = "-g \"$(slurp)\""
|
||||||
|
var REGION_CMD = REGION_FLAG
|
||||||
|
const CLIPBOARD_CMD = "xclip -selection clipboard -t image/png"
|
||||||
|
const CLIPBOARD_CMD_WL = "wl-copy"
|
||||||
|
var CLIP_CMD = CLIPBOARD_CMD
|
||||||
|
|
||||||
|
proc saveToClipboard(filename: string) =
|
||||||
|
let cmd = "cat " & filename & " | " & CLIP_CMD
|
||||||
|
let status = execCmd(cmd)
|
||||||
|
if status == 0 and fileExists(filename):
|
||||||
|
removeFile(filename)
|
||||||
|
return
|
||||||
|
|
||||||
|
proc saveToFile(filename: string) =
|
||||||
|
if fileExists(filename):
|
||||||
|
let new_filename = filename.replace("/tmp/", getHomeDir() & "Screenshots/")
|
||||||
|
copyFile(filename, new_filename)
|
||||||
|
if fileExists(new_filename):
|
||||||
|
removeFile(filename)
|
||||||
|
return
|
||||||
|
|
||||||
|
proc openFile(filename: string) =
|
||||||
|
let cmd = "xdg-open " & filename
|
||||||
|
discard execCmd(cmd)
|
||||||
|
return
|
||||||
|
|
||||||
|
proc showScreenshotSaveSel(filename: string) =
|
||||||
|
let info = newInfo("Save Screenshot")
|
||||||
|
let args = @["clipboard", "save", "open", "---", "exit"]
|
||||||
|
let choice = outputData(info,args)
|
||||||
|
if choice == "---":
|
||||||
|
showScreenshotSaveSel(filename)
|
||||||
|
elif choice == "exit":
|
||||||
|
return
|
||||||
|
elif choice in args:
|
||||||
|
case choice:
|
||||||
|
of "clipboard":
|
||||||
|
saveToClipboard(filename)
|
||||||
|
of "save":
|
||||||
|
saveToFile(filename)
|
||||||
|
of "open":
|
||||||
|
openFile(filename)
|
||||||
|
return
|
||||||
|
|
||||||
|
proc showScreenshotTypeSel() =
|
||||||
|
let info = newInfo("Screenshot type")
|
||||||
|
let args = concat(TYPES,@["---","exit"])
|
||||||
|
let choice = outputData(info,args)
|
||||||
|
if choice in TYPES:
|
||||||
|
screenshot_type = choice
|
||||||
|
elif choice == "---":
|
||||||
|
showScreenshotTypeSel()
|
||||||
|
elif choice == "exit":
|
||||||
|
return
|
||||||
|
return
|
||||||
|
|
||||||
|
proc takeScreenshot() =
|
||||||
|
let filename = TEMP_DIR & FILENAME.replace("%d",DATE_STR)
|
||||||
|
var cmd = RUN_CMD.replace("%f",filename)
|
||||||
|
case screenshot_type:
|
||||||
|
of "window":
|
||||||
|
cmd = cmd.replace("%s",ACTIVE_WINDOW_CMD)
|
||||||
|
of "region":
|
||||||
|
cmd = cmd.replace("%s",REGION_CMD)
|
||||||
|
else: #fullscreen
|
||||||
|
cmd = cmd.replace("%s","")
|
||||||
|
# sleep for a bit otherwise the screen shot grabs dmenu as well
|
||||||
|
sleep(1*500)
|
||||||
|
|
||||||
|
let status = execCmd(cmd)
|
||||||
|
if status == 0:
|
||||||
|
showScreenshotSaveSel(filename)
|
||||||
|
return
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
if wayland:
|
||||||
|
RUN_CMD = SCREENSHOT_CMD_WL
|
||||||
|
REGION_CMD = REGION_FLAG_WL
|
||||||
|
CLIP_CMD = CLIPBOARD_CMD_WL
|
||||||
|
for arg in args:
|
||||||
|
if arg in TYPES:
|
||||||
|
screenshot_type = arg
|
||||||
|
break
|
||||||
|
if screenshot_type == "":
|
||||||
|
showScreenshotTypeSel()
|
||||||
|
if screenshot_type != "":
|
||||||
|
takeScreenshot()
|
13
show_date/show_date.nimble
Normal file
13
show_date/show_date.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "Displays the date in dmenu"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["date"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
52
show_date/src/show_date.nim
Normal file
52
show_date/src/show_date.nim
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[times,osproc,re]
|
||||||
|
|
||||||
|
const default_bg = blue
|
||||||
|
const default_fg = white
|
||||||
|
const default_format = "yyyy-MM-dd"
|
||||||
|
const cal_pos_x = "20"
|
||||||
|
const cal_pos_y = "20"
|
||||||
|
|
||||||
|
proc getObject(date: string): Info =
|
||||||
|
var data = newInfo("Date")
|
||||||
|
data.full_text = date
|
||||||
|
data.border = default_bg
|
||||||
|
data.selected_bg = default_bg
|
||||||
|
data.selected_fg = default_fg
|
||||||
|
return data
|
||||||
|
|
||||||
|
proc newCalendar(): string =
|
||||||
|
var c = """yad --calendar \
|
||||||
|
--undecorated --fixed --close-on-unfocus --no-buttons \
|
||||||
|
--width="222" --height="193" \
|
||||||
|
--posx="%pos_x" --posy="%pos_y" \
|
||||||
|
--title="yad-calendar" --borders 0 > /dev/null
|
||||||
|
"""
|
||||||
|
return c
|
||||||
|
|
||||||
|
proc openCalendar*(input: i3barInput) =
|
||||||
|
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()
|
||||||
|
c = replace(c,re"%pos_x", cal_pos_x)
|
||||||
|
c = replace(c,re"%pos_y", cal_pos_y)
|
||||||
|
discard execCmd(c)
|
||||||
|
|
||||||
|
proc getDate*() =
|
||||||
|
let date_today = times.now().format(default_format)
|
||||||
|
let data = getObject(date_today)
|
||||||
|
let output = outputData(data)
|
||||||
|
if output == date_today:
|
||||||
|
dmenuCalendar()
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
getDate()
|
||||||
|
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
||||||
|
|
72
temperature/src/temperature.nim
Normal file
72
temperature/src/temperature.nim
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import ../../base
|
||||||
|
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("Temperature")
|
||||||
|
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()
|
13
temperature/temperature.nimble
Normal file
13
temperature/temperature.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "Display temp in dmenu"
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["temperature"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
1
tides/nim.cfg
Normal file
1
tides/nim.cfg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
-d:ssl
|
106
tides/src/tides.nim
Normal file
106
tides/src/tides.nim
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#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 ../../base
|
||||||
|
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("Tides")
|
||||||
|
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()
|
13
tides/tides.nimble
Normal file
13
tides/tides.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "Display local tides in dmenu"
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["tides"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
1
translate/nim.cfg
Normal file
1
translate/nim.cfg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
-d:ssl
|
126
translate/src/translate.nim
Normal file
126
translate/src/translate.nim
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[re,httpclient,json,strutils,tables]
|
||||||
|
|
||||||
|
|
||||||
|
# TODO:
|
||||||
|
# Query available languages, maybe have selection panel
|
||||||
|
# Maybe store last used so future translations are more fluent
|
||||||
|
|
||||||
|
const LIBRETRANSLATE_URL = "https://libretranslate.pussthecat.org/"
|
||||||
|
const HOME = "en"
|
||||||
|
let LANG_RE = re"\w+>\w+"
|
||||||
|
let DETECT_RE = re"\bd(etect)?\b"
|
||||||
|
var current = @["de",HOME]
|
||||||
|
var detected = {"detected":"0","confidence":"0","language":"en"}.toTable
|
||||||
|
|
||||||
|
type
|
||||||
|
Request = object
|
||||||
|
source: string
|
||||||
|
target: string
|
||||||
|
q: string
|
||||||
|
format: string
|
||||||
|
|
||||||
|
proc main(messages: varargs[string] = @[])
|
||||||
|
|
||||||
|
proc newRequest(): Request =
|
||||||
|
return Request(source:current[0],
|
||||||
|
target:current[1],
|
||||||
|
q: "",
|
||||||
|
format: "text",)
|
||||||
|
|
||||||
|
proc parseReq(req: string): Request =
|
||||||
|
var langs = findAll(req,LANG_RE)
|
||||||
|
var r = newRequest()
|
||||||
|
let query = replace(req,LANG_RE,"")
|
||||||
|
r.q = query
|
||||||
|
if len(langs) > 0:
|
||||||
|
let lang = langs[0]
|
||||||
|
langs = lang.split(re">")
|
||||||
|
current[0] = langs[0].toLowerAscii()
|
||||||
|
current[1] = langs[1].toLowerAscii()
|
||||||
|
r.source = current[0].toLowerAscii()
|
||||||
|
r.target = current[1].toLowerAscii()
|
||||||
|
if query == "":
|
||||||
|
main()
|
||||||
|
return r
|
||||||
|
|
||||||
|
proc answerTranslate(response: string, req: Request) =
|
||||||
|
let r = parseJson(response)
|
||||||
|
try:
|
||||||
|
let ans = r["translatedText"].getStr()
|
||||||
|
main(ans)
|
||||||
|
except:
|
||||||
|
main("Error : " & r["error"].getStr())
|
||||||
|
|
||||||
|
proc goTranslate(req: string) =
|
||||||
|
let r = parseReq(req)
|
||||||
|
if r.q != "":
|
||||||
|
var client = newHTTPClient()
|
||||||
|
client.headers = newHttpHeaders({"Content-Type":"application/json"})
|
||||||
|
let body = %*r
|
||||||
|
let response = client.request(LIBRETRANSLATE_URL & "translate", httpMethod = HttpPost, body = $body)
|
||||||
|
if response.status != "":
|
||||||
|
answerTranslate(response.body, r)
|
||||||
|
|
||||||
|
proc flipCurrent() =
|
||||||
|
current = @[current[1],current[0]]
|
||||||
|
main()
|
||||||
|
|
||||||
|
proc detectLanguage(str: string) =
|
||||||
|
let term = replace(str,DETECT_RE,"")
|
||||||
|
echo "detecting ", term
|
||||||
|
if term != "":
|
||||||
|
var client = newHTTPClient()
|
||||||
|
client.headers = newHttpHeaders({"Content-Type":"application/json"})
|
||||||
|
let body = %*Request(q:term)
|
||||||
|
let response = client.request(LIBRETRANSLATE_URL & "detect", httpMethod = HttpPost, body = $body)
|
||||||
|
if response.status != "":
|
||||||
|
let r = parseJson(response.body)
|
||||||
|
echo r
|
||||||
|
try:
|
||||||
|
detected["confidence"] = $r[0]["confidence"].getFloat()
|
||||||
|
detected["language"] = r[0]["language"].getStr()
|
||||||
|
detected["detected"] = "1"
|
||||||
|
current[0] = detected["language"]
|
||||||
|
current[1] = HOME
|
||||||
|
goTranslate(term)
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
main("Error : " & r["error"].getStr())
|
||||||
|
except:
|
||||||
|
main("Error Parsing Json")
|
||||||
|
return
|
||||||
|
|
||||||
|
proc main(messages:varargs[string] = @[]) =
|
||||||
|
var info = newInfo("Translate")
|
||||||
|
info.selected_bg = green
|
||||||
|
var args: seq[string] = @[]
|
||||||
|
for msg in messages:
|
||||||
|
if msg != "":
|
||||||
|
args.add(msg)
|
||||||
|
args.add(current[0] & " > " & current[1])
|
||||||
|
if detected["detected"] == "1":
|
||||||
|
args.add("detected language : " & detected["language"])
|
||||||
|
args.add("detected confidence : " & detected["confidence"])
|
||||||
|
detected["detected"] = "0"
|
||||||
|
if len(messages) > 0:
|
||||||
|
args.add("back")
|
||||||
|
args.add("exit")
|
||||||
|
let output = outputData(info,args)
|
||||||
|
if output == "exit" or output == "":
|
||||||
|
return
|
||||||
|
elif output == "back":
|
||||||
|
main()
|
||||||
|
elif output == current[0] & " > " & current[1]:
|
||||||
|
flipCurrent()
|
||||||
|
elif re.startsWith(output,DETECT_RE):
|
||||||
|
detectLanguage(output)
|
||||||
|
elif output in messages:
|
||||||
|
copyToClipboard(output)
|
||||||
|
else:
|
||||||
|
goTranslate(output)
|
||||||
|
return
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
||||||
|
|
13
translate/translate.nimble
Normal file
13
translate/translate.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "Query libretranslate with dmenu"
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["translate"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
126
volume/src/volume.nim
Normal file
126
volume/src/volume.nim
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[os,strutils,sequtils,osproc]
|
||||||
|
|
||||||
|
const audio_tools = @["ncpamixer", "pavucontrol"]
|
||||||
|
const vol_cmd = "pamixer"
|
||||||
|
const vol_up = vol_cmd & " -i %v" # where %v is amount by
|
||||||
|
const vol_down = vol_cmd & " -d %v" # where %v is amount by
|
||||||
|
const vol_set = vol_cmd & " --set-volume %v" # where %v is amount by
|
||||||
|
const vol_mute = vol_cmd & " -t"
|
||||||
|
const vol_default_by = "5"
|
||||||
|
const vol_get = vol_cmd & " --get-volume"
|
||||||
|
const vol_get_mute = vol_cmd & " --get-mute"
|
||||||
|
const default_bg = green
|
||||||
|
const default_fg = black
|
||||||
|
|
||||||
|
|
||||||
|
proc getCurrentVolume(): string {.gcsafe.}
|
||||||
|
|
||||||
|
proc checkVolume(volume: string): string =
|
||||||
|
var vol = volume
|
||||||
|
if strip(vol) == "Connection error":
|
||||||
|
sleep(1000)
|
||||||
|
vol = getCurrentVolume()
|
||||||
|
vol = checkVolume(vol)
|
||||||
|
return vol
|
||||||
|
|
||||||
|
proc getDesign(volume: string): (string,string) =
|
||||||
|
let vol = checkVolume(volume)
|
||||||
|
var icon = " "
|
||||||
|
if vol == "muted":
|
||||||
|
return (icon & "muted", icon & "muted")
|
||||||
|
let pcnt = parseInt(strip(vol))
|
||||||
|
case pcnt:
|
||||||
|
of 85..100:
|
||||||
|
icon = " "
|
||||||
|
of 55..84:
|
||||||
|
icon = " "
|
||||||
|
of 35..54:
|
||||||
|
icon = " "
|
||||||
|
of 10..34:
|
||||||
|
icon = " "
|
||||||
|
else:
|
||||||
|
icon = " "
|
||||||
|
let main_text = icon & $pcnt & "%"
|
||||||
|
let text = "<span size=\"x-large\">" & icon & "</span>" & $pcnt & "%"
|
||||||
|
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() =
|
||||||
|
let cmd = replace(vol_up, "%v", vol_default_by)
|
||||||
|
discard execCmd(cmd)
|
||||||
|
|
||||||
|
proc volumeDown() =
|
||||||
|
let cmd = replace(vol_down, "%v", vol_default_by)
|
||||||
|
discard execCmd(cmd)
|
||||||
|
|
||||||
|
proc volumeMute() =
|
||||||
|
discard execCmd(vol_mute)
|
||||||
|
|
||||||
|
proc getVolume*(run_once: bool = false) =
|
||||||
|
let vol = getCurrentVolume()
|
||||||
|
let (text, main_text) = getDesign(vol)
|
||||||
|
var data = newInfo("Volume")
|
||||||
|
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 option = outputData(data,args)
|
||||||
|
if option == "":
|
||||||
|
return
|
||||||
|
elif option in args:
|
||||||
|
if option in audio_tools:
|
||||||
|
discard(execCmd(option))
|
||||||
|
return
|
||||||
|
case option:
|
||||||
|
of "up":
|
||||||
|
volumeUp()
|
||||||
|
getVolume()
|
||||||
|
of "down":
|
||||||
|
volumeDown()
|
||||||
|
getVolume()
|
||||||
|
of "mute":
|
||||||
|
volumeMute()
|
||||||
|
getVolume()
|
||||||
|
of "---":
|
||||||
|
getVolume()
|
||||||
|
of "exit":
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
let vol = parseInt(option)
|
||||||
|
let cmd = replace(vol_set, "%v", $vol)
|
||||||
|
let x = execCmd(cmd)
|
||||||
|
getVolume()
|
||||||
|
except:
|
||||||
|
echo getCurrentExceptionMsg()
|
||||||
|
getVolume()
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
getVolume()
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
block start:
|
||||||
|
for arg in args:
|
||||||
|
case arg:
|
||||||
|
of "up":
|
||||||
|
volumeUp()
|
||||||
|
break start
|
||||||
|
of "down":
|
||||||
|
volumeDown()
|
||||||
|
break start
|
||||||
|
of "mute":
|
||||||
|
volumeMute()
|
||||||
|
break start
|
||||||
|
main()
|
13
volume/volume.nimble
Normal file
13
volume/volume.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Package
|
||||||
|
|
||||||
|
version = "0.1.0"
|
||||||
|
author = "Paul Wilde"
|
||||||
|
description = "Display and control volume with dmenu"
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["volume"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
64
wlan/src/wlan.nim
Normal file
64
wlan/src/wlan.nim
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import ../../base
|
||||||
|
import std/[os,osproc,strutils,sequtils]
|
||||||
|
|
||||||
|
const default_bg = purple
|
||||||
|
const default_fg = white
|
||||||
|
const wlan_nics: seq[string] = @["wlan0"]
|
||||||
|
const get_ssid_cmd = "iwgetid -r"
|
||||||
|
const mng_cmd = "alacritty -e nmtui-connect"
|
||||||
|
|
||||||
|
proc getSsid(): string =
|
||||||
|
let ssid = execCmdEx(get_ssid_cmd)
|
||||||
|
return strip(ssid.output)
|
||||||
|
|
||||||
|
proc getSignalQuality(): string =
|
||||||
|
let wl = readFile("/proc/net/wireless")
|
||||||
|
let ln = splitLines(wl)[2]
|
||||||
|
let links = split(ln," ")
|
||||||
|
var qual = strip(links[1])
|
||||||
|
qual = replace(qual,".","")
|
||||||
|
return "[" & qual & "]"
|
||||||
|
|
||||||
|
proc getWifi(nic: string): (string, string) =
|
||||||
|
let ssid = getSsid()
|
||||||
|
if ssid == "":
|
||||||
|
return ("disconnected", "disconnected")
|
||||||
|
let quality = getSignalQuality()
|
||||||
|
return (ssid, quality)
|
||||||
|
|
||||||
|
proc getObject(): Info =
|
||||||
|
var data = newInfo("WiFi")
|
||||||
|
data.border = purple
|
||||||
|
data.selected_bg = default_bg
|
||||||
|
data.selected_fg = default_fg
|
||||||
|
return data
|
||||||
|
|
||||||
|
proc getWifiInfo*(nics: seq[string]) =
|
||||||
|
var lst: seq[string] = @[]
|
||||||
|
for nic in nics:
|
||||||
|
let (essid, quality) = getWifi(nic)
|
||||||
|
lst.add(nic & ":" & quality & " " & essid)
|
||||||
|
let data = getObject()
|
||||||
|
let args = concat(lst,@["---", "manage","exit"])
|
||||||
|
let output = outputData(data, args)
|
||||||
|
case output:
|
||||||
|
of "manage":
|
||||||
|
discard execCmd(mng_cmd)
|
||||||
|
of "exit":
|
||||||
|
return
|
||||||
|
of "---":
|
||||||
|
return
|
||||||
|
|
||||||
|
proc main() =
|
||||||
|
var my_nics: seq[string] = @[]
|
||||||
|
for nic in wlan_nics:
|
||||||
|
if dirExists("/sys/class/net/" & nic):
|
||||||
|
my_nics.add(nic)
|
||||||
|
if len(my_nics) > 0:
|
||||||
|
getWifiInfo(my_nics)
|
||||||
|
else:
|
||||||
|
switchTwmMode()
|
||||||
|
echo "No WLAN"
|
||||||
|
|
||||||
|
if isMainModule:
|
||||||
|
main()
|
13
wlan/wlan.nimble
Normal file
13
wlan/wlan.nimble
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# 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 = @["wlan"]
|
||||||
|
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 1.6.6"
|
Loading…
Reference in a new issue