#PB_Window_ScreenCentered issue

Windows specific forum
BarryG
Addict
Addict
Posts: 4127
Joined: Thu Apr 18, 2019 8:17 am

#PB_Window_ScreenCentered issue

Post by BarryG »

This is an actual bug report that a user sent me, based on their triple-monitor setup; so I need to fix it for them.

On Windows, when I have three monitors set as one big desktop, and the third right-hand monitor is the main monitor, then opening a window with #PB_Window_ScreenCentered will center it over that monitor, instead of centered in the middle of all three. This makes some of the window not visible as it's off the monitor.

Open this image in a new tab to see it at 100% size:

Image

Is this a bug? I would expect #PB_Window_ScreenCentered to open in the center of all three (over monitor 2 in the middle).

If not a bug, how can I make my window always open centered over monitor 2 in the image above (ie. the center of the actual big desktop), no matter how many monitors the user has, and no matter which one is the main monitor?

Here's my test code:

Code: Select all

OpenWindow(0,0,0,640,320,"test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: #PB_Window_ScreenCentered issue

Post by Fred »

No, it's a special case than monitor are arranged this way. My dual setup is completely different (one laptop and one big screen, not aligned at all). You will need your special screen centered routine for this, as #PB_Window_ScreenCenter centers the window on the main monitor.
User avatar
Pierre Bellisle
User
User
Posts: 35
Joined: Wed Jun 27, 2018 5:12 am

Re: #PB_Window_ScreenCentered issue

Post by Pierre Bellisle »

What you got is normal. As Fred said, #PB_Window_ScreenCenter centers the window on the main monitor.

To work on a multi monitors setup, you will need more work.

Here some api that you might find useful to connect a window and a monitor. > MonitorFromPoint(), MonitorFromRect(), MonitorFromWindow().

In the "Screen resolution" panel, remember that you can choose the primary monitor
and also drag & drop any monitor at different position to rearrange the order.

The top-left position of the primary monitor will become the reference.
It will be zero horizontally And zero vertically.
This will be the reference position for all monitors.
Any point to the left of the primary monitor will have negative x-values.
Any point above the primary monitor will have negative y-values.

Multi monitors setup

Some code to center a window on any monitor ...

Code: Select all

EnableExplicit

#MONITORINFOF_PRIMARY = 1
#CCHDEVICENAME = 32

Declare WinMain()
WinMain() 
;_____________________________________________________________________________

Procedure.i EnumDisplayMonitorsProc(hMonitor.i, hDC.i, *rc.RECT, MonitorArray.i)
 Static MonitorIndex.l

 MonitorIndex = MonitorIndex + 1 ;Keep track of monitor number 

 PokeI(MonitorArray + MonitorIndex * SizeOf(integer), hMonitor) ;Copy monitor handle in MonitorArray(1, 2, 3...)

 If PeekI(MonitorArray) = MonitorIndex ;Monitor count is in MonitorArray(0) 
   MonitorIndex = 0 ;Reset for possible futureEnumDisplayMonitorsProc() call
   ProcedureReturn #False ;Stop enumeration (Optionnal)
 EndIf 

 ProcedureReturn #True ;Continue enum until no more monitor, enumeration will stop if #False is returned 

EndProcedure
;_____________________________________________________________________________

Procedure WinMain()
 If OpenConsole()
   Protected hConsole.i 
   Protected index.l, MonitorIndex.l, MonitorCount.l 
   Protected sKey.s, MonitorFlag.s
   Protected ConRec.rect, MonRec.rect 
   Dim hMonitorArray.i(0) ;Init array for furure redim
   Dim MonitorInfoArray.MONITORINFOEX(0) ;Init array for furure redim

   ConsoleTitle("PureBasic - Center window on multi monitors")    

   EnableGraphicalConsole(1) ;For ClearConsole()

   ;Get console window handle
   OpenLibrary(0, "kernel32.dll") 
   hConsole = CallFunction(0, "GetConsoleWindow") 
   CloseLibrary(0)

   MonitorIndex = 1 ;One based, zero is reserved for display count

   Repeat ;Looping to center on every monitor
     ClearConsole() : ConsoleColor(7, 0)

     ;Get console size and position
     GetWindowRect_(hConsole, @ConRec)
     PrintN("Console handle 0x" + Hex(hConsole) + ", size: left " + Str(ConRec\left) + ", right "  + Str(ConRec\right) + 
            ", top " + Str(ConRec\top)  + ", bottom " + Str(ConRec\bottom))
     PrintN("")
    
     ;Get visible monitor count 
     MonitorCount = GetSystemMetrics_(#SM_CMONITORS) ;Counts only visible display monitors

     ;Get all monitors handle
     ReDim hMonitorArray.i(MonitorCount) ;Array to keep monitors handles
     hMonitorArray(0) = MonitorCount ;Zero is used buy MonitorIndex in EnumDisplayMonitorsProc()
     EnumDisplayMonitors_(#Null, #Null, @EnumDisplayMonitorsProc(), @hMonitorArray(0)) ;EnumDisplayMonitorsProc will fill hMonitorArray()

     ;GetMonitorInfo for each monitor, rect and name
     ReDim MonitorInfoArray.MONITORINFOEX(MonitorCount)
     For index = 1 To MonitorCount
       MonitorInfoArray(index)\cbSize = SizeOf(MONITORINFOEX) ;Set version for GetMonitorInfo_()
       GetMonitorInfo_(hMonitorArray(index), @MonitorInfoArray(index)) ;Get MonitorInfo

       If MonitorInfoArray(index)\dwFlags = #MONITORINFOF_PRIMARY ;It is the primary monitor
         MonitorFlag = " (Primary monitor)"
       Else
         MonitorFlag = " (Other monitor)"
       EndIf

       If MonitorIndex = index
         ConsoleColor(14, 0) ;Yellow text
       Else
         ConsoleColor(7, 0) ;White text
       EndIf

       PrintN("Monitor " + Str(index) + ", handle 0x" + Hex(hMonitorArray(index)) + ", size " +
              Str((MonitorInfoArray(index)\rcMonitor\right  - MonitorInfoArray(index)\rcMonitor\left)) + " x " +
              Str((MonitorInfoArray(index)\rcMonitor\bottom - MonitorInfoArray(index)\rcMonitor\top)) + MonitorFlag)
       PrintN("left "  + Str(MonitorInfoArray(index)\rcMonitor\left) + ", right "  + Str(MonitorInfoArray(index)\rcMonitor\right) + 
              ", top " + Str(MonitorInfoArray(index)\rcMonitor\top)  + ", bottom " + Str(MonitorInfoArray(index)\rcMonitor\bottom))
       PrintN("")
     Next

     GetWindowRect_(hConsole, @ConRec) ;Get console size and position
     CopyStructure(MonitorInfoArray(MonitorIndex)\rcWork, MonRec, RECT) ;Use MonRec to shorten following lines
     SetWindowPos_(hConsole, #HWND_TOP, 
                   MonRec\Left + (MonRec\Right - MonRec\Left) / 2 - (ConRec\Right - ConRec\Left) / 2,
                   MonRec\Top  + (MonRec\Bottom - MonRec\Top) / 2 - (ConRec\Bottom - ConRec\Top) / 2,  
                   #Null, #Null, #SWP_NOZORDER|#SWP_NOSIZE|#SWP_SHOWWINDOW)
   
     ;Set MonitorIndex
     MonitorIndex = MonitorIndex + 1
     If MonitorIndex > MonitorCount
       MonitorIndex = 1
     EndIf

     ConsoleColor(11, 0) ;Bright cyan text
     PrintN("Strike a key to center on monitor " + Str(MonitorIndex) + ", " + PeekS(@MonitorInfoArray(MonitorIndex)\szDevice, #CCHDEVICENAME) + " (Esc To End)")
     Repeat
       Delay(200)
       sKey = Inkey() 
     Until Len(sKey) ;key down

   Until sKey = Chr(27) ;Escape 

   CloseConsole()

 EndIf
EndProcedure
;_____________________________________________________________________________
;
; IDE Options = PureBasic 5.73 LTS (Windows - x64)
; CursorPosition = 1
; FirstLine = 1
; EnableAsm
; EnableXP
; Executable = ~~Tmp06.exe
; DisableDebugger
; CompileSourceDirectory
BarryG
Addict
Addict
Posts: 4127
Joined: Thu Apr 18, 2019 8:17 am

Re: #PB_Window_ScreenCentered issue

Post by BarryG »

Thanks for your help, Pierre! Will check it out this weekend.
Post Reply