From d477cbd70d788f0ca7d5e3586747de0ae7e21e43 Mon Sep 17 00:00:00 2001 From: Jakob Friedl <71284620+jakobfriedl@users.noreply.github.com> Date: Fri, 26 Sep 2025 13:24:35 +0200 Subject: [PATCH] Fixed the docking problems by having each agent dock once to the bottom or to the node where the "Listeners" table is shown when they get added. --- src/client/config.nims | 1 + src/client/main.nim | 25 +++++++++++++++++++------ src/client/views/dockspace.nim | 27 +++++++++++++++------------ src/client/views/sessions.nim | 20 +++++++++++--------- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/client/config.nims b/src/client/config.nims index 3093b12..771dc32 100644 --- a/src/client/config.nims +++ b/src/client/config.nims @@ -1,5 +1,6 @@ switch "o", "bin/client" +switch "d", "ssl" switch "d", "client" switch "d", "ImGuiTextSelect" diff --git a/src/client/main.nim b/src/client/main.nim index d5064b1..538ceb3 100644 --- a/src/client/main.nim +++ b/src/client/main.nim @@ -52,6 +52,9 @@ proc main() = continue newFrame() + # Initialize dockspace and docking layout + dockspace.draw(addr showConquest, views, addr dockTop, addr dockBottom, addr dockTopLeft, addr dockTopRight) + #[ WebSocket communication with the team server ]# @@ -61,8 +64,8 @@ proc main() = # Receive and parse websocket response message let event = recvEvent(ws.receiveMessage().get()) case event.eventType: - of CLIENT_PROFILE: - profile = parseString(event.data["profile"].getStr()) + # of CLIENT_PROFILE: + # profile = parsetoml.parseString(event.data["profile"].getStr()) of CLIENT_LISTENER_ADD: let listener = event.data.to(UIListener) @@ -73,7 +76,18 @@ proc main() = let agent = event.data.to(UIAgent) dump agent.agentId sessionsTable.agents.add(agent) - + + # Initialize position of console windows to bottom by drawing them once when they are added + var agentConsole = Console(agent) + consoles[agent.agentId] = agentConsole + let listenersWindow = igFindWindowByName("Listeners") + if listenersWindow != nil and listenersWindow.DockNode != nil: + igSetNextWindowDockID(listenersWindow.DockNode.ID, ImGuiCond_FirstUseEver.int32) + else: + igSetNextWindowDockID(dockBottom, ImGuiCond_FirstUseEver.int32) + consoles[agent.agentId].draw(ws) + consoles[agent.agentId].showConsole = false + of CLIENT_AGENT_CHECKIN: discard @@ -89,7 +103,6 @@ proc main() = else: discard # Draw/update UI components/views - dockspace.draw(addr showConquest, views, addr dockTop, addr dockBottom, addr dockTopLeft, addr dockTopRight) if showSessionsTable: sessionsTable.draw(addr showSessionsTable) if showListeners: listenersTable.draw(addr showListeners, ws) if showEventlog: eventlog.draw(addr showEventlog) @@ -100,14 +113,14 @@ proc main() = if console.showConsole: # Ensure that new console windows are docked to the bottom panel by default igSetNextWindowDockID(dockBottom, ImGuiCond_FirstUseEver.int32) - console.draw(ws) + console.draw(ws) newConsoleTable[agentId] = console # Update the consoles table with only those sessions that have not been closed yet # This is done to ensure that closed console windows can be opened again consoles = newConsoleTable - igShowDemoWindow(nil) + # igShowDemoWindow(nil) # render app.render() diff --git a/src/client/views/dockspace.nim b/src/client/views/dockspace.nim index 4051641..4bb871f 100644 --- a/src/client/views/dockspace.nim +++ b/src/client/views/dockspace.nim @@ -43,20 +43,23 @@ proc draw*(component: DockspaceComponent, showComponent: ptr bool, views: Table[ # Setup default docking layout var dockspaceId = igGetID_Str("Dockspace") - if igDockBuilderGetNode(dockspaceId) == nil: - igDockBuilderRemoveNode(dockspaceId) - igDockBuilderAddNode(dockspaceId, ImGuiDockNodeFlags_DockSpace.int32) - igDockBuilderSetNodeSize(dockspaceId, vp.WorkSize) + + if not component.initialized: + if igDockBuilderGetNode(dockspaceId) == nil: + igDockBuilderRemoveNode(dockspaceId) + igDockBuilderAddNode(dockspaceId, ImGuiDockNodeFlags_DockSpace.int32) + igDockBuilderSetNodeSize(dockspaceId, vp.WorkSize) - discard igDockBuilderSplitNode(dockspaceId, ImGuiDir_Down, 0.8f, dockBottom, dockTop) - discard igDockBuilderSplitNode(dockTop[], ImGuiDir_Right, 0.4f, dockTopRight, dockTopLeft) + discard igDockBuilderSplitNode(dockspaceId, ImGuiDir_Down, 0.8f, dockBottom, dockTop) + discard igDockBuilderSplitNode(dockTop[], ImGuiDir_Right, 0.4f, dockTopRight, dockTopLeft) - igDockBuilderDockWindow("Sessions [Table View]", dockTopLeft[]) - igDockBuilderDockWindow("Listeners", dockBottom[]) - igDockBuilderDockWindow("Eventlog", dockTopRight[]) - igDockBuilderDockWindow("Dear ImGui Demo", dockTopRight[]) - - igDockBuilderFinish(dockspaceId) + igDockBuilderDockWindow("Sessions [Table View]", dockTopLeft[]) + igDockBuilderDockWindow("Listeners", dockBottom[]) + igDockBuilderDockWindow("Eventlog", dockTopRight[]) + igDockBuilderDockWindow("Dear ImGui Demo", dockTopRight[]) + + igDockBuilderFinish(dockspaceId) + component.initialized = true # Create dockspace igDockSpace(dockspaceId, vec2(0.0f, 0.0f), component.dockspaceFlags, component.windowClass) diff --git a/src/client/views/sessions.nim b/src/client/views/sessions.nim index 89e832d..cabf0ba 100644 --- a/src/client/views/sessions.nim +++ b/src/client/views/sessions.nim @@ -1,4 +1,4 @@ -import times, tables, strformat +import times, tables, strformat, strutils import imguin/[cimgui, glfw_opengl, simple] import ./console @@ -52,13 +52,14 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) = ImGui_TableFlags_SizingStretchSame.int32 ) - let cols: int32 = 8 + let cols: int32 = 9 if igBeginTable("Sessions", cols, tableFlags, vec2(0.0f, 0.0f), 0.0f): igTableSetupColumn("AgentID", ImGuiTableColumnFlags_NoReorder.int32 or ImGuiTableColumnFlags_NoHide.int32, 0.0f, 0) igTableSetupColumn("Address", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Username", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Hostname", ImGuiTableColumnFlags_None.int32, 0.0f, 0) + igTableSetupColumn("Domain", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("OS", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("Process", ImGuiTableColumnFlags_None.int32, 0.0f, 0) igTableSetupColumn("PID", ImGuiTableColumnFlags_None.int32, 0.0f, 0) @@ -70,10 +71,9 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) = var multiSelectIO = igBeginMultiSelect(ImGuiMultiSelectFlags_ClearOnEscape.int32 or ImGuiMultiSelectFlags_BoxSelect1d.int32, component.selection[].Size, int32(component.agents.len())) ImGuiSelectionBasicStorage_ApplyRequests(component.selection, multiSelectIO) - for row in 0 ..< component.agents.len(): + for row, agent in component.agents: igTableNextRow(ImGuiTableRowFlags_None.int32, 0.0f) - let agent = component.agents[row] if igTableSetColumnIndex(0): # Enable multi-select functionality @@ -92,12 +92,14 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) = if igTableSetColumnIndex(3): igText(agent.hostname) if igTableSetColumnIndex(4): - igText(agent.os) + igText(if agent.domain.isEmptyOrWhitespace(): "-" else: agent.domain) if igTableSetColumnIndex(5): - igText(agent.process) + igText(agent.os) if igTableSetColumnIndex(6): - igText($agent.pid) + igText(agent.process) if igTableSetColumnIndex(7): + igText($agent.pid) + if igTableSetColumnIndex(8): let duration = now() - agent.latestCheckin.fromUnix().utc() let totalSeconds = duration.inSeconds @@ -121,9 +123,9 @@ proc draw*(component: SessionsTableComponent, showComponent: ptr bool) = if igMenuItem("Remove", nil, false, true): # Update agents table with only non-selected ones var newAgents: seq[UIAgent] = @[] - for i in 0 ..< component.agents.len(): + for i, agent in component.agents: if not ImGuiSelectionBasicStorage_Contains(component.selection, cast[ImGuiID](i)): - newAgents.add(component.agents[i]) + newAgents.add(agent) component.agents = newAgents ImGuiSelectionBasicStorage_Clear(component.selection)