Implemented console input field.

This commit is contained in:
Jakob Friedl
2025-09-16 20:17:48 +02:00
parent ce417db941
commit ee397c4fb5
3 changed files with 112 additions and 74 deletions

View File

@@ -1,43 +1,43 @@
[Window][Sessions [Table View]]
Pos=10,43
Size=2117,381
Size=2117,223
Collapsed=0
DockId=0x00000003,0
[Window][Listeners]
Pos=10,426
Size=2528,971
Pos=10,268
Size=2528,1081
Collapsed=0
DockId=0x00000002,0
[Window][Eventlog]
Pos=2129,43
Size=409,381
Size=409,223
Collapsed=0
DockId=0x00000004,0
[Window][Dear ImGui Demo]
Pos=2129,43
Size=409,381
Size=409,223
Collapsed=0
DockId=0x00000004,1
[Window][Dockspace]
Pos=0,0
Size=2548,1407
Size=2548,1359
Collapsed=0
[Window][[FACEDEAD] bob@LAPTOP-02]
Pos=10,426
Size=2528,971
Pos=10,268
Size=2528,1081
Collapsed=0
DockId=0x00000002,3
DockId=0x00000002,1
[Window][[C9D8E7F6] charlie@SERVER-03]
Pos=10,426
Size=2528,971
Pos=10,268
Size=2528,1081
Collapsed=0
DockId=0x00000002,2
DockId=0x00000002,1
[Window][Debug##Default]
Pos=60,60
@@ -45,16 +45,16 @@ Size=400,400
Collapsed=0
[Window][[G1H2I3J5] diana@WORKSTATION-04]
Pos=10,426
Size=2528,971
Pos=10,268
Size=2528,1081
Collapsed=0
DockId=0x00000002,1
[Window][[DEADBEEF] alice@DESKTOP-01]
Pos=10,429
Size=1888,560
Pos=10,604
Size=2848,1081
Collapsed=0
DockId=0x00000002,1
DockId=0x00000002,2
[Window][Example: Console]
Pos=10,466
@@ -80,8 +80,8 @@ Collapsed=0
[Table][0x32886A44,8]
Column 0 Weight=0.6513
Column 1 Weight=0.9753
Column 2 Weight=0.5524
Column 3 Weight=1.0605
Column 2 Weight=0.3274
Column 3 Weight=1.2856
Column 4 Weight=1.7323
Column 5 Weight=1.1492
Column 6 Weight=0.4263
@@ -92,9 +92,9 @@ RefScale=27
Column 0 Sort=0v
[Docking][Data]
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=2528,1354 Split=Y
DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1024,381 Split=X
DockSpace ID=0x85940918 Window=0x260A4489 Pos=10,43 Size=2528,1306 Split=Y
DockNode ID=0x00000001 Parent=0x85940918 SizeRef=1024,559 Split=X
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=613,159 CentralNode=1 Selected=0x61E02D75
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=409,159 Selected=0x5E5F7166
DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,971 Selected=0x8D780333
DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,1081 Selected=0x65D642C0

View File

@@ -3,33 +3,30 @@ import imguin/[cimgui, glfw_opengl, simple]
import ../utils/appImGui
import ../../common/[types]
const MAX_INPUT_LENGTH = 512
type
ConsoleItem = ref object
timestamp: DateTime
logType: LogType
text: string
ConsoleItems = ref object
items: seq[string]
ConsoleComponent* = ref object of RootObj
agent: Agent
showConsole*: bool
inputBuffer: string
consoleItems: ConsoleItems
inputBuffer: array[MAX_INPUT_LENGTH, char]
console: ConsoleItems
textSelect: ptr TextSelect
proc getItemText(item: ConsoleItem): cstring =
let timestamp = item.timestamp.format("dd-MM-yyyy HH:mm:ss")
return fmt"[{timestamp}] {$item.itemType} {item.text}".string
proc getNumLines(data: pointer): csize_t {.cdecl.} =
if data.isNil:
return 0
let consoleItems = cast[ConsoleItems](data)
return consoleItems.items.len().csize_t
let console = cast[ConsoleItems](data)
return console.items.len().csize_t
proc getLineAtIndex(i: csize_t, data: pointer, outLen: ptr csize_t): cstring {.cdecl.} =
if data.isNil:
return nil
let consoleItems = cast[ConsoleItems](data)
let line = consoleItems.items[i].cstring
let console = cast[ConsoleItems](data)
let line = getItemText(console.items[i])
if not outLen.isNil:
outLen[] = line.len.csize_t
return line
@@ -38,16 +35,18 @@ proc Console*(agent: Agent): ConsoleComponent =
result = new ConsoleComponent
result.agent = agent
result.showConsole = true
result.inputBuffer = ""
zeroMem(addr result.inputBuffer[0], MAX_INPUT_LENGTH)
result.consoleItems = new ConsoleItems
result.consoleItems.items = @[]
result.textSelect = textselect_create(getLineAtIndex, getNumLines, cast[pointer](result.consoleItems), 0)
result.console = new ConsoleItems
result.console.items = @[]
result.textSelect = textselect_create(getLineAtIndex, getNumLines, cast[pointer](result.console), 0)
proc draw*(component: ConsoleComponent) =
igBegin(fmt"[{component.agent.agentId}] {component.agent.username}@{component.agent.hostname}", addr component.showConsole, 0)
defer: igEnd()
var focusInput = false
#[
Console items/text section using ImGuiTextSelect in a child window
Supports:
@@ -61,51 +60,74 @@ proc draw*(component: ConsoleComponent) =
- https://github.com/ocornut/imgui/issues/950
Huge thanks to @dinau for implementing ImGuiTextSelect into imguin very rapidly after I requested it.
]#
let footerHeight = igGetStyle().ItemSpacing.y + igGetFrameHeightWithSpacing() * 2
let consolePadding: float = 10.0f
let footerHeight = (consolePadding * 2) + (igGetStyle().ItemSpacing.y + igGetFrameHeightWithSpacing())
let textSpacing = igGetStyle().ItemSpacing.x
if igBeginChild_Str("##Console", vec2(-1.0f, -footerHeight), ImGuiChildFlags_NavFlattened.int32, ImGuiWindowFlags_HorizontalScrollbar.int32):
# Padding
igDummy(vec2(0.0f, consolePadding))
try:
# Set styles of the console window
igPushStyleColor_Vec4(ImGui_Col_FrameBg.int32, vec4(0.1f, 0.1f, 0.1f, 1.0f))
igPushStyleColor_Vec4(ImGui_Col_ScrollbarBg.int32, vec4(0.1f, 0.1f, 0.1f, 1.0f))
igPushStyleColor_Vec4(ImGui_Col_Border.int32, vec4(0.2f, 0.2f, 0.2f, 1.0f))
igPushStyleVar_Float(ImGui_StyleVar_FrameBorderSize .int32, 1.0f)
# Display console items
for entry in component.consoleItems.items:
igTextColored(vec4(0.0f, 1.0f, 1.0f, 1.0f), entry.cstring)
let childWindowFlags = ImGuiChildFlags_NavFlattened.int32 or ImGui_ChildFlags_Borders.int32 or ImGui_ChildFlags_AlwaysUseWindowPadding.int32 or ImGuiChildFlags_FrameStyle.int32
if igBeginChild_Str("##Console", vec2(-1.0f, -footerHeight), childWindowFlags, ImGuiWindowFlags_HorizontalScrollbar.int32):
# Display console items
for entry in component.console.items:
let timestamp = entry.timestamp.format("dd-MM-yyyy HH:mm:ss")
igTextColored(vec4(0.6f, 0.6f, 0.6f, 1.0f), fmt"[{timestamp}]".cstring)
igSameLine(0.0f, textSpacing)
igTextColored(vec4(0.0f, 1.0f, 1.0f, 1.0f), $entry.itemType)
igSameLine(0.0f, textSpacing)
igTextUnformatted(entry.text.cstring, nil)
component.textSelect.textselect_update()
component.textSelect.textselect_update()
# Auto-scroll to bottom if we're already at the bottom
if igGetScrollY() >= igGetScrollMaxY():
igSetScrollHereY(1.0f)
# Auto-scroll to bottom
if igGetScrollY() >= igGetScrollMaxY():
igSetScrollHereY(1.0f)
igEndChild()
except IndexDefect:
# CTRL+A crashes when no items are in the console
discard
# Buttons for testing the console
if igButton("Add Items", vec2(0.0f, 0.0f)):
for i in 1..10:
component.consoleItems.items.add("Hello world!")
finally:
igPopStyleColor(3)
igPopStyleVar(1)
igEndChild()
igSameLine(0.0f, 5.0f)
if igButton("Add Long Items", vec2(0.0f, 0.0f)):
for i in 1..3:
component.consoleItems.items.add("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.")
igSameLine(0.0f, 5.0f)
if igButton("Clear", vec2(0.0f, 0.0f)):
component.consoleItems.items.setLen(0)
# Padding
igDummy(vec2(0.0f, consolePadding))
#[
Input field with prompt indicator
]#
igText(fmt"[{component.agent.agentId}]")
let spacing = igGetStyle().ItemSpacing.x
igSameLine(0.0f, spacing)
igSameLine(0.0f, textSpacing)
# Calculate available width for input
var availableWidth: ImVec2
igGetContentRegionAvail(addr availableWidth)
igSetNextItemWidth(availableWidth.x)
let inputFlags = ImGuiInputTextFlags_EnterReturnsTrue.int32 or ImGuiInputTextFlags_EscapeClearsAll.int32 or ImGuiInputTextFlags_CallbackCompletion.int32 or ImGuiInputTextFlags_CallbackHistory.int32
if igInputText("##Input", component.inputBuffer, 256, inputFlags, nil, nil):
discard
let inputFlags = ImGuiInputTextFlags_EnterReturnsTrue.int32 or ImGuiInputTextFlags_EscapeClearsAll.int32 # or ImGuiInputTextFlags_CallbackCompletion.int32 or ImGuiInputTextFlags_CallbackHistory.int32
if igInputText("##Input", addr component.inputBuffer[0], MAX_INPUT_LENGTH, inputFlags, nil, nil):
let command = $(addr component.inputBuffer[0]).cstring
let commandItem = ConsoleItem(
timestamp: now(),
itemType: LOG_COMMAND,
text: command
)
component.console.items.add(commandItem)
# TODO: Handle command execution
zeroMem(addr component.inputBuffer[0], MAX_INPUT_LENGTH)
focusInput = true
#[
Session information (optional footer)
@@ -115,3 +137,5 @@ proc draw*(component: ConsoleComponent) =
# igText(sessionInfo)
igSetItemDefaultFocus()
if focusInput:
igSetKeyboardFocusHere(-1)

View File

@@ -73,10 +73,14 @@ type
# LOG_ERROR = "[ - ] "
# LOG_SUCCESS = "[ + ] "
# LOG_WARNING = "[ ! ] "
# LOG_COMMAND = "[ > ] "
# LOG_OUTPUT = ""
LOG_INFO = "[INFO] "
LOG_ERROR = "[FAIL] "
LOG_SUCCESS = "[DONE] "
LOG_WARNING = "[WARN] "
LOG_COMMAND = "[>>>>] "
LOG_OUTPUT = ""
SleepObfuscationTechnique* = enum
NONE = 0'u8
@@ -233,3 +237,13 @@ type
arguments*: seq[Argument]
dispatchMessage*: string
execute*: proc(config: AgentCtx, task: Task): TaskResult {.nimcall.}
# Definitions for ImGui User interface
type
ConsoleItem* = ref object
timestamp*: DateTime
itemType*: LogType
text*: string
ConsoleItems* = ref object
items*: seq[ConsoleItem]