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 Dmenu = 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 i3BarInput* = object button*: int x*: int y*: int const font = "Hermit-10" 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 var loop* = false var stoploop* = true var dmenu* = true var rofi* = false var passmenu* = false var command_wrapper* = false var run_command* = "" proc newInfo*(title: string = "Info"): Info = 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 newDmenuConfig(cmd: string = "dmenu"): Dmenu = var dmenu = Dmenu() dmenu.command = cmd dmenu.bottom = "-b" dmenu.grabkb = "-f" dmenu.i_case = "-i" dmenu.lines_shown = "-l" dmenu.monitor = "-m" dmenu.prompt = "-p" dmenu.font = "-fn" dmenu.norm_bg = "-nb" dmenu.norm_fg = "-nf" dmenu.sel_bg = "-sb" dmenu.sel_fg = "-sf" return dmenu proc newRofiConfig(): Dmenu = var rofi = newDmenuConfig() rofi.command = "rofi -dmenu" return rofi proc newDmenu(): Dmenu = if rofi: return newRofiConfig() elif passmenu: return newDmenuConfig("passmenu") elif command_wrapper: return newDmenuConfig(run_command) return newDmenuConfig() 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 discard execCmd("i3-msg mode \"" & mode & "\"") 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 genDmenuCmd*(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 != "": cmd &= data.full_text & "\n" for opt in opts: cmd = cmd & opt & "\n" cmd.removeSuffix("\n") cmd = "echo -e" & quote(cmd) & " | " var dmenu = newDmenu() cmd = cmd & dmenu.command & " " cmd = cmd & dmenu.lines_shown & " " & $MAX_LINES & " " cmd = cmd & dmenu.prompt & quote(data.title) cmd = cmd & dmenu.norm_bg & quote(data.unselected_bg) cmd = cmd & dmenu.norm_fg & quote(data.unselected_fg) cmd = cmd & dmenu.sel_bg & quote(data.selected_bg) cmd = cmd & dmenu.sel_fg & quote(data.selected_fg) cmd = cmd & dmenu.font & quote(font) return cmd proc runDMenu*(data: Info, opts: varargs[string], rofi: bool = false): string = let cmd = genDmenuCmd(data, opts, rofi) #echo cmd # # Run command and get output var output = execCmdEx(cmd) output.output.stripLineEnd() return output.output proc copyToClipboard*(str: string) = discard execCmd("echo -n " & quote(str) & " | xclip -selection clipboard") proc outputData*(data: Info, args: varargs[string]): string {.discardable.} = var output = "" if rofi: output = runDmenu(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 = runDmenu(data,args) return output # At Start up: # 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 dmenu = true of "rofi": stoploop = true rofi = true of ["pass","passmenu"]: passmenu = true break