Building Real-Time Cursor Monitors & Dynamic BIM Tooltips with AcEdInputPointMonitor (dev-cookbook-18)
Implement AcEdInputPointMonitor and AcEdInputPointFilter in GstarCAD 2027 C++ GRX to display real-time XDATA/dictionary tooltips and custom cursor aperture graphics.
1. Cursor Input Pipeline: `AcEdInputPointMonitor` vs. `AcEdInputPointFilter`
Every mouse movement inside a GstarCAD 2027 viewport passes through `AcEdInputPointManager` (`curDoc()->inputPointManager()`). The SDK exposes two distinct hooks into this 60–144 Hz cursor pipeline:
AcEdInputPointMonitor(Read-Only Observer): Receives the computed 3D point (`computedPoint`), active OSNAP modes, aperture entities (`apertureEntities`), and key-state flags (`MK_CONTROL`, `MK_SHIFT`). A monitor can draw temporary transient graphics via `AcGiViewportDraw` and append custom formatted text to the cursor tooltip (`appendToTooltipStr`), but it cannot alter the coordinate returned to the active command.AcEdInputPointFilter(Coordinate Mutator): Executes after monitors and is permitted to override `changedPoint`—ideal for magnetic grid snapping, custom 3D surface elevation locking, or orthogonal pipe routing constraints.
2. C++ Implementation: Live Equipment XDATA & Pressure-Drop Cursor Tooltip
In MEP and process plant engineering (`P&ID`), engineers want to hover over any valve, pump, or duct segment and immediately read its ERP part number, nominal diameter (`DN`), and calculated flow rate in the cursor rollover tooltip without opening the Properties palette. Below is a production `AcEdInputPointMonitor` subclass for GstarCAD 2027:
#include "grxHeaders.h"
#include "acedinpt.h"
class GcMepTooltipMonitor : public AcEdInputPointMonitor {
public:
virtual Acad::ErrorStatus monitorInputPoint(
const AcEdInputPoint& input,
AcEdInputPointMonitorResult& output) override
{
if (!input.pointComputed()) return Acad::eOk;
const AcDbObjectIdArray& pickedIds = input.pickedEntities();
if (pickedIds.isEmpty()) return Acad::eOk;
// Open top-most hovered entity for lightweight read
AcDbObjectPointer<AcDbEntity> pEnt(pickedIds[0], AcDb::kForRead);
if (pEnt.openStatus() != Acad::eOk) return Acad::eOk;
// Query registered application XDATA ("GSTAR_MEP_SPEC")
struct resbuf* pRb = pEnt->xData(L"GSTAR_MEP_SPEC");
if (pRb != nullptr) {
AcString tooltipText = L"
--- GstarCAD MEP Telemetry ---";
for (struct resbuf* pCur = pRb->rbnext; pCur != nullptr; pCur = pCur->rbnext) {
if (pCur->restype == AcDb::kDxfXdAsciiString) {
tooltipText.appendFormat(L"
Spec: %s", pCur->resval.rstring);
} else if (pCur->restype == AcDb::kDxfXdReal) {
tooltipText.appendFormat(L"
Flow Rate: %.2f L/s", pCur->resval.rreal);
}
}
acutRelRb(pRb);
output.setAdditionalTooltipString(tooltipText.kwszPtr());
// Highlight bounding circle in viewport context
if (input.drawContext() != nullptr) {
input.drawContext()->subEntityTraits().setColor(3); // Green highlight
input.drawContext()->geometry().circle(
input.computedPoint(), 150.0, AcGeVector3d::kZAxis);
}
}
return Acad::eOk;
}
};
3. Per-Document Registration Lifecycle & Latency Budgeting
Because `AcEdInputPointManager` is owned by each individual `AcApDocument` instance rather than the global application singleton, you must attach and detach your `GcMepTooltipMonitor` inside an `AcApDocManagerReactor` (`documentCreated` and `documentToBeDestroyed`). Furthermore, because `monitorInputPoint()` fires up to 120 times per second during mouse movement, keep execution time strictly under **0.5 milliseconds**:
| Performance Anti-Pattern | Symptom in Viewport | Recommended GRX Best Practice |
|---|---|---|
| Querying SQL/REST API inside `monitorInputPoint()` | Severe cursor stuttering and dropped mouse frames | Cache external PLM/ERP attributes into an in-memory `std::unordered_map<AcDbHandle, SpecData>` indexed by entity handle. |
| Iterating entire Model Space Block Table Record | 100% single-core CPU spike on 50,000+ entity drawings | Inspect only `input.pickedEntities()[0]` (already spatially indexed by GstarCAD's pickbox aperture tree). |
| Forgetting `removePointMonitor()` on unload | Access violation (`0xC0000005`) on next mouse move | Iterate `acDocManager->newAcApDocumentIterator()` inside `On_kUnloadAppMsg` and detach monitors from all open documents. |
4. Filtering Sub-Entities Inside Nested Blocks (`fullSubentPath`)
In architectural and plant layouts where equipment symbols are nested two or three levels deep inside floor-plan Xrefs (`AcDbBlockReference` chains), `input.pickedEntities()` returns only the top-level leaf entity. To inspect the parent block hierarchy or identify which specific Xref instance is under the cursor, query `input.gsSelectionMark()` alongside `acdbGetObjectId()` or traverse the owner chain via `pEnt->ownerId()`.
If your tooltip monitor also renders transient 3D clearance spheres or flow vectors via `input.drawContext()->geometry()`, always save and restore `subEntityTraits().color()`, `lineWeight()`, and `fillType()` before returning `Acad::eOk` so subsequent monitors in the chain inherit a clean graphics state.
Validate This Workflow in GstarCAD 2027
Engineered by Suzhou Gstarsoft Co., Ltd. (SSE: 688657), GstarCAD 2027 delivers 100% native AC1032 DWG binary fidelity, perpetual licensing sovereignty, and zero-day AutoCAD command parity.