Added console history handling with arrow keys.
This commit is contained in:
@@ -1,24 +1,24 @@
|
||||
[Window][Sessions [Table View]]
|
||||
Pos=10,43
|
||||
Size=2117,223
|
||||
Size=2117,435
|
||||
Collapsed=0
|
||||
DockId=0x00000003,0
|
||||
|
||||
[Window][Listeners]
|
||||
Pos=10,268
|
||||
Size=2528,1081
|
||||
Pos=10,480
|
||||
Size=2528,869
|
||||
Collapsed=0
|
||||
DockId=0x00000002,0
|
||||
DockId=0x00000006,0
|
||||
|
||||
[Window][Eventlog]
|
||||
Pos=2129,43
|
||||
Size=409,223
|
||||
Size=409,435
|
||||
Collapsed=0
|
||||
DockId=0x00000004,0
|
||||
|
||||
[Window][Dear ImGui Demo]
|
||||
Pos=2129,43
|
||||
Size=409,223
|
||||
Size=409,435
|
||||
Collapsed=0
|
||||
DockId=0x00000004,1
|
||||
|
||||
@@ -28,16 +28,16 @@ Size=2548,1359
|
||||
Collapsed=0
|
||||
|
||||
[Window][[FACEDEAD] bob@LAPTOP-02]
|
||||
Pos=10,268
|
||||
Size=2528,1081
|
||||
Pos=10,480
|
||||
Size=2528,869
|
||||
Collapsed=0
|
||||
DockId=0x00000002,1
|
||||
DockId=0x00000006,1
|
||||
|
||||
[Window][[C9D8E7F6] charlie@SERVER-03]
|
||||
Pos=10,268
|
||||
Size=2528,1081
|
||||
Pos=10,480
|
||||
Size=1261,869
|
||||
Collapsed=0
|
||||
DockId=0x00000002,1
|
||||
DockId=0x00000005,0
|
||||
|
||||
[Window][Debug##Default]
|
||||
Pos=60,60
|
||||
@@ -45,22 +45,22 @@ Size=400,400
|
||||
Collapsed=0
|
||||
|
||||
[Window][[G1H2I3J5] diana@WORKSTATION-04]
|
||||
Pos=10,268
|
||||
Size=2528,1081
|
||||
Pos=10,480
|
||||
Size=2528,869
|
||||
Collapsed=0
|
||||
DockId=0x00000002,1
|
||||
DockId=0x00000006,1
|
||||
|
||||
[Window][[DEADBEEF] alice@DESKTOP-01]
|
||||
Pos=10,604
|
||||
Size=2848,1081
|
||||
Collapsed=0
|
||||
DockId=0x00000002,2
|
||||
DockId=0x00000006,2
|
||||
|
||||
[Window][Example: Console]
|
||||
Pos=10,466
|
||||
Size=1888,523
|
||||
Collapsed=0
|
||||
DockId=0x00000002,1
|
||||
DockId=0x00000006,1
|
||||
|
||||
[Window][Example: Assets Browser]
|
||||
Pos=60,60
|
||||
@@ -93,8 +93,10 @@ Column 0 Sort=0v
|
||||
|
||||
[Docking][Data]
|
||||
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=0x00000001 Parent=0x85940918 SizeRef=1024,435 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,1081 Selected=0x65D642C0
|
||||
DockNode ID=0x00000002 Parent=0x85940918 SizeRef=1024,869 Split=X Selected=0x8D780333
|
||||
DockNode ID=0x00000005 Parent=0x00000002 SizeRef=1261,869 Selected=0x65D642C0
|
||||
DockNode ID=0x00000006 Parent=0x00000002 SizeRef=1265,869 Selected=0x8D780333
|
||||
|
||||
|
||||
@@ -6,12 +6,18 @@ import ../../common/[types]
|
||||
const MAX_INPUT_LENGTH = 512
|
||||
type
|
||||
ConsoleComponent* = ref object of RootObj
|
||||
agent: Agent
|
||||
agent*: Agent
|
||||
showConsole*: bool
|
||||
inputBuffer: array[MAX_INPUT_LENGTH, char]
|
||||
console: ConsoleItems
|
||||
console*: ConsoleItems
|
||||
history: seq[string]
|
||||
historyPosition: int
|
||||
currentInput: string
|
||||
textSelect: ptr TextSelect
|
||||
|
||||
#[
|
||||
Helper functions for text selection
|
||||
]#
|
||||
proc getItemText(item: ConsoleItem): cstring =
|
||||
let timestamp = item.timestamp.format("dd-MM-yyyy HH:mm:ss")
|
||||
return fmt"[{timestamp}] {$item.itemType} {item.text}".string
|
||||
@@ -36,11 +42,69 @@ proc Console*(agent: Agent): ConsoleComponent =
|
||||
result.agent = agent
|
||||
result.showConsole = true
|
||||
zeroMem(addr result.inputBuffer[0], MAX_INPUT_LENGTH)
|
||||
|
||||
result.console = new ConsoleItems
|
||||
result.console.items = @[]
|
||||
result.history = @[]
|
||||
result.historyPosition = -1
|
||||
result.currentInput = ""
|
||||
result.textSelect = textselect_create(getLineAtIndex, getNumLines, cast[pointer](result.console), 0)
|
||||
|
||||
#[
|
||||
Text input callback function for managing console history and autocompletion
|
||||
]#
|
||||
proc callback(data: ptr ImGuiInputTextCallbackData): cint {.cdecl.} =
|
||||
|
||||
let component = cast[ConsoleComponent](data.UserData)
|
||||
|
||||
case data.EventFlag:
|
||||
of ImGui_InputTextFlags_CallbackHistory.int32:
|
||||
# Handle command history using arrow-keys
|
||||
|
||||
# Store current input
|
||||
if component.historyPosition == -1:
|
||||
component.currentInput = $(data.Buf)
|
||||
|
||||
let prev = component.historyPosition
|
||||
|
||||
# Move to a new console history item
|
||||
if data.EventKey == ImGuiKey_UpArrow:
|
||||
if component.history.len() > 0:
|
||||
if component.historyPosition < 0: # We are at the current input and move to the last item in the console history
|
||||
component.historyPosition = component.history.len() - 1
|
||||
else:
|
||||
component.historyPosition = max(0, component.historyPosition - 1)
|
||||
|
||||
elif data.EventKey == ImGuiKey_DownArrow:
|
||||
if component.historyPosition != -1:
|
||||
component.historyPosition = min(component.history.len(), component.historyPosition + 1)
|
||||
|
||||
if component.historyPosition == component.history.len():
|
||||
component.historyPosition = -1
|
||||
|
||||
# Update the text buffer if another item was selected
|
||||
if prev != component.historyPosition:
|
||||
let newText = if component.historyPosition == -1:
|
||||
component.currentInput
|
||||
else:
|
||||
component.history[component.historyPosition]
|
||||
|
||||
# Replace text input
|
||||
data.ImGuiInputTextCallbackData_DeleteChars(0, data.BufTextLen)
|
||||
data.ImGuiInputTextCallbackData_InsertChars(0, newText.cstring, nil)
|
||||
|
||||
# Set the cursor to the end of the updated input text
|
||||
data.CursorPos = newText.len().cint
|
||||
data.SelectionStart = newText.len().cint
|
||||
data.SelectionEnd = newText.len().cint
|
||||
|
||||
return 0
|
||||
|
||||
of ImGui_InputTextFlags_CallbackCompletion.int32:
|
||||
# Handle Tab-autocompletion
|
||||
discard
|
||||
|
||||
else: discard
|
||||
|
||||
proc draw*(component: ConsoleComponent) =
|
||||
igBegin(fmt"[{component.agent.agentId}] {component.agent.username}@{component.agent.hostname}", addr component.showConsole, 0)
|
||||
defer: igEnd()
|
||||
@@ -49,15 +113,16 @@ proc draw*(component: ConsoleComponent) =
|
||||
|
||||
#[
|
||||
Console items/text section using ImGuiTextSelect in a child window
|
||||
Supports:
|
||||
- horizontal+vertical scrolling,
|
||||
- autoscroll
|
||||
- colored text
|
||||
- text selection and copy functionality
|
||||
Features:
|
||||
- Horizontal+vertical scrolling,
|
||||
- Autoscroll
|
||||
- Colored text output
|
||||
- Text highlighting, copy/paste
|
||||
|
||||
Problems I encountered with other approaches (Multi-line Text Input, TextEditor, ...):
|
||||
- https://github.com/ocornut/imgui/issues/383#issuecomment-2080346129
|
||||
- https://github.com/ocornut/imgui/issues/950
|
||||
|
||||
Huge thanks to @dinau for implementing ImGuiTextSelect into imguin very rapidly after I requested it.
|
||||
]#
|
||||
let consolePadding: float = 10.0f
|
||||
@@ -113,8 +178,8 @@ proc draw*(component: ConsoleComponent) =
|
||||
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", addr component.inputBuffer[0], MAX_INPUT_LENGTH, inputFlags, nil, nil):
|
||||
let inputFlags = ImGuiInputTextFlags_EnterReturnsTrue.int32 or ImGuiInputTextFlags_EscapeClearsAll.int32 or ImGuiInputTextFlags_CallbackHistory.int32 or ImGuiInputTextFlags_CallbackCompletion.int32
|
||||
if igInputText("##Input", addr component.inputBuffer[0], MAX_INPUT_LENGTH, inputFlags, callback, cast[pointer](component)):
|
||||
|
||||
let command = $(addr component.inputBuffer[0]).cstring
|
||||
let commandItem = ConsoleItem(
|
||||
@@ -125,6 +190,11 @@ proc draw*(component: ConsoleComponent) =
|
||||
component.console.items.add(commandItem)
|
||||
|
||||
# TODO: Handle command execution
|
||||
# console.handleCommand(command)
|
||||
|
||||
# Add command to console history
|
||||
component.history.add(command)
|
||||
component.historyPosition = -1
|
||||
|
||||
zeroMem(addr component.inputBuffer[0], MAX_INPUT_LENGTH)
|
||||
focusInput = true
|
||||
|
||||
@@ -75,11 +75,11 @@ type
|
||||
# LOG_WARNING = "[ ! ] "
|
||||
# LOG_COMMAND = "[ > ] "
|
||||
# LOG_OUTPUT = ""
|
||||
LOG_INFO = "[INFO] "
|
||||
LOG_ERROR = "[FAIL] "
|
||||
LOG_SUCCESS = "[DONE] "
|
||||
LOG_WARNING = "[WARN] "
|
||||
LOG_COMMAND = "[>>>>] "
|
||||
LOG_INFO = "[INFO]"
|
||||
LOG_ERROR = "[FAIL]"
|
||||
LOG_SUCCESS = "[DONE]"
|
||||
LOG_WARNING = "[WARN]"
|
||||
LOG_COMMAND = "[>>>>]"
|
||||
LOG_OUTPUT = ""
|
||||
|
||||
SleepObfuscationTechnique* = enum
|
||||
|
||||
@@ -43,16 +43,16 @@ template writeLine*(cq: Conquest, args: varargs[untyped] = "") =
|
||||
|
||||
# Wrapper functions for logging/console output
|
||||
template info*(cq: Conquest, args: varargs[untyped] = "") =
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", $LOG_INFO, resetStyle, args)
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", $LOG_INFO, resetStyle, " ", args)
|
||||
|
||||
template error*(cq: Conquest, args: varargs[untyped] = "") =
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgRed, $LOG_ERROR, resetStyle, args)
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgRed, $LOG_ERROR, resetStyle, " ", args)
|
||||
|
||||
template success*(cq: Conquest, args: varargs[untyped] = "") =
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgGreen, $LOG_SUCCESS, resetStyle, args)
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgGreen, $LOG_SUCCESS, resetStyle, " ", args)
|
||||
|
||||
template warning*(cq: Conquest, args: varargs[untyped] = "") =
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgYellow, styleDim, $LOG_WARNING, resetStyle, args)
|
||||
cq.writeLine(fgBlack, styleBright, fmt"[{getTimestamp()}] ", fgYellow, styleDim, $LOG_WARNING, resetStyle, " ", args)
|
||||
|
||||
template input*(cq: Conquest, args: varargs[untyped] = "") =
|
||||
if cq.interactAgent != nil:
|
||||
|
||||
Reference in New Issue
Block a user