2020-05-30 16:16:50 +00:00
; Copyright (c) 2019-2020 David Vogel
2019-10-20 14:32:48 +00:00
;
; This software is released under the MIT License.
; https://opensource.org/licenses/MIT
UsePNGImageEncoder()
2019-10-20 14:28:17 +00:00
Declare Worker(*Dummy)
Structure QueueElement
2019-11-01 01:40:21 +00:00
img.i
x.i
y.i
2019-10-20 14:28:17 +00:00
EndStructure
2019-11-01 01:40:21 +00:00
; Source: https://www.purebasic.fr/english/viewtopic.php?f=13&t=29981&start=15
Procedure EnumWindowsProc(hWnd.l, *lParam.Long)
Protected lpProc.l
GetWindowThreadProcessId_(hWnd, @lpProc)
If *lParam\l = lpProc ; Check if current window's processID matches
*lParam\l = hWnd ; Replace processID in the param With the hwnd As result
ProcedureReturn #False ; Return false to stop iterating
EndIf
ProcedureReturn #True
EndProcedure
; Source: https://www.purebasic.fr/english/viewtopic.php?f=13&t=29981&start=15
; Returns the first window associated with the given process handle
Procedure GetProcHwnd()
Protected pID.l = GetCurrentProcessId_()
Protected tempParam.l = pID
EnumWindows_(@EnumWindowsProc(), @tempParam)
If tempParam = pID ; Check if anything was found
ProcedureReturn #Null
EndIf
ProcedureReturn tempParam ; This is a valid hWnd at this point
EndProcedure
; Get the client rectangle of the "Main" window of this process in screen coordinates
ProcedureDLL GetRect(*rect.RECT)
Protected hWnd.l = GetProcHwnd()
If Not hWnd
ProcedureReturn #False
EndIf
If Not *rect
ProcedureReturn #False
EndIf
GetClientRect_(hWnd, *rect)
; A RECT consists basically of two POINT structures
ClientToScreen_(hWnd, @*rect\left)
ClientToScreen_(hWnd, @*rect\Right)
ProcedureReturn #True
EndProcedure
2019-10-20 14:28:17 +00:00
ProcedureDLL AttachProcess(Instance)
2019-11-01 01:40:21 +00:00
Global Semaphore = CreateSemaphore()
Global Mutex = CreateMutex()
Global NewList Queue.QueueElement()
2019-10-25 18:25:41 +00:00
2019-11-01 01:40:21 +00:00
CreateDirectory("mods/noita-mapcap/output/")
2019-10-25 18:25:41 +00:00
2019-11-01 01:40:21 +00:00
For i = 1 To 4
CreateThread(@Worker(), #Null)
Next
2019-10-20 14:28:17 +00:00
EndProcedure
Procedure Worker(*Dummy)
2019-11-01 01:40:21 +00:00
Protected img, x, y
Repeat
WaitSemaphore(Semaphore)
LockMutex(Mutex)
FirstElement(Queue())
img = Queue()\img
x = Queue()\x
y = Queue()\y
DeleteElement(Queue())
UnlockMutex(Mutex)
SaveImage(img, "mods/noita-mapcap/output/" + x + "," + y + ".png", #PB_ImagePlugin_PNG)
;SaveImage(img, "" + x + "," + y + ".png", #PB_ImagePlugin_PNG) ; Test
FreeImage(img)
ForEver
2019-10-20 14:28:17 +00:00
EndProcedure
ProcedureDLL Capture(px.i, py.i)
2020-10-17 22:39:58 +00:00
Protected hWnd.l = GetProcHwnd()
If Not hWnd
ProcedureReturn #False
EndIf
2019-11-01 01:40:21 +00:00
2020-10-17 22:39:58 +00:00
Protected rect.RECT
2019-11-01 01:40:21 +00:00
If Not GetRect(@rect)
ProcedureReturn #False
EndIf
imageID = CreateImage(#PB_Any, rect\right-rect\left, rect\bottom-rect\top)
If Not imageID
ProcedureReturn #False
EndIf
; Get DC of whole screen
2020-10-17 22:39:58 +00:00
windowDC = GetDC_(hWnd)
If Not windowDC
2019-11-01 01:40:21 +00:00
FreeImage(imageID)
ProcedureReturn #False
EndIf
hDC = StartDrawing(ImageOutput(imageID))
If Not hDC
2020-10-17 22:39:58 +00:00
ReleaseDC_(hWnd, windowDC)
2019-11-01 01:40:21 +00:00
FreeImage(imageID)
ProcedureReturn #False
EndIf
2020-10-17 22:39:58 +00:00
If Not BitBlt_(hDC, 0, 0, rect\right-rect\left, rect\bottom-rect\top, windowDC, 0, 0, #SRCCOPY) ; After some time BitBlt will fail, no idea why. Also, that's moments before noita crashes.
2019-11-01 01:40:21 +00:00
StopDrawing()
2020-10-17 22:39:58 +00:00
ReleaseDC_(hWnd, windowDC)
FreeImage(imageID)
2019-11-01 01:40:21 +00:00
ProcedureReturn #False
EndIf
StopDrawing()
2020-10-17 22:39:58 +00:00
ReleaseDC_(hWnd, windowDC)
2019-11-01 01:40:21 +00:00
LockMutex(Mutex)
; Check if the queue has too many elements, if so, wait. (Simulate go's channels)
While ListSize(Queue()) > 0
UnlockMutex(Mutex)
Delay(10)
LockMutex(Mutex)
Wend
LastElement(Queue())
AddElement(Queue())
Queue()\img = imageID
Queue()\x = px
Queue()\y = py
UnlockMutex(Mutex)
SignalSemaphore(Semaphore)
ProcedureReturn #True
2019-10-20 14:28:17 +00:00
EndProcedure
2019-11-01 01:40:21 +00:00
; #### Test
;AttachProcess(0)
;OpenWindow(0, 100, 200, 195, 260, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
;Delay(1000)
;Capture(123, 123)
;Delay(1000)
2020-10-17 22:39:58 +00:00
; IDE Options = PureBasic 5.72 (Windows - x64)
2019-10-20 14:28:17 +00:00
; ExecutableFormat = Shared dll
2020-10-17 22:39:58 +00:00
; CursorPosition = 90
; FirstLine = 77
2019-11-01 01:40:21 +00:00
; Folding = --
2019-10-20 14:28:17 +00:00
; EnableThread
; EnableXP
; Executable = capture.dll
; Compiler = PureBasic 5.71 LTS (Windows - x86)