I don't know if this is a bug or not, but I'm getting some weird behavior with the mouse...
I have a panel that resizes with a window. I have a canvas in the middle of the window which is 800x500. When I maximize the window the controls of my game go wonky. I have tracked it down to the mouse code in max behaving strangely.
In my app I have the following code:
When debugging I get:
DebugLog:400,250
DebugLog:760,0
In other words, MouseX and MouseY are returning coordinates in a different space than that which is used when I set the coordinates.
Furthermore, said space makes no sense. The X is almost doubled, while the Y is zeroed out.
Here's code which reproduces the "bug":
To reproduce the bug, run the above in debugmode, move the mouse around a bit, then press ALT to get back your mouse cursor, and hit the maximize button. Then move the mouse around some more, and hit ESC. You'll see the numbers don't match up anymore in the debug window after you hit maximize.
I have a panel that resizes with a window. I have a canvas in the middle of the window which is 800x500. When I maximize the window the controls of my game go wonky. I have tracked it down to the mouse code in max behaving strangely.
In my app I have the following code:
MoveMouse App.Width/2, App.Height/2 DebugLog(App.Width/2 + "," + App.Height/2) DebugLog(MouseX() + "," + MouseY())
When debugging I get:
DebugLog:400,250
DebugLog:760,0
In other words, MouseX and MouseY are returning coordinates in a different space than that which is used when I set the coordinates.
Furthermore, said space makes no sense. The X is almost doubled, while the Y is zeroed out.
Here's code which reproduces the "bug":
SuperStrict Import MaxGUI.Drivers ' Globals: ' Use Time% for game timing. Time% is paused when the game is minimized and starts at 0, so it will not roll over for 23 days. Global FPS% Global State% ' Timing Global Time%, Time_Delta%, Time_Delta_Sec#, Last_Time_Delta%, Last_Time_Delta_Sec# Global System_Time%, Last_System_Time% ' Frame smoothing Global Frames% = 1 ' Initially the FrameTime array will only have one frame's time stored in it, but the array and this count of the number of values in it will be Global FrameTime%[Frames] ' resized each frame to have more or less frames depending on the current framerate. Global Max_FrameTime% Global Frame% Global FrameDelay% Global Loop% Global IMG_Dust:TImage[100] ' Main: ' Set the video mode. ' Currently, we are setting the video mode to that of the desktop, and running the game fullscreen. But it may be desirable to run the game at some other resolution so it can run ' in a window, or run faster. The scaling code will work no matter what resolution is used here, but it would not be desirable to use an aspect ratio for windowed mode which is ' different from the 16:10 aspect ratio of the game, because there would be uneccessary letterboxing displayed. 'App.Create("Attack of the Alien Space Beetles!", ClientWidth(Desktop()), ClientHeight(Desktop()), 0, False, False) App.Create("Attack of the Alien Space Beetles!", 800, 600, 0, False, False) AutoRecenterMouse(True) 'For Loop = 1 To 1 ' IMG_Dust[Loop] = LoadImage("gfx\background\dust2.jpg") 'Next ' Scale the graphics onscreen and display them letterboxed if needed. ' ProjectionMatrix.SetLetterBox(800, 500) SeedRnd MilliSecs() System_Time = MilliSecs() Repeat Repeat Last_System_Time = System_Time ' Store start time of last frame. System_Time = MilliSecs() ' Get the current system time. Time_Delta = System_Time - Last_System_Time ' Calculate how long last frame took to render, in milliseconds. Time_Delta_Sec# = Float(Time_Delta)/1000.0 ' Convert frame time to seconds. Until (Time_Delta > 0) And (Time_Delta < 250) ' Handle unnaturally long pauses between frames gracefully. EventHandler() ' Process window events. If Not AppSuspended() Time = Time + Time_Delta ' Calculate current game time. 'Sprite.SetTimeStep(Time_Delta) ' Set the timestep for the sprites this frame. If App.Windowed Then ActivateGadget App.Canvas ' If windowed, make sure canvas stays active, or else polled input will go elsewhere. UpdateMouse() ' Get mouse input. 'StateSystem.Update() ' Update game. 'Sprite.Update() ' Animate sprites, do physics. SetClsColor 0, 0, 0 ' Clear screen and draw sprites. Cls 'Sprite.DrawAll() ' Draw the sprites. ' Draw letterbox. ProjectionMatrix.DrawLetterbox() If KeyHit(KEY_F) Then App.ToggleFullscreen() ' Toggle fullscreen/window mode when user presses F. (May need to disable this during user input.) If KeyHit(KEY_ESCAPE) Then End 'Last_Time_Delta = Time_Delta ' Store timestep for this frame so we can reference it next frame. 'Last_Time_Delta_Sec# = Time_Delta_Sec# ' Smooth out framerate. If Not KeyDown(KEY_S) ' Find the longest amount of time a frame took to render over the last few frames. Max_FrameTime = 0 For Frame = 0 To Frames-1 If FrameTime[Frame] > Max_FrameTime Then Max_FrameTime = FrameTime[Frame] Next ' Shift all times toward the end of the array by one to free up the first slot for a new time. For Frame = Frames-1 To 1 Step -1 FrameTime[Frame] = FrameTime[Frame-1] Next ' Resize the array to have 1/8 as many elements as we are rendering frames per second. ' The reason we do this is so that no matter what the framerate is, the system will adjust to big changes in framerate within 1/8 ' of a second. The response time is only a concern so far as the system would render at a lower framerate for longer than it ' really needs to. If Max_FrameTime > 0 Then Frames = Floor((1000.0 / Max_FrameTime) / 2.0) If Frames = 0 Then Frames = 1 FrameTime = FrameTime[..Frames] ' Calculate how long this frame took to render. Time_Delta = MilliSecs()-System_Time ' Add this frame's render time to the start of the array. FrameTime[0] = Time_Delta ' Delay this frame to make it last as long as the longest in our array of frame times. If Max_FrameTime < 70 While Time_Delta < Max_FrameTime Delay 0 Time_Delta = MilliSecs()-System_Time Wend EndIf EndIf ' Draw framerate if user presses tab. If KeyDown(KEY_TAB) FPS = 1000.0 / Float(Time_Delta) SetScale 1, 1 DrawText FPS, 16, 16*1 'DrawText "Sprites = " + Sprite.SpriteList.Count(), 16, 16*2 'DrawText "FruitSpawned = " + Powerup.FruitSpawned, 16, 16*4 'DrawText "FruitCollected = " + Powerup.FruitCollected, 16, 16*5 EndIf ' Flip new frame into view. Flip 0 EndIf Forever ' --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ' This type allows you to make games that can run at any resolution. ' --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Type ProjectionMatrix Global _Width% ' The size of the screen, in BlitzMax coordinates. Global _Height% ' Normally BlitzMax coordinates correspond 1:1 with pixels on the screen, but when you adjust the projection matrix, that relationship changes. Global _VirtualWidth% ' The size of the visible region in which gameplay takes place. The area inside the letterbox. Global _VirtualHeight% ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function sets the scale of the projection matrix. ' ' If you simply wish your game to stretch vertically and horizontally to match the current resolution, and fill the screen, call this function with your desired "virtual" resolution. ' Ie, if you want to build your game around an 800x600 resolution, then set Width and Height to 800,600. If the game is then run at 1920x1200, it will be squashed vertically. ' ' If you want letterboxing however, call InitLetterbox() and then DrawLetterBox() every frame just before you flip. ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Function SetScale(Width%, Height%) _Width = Width _Height = Height _VirtualWidth = Width _VirtualHeight = Height ?Win32 Local D3D7Driver:TD3D7Max2DDriver = TD3D7Max2DDriver(_max2dDriver) If D3D7Driver Local Matrix#[] = [2.0/Width, 0.0, 0.0, 0.0,.. 0.0, -2.0/Height, 0.0, 0.0,.. 0.0, 0.0, 1.0, 0.0,.. -1-(1.0/Width), 1+(1.0/Height), 1.0, 1.0] D3D7Driver.device.SetTransform(D3DTS_PROJECTION, Matrix) Else ? ' If on platform other than Win32, or using OpenGL run this code. glMatrixMode(GL_PROJECTION) glLoadIdentity() glortho(0, Width, Height, 0, 0, 1) glMatrixMode(GL_MODELVIEW) glLoadIdentity() ?Win32 EndIf ? End Function ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function initializes the letterbox. ' ' Call it with the virtual resolution you want to use for your game after you have set the graphics mode, and then DrawLetterBox() every frame just before you flip. ' ' These functions will automatically handle both letterboxing on screens which are too tall, and pillarboxing on screens which are too wide. So if you design your game for 800x600, ' then InitLetterbox() will squash things horizontally to maintain the proper aspect ratio, and DrawLetterbox() will add black bars to the sides of the display for you. ' ' ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Function SetLetterbox(Width%, Height%) Local NewWidth#, NewHeight# NewWidth# = Width NewHeight# = Float(GraphicsHeight()) / (Float(GraphicsWidth()) / Float(Width)) ' If screen is wider than the desired apsect ratio... If NewHeight# < Height ' Use pillarboxing instead of letterboxing. NewHeight# = Height NewWidth# = Float(GraphicsWidth()) / (Float(GraphicsHeight())/Float(Height)) ' Commenting this out will squash the game vertically on screens which are too wide. ' But without additional changes, black bars will still be drawn. EndIf ' Adjust the scale of the projection matrix to achieve the desired result. ProjectionMatrix.SetScale(NewWidth#, NewHeight#) ' Store the size of the visible game region. _VirtualWidth = Width _VirtualHeight = Height End Function ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function draws black bars over the portions of the screen which are outside the gameplay area. ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Function DrawLetterbox() Local Size% RenderState.Push() If _VirtualHeight < _Height ' Draw Letterbox. Size = (_Height-_VirtualHeight) / 2 SetColor(0,0,0) DrawRect(0, 0, _Width, Size) DrawRect(0, _Height-Size, _Width, Size) Else ' Draw pillarbox. Size = (_Width-_VirtualWidth) / 2 SetColor(0,0,0) DrawRect( 0, 0, Size, _Height) DrawRect(_Width-Size, 0, Size, _Height) EndIf RenderState.Pop() ' Render push and pop simply resets the color and other display properties to whatever they were beforehand. These two functions are in the code archives. End Function ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ' These functions return the virtual width and height of the usuable region of the screen, inside any letterboxing. ' ' Basically, these functions are equivalent to GraphicsWidth() and GraphicsHeight() when using a projection matrix where coordinates don't match up 1:1 to pixels ' and/or letterboxing is being used. ' ' These functions will not return useful values until either ProjectionMatrix.SetScale() is called, or ProjectonMatrix.SetLetterbox() is called! ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Function Width#() Return _VirtualWidth End Function Function Height#() Return _VirtualHeight End Function ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ' These functions tell you where the center of the screen is when using letterboxing. ' ' Note that they do not calculate this using the width and height of the visible area of the screen, but rather, the size of the whole screen including the letterboxed regions. ' This is important, because if you draw objects using the size of the visible region only, they will be higher on the screen than they should be, or more to the left. ' ' In my games, what I do is create a pivot called Origin, and place it in the center of the screen, and attach all my sprites to that, so the only time I need to worry about where ' the center of the screen really is is when I position that pivot initially. ' ' Then when I've done that, the top of the screen is at -ProjectionMatrix.Height#()/2, half the height I passed to SetLetterbox(), and so on. ' ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Function CenterX#() Return _Width/2 End Function Function CenterY#() Return _Height/2 End Function End Type Const DEFAULT_IMAGE_FLAGS% = MIPMAPPEDIMAGE|FILTEREDIMAGE ' BlitzMax defaults to MASKEDIMAGE|MIPMAPPEDIMAGE|FILTEREDIMAGE. Type App Global Name$ Global Width% Global Height% Global Depth% Global Window:TGadget Global Canvas:TGadget Global Panel:TGadget Global TG_FullScreen:TGraphics Global TG_Canvas:TGraphics Global Windowed% ' ------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function creates the game window, and sets up the graphics objects for windowed and fullscreen modes. ' ------------------------------------------------------------------------------------------------------------------------------------------------------- Function Create(AppName$, AppWidth%, AppHeight%, AppDepth%=0, Fullscreen%=False, UseGL%=False) Local X%, Y% ' If we should use OpenGL on all platforms, then enable the openGL driver. If UseGL% Then SetGraphicsDriver GLMax2DDriver() ' Warn programmer if the game is running in debug mode so they don't wonder why game is running choppy. DebugLog("WARNING: Game running debug mode - Performance will be reduced!") ' Store application settings. Name$ = AppName$ Width = AppWidth Height = AppHeight Depth = AppDepth ' If Depth is 0, try to find a suitable fullscreen bit depth. If Depth = 0 If GraphicsModeExists(Width, Height, 16) Then Depth = 16 If GraphicsModeExists(Width, Height, 24) Then Depth = 24 If GraphicsModeExists(Width, Height, 32) Then Depth = 32 EndIf ' Create a window in the center of the screen. ' ClientWidth/Height is used because GadgetWidth/Height returns the entire width of the desktop in dual screen setups. X = ClientWidth(Desktop())/2 - Width/2 Y = ClientHeight(Desktop())/2 - Height/2 Window = CreateWindow(Name$, X, Y, Width, Height, Null, WINDOW_TITLEBAR|WINDOW_RESIZABLE|WINDOW_CLIENTCOORDS|WINDOW_HIDDEN) SetMinWindowSize(Window, GadgetWidth(Window), GadgetHeight(Window)) ' Create a panel in the window, drawn behind the canvas, which fills the empty space around the gameplay area when the window is maximized. Panel = CreatePanel(0, 0, Width, Height, Window) SetPanelColor(Panel, 0, 0, 0) SetGadgetLayout(Panel, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED, EDGE_ALIGNED) ' Set game to windowed or fullscreen as desired. Select Fullscreen Case False SetWindowed() Case True SetFullscreen() End Select End Function ' ------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function toggles between fullscreen or windowed mode, choosing the opposite of whichever is currently enabled. ' ------------------------------------------------------------------------------------------------------------------------------------------------------- Function ToggleFullscreen() Select Windowed Case False SetWindowed() Case True If (Depth <> 0) Then SetFullscreen() End Select End Function ' ------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function puts the application in windowed mode. ' ------------------------------------------------------------------------------------------------------------------------------------------------------- Function SetWindowed() ' End fullscreen mode if enabled. If TG_Fullscreen <> Null CloseGraphics TG_Fullscreen TG_Fullscreen = Null EndIf ' Create a canvas. If Canvas = Null Canvas = CreateCanvas(ClientWidth(Window)/2-Width/2, ClientHeight(Window)/2-Height/2, Width, Height, Panel) TG_Canvas = CanvasGraphics(Canvas) EndIf If Canvas = Null Then RuntimeError("Error in Application.SetWindowed() : Could not create canvas!") If TG_Canvas = Null Then RuntimeError("Error in Application.SetWindowed() : Could not get canvas graphics context!") SetGraphics TG_Canvas ShowGadget Window ActivateGadget Canvas ' Record that windowed mode is enabled. Windowed = True ' Canvas must be activated for input or else game will make user click on canvas before any polled keyboard input is returned. EnablePolledInput() ' Show mouse if mouse is currently supposed to be visible. If MouseVisible Then ShowMouse(True) ' Set the blending mode to alpha. SetBlend(ALPHABLEND) ' Set mask color to magenta so black pixels are not made transparent when images without an alpha mask are loaded. SetMaskColor(255, 0, 255) ' Set midhandle so images are loaded properly. AutoMidHandle True ' Set image flags. AutoImageFlags(DEFAULT_IMAGE_FLAGS) ' Buffer sprite images in video memory. ' Sprite.BufferImages() ' Scale the graphics onscreen and display them letterboxed if needed. ProjectionMatrix.SetLetterBox(800, 500) End Function ' ------------------------------------------------------------------------------------------------------------------------------------------------------- ' This method sets the application to fullscreen mode, unless no fullscreen mode in the desired resolution exists. ' ------------------------------------------------------------------------------------------------------------------------------------------------------- Function SetFullscreen() ' If no fullscreen mode is available, set windowed mode instead. If Depth = 0 DebugLog("Error in Application.SetFullscreen() : Depth = 0. Could not set full screen mode!") SetWindowed() Return EndIf ' Hide window, free canvas. HideGadget Window If Canvas <> Null CloseGraphics(TG_Canvas) FreeGadget Canvas Canvas = Null TG_Canvas = Null EndIf ' Set drawing operations to fullscreen. If TG_Fullscreen = Null Then TG_Fullscreen = Graphics(Width, Height, Depth) ' If the attempt to set a fullscreen mode fails, put us back in windowed mode. If TG_Fullscreen = Null DebugLog("Error in Application.SetFullscreen() : TG_Fullscreen = Null. Could not set full screen mode!") DebugLog(Width + "," + Height + "," + Depth) SetWindowed() Return EndIf ' Record that fullscreen is enabled. Windowed = False ' Show mouse if mouse is currently supposed to be visible. If MouseVisible Then ShowMouse(True) ' Set the blending mode to alpha. SetBlend(ALPHABLEND) ' Set mask color to magenta so black pixels are not made transparent when images without an alpha mask are loaded. SetMaskColor(255, 0, 255) ' Set midhandle so images are loaded properly. AutoMidHandle True ' Set image flags. AutoImageFlags(DEFAULT_IMAGE_FLAGS) ' Buffer sprite images in video memory. ' Sprite.BufferImages() ' Scale the graphics onscreen and display them letterboxed if needed. ProjectionMatrix.SetLetterBox(800, 500) End Function End Type ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- ' These functions override the regular image loading functions so that we can display an error when an image is missing. ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- Function LoadImage:TImage(Url:Object, Flags%=-1, ReturnNull%=False) Local Image:TImage Image = Brl.Max2D.LoadImage(Url, Flags) If Image = Null If ReturnNull Return Null Else RuntimeError("The following image failed to load: '" + String(Url) + "'. Please reinstall the game.") EndIf EndIf ' Buffer image in video ram. DrawImage Image, App.Width, App.Height 'LoadScreen.Update(Url) Return Image End Function Function LoadAnimImage:TImage(Url:Object, Cell_Width%, Cell_Height%, First_Cell%, Cell_Count%, Flags%=-1, ReturnNull%=False) Local Image:TImage Local Frame% Image = Brl.Max2D.LoadAnimImage(Url, Cell_Width, Cell_Height, First_Cell, Cell_Count, Flags) If Image = Null If ReturnNull Return Null Else RuntimeError("The following image failed to load: '" + String(Url) + "'. Please reinstall the game.") EndIf EndIf ' Buffer image in video ram. For Frame = First_Cell To First_Cell+(Cell_Count-1) DrawImage Image, App.Width, App.Height, Frame Next 'LoadScreen.Update(Url) Return Image End Function Function LoadPixmap:TPixmap(Url:Object, ReturnNull%=False) Local Pixmap:TPixmap Pixmap = Brl.Pixmap.LoadPixmap(Url) If Pixmap = Null If ReturnNull Return Null Else RuntimeError("The following image failed to load: '" + String(Url) + "'. Please reinstall the game.") EndIf EndIf 'LoadScreen.Update(Url) Return Pixmap End Function ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function overrides the standard RuntimeError function which does not work properly. (Assert also does not work.) ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- Function RuntimeError(Error$) EndGraphics DebugLog(Error$) Notify(Error$, True) End End Function ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function oveerides the standard Flip() function with a lag fix for DirectX. ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- ' No longer needed in latest version of BlitzMax. 'Function Flip(Sync%=-1) 'Brl.Graphics.Flip Sync '?Win32 ' If TD3D7Max2DDriver(_max2dDriver) ' Local sdesc:DDSurfaceDesc2 = New DDSurfaceDesc2 ' sdesc.dwSize = SizeOf(sdesc) ' Local res:Int = PrimaryDevice.backbuffer.Lock(Null,sdesc,DDLOCK_WAIT|DDLOCK_READONLY,Null) ' PrimaryDevice.backbuffer.unlock(Null) ' EndIf '? 'End Function ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- ' These functions override the standard mouse visibility functions so that we can keep track of the mouse visible state. ' ' For ShowMouse: ' ' If Force% is False, then the mouse will not be shown if the current state indicates it is visible. This is to avoid mouse flicker which is caused by ' showing the mouse over and over. ' ' If Force% is True, the mouse is shown regardless of what state MouseVisible indicates it is in. This is used during fullscreen/window mode switches where ' the mouse is hidden by the system automatically. ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- Global MouseDown_Left%, MouseDown_Right%, MouseDown_Middle% Global MouseHit_Left%, MouseHit_Right%, MouseHit_Middle% Global Mouse_X#, Mouse_Y# Global Old_Mouse_X# = MouseX() Global Old_Mouse_Y# = MouseY() Global Mouse_Speed_X#, Mouse_Speed_Y# Global MouseVisible% = True Global Mouse_ForceRecenter% Function ShowMouse(Force%=False) If (MouseVisible = False) Or (Force = True) MouseVisible = True Brl.System.ShowMouse() EndIf End Function Function HideMouse() MouseVisible = False Brl.System.HideMouse() End Function Function AutoRecenterMouse(Recenter%=False) Mouse_ForceRecenter = Recenter Select Recenter Case True HideMouse() MoveMouse App.Width/2, App.Height/2 Case False ShowMouse() End Select End Function ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function gets the mouse input for this frame. ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- Function UpdateMouse() Const SAFEZONE_RADIUS# = 100 Mouse_X# = MouseX() Mouse_Y# = MouseY() Mouse_Speed_X# = Mouse_X# - Old_Mouse_X# Mouse_Speed_Y# = Mouse_Y# - Old_Mouse_Y# MouseDown_Left = MouseDown(1) MouseDown_Right = MouseDown(2) MouseDown_Middle = MouseDown(3) MouseHit_Left = MouseHit(1) MouseHit_Right = MouseHit(2) MouseHit_Middle = MouseHit(3) ' Recenter mouse if desired. ' Recentering the mouse allows Mouse_Speed to work consistently. If Mouse_ForceRecenter If (Abs(App.Width/2 - Mouse_X#) > SAFEZONE_RADIUS#) Or (Abs(App.Height/2 - Mouse_Y#) > SAFEZONE_RADIUS#) MoveMouse App.Width/2, App.Height/2 DebugLog(App.Width/2 + "," + App.Height/2) DebugLog(MouseX() + "," + MouseY()) Old_Mouse_X# = App.Width/2 - Mouse_Speed_X# Old_Mouse_Y# = App.Height/2 - Mouse_Speed_Y# Else Old_Mouse_X# = Mouse_X# Old_Mouse_Y# = Mouse_Y# EndIf EndIf End Function ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- ' This function checks for events in the event queue and acts upon them. ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- Function EventHandler() MouseHit_Left = False MouseHit_Right = False MouseHit_Middle = False While PollEvent() <> 0 'DebugLog("EventID: " + EventID()) 'If Scoreboard.TEXT_AngerMeter <> Null ' If EventID() <= 258 Then Scoreboard.TEXT_AngerMeter.Set(EventID()) 'EndIf Select EventID() Case EVENT_APPSUSPEND ' Application suspended. Triggered when window is minimized. ' Pause music. ' If Sound.MusicChannel <> Null Then Sound.MusicChannel.SetPaused(True) Case EVENT_APPRESUME ' Application resumed. ' Resume music. ' If Sound.MusicChannel <> Null Then Sound.MusicChannel.SetPaused(False) Case EVENT_APPTERMINATE ' Application wants to terminate. Case EVENT_KEYDOWN ' Key pressed. Event data contains keycode. Case EVENT_KEYUP ' Key released. Event data contains keycode. Case EVENT_KEYCHAR ' Key character. Event data contains unicode value. Case EVENT_MOUSEDOWN ' Mouse button pressed. Event data contains mouse button code. Select EventData() Case 1 MouseDown_Left = True MouseHit_Left = True Case 2 MouseDown_Right = True MouseHit_Right = True Case 3 MouseDown_Middle = True MouseHit_Right = True End Select Case EVENT_MOUSEUP ' Mouse button released. Event data contains mouse button code. Select EventData() Case 1 MouseDown_Left = False Case 2 MouseDown_Right = False Case 3 MouseDown_Middle = False End Select Case EVENT_MOUSEMOVE ' Mouse moved. Event x and y contain mouse coordinates. 'Mouse_X = EventX() 'Mouse_Y = EventY() Case EVENT_MOUSEWHEEL ' Mouse wheel spun. Event data contains delta clicks. Case EVENT_MOUSEENTER ' Mouse entered gadget area. Case EVENT_MOUSELEAVE ' Mouse left gadget area. Case EVENT_TIMERTICK ' Timer ticked. Event source contains timer object. Case EVENT_HOTKEYHIT ' Hot key hit. Event data and mods contains hotkey keycode and modifier. Case EVENT_MENUACTION ' Menu has been selected. Case EVENT_WINDOWMOVE ' Window has been moved. Case EVENT_WINDOWSIZE ' Window has been resized. Case EVENT_WINDOWCLOSE ' Window close icon clicked. End Case EVENT_WINDOWACTIVATE ' Window activated. Case EVENT_WINDOWACCEPT ' Drag and drop operation was attempted. Case EVENT_GADGETACTION ' Gadget state has been updated. Case EVENT_GADGETPAINT ' A canvas gadget needs to be redrawn. Case EVENT_GADGETSELECT ' A treeview node has been selected. Case EVENT_GADGETMENU ' User has right clicked a treeview node or textarea gadget. Case EVENT_GADGETOPEN ' A treeview node has been expanded. Case EVENT_GADGETCLOSE ' A treeview node has been collapsed. Case EVENT_GADGETDONE ' An HTMLview has completed loading a page. End Select Wend End Function ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- ' This hooks the gadget paint event so it can redraw the canvas as needed while another window is dragged over the game window. ' ----------------------------------------------------------------------------------------------------------------------------------------------------------- AddHook EmitEventHook, MyHook Function MyHook:Object(iId:Int, tData:Object, tContext:Object) Local Event:TEvent=TEvent(tData) If App.Canvas <> Null If (Event.Source = App.Canvas) And (Event.ID = EVENT_GADGETPAINT) Flip Return Null EndIf EndIf Return tData End Function Type RenderState Global RenderStateList:TList = CreateList() Field Alpha# Field Blend% Field ClsColor_R%, ClsColor_G%, ClsColor_B% Field Color_R%, Color_G%, Color_B% Field Handle_X#, Handle_Y# Field ImageFont:TImageFont Field LineWidth# Field MaskColor_R%, MaskColor_G%, MaskColor_B% Field Origin_X#, Origin_Y# Field Rotation# Field Scale_X#, Scale_Y# Field Viewport_X%, Viewport_Y%, Viewport_Width%, Viewport_Height% ' ------------------------------------------------------------------------------------------------------------------------------------------------------- ' These methods allow you to save and restore the current render settings ' ' Each time you call the push method, the current state is placed on the stack. ' Each time you call the pop method, the last state placed on the stack is restored and removed from the stack. ' ------------------------------------------------------------------------------------------------------------------------------------------------------- Function Push() Local RS:RenderState = New RenderState RS.Alpha# = GetAlpha#() RS.Blend = GetBlend() GetClsColor(RS.ClsColor_R, RS.ClsColor_G, RS.ClsColor_B) GetColor(RS.Color_R, RS.Color_G, RS.Color_B) GetHandle(RS.Handle_X#, RS.Handle_Y#) RS.ImageFont = GetImageFont() RS.LineWidth# = GetLineWidth#() GetMaskColor(RS.MaskColor_R, RS.MaskColor_G, RS.MaskColor_B) GetOrigin(RS.Origin_X#, RS.Origin_Y#) RS.Rotation# = GetRotation#() GetScale(RS.Scale_X#, RS.Scale_Y#) GetViewport(RS.Viewport_X, RS.Viewport_Y, RS.Viewport_Width, RS.Viewport_Height) RenderStateList.AddLast(RS) End Function Function Pop() Local RS:RenderState = RenderState(RenderStateList.RemoveLast()) SetAlpha(RS.Alpha#) SetBlend(RS.Blend) SetClsColor(RS.ClsColor_R, RS.ClsColor_G, RS.ClsColor_B) SetColor(RS.Color_R, RS.Color_G, RS.Color_B) SetHandle(RS.Handle_X#, RS.Handle_Y#) SetImageFont(RS.ImageFont) SetLineWidth(RS.LineWidth#) SetMaskColor(RS.MaskColor_R, RS.MaskColor_G, RS.MaskColor_B) SetOrigin(RS.Origin_X#, RS.Origin_Y#) SetRotation(RS.Rotation#) SetScale(RS.Scale_X#, RS.Scale_Y#) SetViewport(RS.Viewport_X, RS.Viewport_Y, RS.Viewport_Width, RS.Viewport_Height) End Function End Type
To reproduce the bug, run the above in debugmode, move the mouse around a bit, then press ALT to get back your mouse cursor, and hit the maximize button. Then move the mouse around some more, and hit ESC. You'll see the numbers don't match up anymore in the debug window after you hit maximize.