Client Area Painting
When setting a callback function with CSSetOnWmPaintCallback, Consoul will call back the host when it handles the WM_PAINT
events for the Consoul window client area. This allows the host to paint anywhere on the Consoul window client area.
There are two moments in the paint process when Consoul calls back the host. Which means the host callback function will be called once or twice for each paint event, depending on the pwCbkMode
value that was passed to CSSetOnWmPaintCallback.
Consoul paints on a memory display context before rendering it to the actual window display context. It is when it paints on the memory display context that Consoul will call the host back. While working in its callback function the host is passed the handle to the memory display context (phDC
) that the Consoul rendering engine is using to draw the necessary console content.
Paint callback function
The signature of the function that the host must adhere to is the following:
VB/A 32bits
Public Function OnConsoulWmPaint( _
ByVal phWnd As Long, _
ByVal pwCbkMode As Integer, _
ByVal phDC As Long, _
ByVal lprcLinePos As Long, _
ByVal lprcLineRect As Long, _
ByVal lprcPaint As Long _
) As Integer
VB/A 64bits
Public Function OnConsoulWmPaint( _
ByVal phWnd As LongPtr, _
ByVal pwCbkMode As Integer, _
ByVal phDC As LongPtr, _
ByVal lprcLinePos As LongPtr, _
ByVal lprcLineRect As LongPtr, _
ByVal lprcPaint As LongPtr _
) As Integer
Parameters
phWnd
The handle of the Consoul window, as returned at creation time by the CSCreateLogWindow API function.
pwCbkMode
The moment at which Consoul is calling the host back, either before doing its own painting WMPAINTCBK_BEFORE (=1) or after having drawn the console lines affected by the paint event WMPAINTCBK_AFTER (=2). Only one or the other bit is set when Consoul calls back. When the WMPAINTCBK_BEFORE is set, Consoul already has cleared the background and filled it with the Consoul window backcolor. In either case, the Consoul rendering engine has already "prepared" the display context font and attributes (etc...).
phDC
The Win32 API device context handle which the callback function can use to paint on the console surface.
lprcLinePos
Pointer to a RECT structure that contains the coordinates - in console line an column, not pixels - intersecting with the update rectangle of the WM_PAINT
event.
RECT member | line / column |
---|---|
left | Leftmost column |
top | First line |
right | Rightmost column |
bottom | Last line |
lprcLineRect
Pointer to a RECT structure that maps the rectangle pointed by lprcLinePos
to pixels coordinates.
lprcPaint
Pointer to a RECT structure that is the exact update rectangle that is requested in the WM_PAINT
message sent to the Consoul window. This is the rcUpdate
member of the PAINTSTRUCT structure sent along the WM_PAINT
message.
Remarks
Column values are computed by Consoul based on the average char width of the Consoul window font (CSGetCharWidth), which may be of poor precision when the font is not a monospaced font but a proportional or irregular character width font.
Dereferencing RECT pointers
A VB/A host cannot really "dereference" pointers, but the (Win32) RECT pointers returned by Consoul in the callback function can be used to copy the data to local variables defined as RECT, where RECT is a structured type in a standard module as:
'RECT is a classic Win32 API struct that we may use anywhere
Public Type RECT
left As Long
Top As Long
Right As Long
Bottom As Long
End Type
A function like CopyRectByAddress
can then copy the pointed rectangle to another address which is the address of the VB/A RECT variable:
#If Win64 Then
Private Declare PtrSafe Function apiCopyRect Lib "user32" Alias "CopyRect" (ByVal lpDestRect As LongPtr, ByVal lpSourceRect As LongPtr) As Long
#Else
Private Declare PtrSafe Function apiCopyRect Lib "user32" Alias "CopyRect" (ByVal lpDestRect As Long, ByVal lpSourceRect As Long) As Long
#End If
#If Win64 Then
Public Function CopyRectByAddress(ByVal lpDestRect As LongPtr, ByVal lpSrcRect As LongPtr) As Long
#Else
Public Function CopyRectByAddress(ByVal lpDestRect As Long, ByVal lpSrcRect As Long) As Long
#End If
CopyRectByAddress = apiCopyRect(lpDestRect, lpSrcRect)
End Function
Example
The following function could be called from the paint callback function. CConsoul
is a VB/A class that wraps the Consoul DLL API and can be found in the Consoul VB/A SDK.
This is actually the function used by AsciiPaint 2 to display the grid on the canvas.
32bits sample
Public Declare PtrSafe Function apiGetStockObject Lib "gdi32" Alias "GetStockObject" (ByVal nIndex As Long) As LongPtr
Public Declare PtrSafe Function apiSelectObject Lib "gdi32" Alias "SelectObject" (ByVal hdc As LongPtr, ByVal hObject As LongPtr) As LongPtr
Public Declare PtrSafe Function apiCreatePen Lib "gdi32" Alias "CreatePen" (ByVal nPenStyle As Long, ByVal nWidth As Long, ByVal crColor As Long) As LongPtr
Public Declare PtrSafe Function apiDeleteObject Lib "gdi32" Alias "DeleteObject" (ByVal hObject As LongPtr) As Long
Public Declare PtrSafe Function apiMoveToEx Lib "gdi32" Alias "MoveToEx" (ByVal hdc As LongPtr, ByVal x As Long, ByVal y As Long, ByVal lpPoint As LongPtr) As Long
Public Declare PtrSafe Function apiLineTo Lib "gdi32" Alias "LineTo" (ByVal hdc As LongPtr, ByVal x As Long, ByVal y As Long) As Long
Public Function OnPaintConsoleGrid( _
ByRef poConsole As CConsoul, _
ByVal phWnd As Long, _
ByVal phDC As LongPtr, _
ByVal lprcLinePos As Long, _
ByVal lprcLineRect As Long,_
ByVal lprcPaint As Long _
) As Long
Dim rcPaint As RECT
Dim rcLinePos As RECT
Dim rcLineRect As RECT
Dim hOldBrush As LongPtr
Dim hBrush As LongPtr
Dim hDotPen As LongPtr
Dim hOldPen As LongPtr
On Error Resume Next
CopyRectByAddress VarPtr(rcPaint), lprcPaint
CopyRectByAddress VarPtr(rcLinePos), lprcLinePos
CopyRectByAddress VarPtr(rcLineRect), lprcLineRect
hBrush = apiGetStockObject(NULL_BRUSH)
hOldBrush = apiSelectObject(phDC, hBrush)
hDotPen = apiCreatePen(PS_DOT, 1, RGB(200, 200, 200))
hOldPen = apiSelectObject(phDC, hDotPen)
Dim iCol As Integer
Dim x As Integer
Dim iCharHeight As Integer
Dim iCharWidth As Integer
iCharHeight = poConsole.LineHeight
iCharWidth = poConsole.CharWidth
For iCol = 0 To rcLinePos.Right - rcLinePos.left
x = rcLineRect.left + (iCol * iCharWidth)
Call apiMoveToEx(phDC, x, rcPaint.Top, 0)
Call apiLineTo(phDC, x, rcPaint.Bottom)
Next iCol
Dim iRow As Integer
Dim y As Integer
Dim iCount As Integer
If iCharHeight > 0 Then
iCount = (rcLineRect.Bottom - rcLineRect.Top) \ iCharHeight
End If
For iRow = 0 To iCount
y = rcLineRect.Top + (iRow * iCharHeight)
Call apiMoveToEx(phDC, rcPaint.left, y, 0)
Call apiLineTo(phDC, rcPaint.Right, y)
Next iRow
Call apiSelectObject(phDC, hOldBrush)
Call apiSelectObject(phDC, hOldPen)
Call apiDeleteObject(hDotPen)
OnPaintConsoleGrid = 0
End Function
Remarks
Win32 API declarations for VB/A 64bits can be found in the Win32API_PtrSafe.txt file (dating back from Office 2010). They're also valid with 32bits VBA7.