Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multi-display device support #8072

Open
wants to merge 57 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
aacae0d
Upon file deletion scroll to previous file or 0 if first on the list
MaMadDl Nov 22, 2023
fddf90f
styling fix
MaMadDl Nov 23, 2023
622ccc3
_displayMonitor Property Decleration
MaMadDl Nov 23, 2023
086cbcb
adding _displayMonitor Property to BaseRenderEngine and Display Class
MaMadDl Nov 23, 2023
2c42c9c
passing Display Count to render Engine from Display
MaMadDl Nov 23, 2023
b9be3e0
- Changing GraphicsSetting Dialog to 6 Button ICN
MaMadDl Nov 23, 2023
1f49b72
Setting Display Before Create the Window either from setting or default
MaMadDl Nov 23, 2023
3651946
styling fix
MaMadDl Nov 23, 2023
0fcc4ea
more sudden style fix
MaMadDl Nov 23, 2023
64aa1cf
More Sudden styles fixes from Visual studio
MaMadDl Nov 23, 2023
e121a1d
Reordering for the Analyzers
MaMadDl Nov 23, 2023
d88c6fd
More Reordering For Analyzers
MaMadDl Nov 23, 2023
604f0df
Update src/engine/screen.h
MaMadDl Nov 23, 2023
04dc878
Update src/engine/screen.h
MaMadDl Nov 23, 2023
20f480e
Update screen.h
MaMadDl Nov 23, 2023
53b5b46
Update dialog_graphics_settings.cpp
MaMadDl Nov 24, 2023
2aa5eae
Resolving Some Reviews For PR #8072
MaMadDl Nov 25, 2023
6f62708
Adding InterfaceType to Graphic Settings
MaMadDl Dec 1, 2023
d1b65a2
rename _displayMonitor property to _displayId with it's getter and se…
MaMadDl Dec 1, 2023
5566d2d
remove inplace initilization
MaMadDl Dec 1, 2023
1fc9224
fix to style
MaMadDl Dec 1, 2023
a193e3e
Limiting Screen Switch Ability to windows only
MaMadDl Dec 7, 2023
8989928
Changing Mistaken Variable
MaMadDl Dec 7, 2023
714d867
Addressing Reviews
MaMadDl Dec 10, 2023
d805b6d
styling fix
MaMadDl Dec 10, 2023
adf13d6
Fix Safeguard
MaMadDl Dec 11, 2023
ff368c5
Style Fix
MaMadDl Dec 11, 2023
52f7a53
Remove settings.h
MaMadDl Dec 11, 2023
4444c85
Addressing Reviews
MaMadDl Dec 12, 2023
c2f1f5e
- Add function to change display in runtime
MaMadDl Dec 13, 2023
cec93b1
Changing dialog_graphics_settings to 5 item layout with both good and…
MaMadDl Dec 13, 2023
662c474
Adressing Reviews
MaMadDl Jan 13, 2024
7f36dd5
Removing display Instantiation
MaMadDl Jan 13, 2024
d19573b
Changing Required Headers Date to 2024
MaMadDl Jan 13, 2024
051f5e8
Addressing Reviews
MaMadDl Jan 13, 2024
76c9b98
- Changing displayId name to displayIndex
MaMadDl Jan 14, 2024
5865c24
- Changing Description
MaMadDl Jan 15, 2024
b1a9987
in Graphics Settings: Changing How Background of 2nd Row is created
MaMadDl Jan 15, 2024
0a70c34
style fix
MaMadDl Jan 15, 2024
3ce8998
Addressing Reviews
MaMadDl Jan 17, 2024
deed982
Addressing Reviews
MaMadDl Jan 19, 2024
d5cbd89
Update screen.cpp
ihhub Jan 20, 2024
6f11a10
Merge branch 'master' into pr/8072
ihhub Jan 20, 2024
70fd633
Merge branch 'multiMonitorSupport' of https://github.com/MaMadDl/fher…
ihhub Jan 20, 2024
7ff712c
Fix main menu not generating after the display change
MaMadDl Feb 6, 2024
0287e22
Checking for supported display
MaMadDl Feb 6, 2024
c2bc9d7
Fixing error for display index on startup
MaMadDl Mar 4, 2024
6c06be6
remove unused variable
MaMadDl Mar 4, 2024
3af1d73
Fix lambda to calculate on every function call instead of only once
MaMadDl Mar 4, 2024
e6e8050
return last available display in case cureent display is out of bound
MaMadDl Mar 28, 2024
2f8e5ef
handiling SDL_DISPLAYEVENT in a display is disconnected
MaMadDl Mar 28, 2024
028e3b4
fix format
MaMadDl Mar 28, 2024
0bc0b0f
fix header
MaMadDl Mar 28, 2024
c2af17e
remove settings from localevent source
MaMadDl Mar 28, 2024
89b9900
add Header asked by IWYU
MaMadDl Mar 28, 2024
593b967
fix enum
MaMadDl Mar 28, 2024
222d1ad
replacing enum SDL_DISPLAYEVENT_DISCONNECTED with it's value
MaMadDl Mar 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/engine/localevent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "logging.h"
#include "render_processor.h"
#include "screen.h"
#include "settings.h"
MaMadDl marked this conversation as resolved.
Show resolved Hide resolved
#include "tools.h"

namespace
Expand Down Expand Up @@ -737,6 +738,7 @@ bool LocalEvent::HandleEvents( const bool sleepAfterEventProcessing, const bool
_mouseCursorRenderArea = {};

fheroes2::Display & display = fheroes2::Display::instance();
fheroes2::BaseRenderEngine & engine = fheroes2::engine();

if ( fheroes2::RenderProcessor::instance().isCyclingUpdateRequired() ) {
// To maintain color cycling animation we need to render the whole frame with an updated palette.
Expand Down Expand Up @@ -852,6 +854,26 @@ bool LocalEvent::HandleEvents( const bool sleepAfterEventProcessing, const bool
// As of now we have no logic for this so we at least log this event.
DEBUG_LOG( DBG_ENGINE, DBG_WARN, "OS indicates low memory. Release some resources." )
break;
case SDL_DISPLAYEVENT:
if ( event.display.event == SDL_DISPLAYEVENT_DISCONNECTED ) {
DEBUG_LOG( DBG_ENGINE, DBG_INFO, "The display with id %d was disconnected " << event.display.display );
engine.setDisplayIndex( engine.getCurrentDisplayIndex() );

fheroes2::Image temp;
assert( display.singleLayer() );
temp._disableTransformLayer();
fheroes2::Copy( display, temp );

const fheroes2::ResolutionInfo currentResolution{ display.width(), display.height(), display.screenSize().width, display.screenSize().height };

display.setResolution( currentResolution );
fheroes2::Copy( temp, display );

// Save new index in conf as well
Settings & conf = Settings::Get();
conf.Save( Settings::configFileName );
}
break;
default:
// If this assertion blows up then we included an event type but we didn't add logic for it.
assert( eventTypeStatus.count( event.type ) == 0 );
Expand Down Expand Up @@ -1526,7 +1548,7 @@ void LocalEvent::setEventProcessingStates()
setEventProcessingState( SDL_APP_DIDENTERFOREGROUND, false );
// SDL_LOCALECHANGED is supported from SDL 2.0.14
// TODO: we don't process this event. Add the logic.
setEventProcessingState( SDL_DISPLAYEVENT, false );
setEventProcessingState( SDL_DISPLAYEVENT, true );
setEventProcessingState( SDL_WINDOWEVENT, true );
// TODO: verify why disabled processing of this event.
setEventProcessingState( SDL_SYSWMEVENT, false );
Expand Down
77 changes: 66 additions & 11 deletions src/engine/screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,15 +851,16 @@ namespace

std::vector<fheroes2::ResolutionInfo> getAvailableResolutions() const override
{
static const std::vector<fheroes2::ResolutionInfo> filteredResolutions = []() {
int currentIndex = static_cast<int>( getCurrentDisplayIndex() );
std::vector<fheroes2::ResolutionInfo> filteredResolutions = [=]() {
std::set<fheroes2::ResolutionInfo> resolutionSet;

const int displayCount = SDL_GetNumVideoDisplays();
if ( displayCount > 0 ) {
const int displayModeCount = SDL_GetNumDisplayModes( 0 );
const int displayModeCount = SDL_GetNumDisplayModes( currentIndex );
for ( int i = 0; i < displayModeCount; ++i ) {
SDL_DisplayMode videoMode;
const int returnCode = SDL_GetDisplayMode( 0, i, &videoMode );
const int returnCode = SDL_GetDisplayMode( currentIndex, i, &videoMode );
if ( returnCode < 0 ) {
ERROR_LOG( "Failed to get display mode. The error value: " << returnCode << ", description: " << SDL_GetError() )
}
Expand Down Expand Up @@ -938,13 +939,59 @@ namespace
}
}

uint8_t getCurrentDisplayIndex() const override
{
if ( uint8_t maxDis = getMaximumDisplays(); _displayIndex >= maxDis ) {
return maxDis - 1;
}
return _displayIndex;
}

void setDisplayIndex( const uint8_t display ) override
{
if ( display == _displayIndex )
return;

SDL_GetWindowPosition( _window, &_prevWindowPos.x, &_prevWindowPos.y );

if ( display < getMaximumDisplays() ) {
_displayIndex = display;
}
else {
_displayIndex = 0;
}

clear();
}

uint8_t getMaximumDisplays() const override
{
if ( !isAllocated() )
// If engine is not allocated returning highest uint8_t number
return 255;

const int displayCount = SDL_GetNumVideoDisplays();
if ( displayCount > 0 ) {
return static_cast<uint8_t>( displayCount );
}
ERROR_LOG( "Failed to Get Number of Displays, description: " << SDL_GetError() );
// There should be one display at least.
return 1;
}

const char * getDisplayName( const uint8_t display ) override
{
return SDL_GetDisplayName( display );
}

protected:
RenderEngine()
: _window( nullptr )
, _surface( nullptr )
, _renderer( nullptr )
, _texture( nullptr )
, _driverIndex( -1 )
, _displayIndex( 0 )
MaMadDl marked this conversation as resolved.
Show resolved Hide resolved
, _prevWindowPos( SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED )
, _isVSyncEnabled( false )
{
Expand Down Expand Up @@ -1045,9 +1092,7 @@ namespace

const std::vector<fheroes2::ResolutionInfo> resolutions = getAvailableResolutions();
assert( !resolutions.empty() );
if ( !resolutions.empty() ) {
resolutionInfo = GetNearestResolution( resolutionInfo, resolutions );
}
resolutionInfo = GetNearestResolution( resolutionInfo, resolutions );

#if defined( ANDROID )
// Same as ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
Expand All @@ -1057,6 +1102,7 @@ namespace
#endif

MaMadDl marked this conversation as resolved.
Show resolved Hide resolved
uint32_t flags = SDL_WINDOW_SHOWN;

if ( isFullScreen ) {
#if defined( _WIN32 )
if ( fheroes2::cursor().isSoftwareEmulation() ) {
Expand All @@ -1071,8 +1117,8 @@ namespace
}

flags |= SDL_WINDOW_RESIZABLE;

_window = SDL_CreateWindow( _previousWindowTitle.data(), _prevWindowPos.x, _prevWindowPos.y, resolutionInfo.screenWidth, resolutionInfo.screenHeight, flags );
_window = SDL_CreateWindow( _previousWindowTitle.data(), SDL_WINDOWPOS_CENTERED_DISPLAY( getCurrentDisplayIndex() ),
SDL_WINDOWPOS_CENTERED_DISPLAY( getCurrentDisplayIndex() ), resolutionInfo.screenWidth, resolutionInfo.screenHeight, flags );
if ( _window == nullptr ) {
ERROR_LOG( "Failed to create an application window of " << resolutionInfo.screenWidth << " x " << resolutionInfo.screenHeight
<< " size. The error: " << SDL_GetError() )
Expand Down Expand Up @@ -1155,12 +1201,19 @@ namespace
return ( _window != nullptr ) && ( ( SDL_GetWindowFlags( _window ) & SDL_WINDOW_MOUSE_FOCUS ) == SDL_WINDOW_MOUSE_FOCUS );
}

bool isAllocated() const override
{
// We should check for at least one of the variables destroyed in Clear()
return _window != nullptr;
}

private:
SDL_Window * _window;
SDL_Surface * _surface;
SDL_Renderer * _renderer;
SDL_Texture * _texture;
int _driverIndex;
uint8_t _displayIndex;
MaMadDl marked this conversation as resolved.
Show resolved Hide resolved

MaMadDl marked this conversation as resolved.
Show resolved Hide resolved
std::string _previousWindowTitle;
fheroes2::Point _prevWindowPos;
Expand Down Expand Up @@ -1333,17 +1386,19 @@ namespace fheroes2
void Display::setResolution( ResolutionInfo info )
{
if ( width() > 0 && height() > 0 && info.gameWidth == width() && info.gameHeight == height() && info.screenWidth == _screenSize.width
&& info.screenHeight == _screenSize.height ) // nothing to resize
&& info.screenHeight == _screenSize.height && _engine->isAllocated() ) {
// Nothing to resize
return;
}

const bool isFullScreen = _engine->isFullScreen();

// deallocate engine resources
// Deallocate engine resources
_engine->clear();

MaMadDl marked this conversation as resolved.
Show resolved Hide resolved
_prevRoi = {};

// allocate engine resources
// Allocate engine resources
if ( !_engine->allocate( info, isFullScreen ) ) {
clear();
}
Expand Down
27 changes: 26 additions & 1 deletion src/engine/screen.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2020 - 2023 *
* Copyright (C) 2020 - 2024 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand Down Expand Up @@ -139,6 +139,26 @@ namespace fheroes2
return _nearestScaling;
}

virtual uint8_t getCurrentDisplayIndex() const
{
return 0;
}

virtual void setDisplayIndex( const uint8_t )
MaMadDl marked this conversation as resolved.
Show resolved Hide resolved
{
// Do nothing.
}

virtual uint8_t getMaximumDisplays() const
{
return 1;
}

virtual const char * getDisplayName( const uint8_t )
MaMadDl marked this conversation as resolved.
Show resolved Hide resolved
{
return "";
}

protected:
BaseRenderEngine()
: _isFullScreen( false )
Expand All @@ -162,6 +182,11 @@ namespace fheroes2
return false;
}

virtual bool isAllocated() const
{
return true;
}

virtual bool isMouseCursorActive() const
{
return false;
Expand Down
57 changes: 48 additions & 9 deletions src/fheroes2/dialog/dialog_graphics_settings.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2022 - 2023 *
* Copyright (C) 2022 - 2024 *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
Expand Down Expand Up @@ -49,17 +49,21 @@ namespace
Mode,
VSync,
SystemInfo,
SwitchDisplay,
Exit
};

const fheroes2::Size offsetBetweenOptions{ 118, 110 };
const fheroes2::Point optionOffset{ 69, 47 };
const fheroes2::Size offsetBetweenOptions{ 92, 110 };
const fheroes2::Point optionOffset{ 36, 47 };
const int32_t optionWindowSize{ 65 };
const int32_t centerAlignmentOffset{ offsetBetweenOptions.width / 2 };

const fheroes2::Rect resolutionRoi{ optionOffset.x, optionOffset.y, optionWindowSize, optionWindowSize };
const fheroes2::Rect modeRoi{ optionOffset.x + offsetBetweenOptions.width, optionOffset.y, optionWindowSize, optionWindowSize };
const fheroes2::Rect vSyncRoi{ optionOffset.x, optionOffset.y + offsetBetweenOptions.height, optionWindowSize, optionWindowSize };
const fheroes2::Rect systemInfoRoi{ optionOffset.x + offsetBetweenOptions.width, optionOffset.y + offsetBetweenOptions.height, optionWindowSize, optionWindowSize };
const fheroes2::Rect vSyncRoi{ optionOffset.x + centerAlignmentOffset, optionOffset.y + offsetBetweenOptions.height, optionWindowSize, optionWindowSize };
const fheroes2::Rect systemInfoRoi{ optionOffset.x + offsetBetweenOptions.width + centerAlignmentOffset, optionOffset.y + offsetBetweenOptions.height,
optionWindowSize, optionWindowSize };
const fheroes2::Rect switchDisplayRoi{ optionOffset.x + offsetBetweenOptions.width * 2, optionOffset.y, optionWindowSize, optionWindowSize };

void drawResolution( const fheroes2::Rect & optionRoi )
{
Expand Down Expand Up @@ -120,15 +124,25 @@ namespace
fheroes2::drawOption( optionRoi, image, _( "System Info" ), isSystemInfoDisplayed ? _( "on" ) : _( "off" ), fheroes2::UiOptionTextWidth::TWO_ELEMENTS_ROW );
}

void drawDisplay( const fheroes2::Rect & optionRoi )
{
const uint8_t displayIndex = fheroes2::engine().getCurrentDisplayIndex();
fheroes2::drawOption( optionRoi, fheroes2::AGG::GetICN( ICN::GAME_OPTION_ICON, 1 ), _( "Display Index" ),
std::to_string( displayIndex + 1 ) + ": " + fheroes2::engine().getDisplayName( displayIndex ),
fheroes2::UiOptionTextWidth::TWO_ELEMENTS_ROW );
}

SelectedWindow showConfigurationWindow()
{
fheroes2::Display & display = fheroes2::Display::instance();

const Settings & conf = Settings::Get();
const bool isEvilInterface = conf.isEvilInterfaceEnabled();
const fheroes2::Sprite & dialog = fheroes2::AGG::GetICN( ( isEvilInterface ? ICN::ESPANBKG_EVIL : ICN::ESPANBKG ), 0 );
const fheroes2::Sprite & dialog = fheroes2::AGG::GetICN( ( isEvilInterface ? ICN::CSPANBKE : ICN::CSPANBKG ), 0 );
const fheroes2::Sprite & dialogShadow = fheroes2::AGG::GetICN( ( isEvilInterface ? ICN::CSPANBKE : ICN::CSPANBKG ), 1 );

const fheroes2::Sprite & secondRowBackground = fheroes2::AGG::GetICN( Settings::Get().isEvilInterfaceEnabled() ? ICN::SURDRBKE : ICN::SURDRBKG, 0 );

const fheroes2::Point dialogOffset( ( display.width() - dialog.width() ) / 2, ( display.height() - dialog.height() ) / 2 );
const fheroes2::Point shadowOffset( dialogOffset.x - BORDERWIDTH, dialogOffset.y );

Expand All @@ -139,18 +153,25 @@ namespace
fheroes2::Blit( dialogShadow, display, windowRoi.x - BORDERWIDTH, windowRoi.y + BORDERWIDTH );
fheroes2::Blit( dialog, display, windowRoi.x, windowRoi.y );

// The image is taken from another big ICN and cropped, with its background at an adequate size.
// The offsets are statically fixed based on the original image to fill the empty spaces on the second row alongside it's shadow.
fheroes2::Blit( secondRowBackground, 50, 30, display, windowRoi.x + BORDERWIDTH + 10, windowRoi.y + optionOffset.y - 10 + offsetBetweenOptions.height,
dialog.width() - BORDERWIDTH * 2 - 20, optionWindowSize + 20 );

fheroes2::ImageRestorer emptyDialogRestorer( display, windowRoi.x, windowRoi.y, windowRoi.width, windowRoi.height );

const fheroes2::Rect windowResolutionRoi( resolutionRoi + windowRoi.getPosition() );
const fheroes2::Rect windowModeRoi( modeRoi + windowRoi.getPosition() );
const fheroes2::Rect windowVSyncRoi( vSyncRoi + windowRoi.getPosition() );
const fheroes2::Rect windowSystemInfoRoi( systemInfoRoi + windowRoi.getPosition() );
const fheroes2::Rect windowSwitchDisplayRoi( switchDisplayRoi + windowRoi.getPosition() );

const auto drawOptions = [&windowResolutionRoi, &windowModeRoi, &windowVSyncRoi, &windowSystemInfoRoi]() {
const auto drawOptions = [&windowResolutionRoi, &windowModeRoi, &windowVSyncRoi, &windowSwitchDisplayRoi, &windowSystemInfoRoi]() {
drawResolution( windowResolutionRoi );
drawMode( windowModeRoi );
drawVSync( windowVSyncRoi );
drawSystemInfo( windowSystemInfoRoi );
drawDisplay( windowSwitchDisplayRoi );
MaMadDl marked this conversation as resolved.
Show resolved Hide resolved
};

drawOptions();
Expand Down Expand Up @@ -187,7 +208,9 @@ namespace
if ( le.MouseClickLeft( windowSystemInfoRoi ) ) {
return SelectedWindow::SystemInfo;
}

if ( le.MouseClickLeft( windowSwitchDisplayRoi ) ) {
return SelectedWindow::SwitchDisplay;
}
if ( le.MousePressRight( windowResolutionRoi ) ) {
fheroes2::showStandardTextMessage( _( "Select Game Resolution" ), _( "Change the resolution of the game." ), 0 );
}
Expand All @@ -197,7 +220,10 @@ namespace
else if ( le.MousePressRight( windowVSyncRoi ) ) {
fheroes2::showStandardTextMessage( _( "V-Sync" ), _( "The V-Sync option can be enabled to resolve flickering issues on some monitors." ), 0 );
}
if ( le.MousePressRight( windowSystemInfoRoi ) ) {
else if ( le.MousePressRight( windowSwitchDisplayRoi ) ) {
fheroes2::showStandardTextMessage( _( "Display Selection" ), _( "Toggle Between available Displays" ), 0 );
}
else if ( le.MousePressRight( windowSystemInfoRoi ) ) {
fheroes2::showStandardTextMessage( _( "System Info" ), _( "Show extra information such as FPS and current time." ), 0 );
}
else if ( le.MousePressRight( okayButton.area() ) ) {
Expand Down Expand Up @@ -253,6 +279,19 @@ namespace fheroes2
conf.Save( Settings::configFileName );
windowType = SelectedWindow::Configuration;
break;
case SelectedWindow::SwitchDisplay: {
fheroes2::BaseRenderEngine & engine = fheroes2::engine();
engine.setDisplayIndex( ( engine.getCurrentDisplayIndex() + 1 ) % engine.getMaximumDisplays() );

fheroes2::Display & display = fheroes2::Display::instance();
const fheroes2::ResolutionInfo currentResolution{ display.width(), display.height(), display.screenSize().width, display.screenSize().height };
display.setResolution( currentResolution );
updateUI();

conf.Save( Settings::configFileName );
windowType = SelectedWindow::Configuration;
break;
}
default:
return;
}
Expand Down