Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How to switch between window/fullscreen mode at runtime with SFML.NET?  (Read 9048 times)

0 Members and 1 Guest are viewing this topic.

mkalex777

  • Full Member
  • ***
  • Posts: 206
    • View Profile
Hi guys,

I'm looking for a solution to add shortcut key to switch between window and fullscreen mode.
I tried to recreate the window like it's recommended for C++, but there is a problem with event handling.

Example:

        private RenderWindow _window;
        private bool _isFullScreen;

        public void Run()
        {
            Init();

            _window = new RenderWindow(
                _isFullScreen ? VideoMode.DesktopMode : new VideoMode(800, 800),
                "MyApp",
                _isFullScreen ? Styles.Fullscreen : Styles.Default,
                new ContextSettings() { AntialiasingLevel = 16 });
            _window.KeyPressed += Window_OnKeyPressed;
            _window.Closed += (s, e) => _window.Close();
            _window.SetVisible(true);
            _window.SetActive(true);

            while (_window.IsOpen)
            {
                _window.DispatchEvents();   // <== Exception appears here

                Update();
                Render();
            }
        }

        private void Window_OnKeyPressed(object sender, KeyEventArgs e)
        {
            if (e.Code == Keyboard.Key.F11)
            {
                // close exist window
                _window.KeyPressed -= Window_OnKeyPressed;
                _window.MouseButtonPressed -= Window_OnMouseButtonPressed;
                _window.MouseButtonReleased -= Window_OnMouseButtonReleased;
                _window.MouseWheelMoved -= Window_OnMouseWheelMoved;
                _window.Close();
                _window.Dispose();

                // create new one
                _window = new RenderWindow(
                                _isFullScreen ? VideoMode.DesktopMode : new VideoMode(800, 800),
                                "MyApp",
                                _isFullScreen ? Styles.Fullscreen : Styles.Default,
                                new ContextSettings() { AntialiasingLevel = 16 });
                _window.KeyPressed += Window_OnKeyPressed;
                _window.Closed += (s, e) => _window.Close();
                _window.SetVisible(true);
                _window.SetActive(true);
            }
        }
 

This code will produce Access violation exception inside DispatchEvents method:
Quote
System.AccessViolationException occurred
  HResult=-2147467261
  Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
  Source=sfmlnet-graphics-2
  StackTrace:
       at SFML.Graphics.RenderWindow.sfRenderWindow_pollEvent(IntPtr CPointer, Event& Evt)
       at SFML.Graphics.RenderWindow.PollEvent(Event& eventToFill)
       at SFML.Window.Window.DispatchEvents()

Any idea on how to handle keyboard event with no call to DispatchEvent?
Thanks

UPDATED: I found the reason, it's because I call the method Window.Dispose. I commented it and now it works :)
But there is still question - how to dispose window in such case?
« Last Edit: September 23, 2015, 05:12:12 am by mkalex777 »

Dejan Geci

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: How to switch between window/fullscreen mode at runtime with SFML.NET?
« Reply #1 on: September 29, 2015, 01:46:47 pm »
This is a good question, I am also interested in the answer. Seems like you cant dispose of any window you create because they share some resource (probably GL context). The user can fullscreen-toggle himself out of memory.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: How to switch between window/fullscreen mode at runtime with SFML.NET?
« Reply #2 on: September 29, 2015, 02:08:04 pm »
The problem is that after Window_OnKeyPressed returns, SFML.Net is still inside its internal call to window.pollEvent (triggered by _window.DispatchEvents()). But since you disposed the window, the next internal call that accesses it fails.

You shouldn't explicitly dispose the window instance, just let the GC do its job. Calling Close() is all you have to do.
« Last Edit: September 29, 2015, 02:09:42 pm by Laurent »
Laurent Gomila - SFML developer

mkalex777

  • Full Member
  • ***
  • Posts: 206
    • View Profile
Re: How to switch between window/fullscreen mode at runtime with SFML.NET?
« Reply #3 on: September 29, 2015, 07:08:38 pm »
The problem is that after Window_OnKeyPressed returns, SFML.Net is still inside its internal call to window.pollEvent (triggered by _window.DispatchEvents()). But since you disposed the window, the next internal call that accesses it fails.

You shouldn't explicitly dispose the window instance, just let the GC do its job. Calling Close() is all you have to do.

Sorry, but I don't agree, according to the good practice, each object which is inherited from IDisposable, should be explicitly disposed. Otherwise it may cause memory leaks. If you don't do it, you will get a lot of warnings from code analyzing tools (suh as fxcop, resharper, etc).

It especially important for the objects which implementing finalizer. Such objects (with finalizer) will be marked as 2'nd generation object which actually never collected by the GC (except for the specific conditions, such as low memory, etc). GC will not touch such objects.
This is why adding finalizer to the object is a bad practice. Because such objects will be marked as 2'nd generation object and it will prevent GC to manage memory effectively, because GC actually will not work with such objects and it will consume a lot of memory.

GC is not intended to work effectively with objects which implementing finalizer. Such objects looks like hell for GC.


By the way, I've found a solution on how to avoid memory leaks on toggle window/fullscreen.
It works in the following way:
1) add two fields to store window instance: _window and _windowCache
2) On startup initialize _window in the usual way so it will works in windowed mode
3) When user requests fullscreen:
    a) unsubscribe all events from _window
    b) _window.SetVisible(false)
    c) if _windowCache already initialized, just swap _window and _windowCache. Otherwise initialize fullscreen instance, assign it to _windowCache, and then swap _window and _windowCache.
    d) resubscibe events for _window
    e) _window.SetVisible(true)

So the application will hold two window instances - the first for windowed mode and the second for fullscreen mode. The second window just hidden and is not used. But, when user requests change, you just swap instances and continue to work.

Some kind of "Hot Swap" pattern for window  ;D

As a bouns it will provide extremely fast windowed/fullscreen switch with no artefacts. It works really good ;)
It will take some time on the first switch (for the new window initialization). But the next switch will works just on the fly.Of course you can initialize both instances on startup, but it will cause a little longer startup time.

As a second bonus you don't need to store window position and size to restore it after switch back from fullscreen mode (I encountered such problem when used close window and create new one).

PS: 2Laurent: your approach for game loop (with separate window object) is really cool, I just used it for a small Direct2D prototype and I needs to say it's really cool, because it's very simple and extensible - the best implementation that I seen before - nice job! :)
   
« Last Edit: September 29, 2015, 07:41:08 pm by mkalex777 »

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: How to switch between window/fullscreen mode at runtime with SFML.NET?
« Reply #4 on: September 29, 2015, 08:03:48 pm »
Sorry, but I don't agree, according to the good practice, each object which is inherited from IDisposable, should be explicitly disposed. Otherwise it may cause memory leaks. If you don't do it, you will get a lot of warnings from code analyzing tools (suh as fxcop, resharper, etc).

You are exactly correct and I am working on a fix for the above issue (disposing a window while polling events).
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

mkalex777

  • Full Member
  • ***
  • Posts: 206
    • View Profile
Re: How to switch between window/fullscreen mode at runtime with SFML.NET?
« Reply #5 on: September 29, 2015, 08:09:40 pm »
Sorry, but I don't agree, according to the good practice, each object which is inherited from IDisposable, should be explicitly disposed. Otherwise it may cause memory leaks. If you don't do it, you will get a lot of warnings from code analyzing tools (suh as fxcop, resharper, etc).

You are exactly correct and I am working on a fix for the above issue (disposing a window while polling events).

I think that it's not so important, because with using approach that I described before you don't need to close window inside event handler. You just need to hide the window. So you can dispose it when execution will leave game loop.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: How to switch between window/fullscreen mode at runtime with SFML.NET?
« Reply #6 on: September 29, 2015, 10:30:12 pm »
Your approach is a good one, but I think it's worth mentionning what the problem is, and the most simple way to avoid it ;)
Laurent Gomila - SFML developer

DaskDun

  • Newbie
  • *
  • Posts: 1
    • View Profile
    • Email
Re: How to switch between window/fullscreen mode at runtime with SFML.NET?
« Reply #7 on: September 30, 2015, 11:26:00 pm »
I don't know if this help you. It's part of my code.


public static void ToggleFullscren(Game Game)
        {
            Game.Fullscreen = !Game.Fullscreen;
            Game.RenderWindow.Close();
            Game.RenderWindow = new RenderWindow(new VideoMode(960, 720), "My Game", (Game.Fullscreen ? Styles.Fullscreen : Styles.Close));
            Game.RenderWindow.Position = new Vector2i(200, 0);
            Game.RenderWindow.SetFramerateLimit(60);
            Game.RenderWindow.SetVerticalSyncEnabled(true);
            Game.StackStateManager.PeekState().addEvents();
            Game.RenderWindow.Capture();
            Game.RenderWindow.Display();
           
        }

 

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: How to switch between window/fullscreen mode at runtime with SFML.NET?
« Reply #8 on: October 01, 2015, 12:01:55 am »
Quote
I don't know if this help you
Probably not. The problem is not how to switch to fullscreen, but how to do it in an event handler.
Laurent Gomila - SFML developer

mkalex777

  • Full Member
  • ***
  • Posts: 206
    • View Profile
Re: How to switch between window/fullscreen mode at runtime with SFML.NET?
« Reply #9 on: October 02, 2015, 09:08:54 am »
here is an example on how I'm using fullscreen toggle.
It's much faster and solves issues such as restore window position/size/state and possible memory leaks.

        private readonly Dictionary<bool, RenderWindow> _windowCache = new Dictionary<bool, RenderWindow>();
        private RenderWindow _window;
        private bool _isFullScreen;


        public void Dispose()
        {
            DetachWindow();
            foreach (var key in _windowCache.Keys.ToArray())
            {
                _windowCache[key].Close();
                _windowCache[key].Dispose();
                _windowCache.Remove(key);
            }
        }

        public void Run(string[] args)
        {
            Init();
            AttachWindow();
            while (_window.IsOpen)
            {
                _window.DispatchEvents();
                Update();
                Render();
            }
        }

        private void AttachWindow()
        {
            DetachWindow();
            if (!_windowCache.ContainsKey(_isFullScreen))
            {
                var window = new RenderWindow(
                    _isFullScreen ? VideoMode.DesktopMode : new VideoMode(800, 800),
                    "MyApp",
                    _isFullScreen ? Styles.Fullscreen : Styles.Default,
                    new ContextSettings() { AntialiasingLevel = 16 });
                window.Closed += (s, e) => _window.Close();
                using (var image = new Image("Resources\\AppIcon.png"))
                {
                    window.SetIcon(image.Size.X, image.Size.Y, image.Pixels);
                }
                _windowCache[_isFullScreen] = window;
            }
            _window = _windowCache[_isFullScreen];
            _window.KeyPressed += Window_OnKeyPressed;
            _window.TextEntered += Window_OnTextEntered;
            _window.MouseButtonPressed += Window_OnMouseButtonPressed;
            _window.MouseButtonReleased += Window_OnMouseButtonReleased;
            _window.MouseWheelMoved += Window_OnMouseWheelMoved;

            _window.SetVerticalSyncEnabled(GameSettings.VSync);
            _window.SetVisible(true);
            _window.SetActive(true);
            _window.RequestFocus();
        }

        private void DetachWindow()
        {
            if (_window == null)
            {
                return;
            }
            _window.KeyPressed -= Window_OnKeyPressed;
            _window.TextEntered -= Window_OnTextEntered;
            _window.MouseButtonPressed -= Window_OnMouseButtonPressed;
            _window.MouseButtonReleased -= Window_OnMouseButtonReleased;
            _window.MouseWheelMoved -= Window_OnMouseWheelMoved;
            _window.SetVisible(false);
            _window = null;
        }

        private void Window_OnKeyPressed(object sender, KeyEventArgs e)
        {
            if (e.Code == Keyboard.Key.F11 ||
                (e.Code == Keyboard.Key.Return && e.Alt))
            {
                _isFullScreen = !_isFullScreen;
                AttachWindow();
                return;
            }
        }
 
« Last Edit: October 02, 2015, 09:12:09 am by mkalex777 »