start work on v2
This commit is contained in:
parent
91ca8e12a4
commit
1a478fca9e
60 changed files with 81 additions and 661 deletions
|
@ -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"
|
|
|
@ -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()
|
|
|
@ -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"
|
|
|
@ -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()
|
|
278
globurrl.nim
278
globurrl.nim
|
@ -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,"\"",""")
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
7
src/common.nim
Normal file
7
src/common.nim
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import model/config
|
||||||
|
|
||||||
|
export config
|
||||||
|
|
||||||
|
var myConfig* = newConfig()
|
||||||
|
|
||||||
|
|
40
src/model/config.nim
Normal file
40
src/model/config.nim
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import os
|
||||||
|
import parsetoml
|
||||||
|
|
||||||
|
type
|
||||||
|
Config* = ref object
|
||||||
|
exec*: string
|
||||||
|
max_lines*: int
|
||||||
|
prepend*: bool
|
||||||
|
|
||||||
|
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 &= "max_lines = " & $c.max_lines
|
||||||
|
str &= "\n"
|
||||||
|
return str
|
||||||
|
|
||||||
|
proc newConfig*(): Config =
|
||||||
|
var cfg = Config()
|
||||||
|
cfg.exec = "rofi -dmenu"
|
||||||
|
cfg.prepend = true
|
||||||
|
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
|
||||||
|
except:
|
||||||
|
echo "Error with Config File:"
|
||||||
|
echo getCurrentExceptionMsg()
|
||||||
|
return cfg
|
11
src/model/info.nim
Normal file
11
src/model/info.nim
Normal 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)
|
1
src/util/translaturr/nim.cfg
Normal file
1
src/util/translaturr/nim.cfg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
-d:ssl
|
8
src/wmtools.nim
Normal file
8
src/wmtools.nim
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
echo myConfig.exec
|
14
wm_tools.nimble
Normal file
14
wm_tools.nimble
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# 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"
|
Loading…
Reference in a new issue