I've cleaned up some code I wrote a few years ago...
I'm sharing it with you because it might be useful, especially for beginners.
If anyone has any suggestions for improvement, please feel free to share them.
Code: Select all
EnableExplicit
; -----------------------------------------------------------------------------
; Structure containing all parameters and states related to display
; -----------------------------------------------------------------------------
Structure screenStruct
isFullScreen.b ; 0 = windowed, 1 = fullscreen
screenWidth.l ; desired width
screenHeight.l ; desired height
screenDepth.l ; color depth (bits)
screenFlags.l ; PB flags for the screen (sync, etc.)
screenTitle.s ; title
spriteMouse.i ; sprite ID used as cursor
mouseX.l ; mouse X position
mouseY.l ; mouse Y position
EndStructure
; Global instance of the screen structure
Global Screen.screenStruct
; -----------------------------------------------------------------------------
; Initialize default values
; -----------------------------------------------------------------------------
Screen\isFullScreen = #False
Screen\screenWidth = 1280
Screen\screenHeight = 720
Screen\screenDepth = 32
Screen\screenFlags = #PB_Screen_SmartSynchronization
Screen\screenTitle = "Demo Alt-Tab / Alt-Enter"
; -----------------------------------------------------------------------------
; Create assets (here a 16x16 sprite to simulate a red cursor)
; -----------------------------------------------------------------------------
Procedure InitAssets()
; Create the sprite + start a drawing context on this sprite
If CreateSprite(Screen\spriteMouse, 16, 16, #PB_Sprite_AlphaBlending) And StartDrawing(SpriteOutput(Screen\spriteMouse))
; Allows drawing with full alpha handling (RGBA channels)
DrawingMode(#PB_2DDrawing_AllChannels)
; Draw a centered red circle at (8,8) with radius 8 (simple cursor)
Circle(8, 8, 8, RGBA(255, 0, 0, 255))
StopDrawing()
Else
Debug "Error Draw cursor"
EndIf
EndProcedure
; -----------------------------------------------------------------------------
; Initialize the screen depending on the mode: fullscreen or windowed
; Returns #True if OK
; -----------------------------------------------------------------------------
Procedure.b InitScreen()
If Screen\isFullScreen
; In fullscreen mode, iterate through available modes to find a mode
; at least equal to the requested resolution, with a 60 Hz refresh.
ExamineScreenModes()
While NextScreenMode()
If ScreenModeWidth() >= Screen\screenWidth And ScreenModeHeight() >= Screen\screenHeight And ScreenModeRefreshRate() = 60
Screen\screenWidth = ScreenModeWidth()
Screen\screenHeight = ScreenModeHeight()
Break
EndIf
Wend
; Open the fullscreen screen
If OpenScreen(Screen\screenWidth, Screen\screenHeight, Screen\screenDepth, Screen\screenTitle, #PB_Screen_WaitSynchronization)
ProcedureReturn #True
Else
MessageRequester("Error", "Unable to open the screen")
End
EndIf
Else
; Windowed mode: create a PB window then a "WindowedScreen" inside it.
; DesktopUnscaledX/Y avoids surprises with Windows scaling (DPI).
If OpenWindow(0, 0, 0, DesktopUnscaledX(Screen\screenWidth), DesktopUnscaledY(Screen\screenHeight),
Screen\screenTitle, #PB_Window_ScreenCentered) And
OpenWindowedScreen(WindowID(0), 0, 0, Screen\screenWidth, Screen\screenHeight, #False, 0, 0, #PB_Screen_WaitSynchronization)
ProcedureReturn #True
EndIf
EndIf
EndProcedure
; -----------------------------------------------------------------------------
; Handle ALT+TAB (focus loss / inactive screen)
; Idea: if the screen is no longer active, close it and wait until activation returns.
; -----------------------------------------------------------------------------
Procedure CheckScreenFocus()
; IsScreenActive() = 0 => the screen is no longer active (e.g., Alt-Tab)
If IsScreenActive() = 0
; Release sprites
;TODO FreeAssets()
FreeSprite(Screen\spriteMouse)
; Create a tiny minimized/borderless window to "keep a window handle"
; while closing the screen. The goal is to wait for an activation event.
OpenWindow(0, 1, 1, 1, 1, Screen\screenTitle, #PB_Window_Minimize | #PB_Window_BorderLess)
; Close fullscreen / windowed screen
CloseScreen()
Protected Event.l
Repeat
Event = WaitWindowEvent()
; If the user closes the window, quit
If Event = #PB_Event_CloseWindow
End
Break
EndIf
; Wait until the window receives the activation event
Until Event = #PB_Event_ActivateWindow
; Once reactivated, close the small window
CloseWindow(0)
; Reinitialize screen + assets
InitScreen()
InitAssets()
EndIf
EndProcedure
; -----------------------------------------------------------------------------
; Toggle windowed <-> fullscreen via ALT + Enter
; -----------------------------------------------------------------------------
Procedure ToggleFullScreen()
; Logical XOR: invert 0/1
Screen\isFullScreen ! 1
; If switching to fullscreen, also close the windowed window.
If Screen\isFullScreen = #True
CloseScreen()
CloseWindow(0)
Else
; If switching back to windowed, only close the screen (window will be recreated)
CloseScreen()
EndIf
; Re-open the correct mode + recreate assets (cursor)
InitScreen()
InitAssets()
EndProcedure
; -----------------------------------------------------------------------------
; Main loop
; -----------------------------------------------------------------------------
Procedure Main()
Protected Event.i
; Initialize subsystems
InitSprite()
InitKeyboard()
InitMouse()
; Initialize display + assets
InitScreen()
InitAssets()
Repeat
; In windowed mode, drain window events to avoid "freeze"
If Screen\isFullScreen = #False
Repeat
Event = WindowEvent()
Until Event = 0
EndIf
; Clear the screen
ClearScreen(RGB(0, 0, 32))
; Update keyboard/mouse
ExamineKeyboard()
ExamineMouse()
; Read mouse position
Screen\mouseX = MouseX()
Screen\mouseY = MouseY()
; Display the cursor sprite at the mouse position
DisplayTransparentSprite(Screen\spriteMouse, Screen\mouseX, Screen\mouseY)
; ALT + Enter => toggle fullscreen
If KeyboardPushed(#PB_Key_LeftAlt) Or KeyboardPushed(#PB_Key_RightAlt)
If KeyboardReleased(#PB_Key_Return)
ToggleFullScreen()
EndIf
EndIf
; Swap buffers (double buffering)
FlipBuffers()
; Handle ALT+TAB / activation loss
CheckScreenFocus()
; Small CPU pause (1ms) to avoid monopolizing resources
Delay(1)
Until KeyboardPushed(#PB_Key_Escape)
; Cleanup
CloseScreen()
EndProcedure
Main()
