Well, I can't help you with your multi-monitor environment, but this is what I've always used.....
I have created a small example here. Move the windows around and you'll get an idea of the function.
I'm curious if this works with multiple monitors as well.
Code: Select all
;/
;| Example of options for moving and resizing windows with Win API
;| proudly presented by Axolotl
;\
;-
;----== Low Level Procedures ==----------------------------------------------------------------------------------------
Procedure min(a, b) ; .. calculates the minimum of two numbers
If a < b ; returns:
ProcedureReturn a ; the smaller of the two numbers
EndIf
ProcedureReturn b
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure max(a, b) ; .. calculates the maximum of two numbers
If a > b ; returns:
ProcedureReturn a ; the larger of the two parameter values
EndIf
ProcedureReturn b
EndProcedure
; ---------------------------------------------------------------------------------------------------------------------
Procedure constrain(x, a, b) ; .. constrains a number 'x' to be within a range 'a' to 'b'
If x < a ; returns
ProcedureReturn a ; x: if x is between a and b
Else ; a: if x is less than a
If x > b ; b: If x is greater than b
ProcedureReturn b
EndIf
EndIf
ProcedureReturn x
EndProcedure
Procedure.l GetSystemMetrics(Index) ; .l is important (works in 64-bit) .. variable can be a .i type, index == i.e. #SM_XVIRTUALSCREEN
ProcedureReturn GetSystemMetrics_(Index) ; return depends on Index, i.e. #SM_XVIRTUALSCREEN
EndProcedure
Macro VirtualScreenX()
GetSystemMetrics(#SM_XVIRTUALSCREEN)
EndMacro
Macro VirtualScreenY()
GetSystemMetrics(#SM_YVIRTUALSCREEN)
EndMacro
Macro VirtualScreenWidth()
GetSystemMetrics(#SM_CXVIRTUALSCREEN)
EndMacro
Macro VirtualScreenHeight()
GetSystemMetrics(#SM_CYVIRTUALSCREEN)
EndMacro
Macro CountMonitors()
GetSystemMetrics(#SM_CMONITORS)
EndMacro
Macro SameDisplayFormat()
GetSystemMetrics(#SM_SAMEDISPLAYFORMAT)
EndMacro
Macro MonitorAsString() ; returns <Number of Monitors> _ <entire width> x <entire height> i.e. '1_1920x1080'
Str(GetSystemMetrics(#SM_CMONITORS)) + "_" + Str(GetSystemMetrics(#SM_CXVIRTUALSCREEN)) + "x" + Str(GetSystemMetrics(#SM_CYVIRTUALSCREEN))
EndMacro
;
;- MSDN: ClipOrCenterWindow example adapted to PureBasic :)
;
#MONITOR_CLIP = $0000 ; clip rect to monitor
#MONITOR_CENTER = $0001 ; center rect to monitor
#MONITOR_AREA = $0000 ; use monitor entire area
#MONITOR_WORKAREA = $0002 ; use monitor work area
Procedure SetRectToMonitor(*rc.RECT, Flags = #MONITOR_AREA | #MONITOR_CLIP) ; returns nothing .. *rc contains the modified rectangle
;
; Note, do not assume that the RECT is based on the origin (0,0).
;
; The most common problem apps have when running on a multimonitor system is that they "clip" or "pin" windows
; based on the SM_CXSCREEN and SM_CYSCREEN system metrics.
; Because of app compatibility reasons these system metrics return the size of the primary monitor.
; This shows how you use the multi-monitor functions to do the same thing.
;
Protected w, h, hMonitor, mi.MONITORINFO, rcMon.RECT :Debug #LF$+#PB_Compiler_Procedure+"()", 9
w = *rc\right - *rc\left ; calc w, h based on *rc
h = *rc\bottom - *rc\top ;
hMonitor = MonitorFromRect_(*rc, #MONITOR_DEFAULTTONEAREST) ; find monitor, at least the primary monitor is returned
mi\cbSize = SizeOf(mi) ; get the work area or entire monitor rect, depends on Flags
If GetMonitorInfo_(hMonitor, @mi) ; fill monitorinfo structure
If (Flags & #MONITOR_WORKAREA)
rcMon = mi\rcWork ; workarea rectangle without the Taskbar
Else
rcMon = mi\rcMonitor ; the entire monitor rectangle
EndIf
;' center or clip the passed rect to the monitor rectangle
If (Flags & #MONITOR_CENTER)
*rc\left = rcMon\left + (rcMon\right - rcMon\left - w) / 2
*rc\top = rcMon\top + (rcMon\bottom - rcMon\top - h) / 2
*rc\right = *rc\left + w
*rc\bottom = *rc\top + h
Else
*rc\left = max(rcMon\left, min(rcMon\right - w, *rc\left))
*rc\top = max(rcMon\top, min(rcMon\bottom - h, *rc\top))
*rc\right = *rc\left + w
*rc\bottom = *rc\top + h
EndIf
EndIf
EndProcedure
Procedure SetWindowPosition(hWnd, hwndParent = 0) ; move hWnd inside nearest monitor (centered to hwndParent if <> 0)
Protected w, h, rcClient.RECT, rcParent.RECT
If IsWindow_(hWnd) And GetWindowRect_(hWnd, @rcClient) ; client window rect, keep the width and hight
w = rcClient\right - rcClient\left
h = rcClient\bottom - rcClient\top
If hWndParent <> 0 ; use the valid parent window and center client window
GetWindowRect_(hwndParent, @rcParent)
rcClient\left = (((rcParent\right - rcParent\left) - w) / 2) + rcParent\left
rcClient\top = (((rcParent\bottom - rcParent\top) - h) / 2) + rcParent\top
rcClient\right = rcClient\left + w
rcClient\bottom = rcClient\top + h
EndIf ; hWndParent <> 0
SetRectToMonitor(@rcClient, #MONITOR_AREA | #MONITOR_CLIP) ; rcClient is moved inside the nearest monitor
SetWindowPos_(hWnd, #Null, rcClient\left, rcClient\top, 0, 0, #SWP_NOSIZE | #SWP_NOZORDER | #SWP_NOACTIVATE) ; move the window to rcClient x, y coordinates
EndIf
EndProcedure
Procedure EnsureWindowOnMonitor(Window, Flags = #MONITOR_AREA | #MONITOR_CLIP) ; returns nothing
Protected hwnd, rc.RECT
hwnd = WindowID(Window)
If GetWindowRect_(hwnd, @rc)
SetRectToMonitor(@rc, Flags)
SetWindowPos_(hwnd, #Null, rc\left, rc\top, 0, 0, #SWP_NOSIZE|#SWP_NOZORDER|#SWP_NOACTIVATE) ; move the window to rc x, y coordinates
EndIf
EndProcedure
CompilerIf #PB_Compiler_IsMainFile ; <<< Test Application >>>
#WINDOW_Main = 1 ; the easy way of programming
#WINDOW_Child = 2 ;
Procedure main()
Protected hwndMain, hwndChild
If OpenWindow(#WINDOW_Main, 0, 0, 600, 400, "Main Window", #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_SizeGadget)
; return value of OpenWindow is hwndMain as well (but not documented)
hwndMain = WindowID(#WINDOW_Main) ; this is the safe way
EndIf
If OpenWindow(#WINDOW_Child, 0, 0, 300, 200, "Child Window", #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_SizeGadget, hwndMain)
; return value of OpenWindow is hwndChild as well (but not documented)
hwndChild = WindowID(#WINDOW_Child) ; this is the safe way
EndIf
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Select EventWindow()
Case #WINDOW_Main
Break ; bye
Case #WINDOW_Child
; SetActiveWindow(#WINDOW_Main)
Break ; bye, this time ....
EndSelect
Case #PB_Event_MoveWindow ; after finishing moving the window
Select EventWindow()
Case #WINDOW_Main
EnsureWindowOnMonitor(#WINDOW_Main)
SetWindowPosition(hwndChild, hwndMain)
Case #WINDOW_Child
SetWindowPosition(hwndChild)
EndSelect
Case #PB_Event_SizeWindow ; after finishing sizing the window
Select EventWindow()
Case #WINDOW_Main
EnsureWindowOnMonitor(#WINDOW_Main)
SetWindowPosition(hwndChild, hwndMain)
Case #WINDOW_Child
SetWindowPosition(hwndChild)
EndSelect
EndSelect
ForEver
ProcedureReturn 0 ; default return value on windows
EndProcedure
End main()
CompilerEndIf