[bmx] Full-screen borderless window (Win32) by BlitzSupport [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:38

Previous topic - Next topic

BlitzBot

Title : Full-screen borderless window (Win32)
Author : BlitzSupport
Posted : 1+ years ago

Description : Quick test opens a BlitzMax Graphics display in windowed mode (depth 0), then resizes it to remove window borders, resulting in the now-popular full-screen borderless window.

For real-world use, read the MSDN docs for the functions used and check results!

NOT heavily tested!


Code :
Code (blitzmax) Select
' https://stackoverflow.com/questions/2382464/win32-full-screen-and-hiding-taskbar
' https://stackoverflow.com/questions/2398746/removing-window-border

' Used method in second code box of first link, plus second link for modifying border.

' Just found this method is recommended by Raymond Chen of M$, who knows his stuff:

' http://blogs.msdn.com/b/oldnewthing/archive/2005/05/05/414910.aspx

SuperStrict

' Used by FindWindow; set to name of your app!

AppTitle = "MY GAME"

' Open window at full size, depth = 0 for windowed mode...

Graphics DesktopWidth (), DesktopHeight (), 0

' Win32 monitor info structure...

Type MONITORINFO

Field cbSize:Int

Field rcMonitorLeft:Int
Field rcMonitorTop:Int
Field rcMonitorRight:Int
Field rcMonitorBottom:Int

Field rcWorkLeft:Int
Field rcWorkTop:Int
Field rcWorkRight:Int
Field rcWorkBottom:Int

Field dwFlags:Int

End Type

' Win32 constants...

Const MONITOR_DEFAULTTONEAREST:Int = 2

Const GWL_STYLE:Int = -16
Const GWL_EXSTYLE:Int = -20

Const HWND_TOP:Int = 0
Const SWP_FRAMECHANGED:Int = $20

Const WS_POPUP:Int = $80000000
Const WS_VISIBLE:Int = $10000000
Const WS_CAPTION:Int = $C00000
Const WS_THICKFRAME:Int = $00040000
Const WS_MINIMIZE:Int = $20000000
Const WS_MAXIMIZE:Int = $01000000
Const WS_SYSMENU:Int = $80000

Const WS_EX_DLGMODALFRAME:Int = $00000001
Const WS_EX_CLIENTEDGE:Int = $200
Const WS_EX_STATICEDGE:Int = $20000

' Win32 function pointers...

Global FindWindow (lpClassName:Byte Ptr, lpWindowName:Byte Ptr) "win32"
Global MonitorFromWindow (hwnd:Int, dwFlags:Int) "win32"
Global GetMonitorInfo (hMonitor:Int, lpmi:Byte Ptr) "win32"
Global GetWindowLong (hwnd:Int, nIndex:Int) "win32"
Global SetWindowLong (hwnd:Int, nIndex:Int, dwNewLong:Int) "win32"
Global SetWindowPos (hWnd:Int, hWndInsertAfter:Int, X:Int, Y:Int, cx:Int, cy:Int, uFlags:Int)

' Set up function pointers...

Local user32:Int = LoadLibraryA ("user32.dll")

If user32

FindWindow = GetProcAddress (user32, "FindWindowA")
MonitorFromWindow = GetProcAddress (user32, "MonitorFromWindow")
GetMonitorInfo = GetProcAddress (user32, "GetMonitorInfoA")
GetWindowLong = GetProcAddress (user32, "GetWindowLongA")
SetWindowLong = GetProcAddress (user32, "SetWindowLongA")
SetWindowPos = GetProcAddress (user32, "SetWindowPos")

If Not (FindWindow And MonitorFromWindow And GetMonitorInfo And GetWindowLong And SetWindowLong And SetWindowPos)
Print "Missing function!"
End
EndIf

EndIf

' AppTitle allocated as C-string...

Local cbytes:Byte Ptr = AppTitle.ToCString ()

' Find app window...

Local appwindow:Int = FindWindow (Null, cbytes)

' Free C-string memory...

MemFree cbytes

' Find which monitor the app window is on...

Local monitor:Int = MonitorFromWindow (appwindow, MONITOR_DEFAULTTONEAREST)

' Monitor info...

Local mi:MONITORINFO = New MONITORINFO
mi.cbSize = SizeOf (MONITORINFO)

GetMonitorInfo monitor, mi

' Read window style/extended style...

Local style:Int = GetWindowLong (appwindow, GWL_STYLE)
Local exstyle:Int = GetWindowLong (appwindow, GWL_EXSTYLE)

' Prepare to remove unwanted styles...

style = style And Not (WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU)
exstyle = exstyle And Not (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)

' Remove unwanted styles...

SetWindowLong (appwindow, GWL_STYLE, style | WS_POPUP | WS_VISIBLE | SWP_FRAMECHANGED)
SetWindowLong (appwindow, GWL_EXSTYLE, exstyle)

' Update window...

SetWindowPos appwindow, HWND_TOP, mi.rcMonitorLeft, mi.rcMonitorTop, mi.rcMonitorRight - mi.rcMonitorLeft, mi.rcMonitorBottom - mi.rcMonitorTop, 0

Repeat

Cls

DrawRect MouseX (), MouseY (), 32, 32
Flip

Until KeyHit (KEY_ESCAPE)

End


Comments :


Grey Alien(Posted 1+ years ago)

 Cool. What Oses have you tested this on so far? Thanks.


AdamStrange(Posted 1+ years ago)

 the answer to that is in the code (win32)So this will only work on windows machines


BlitzSupport(Posted 1+ years ago)

 Yeah, sorry, thought it was obvious but... obviously not! Don't know anything about doing this on Mac/Linux, I'm afraid.(Updated title.)


Grey Alien(Posted 1+ years ago)

 My bad, I just skimmed it. Thanks for confirmation.


Grey Alien(Posted 1+ years ago)

 Implementing it now. I've got a few question about this if you would be kind enough to answer:So it sets up a graphics window at desktop resolution, but then resizes it to the current monitor yes?Why does it do that?Is the desktop resolution returned by DesktopWidth() and DesktopHeight() extra large when you have two or more monitors or is it just the size of the current monitor?  I've always assumed it's the size of the primary monitor but I guess that could be badly wrong.However, if desktop res *is* the size of the primary monitor, and the window appears on the primary monitor, why do you need to resize the window? I can see why it needs to be repositioned once borders + title bar are removed, but I don't get why it needs to be resized, or is that just a precaution?I'm mostly asking because if it does resize, I ought to store that new size and then use SetVirtualResolution to map to it I guess.


Grey Alien(Posted 1+ years ago)

 btw, instead of using findwindow you can do this:
Local my_driver:TD3D9GraphicsDriver = D3D9GraphicsDriver()
Local my_graphics:TD3D9Graphics = my_driver.Graphics()
Return my_graphics._hwnd
Needs some ifs to check for nulls but you get the idea.but that does mean the following module tweak is required:modify d3d9graphics.bmx

add this method to TD3D9GraphicsDriver:

Method Graphics:TD3D9Graphics()
Return _graphics
End Method
So I guess that defaults the point of your code being a non-module tweak.


BlitzSupport(Posted 1+ years ago)

 If you leave out this line:SetWindowPos appwindow, HWND_TOP, mi.rcMonitorLeft, mi.rcMonitorTop, mi.rcMonitorRight - mi.rcMonitorLeft, mi.rcMonitorBottom - mi.rcMonitorTop, 0
... it doesn't actually hide the taskbar automatically. In fact, it looks like the Blitz window remains offset upwards by the titlebar height after removing the border styles, hence the need to reposition.From BlitzMax's DesktopWidth/Height, I get 1920 x 1080 regardless -- I assume it only takes the primary monitor into account.You can select to clone/duplicate the desktop on the secondary display, or extend to form one large desktop, hence my belief that Blitz is only taking the primary monitor into account.In fact, if I force both displays to be different resolutions, the results are those for whichever display I set as primary, eg:Monitor: 1920 x 1080 [PRIMARY]
TV     : 1600 x 900

' Returns 1920, 1080

Monitor: 1920 x 1080
TV     : 1600 x 900  [PRIMARY]

' Returns 1600, 900

Hope that makes some kind of sense...(WRT the FindWindow thing, that was just for demo purposes -- wouldn't rely on titlebar text for real, though many programs do!)


Grey Alien(Posted 1+ years ago)

 Right, so it must be moved but the size basically ends up the same i.e. desktop size. OK that's good to know, otherwise I was going to have to adjust everything.Thanks for the confirmation, appreciated!I might still rely on the findwindow method as I have a mutex to stop multiple versions of my games running at once.


Grey Alien(Posted 1+ years ago)

 I'm experiencing some weirdness where if I compare a non-borderless window's graphics with a borderless one, everything is stretched vertically slightly. Like probably by the amount that the title bar is tall. I'm not sure what is going on yet, but it doesn't look great.[EDIT] Hmm, I think it's because the client area is changing size (expanding by 22 pixels in my case) and Windows is scaling everything to take that into account.[EDIT2] Wow I've been on quite a few wild goosechases today. Basically the issue is that the D3Device has to be recreated with the new client size.  So if I do this (after calling your code), it looks correct now: Local my_driver:TD3D9GraphicsDriver = D3D9GraphicsDriver()
Local my_graphics:TD3D9Graphics = my_driver.Graphics()
my_graphics._width = DesktopWidth()
my_graphics._height = DesktopHeight()
my_graphics.Attach(appwindow, 0)
However, that's a total bodge as I'm not freeing up the old D3Device first. I suspect I should be calling CloseD3DDevice() (which is Private in d3d9graphics.bmx, easily fixed by moving Public and recompiling the mod).Also I've no idea if that is safe. For example, if I check my_graphics._attached before I call the code above, it is 0, and afterwards it is 1. I've no idea what repercussions that could have.Alternatively, instead of calling Attach() I could make OpenD3DDevice() public and call that like this:OpenD3DDevice(appwindow, 0,0, DesktopWidth(), DesktopHeight(),0)
Flags is 0 though, and I'm not sure if that's OK. I don't tend to use flags when I call Graphics() so it might be OK in my case IF that's the same type of flags being used, but it might not be!Alternatively to all this, it might be simplest to make this mod tweak ( <a href="../Community/postsab9d.html?topic=102797" target="_blank">http://www.blitzbasic.com/Community/posts.php?topic=102797</a> ) to add in support for borderless windows on creation. Then there's no messing around with having to adjust a window border afterwards and having a screwed up D3Device... (thought I wish your code had worked off the bat for my purposes, it would have been cool!)


BlitzSupport(Posted 1+ years ago)

 I'll try have a look tonight, couple of ideas... whether I can sort it or not I've yet to find out!


Grey Alien(Posted 1+ years ago)

 Cool. I will try out the module tweak solution today as a matter of interest to see if it has any similar problems.


BlitzSupport(Posted 1+ years ago)

 Think it will have to be a module tweak.For some reason, on my 1920 x 1080 display, DesktopWidth and DesktopHeight are correct, but creating a Graphics display from these results in GraphicsHeight coming out 16 pixels less. The title bar is 32 pixels, so it's apparently not that, and the taskbar is even bigger...Graphics DesktopWidth (), DesktopHeight (), 0

Print GraphicsWidth ()    ' 1920
Print GraphicsHeight ()   ' 1064

Repeat
Until KeyHit (KEY_ESCAPE)
... and I'm not convinced that even if you managed to open the window at the right size, the GraphicsWidth/Height sizes would be adjusted; hence, I think the correct graphics size needs to be set as soon as the window opens, which I'd say you can't do without modifying the module code.


BlitzSupport(Posted 1+ years ago)

 Oh, just saw your fix there! Looks like you've figured it all out before me...


Grey Alien(Posted 1+ years ago)

 Thanks for looking into it.Yep, in the end I used the module tweak in that other thread: <a href="../Community/postsab9d.html?topic=102797#1232692" target="_blank">http://www.blitzbasic.com/Community/posts.php?topic=102797#1232692</a>Testing in DX and GL and it works great, about to test on Mac.I just wasn't confident that my fixes above were going to be safe, and they probably needed a module tweak anyway, which defeats the original point of your code, nice as it would have been.


Grey Alien(Posted 1+ years ago)

 OK have tested Mac version of the module tweaks above but it still shows the apple bar along the top with app name, clock, battery status etc, and if you mouse over the bottom of the screen the task bar pops up. Not sure if that's then intended result.  It does appear to be a borderless window at least but it doesn't cover the whole screen is what I'm saying.


dw817(Posted 1+ years ago)

 BlitzSupport, have you (or anyone else) written in new smaller code for generating a frameless window since ?


RemiD(Posted 1+ years ago)

 Yes, here : <a href="codearcsb0cc.html?code=3212" target="_blank">http://www.blitzbasic.com/codearcs/codearcs.php?code=3212</a>


AdamStrange(Posted 1+ years ago)

 <div class="quote"> Mac version of the module tweaks above but it still shows the apple bar along the top with app name, clock, battery status etc </div>That is the default BlitzMax version (it's not how it should be done).Mac has a special 'fullscreen' mode (it's not a window, but a version of). To fully support it you will need to trap ctrl+cmd+f, and also have a menu item view>fullscreen (window>fullscreen is the old version)I can do a bit of digging and see what I can re find?Note - On macs you might also need to look into trackpad support, etc as well? I had code to do this with V1.46 but mark rewrote the new version to (not correctly) support fullscreen.In essence you will need to hack the mac os routines in blitzmax to add these features correctly


dw817(Posted 1+ years ago)

 A frameless window shouldn't have to take all that. Just use this which includes calculating the taskbar size below:Strict
Import maxgui.drivers ' necessary for frameless window
Local deskrect[4],a,b,c,d,i,j
GetDesktopArea(deskrect) '<- Get Desktop size
SetGraphicsDriver(GLMax2DDriver()) ' necessary for aggressive pixels
a=deskrect[0] ; b=deskrect[1] ; c=deskrect[2] ; d=deskrect[3]
Local zvideo:tgadget=createwindow("",a,b,c,d,,0) ' special graphics
Local zframe:tgadget=createcanvas(a,b,c,d,zvideo,0) ' is frameless
SetGraphics canvasgraphics(zframe) ' set new graphics
activategadget zframe ' necessary so it's active
SetBlend alphablend ' good graphic blending
EnablePolledInput() ' necessary for normal input
For i=b To d Step 2
  For j=a To c Step 2 ' test screen with dots
    Plot j,i
  Next
Next
Flip
WaitKey

' >> GET DESKTOP AREA MINUS TASKBAR
' original code by DAN UPRIGHT
Function getdesktoparea(lprect:Int Ptr)
  systemparametersinfoa(spi_getworkarea,0,lprect,0)
End Function
Extern "Win32"
  Function SystemParametersInfoA:Int(action:Int, param:Int, param2:Byte Ptr, winini:Int)
EndExtern
Hope This Helps !


Foolish(Posted 6 months ago)

 I spent quite awhile trying to figure this out. Many thanks. It works like a charm. [/i]