5 1 new backport x11 fs rework (#1485)

* Use XRandR 1.2 to set fullscreen resolution for single output

Squash of roothorick's PR #497
(also includes Kyzentun's CMake changes from PR #716)

* Cherry-pick json c++1x stuff (b9e3d7174e)

* Cherry-pick c++11 support from 5bba5c0038 and 9f8b045309

* rework Linux (X11) fullscreen, improve display-related Graphics Options

Implement option to select between monitors for exclusive fullscreen mode
on X11 (using XRandR 1.2), or use a fullscreen borderless window.

Reimplement resolution/refresh rate/display mode-related option rows
using Lua, update choices dynamically so only known-good groupings of
resolution/refresh rate/aspect ratio can be selected.

Minimally update Windows/MacOS LowLevelWindow implementations to support
changes made for Linux side. Fullscreen Borderless Window/multi monitor
support from X11 not implemented for those in this commit.

* allow forcibly disabling xinerama use on Linux

When libXinerama is available, SM tries to use it to find the proper
monitor indexes to use to set _NET_WM_FULLSCREEN_MONITORS (on borderless
fullscreen). xfwm4 seems to assume that monitors are numbered in increasing
order from left to right (rather than using the Xinerama-assigned numbers),
so _NET_WM_FULLSCREEN_MONITORS misbehaves on Xfce.

This commit bypasses use of libXinerama, and instead forces SM to induce fullscreen
on the desired monitor in the backup, hacky way: remove all window hints, move window
to desired monitor, then add _NET_WM_STATE_FULLSCREEN hint. This works on
mutter and Xfce.

* Remove multiple warnings on redundant define.

This used to be hard-coded due to pthread related items, but now it's dynamically determined.

* fix _fallback menu behavior for unrecognized aspect ratios

* Fix error recreating existing FS texture

* Bump deployment target to 10.7 to use libc++ on XCode 8

* Add explicit casts to please clang

* Update changelog
This commit is contained in:
Drew Barbarello
2017-06-18 11:55:16 -04:00
committed by Colby Klein
parent 7ef14c340d
commit 557be7cf1b
57 changed files with 2616 additions and 370 deletions
+7 -1
View File
@@ -27,7 +27,13 @@ function(sm_add_compile_definition target def)
endfunction()
function(sm_add_compile_flag target flag)
sm_append_simple_target_property(${target} COMPILE_FLAGS ${flag})
get_target_property(current_property ${target} COMPILE_FLAGS)
if (current_property)
set(current_property "${current_property} ${flag}")
set_target_properties(${target} PROPERTIES COMPILE_FLAGS "${current_property}")
else()
set_target_properties(${target} PROPERTIES COMPILE_FLAGS ${flag})
endif()
endfunction()
function(sm_add_link_flag target flag)
+1
View File
@@ -68,4 +68,5 @@ elseif(LINUX)
option(WITH_GTK2 "Build with GTK2 Support." ON)
option(WITH_PARALLEL_PORT "Build with Parallel Lights I/O Support." OFF)
option(WITH_CRASH_HANDLER "Build with Crash Handler Support." ON)
option(WITH_XINERAMA "Build using libXinerama to query for monitor numbers (if available)." ON)
endif()
+52
View File
@@ -0,0 +1,52 @@
# This is borrowed from FreeRDP.
# - Find XINERAMA
# Find the XINERAMA libraries
#
# This module defines the following variables:
# XINERAMA_FOUND - true if XINERAMA_INCLUDE_DIR & XINERAMA_LIBRARY are found
# XINERAMA_LIBRARIES - Set when XINERAMA_LIBRARY is found
# XINERAMA_INCLUDE_DIRS - Set when XINERAMA_INCLUDE_DIR is found
#
# XINERAMA_INCLUDE_DIR - where to find Xinerama.h, etc.
# XINERAMA_LIBRARY - the XINERAMA library
#
#=============================================================================
# Copyright 2011 O.S. Systems Software Ltda.
# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
find_path(XINERAMA_INCLUDE_DIR NAMES X11/extensions/Xinerama.h
PATHS /opt/X11/include
PATH_SUFFIXES X11/extensions
DOC "The Xinerama include directory"
)
find_library(XINERAMA_LIBRARY NAMES Xinerama
PATHS /opt/X11/lib
DOC "The Xinerama library"
)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Xinerama DEFAULT_MSG XINERAMA_LIBRARY XINERAMA_INCLUDE_DIR)
if(XINERAMA_FOUND)
set( XINERAMA_LIBRARIES ${XINERAMA_LIBRARY} )
set( XINERAMA_INCLUDE_DIRS ${XINERAMA_INCLUDE_DIR} )
endif()
mark_as_advanced(XINERAMA_INCLUDE_DIR XINERAMA_LIBRARY)
+5
View File
@@ -3,6 +3,11 @@ ________________________________________________________________________________
The StepMania 5.1 Changelog covers all post-5.0.12 changes.
________________________________________________________________________________
2017/06/18
----------
* [RageDisplay, LLW_X11] Backport fullscreen borderless window, per-monitor
fullscreen options to X11 backend
2017/06/06
----------
* [General] Backport custom song support. [kyzentun]
+35
View File
@@ -744,6 +744,23 @@
<Function name='get_dirty'/>
<Function name='destroy'/>
</Class>
<Class name='DisplayMode'>
<Function name='GetWidth'/>
<Function name='GetHeight'/>
<Function name='GetRefreshRate'/>
</Class>
<Class name='DisplaySpec'>
<Function name='GetId'/>
<Function name='GetName'/>
<Function name='GetCurrentMode'/>
<Function name='GetSupportedModes'/>
<Function name='IsVirtual'/>
</Class>
<Class name='DisplaySpecs'>
<Function name='__len'/>
<Function name='__index'/>
<Function name='__tostring'/>
</Class>
<Class base='Sprite' name='DifficultyIcon'>
<Function name='SetFromDifficulty'/>
<Function name='SetFromSteps'/>
@@ -1480,8 +1497,11 @@
<Function name='GetCumFPS'/>
<Function name='GetDisplayHeight'/>
<Function name='GetDisplayWidth'/>
<Function name='GetDisplaySpecs'/>
<Function name='GetFPS'/>
<Function name='GetVPF'/>
<Function name='SupportsFullscreenBorderlessWindow'/>
<Function name='SupportsRenderToTexture'/>
</Class>
<Class name='RageFile'>
<Function name='AtEOF'/>
@@ -2479,6 +2499,15 @@
<EnumValue name='&apos;NoteType_64th&apos;' value='7'/>
<EnumValue name='&apos;NoteType_192nd&apos;' value='8'/>
</Enum>
<Enum name='OptEffect'>
<EnumValue name='&apos;OptEffect_SavePreferences&apos;' value='1'/>
<EnumValue name='&apos;OptEffect_ApplyGraphics&apos;' value='2'/>
<EnumValue name='&apos;OptEffect_ApplyTheme&apos;' value='4'/>
<EnumValue name='&apos;OptEffect_ChangeGame&apos;' value='8'/>
<EnumValue name='&apos;OptEffect_ApplySound&apos;' value='16'/>
<EnumValue name='&apos;OptEffect_ApplySong&apos;' value='32'/>
<EnumValue name='&apos;OptEffect_ApplyAspectRatio&apos;' value='64'/>
</Enum>
<Enum name='PaneCategory'>
<EnumValue name='&apos;PaneCategory_NumSteps&apos;' value='0'/>
<EnumValue name='&apos;PaneCategory_Jumps&apos;' value='1'/>
@@ -2566,6 +2595,11 @@
<EnumValue name='&apos;RankingType_Category&apos;' value='0'/>
<EnumValue name='&apos;RankingType_Course&apos;' value='1'/>
</Enum>
<Enum name='ReloadChanged'>
<EnumValue name='&apos;ReloadChanged_None&apos;' value='0'/>
<EnumValue name='&apos;ReloadChanged_Enabled&apos;' value='1'/>
<EnumValue name='&apos;ReloadChanged_All&apos;' value='2'/>
</Enum>
<Enum name='SampleMusicPreviewMode'>
<EnumValue name='&apos;SampleMusicPreviewMode_Normal&apos;' value='0'/>
<EnumValue name='&apos;SampleMusicPreviewMode_StartToPreview&apos;' value='1'/>
@@ -2800,6 +2834,7 @@
<Constants>
<Constant name='ASPECT_SCALE_FACTOR' value='854'/>
<Constant name='NUM_PLAYERS' value='2'/>
<Constant name='REFRESH_DEFAULT'/>
<Constant name='SAFE_HEIGHT' value='36'/>
<Constant name='SAFE_WIDTH' value='64'/>
<Constant name='SCREEN_BOTTOM' value='480'/>
+81 -14
View File
@@ -21,7 +21,7 @@
save yourself some time, copy this for undocumented things:
<Function name='' return='' arguments=''>
</Function>
-->
@@ -451,7 +451,7 @@ save yourself some time, copy this for undocumented things:
Returns a formatted percent with the specified <code>num</code>erator and <code>den</code>ominator.
</Function>
<Function name='print' theme='_fallback' return='void' arguments='string s'>
[00 init.lua]
[00 init.lua]
</Function>
<Function name='PrintTable' theme='_fallback' return='void' arguments='table t'>
[03 ThemePrefs.lua] Prints a table's contents to the log.
@@ -568,7 +568,7 @@ save yourself some time, copy this for undocumented things:
[03 UserPreferences2.lua] Themer-facing function for setting a user preference.
</Function>
<Function name='ShowHoldJudgments' theme='_fallback' return='bool' arguments=''>
[03 Gameplay.lua]
[03 Gameplay.lua]
</Function>
<Function name='ShowStandardDecoration' theme='_fallback' return='bool' arguments='string sMetricsName'>
[02 ActorDef.lua] Returns <code>true</code> if a decoration should be shown on the current screen or not.
@@ -586,13 +586,13 @@ save yourself some time, copy this for undocumented things:
[02 Colors.lua]
</Function>
<Function name='StandardDecorationFromTable' theme='_fallback' return='Actor' arguments='string sMetricsName, table t'>
[02 ActorDef.lua]
[02 ActorDef.lua]
</Function>
<Function name='StandardDecorationFromFile' theme='_fallback' return='Actor' arguments='string sMetricsName, string sFileName'>
[02 ActorDef.lua]
[02 ActorDef.lua]
</Function>
<Function name='StandardDecorationFromFileOptional' theme='_fallback' return='Actor' arguments='string sMetricsName, string sFileName'>
[02 ActorDef.lua]
[02 ActorDef.lua]
</Function>
<Function name='tableshuffle' theme='_fallback' return='table' arguments='table t'>
[02 Utilities.lua] Returns a shuffled version of <code>t</code>.
@@ -760,7 +760,7 @@ save yourself some time, copy this for undocumented things:
<Function name='GetZoom' return='float' arguments='PlayerState ps'>
Returns the zoom of a note for the provided PlayerState.
</Function>
<Function name='GetFrameWidthScale' return='float' arguments='PlayerState ps, float fYOffset, fOverlappedTime'>
<Function name='GetFrameWidthScale' return='float' arguments='PlayerState ps, float fYOffset, float fOverlappedTime'>
Returns the FrameWidthScale of a hold part with a Y offset of <code>fYOffset</code> for the provided PlayerState.<br/> <code>fOverlappedTime</code> is optional and will default to 0.
</Function>
</Namespace>
@@ -776,7 +776,7 @@ save yourself some time, copy this for undocumented things:
of <code>Enum</code>. Instead of
<code><Link function='GetName'>Enum.GetName</Link>(
<Link class='ENUM' function='PlayerNumber' /> )</code> or
<code><Link function='Reverse'>Enum.Reverse</Link>(
<code><Link function='Reverse'>Enum.Reverse</Link>(
<Link class='ENUM' function='PlayerNumber' /> )</code>, one can use
<code><Link class='ENUM' function='PlayerNumber' />:<!--
--><Link function='GetName'>GetName</Link>()</code> or
@@ -2353,6 +2353,61 @@ save yourself some time, copy this for undocumented things:
Blanks the DifficultyIcon.
</Function>
</Class>
<Class name='DisplayMode'>
<Function name='GetWidth' return='int' arguments=''>
Return the width of the display in this mode.
</Function>
<Function name='GetWidth' return='int' arguments=''>
Return the height of the display in this mode.
</Function>
<Function name='GetRefreshRate' return='float' arguments=''>
Return the refresh rate of the display in this mode.
</Function>
</Class>
<Class name='DisplaySpec'>
<Description>
An object describing a display: its supported and current video modes.
</Description>
<Function name='GetId' return='string' arguments=''>
Return the unique identifier of the display.
</Function>
<Function name='GetName' return='string' arguments=''>
Return the &quot;human-readable&quot; display name.
</Function>
<Function name='GetSupportedModes' return='{DisplayMode}' arguments=''>
Return the <Link class='DisplayMode' />s supported by this device.
</Function>
<Function name='GetCurrentMode' return='DisplayMode' arguments=''>
Return the currently active <Link class='DisplayMode' /> for this display,
or <code>nil</code> if there is no such mode.
</Function>
<Function name='IsVirtual' return='bool' arguments=''>
Return <code>true</code> if this <code>DisplaySpec</code> is describing the
&quot;logical display&quot; like an X screen or the Win32 &quot;Virtual screen&quot;, or
<code>false</code> otherwise (if this describes a physical display).
</Function>
</Class>
<Class name='DisplaySpecs'>
<Description>
An array-like <code>userdata</code> object which describes the displays
configured on the user's machine.
This object supports the <code>__len</code> operator (<code>#t</code>), and
integer indexing, but does not support
iteration via <code>pairs</code> or <code>ipairs</code>.
Each element of the array is a <Link class='DisplaySpec' />.
</Description>
<Function name='__len' return='int' arguments=''>
Return the number of <Link class='DisplaySpec' /> instances in the array.
</Function>
<Function name='__index' return='DisplaySpec' arguments='int i'>
Return the <Link class='DisplaySpec' /> instance at index <code>i</code>
</Function>
<Function name='__tostring' return='string' arguments=''>
</Function>
</Class>
<Class name='FadingBanner'>
<Function name='GetLatestIndex' return='int' arguments=''>
Returns the index of the last banner loaded.
@@ -2518,7 +2573,7 @@ save yourself some time, copy this for undocumented things:
<code>musicLength</code> seconds one time. Both <code>fadeIn</code> and
<code>fadeOut</code> can be customized as required. <code>loop</code>
tells the sound manager to loop the music part. <code>applyRate</code>
tells the sound manager to apply the current music rate. If <code>alignBeat</code>
tells the sound manager to apply the current music rate. If <code>alignBeat</code>
is true or nil, the length is automatically adjusted to cover an integer number of beats.
</Function>
<Function name='PlayOnce' return='void' arguments='string sPath, bool is_action'>
@@ -2903,7 +2958,7 @@ save yourself some time, copy this for undocumented things:
Sets if the Jukebox should use modifiers.
</Function>
<Function name='SetMultiplayer' return='void' arguments='bool b'>
</Function>
<Function name='SetNumMultiplayerNoteFields' return='void' arguments='int iFields'>
Sets the number of multiplayer notefields to <code>iFields</code>
@@ -3183,7 +3238,7 @@ save yourself some time, copy this for undocumented things:
<Function name='SelectCourse' return='bool' arguments='Course cCourse'>
Selects a course. Returns <code>false</code> on failure.
</Function>
</Class>
</Class>
<Class name='NoteSkinManager'>
<Function name='GetMetric' return='string' arguments='string sElement, string sValue'>
Returns a string from the specified element and value.
@@ -4162,7 +4217,7 @@ save yourself some time, copy this for undocumented things:
<Function name='ProfileWasLoadedFromMemoryCard' return='bool' arguments='PlayerNumber pn'>
Returns <code>true</code> if <code>pn</code>'s Profile was loaded from a memory card.
</Function>
<Function name='LastLoadWasFromLastGood' return='bool' arguments='PlayerNumber pn'>
Returns <code>true</code> if the last load of player <code>pn</code>'s profile was a LastGood copy of the profile.
</Function>
@@ -4229,6 +4284,18 @@ save yourself some time, copy this for undocumented things:
<Function name='GetCumFPS' return='int' arguments=''>
Return the cumulative FPS.
</Function>
<Function name='GetDisplaySpecs' return='DisplaySpecs' arguments=''>
Return an array-like <code>userdata</code> of type <Link class='DisplaySpecs' />,
which describes the displays configured on the user's machine.
</Function>
<Function name='SupportsRenderToTexture' return='bool' arguments=''>
Return <code>true</code> if the current renderer supports render-to-texture,
<code>false</code> otherwise.
</Function>
<Function name='SupportsFullscreenBorderlessWindow' return='bool' arguments=''>
Return <code>true</code> if the current renderer/window implementation supports
a fullscreen borderless-window mode, <code>false</code> otherwise.
</Function>
</Class>
<Class name='RageFile'>
<Description>
@@ -5516,7 +5583,7 @@ save yourself some time, copy this for undocumented things:
Returns <code>true</code> if the theme has the specified metric.
</Function>
<Function name='HasString' return='bool' arguments='string section, string value'>
Returns <code>true</code> if the theme has the specified string.
Returns <code>true</code> if the theme has the specified string.
</Function>
<Function name='IsThemeSelectable' return='bool' arguments='string theme'>
Returns <code>true</code> if the specified theme is selectable.
@@ -5525,7 +5592,7 @@ save yourself some time, copy this for undocumented things:
Reloads the current theme's metrics.
</Function>
<Function name='RunLuaScripts' return='void' arguments='string sMask'>
</Function>
<Function name='SetTheme' return='void' arguments='string theme'>
Changes the current theme.<br />
@@ -59,13 +59,23 @@ function FooMods()
return {PLAYER_2}
end,
-- Optional function. If non-nil, this function must return the string representation
-- of a member of the ReloadChanged enum. This function is called in response to a message
-- listed in the optional ReloadRowMessages table below. For example,
-- to completely change the Choices presented by this OptionRow, modify
-- self.Choices in the body of this function and return "ReloadChanged_All"
Reload= function(self)
Trace("FooMods:Reload() called.")
return "ReloadChanged_None"
end,
-- A table of strings that are the names of choices. Choice names are not
-- localized.
Choices= {"a", "b", "c", "d"},
-- Optional table. If non-nil, this table must contain a list of messages
-- this row should listen for. If one of the messages is recieved, the
-- row is reloaded (and the EnabledForPlayers function is called if it is
-- row is reloaded (and each of the EnabledForPlayers/Reload functions is called if it is
-- non-nil).
ReloadRowMessages= {"ReloadFooMods"},
@@ -90,6 +100,12 @@ function FooMods()
-- SaveSelections should examine the list of what the player has selected
-- and apply the appropriate modifiers to the player.
-- Same args as LoadSelections.
--
-- May optionally return an "effects" bit mask (constructed by combination of
-- the bit masks from the OptEffect enum), which dictates actions the
-- game should take in response to the selected option. For example,
-- OPT_SAVE_PREFERENCES directs the game to save Preferences.ini,
-- OPT_APPLY_ASPECT_RATIO directs the game to recompute the projection matrix, etc.
SaveSelections= function(self, list, pn)
Trace("FooMods:SaveSelections(" .. pn .. ")")
for i, choice in ipairs(self.Choices) do
+14 -4
View File
@@ -19,18 +19,21 @@ set(SM_EXE_NAME "StepMania")
# Some OS specific helpers.
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
set(LINUX TRUE)
set(SM_CPP_STANDARD "gnu++11")
else()
set(LINUX FALSE)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
set(MACOSX TRUE)
set(SM_CPP_STANDARD "gnu++14")
else()
set(MACOSX FALSE)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "BSD")
set(BSD TRUE)
set(SM_CPP_STANDARD "gnu++11")
else()
set(BSD FALSE)
endif()
@@ -279,8 +282,8 @@ elseif(MACOSX)
# Apple Archs needs to be 32-bit for now.
# When SDL2 is introduced, this may change.
set(CMAKE_OSX_ARCHITECTURES "i386")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.6")
set(CMAKE_OSX_DEPLOYMENT_TARGET_FULL "10.6.8")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
set(CMAKE_OSX_DEPLOYMENT_TARGET_FULL "10.7.1")
find_library(MAC_FRAME_ACCELERATE Accelerate ${CMAKE_SYSTEM_FRAMEWORK_PATH})
find_library(MAC_FRAME_APPKIT AppKit ${CMAKE_SYSTEM_FRAMEWORK_PATH})
@@ -346,11 +349,18 @@ elseif(LINUX)
find_package(Dl)
find_package(Xrandr)
find_package(Xrandr REQUIRED)
if (${XRANDR_FOUND})
set(HAS_XRANDR TRUE)
else()
set(HAX_XRANDR FALSE)
set(HAS_XRANDR FALSE)
endif()
find_package(Xinerama)
if (${XINERAMA_FOUND})
set(HAS_XINERAMA TRUE)
else()
set(HAS_XINERAMA FALSE)
endif()
find_package(PulseAudio)
+2
View File
@@ -397,6 +397,7 @@ DelayedTextureDelete=Set to ON hold textures in memory instead of freeing them.
Delete=
Difficulty=
DisplayAspectRatio=Change the width-to-height ratio of graphics in the game. Most modern displays are either 16:9 or 16:10.
FullscreenType=Change whether fullscreen mode uses the display exclusively (traditional), or is a borderless window (nicer).
DisplayColorDepth=Choose the color depth of the window. This option may not take effect in windowed mode.
DisplayResolution=Choose the output resolution. A higher resolution will show more detail, but requires a faster video card.
Display Options=BGFit, Appearance and UI options.
@@ -1055,6 +1056,7 @@ DisplayAspectRatio=Aspect Ratio
DisplayColorDepth=Display Color
DisplayResolution=Display Resolution
Display Options=Display Options
FullscreenType=Fullscreen Type
Disqualification=Disqualification
Duration seconds=Duration seconds
EasterEggs=Easter Eggs
+25 -21
View File
@@ -1,6 +1,6 @@
-- Find a key in tab with the given value.
function FindValue(tab, value)
for key, name in tab do
for key, name in pairs(tab) do
if value == name then
return key
end
@@ -11,7 +11,7 @@ end
-- Return the index of a true value in list.
function FindSelection(list)
for index, on in list do
for index, on in ipairs(list) do
if on then
return index
end
@@ -32,7 +32,7 @@ end
function wrap(val,n)
local x = val
Trace( "wrap "..x.." "..n )
if x<0 then
if x<0 then
x = x + (math.ceil(-x/n)+1)*n
end
Trace( "adjusted "..x )
@@ -122,18 +122,26 @@ end
--Get the count of all items in a table
function table.itemcount(t)
local i = 0
while next(t)~=nil do
i=i+1
for _, v in pairs(t) do
i = i+1
end
return i
end
function math.round(num, pre)
if pre and pre < 0 then pre = 0 end
local mult = 10^(pre or 0)
if num >= 0 then return math.floor(num*mult+.5)/mult
local mult = 10^(pre or 0)
if num >= 0 then return math.floor(num*mult+.5)/mult
else return math.ceil(num*mult-.5)/mult end
end
function math.gcd(a, b)
while b ~= 0 do
a, b = b, math.mod(a, b)
end
return a
end
-- deprecated?
function round(val, decimal)
if decimal then
@@ -197,7 +205,7 @@ function ArgsIfPlayerJoinedOrNil(arg1,arg2)
if arg1==nil then arg1=arg2
elseif arg2==nil then arg2=arg1 end
return (GAMESTATE:IsSideJoined(PLAYER_1) and arg1 or nil),(GAMESTATE:IsSideJoined(PLAYER_2) and arg2 or nil)
end
end
function Center1Player()
local styleType = GAMESTATE:GetCurrentStyle():GetStyleType()
@@ -345,16 +353,13 @@ local function round(num, idp)
end
function IsUsingWideScreen()
local curAspect = round(GetScreenAspectRatio(),5);
for k,v in pairs(AspectRatios) do
if AspectRatios[k] == curAspect then
if k == "SixteenNine" or k == "SixteenTen" then
return true;
else return false;
end;
end;
end;
end;
local curAspect = GetScreenAspectRatio()
if math.abs(curAspect-16/9) <= .044 or math.abs(curAspect - 16/10) <= .044 then
return true
else
return false
end
end
-- Usage: Pass in an ActorFrame and a string to put in front of every line.
-- indent will be appended to at each level of the recursion, to indent each
@@ -511,7 +516,7 @@ end
-- (c) 2005-2011 Glenn Maynard, Chris Danford, SSC
-- All rights reserved.
--
--
-- Permission is hereby granted, free of charge, to any person obtaining a
-- copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
@@ -521,7 +526,7 @@ end
-- copyright notice(s) and this permission notice appear in all copies of
-- the Software and that both the above copyright notice(s) and this
-- permission notice appear in supporting documentation.
--
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
@@ -531,4 +536,3 @@ end
-- OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-- PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,614 @@
-- A shim to make an honest-to-goodness lua table (array) out of the
-- userdata DisplaySpecs we get from c++
local cachedSpecs = nil
local function GetDisplaySpecs()
if cachedSpecs == nil then
local specs = DISPLAY:GetDisplaySpecs()
t = {
ById= function(self, id)
for i, d in ipairs(self) do
if d:GetId() == id or
((id == '' or id == nil) and d:IsVirtual()) then
return self[i]
end
end
-- default to returning first DisplaySpec
return self[1]
end,
-- hold on to a reference to the userdata, so it isn't GC'd prematurely
c_specs= specs
}
for i = 1, #specs do
t[i] = specs[i]
end
cachedSpecs = t
end
return cachedSpecs
end
-- Ease lookup of OptionRow effects masks
local OE = OptEffect:Reverse()
function ConfDisplayMode()
local effectsMask = 2^OE["OptEffect_SavePreferences"]
effectsMask = effectsMask + 2^OE["OptEffect_ApplyGraphics"]
local specs = GetDisplaySpecs()
local choices = {}
for i, d in ipairs(specs) do
choices[i] = d:GetName()
end
local origDisplay = PREFSMAN:GetPreference("DisplayId")
local origWindowed = PREFSMAN:GetPreference("Windowed")
choices[#choices + 1] = "Windowed"
local t = {
Name= "Windowed",
GoToFirstOnStart= false,
OneChoiceForAllPlayers= true,
ExportOnChange= false,
LayoutType= "ShowAllInRow",
SelectType= "SelectOne",
Choices= choices,
LoadSelections= function(self, list, pn)
local windowed = PREFSMAN:GetPreference("Windowed")
local displayIdx = FindValue(specs, specs:ById(PREFSMAN:GetPreference("DisplayId")))
local i = windowed and #self.Choices or displayIdx
list[i] = true
end,
SaveSelections= function(self, list, pn)
local choice = FindSelection(list)
local selWindowed = choice == #self.Choices
local selDisplay = selWindowed and origDisplay or specs[choice]:GetId()
-- If we've changed from the established preferences in place at our
-- instantiation, then we'll return our effects
if selWindowed ~= origWindowed
or selDisplay ~= origDisplay then
return effectsMask
else
return 0
end
end,
NotifyOfSelection= function(self, pn, choice)
local curDisplay = PREFSMAN:GetPreference("DisplayId")
local curWindowed = PREFSMAN:GetPreference("Windowed")
local selWindowed = choice == #self.Choices
local selDisplay = selWindowed and curDisplay or specs[choice]:GetId()
-- If we've changed from what's currently saved in PREFSMAN,
-- then we'll broadcast that state
if selWindowed ~= curWindowed
or selDisplay ~= curDisplay then
PREFSMAN:SetPreference("Windowed", selWindowed)
PREFSMAN:SetPreference("DisplayId", selDisplay)
MESSAGEMAN:Broadcast("DisplayChoiceChanged")
end
end
}
return t
end
-- TODO: here, I'm replicating the AspectRatios table from "02 Utilities.lua",
-- but in a friendly format
local winFracs = {
{n=3, d=4},
{n=1, d=1},
{n=5, d=4},
{n=4, d=3},
{n=16, d=10},
{n=16, d=9},
{n=8, d=3}
}
-- Maximum distance between aspect ratios to be considered equivalent
-- (approximately half the distance between 16:9 and 16:10)
local RATIO_EPSILON = .044
local function GetWindowAspectRatios()
local ratios = {}
for i, f in ipairs(winFracs) do
ratios[math.round(f.n/f.d, 3)] = f
end
return ratios
end
-- build a map from DisplayId to supported aspect ratios
local function GetDisplayAspectRatios(specs)
local ratios = {}
local recognized = GetWindowAspectRatios()
for _, d in ipairs(specs) do
local dRatios = {}
for j, mode in ipairs(d:GetSupportedModes()) do
local trueRatio = mode:GetWidth()/mode:GetHeight()
local truncRatio = math.round(trueRatio, 3)
if not dRatios[truncRatio] then
local f = {}
-- Check if this is a known aspect ratio, or practically the
-- same (e.g. 1366x768 is typically considered 16:9)
for knownRatio, fraction in pairs(recognized) do
if math.abs(trueRatio - (fraction.n/fraction.d)) < RATIO_EPSILON then
truncRatio = knownRatio
f = fraction
break
end
end
-- If we didn't find a recognized ratio, then register a new ratio
-- as the reduced fraction of width / height
if not f.n then
local w = mode:GetWidth()
local h = mode:GetHeight()
local gcd = math.gcd(w, h)
dRatios[trueRatio] = {n=w/gcd, d=h/gcd}
else
dRatios[truncRatio] = f
end
end
end
ratios[d:GetId()] = dRatios
-- Allow looking up the virtual display's aspect ratio by empty string
if d:IsVirtual() then
ratios[""] = dRatios
end
end
return ratios
end
-- hack, use as a space to share value between ConfAspectRatio() and
-- ConfDisplayResoltuion(). Can't use PREFSMAN:SetPreference("DisplayAspectRatio", x)
-- because that change take effect *immediately* (it is read in the rendering loop)
local CurAspectRatio = 0
function ConfAspectRatio()
local effectsMask = 2^OE["OptEffect_SavePreferences"]
effectsMask = effectsMask + 2^OE["OptEffect_ApplyGraphics"]
effectsMask = effectsMask + 2^OE["OptEffect_ApplyAspectRatio"]
local specs = GetDisplaySpecs()
local origAspectRatio = PREFSMAN:GetPreference("DisplayAspectRatio")
CurAspectRatio = origAspectRatio
local dRatios = GetDisplayAspectRatios(specs)
local wRatios = GetWindowAspectRatios()
local t = {
Name= "DisplayAspectRatio",
GoToFirstOnStart= false,
OneChoiceForAllPlayers= true,
ExportOnChange= false,
LayoutType= "ShowAllInRow",
SelectType= "SelectOne",
ReloadRowMessages= {"DisplayChoiceChanged"},
LoadSelections= function(self, list, pn)
local r = CurAspectRatio
for i, v in ipairs(self.ChoiceVals) do
if math.abs(CurAspectRatio - v) < RATIO_EPSILON then
list[i] = true
break
end
end
end,
SaveSelections= function(self, list, pn)
local i = FindSelection(list)
PREFSMAN:SetPreference("DisplayAspectRatio", self.ChoiceVals[i])
if math.abs(self.ChoiceVals[i] - origAspectRatio) < RATIO_EPSILON then
return 0
else
return effectsMask
end
end,
NotifyOfSelection= function(self, pn, choice)
local selRatio = self.ChoiceVals[choice]
if math.abs(selRatio-CurAspectRatio) >= RATIO_EPSILON then
CurAspectRatio = selRatio
MESSAGEMAN:Broadcast("AspectRatioChoiceChanged")
end
end,
Reload= function(self)
local origVals = self.ChoiceVals
self:GenChoices()
-- Pass along the message to ConfDisplayResolution()
-- regardless, but important we do this *after* self:GenChoices()
-- to ensure we've updated the CurAspectRatio in accordance
-- with what this display supports
MESSAGEMAN:Broadcast("AspectRatioChoiceChanged")
if #origVals ~= #self.ChoiceVals then
return "ReloadChanged_All"
end
for i, v in ipairs(origVals) do
if origVals[i] ~= self.ChoiceVals[i] then
return "ReloadChanged_All"
end
end
return "ReloadChanged_None"
end,
GenChoices= function(self)
-- Determine the available aspect ratios for the current display mode
local isWindowed = PREFSMAN:GetPreference("Windowed")
local curDisplayId = PREFSMAN:GetPreference("DisplayId")
local ratios = isWindowed and wRatios or (dRatios[curDisplayId] or dRatios[specs[1]:GetId()])
local choices = {}
local vals = {}
foreach_ordered(ratios, function(v, f)
vals[#vals + 1] = f.n/f.d
choices[#choices + 1] = tostring(f.n) .. ':' .. tostring(f.d)
end)
self.Choices = choices
self.ChoiceVals = vals
-- Get the closest aspect ratio to the currently-configured one
-- that this display mode supports
local closestRatio = vals[1]
local closestDist = math.abs(vals[1] - CurAspectRatio)
for _, v in ipairs(vals) do
local dist = math.abs(v - CurAspectRatio)
if dist < closestDist then
closestRatio = v
closestDist = dist
end
end
CurAspectRatio = closestRatio
end
}
t:GenChoices()
return t
end
local function GenerateFeasibleWindowSizesForRatio(specs, r)
-- Get the largest feasible window size *for a single monitor*,
-- then generate a sequence of smaller window sizes, until
-- one of height/width < 300 (arbitrary)
-- TODO: Get fancy and consider larger rectangles that span
-- multiple monitors
local maxWinWidth = 0
for _, d in ipairs(specs) do
local modes = d:GetSupportedModes()
local dLargestMode = modes[#modes]
local w = dLargestMode:GetWidth()
local h = dLargestMode:GetHeight()
local dMaxWinWidth = w/r <= h and w or r*h
maxWinWidth = dMaxWinWidth > maxWinWidth and dMaxWinWidth or maxWinWidth
end
-- Generate successively smaller rectangles
local sizes = {}
local w = maxWinWidth
local h = maxWinWidth/r
while w > 300 and h > 300 do
sizes[#sizes + 1] = {w=math.round(w), h=math.round(h)}
w = 0.75*w
h = w/r
end
-- Reverse the list (sort in increasing size)
local rsizes = {}
for i = #sizes, 1, -1 do
rsizes[i] = sizes[#sizes - i + 1]
end
return rsizes
end
local function GetFeasibleWindowSizesForRatio(specs, r)
-- First, try to see if we have a sufficiently large set of
-- resolutions to pick from in the list(s) of supported modes for the display(s)
-- (since those resolutions tend to be "natural" looking), otherwise just generate
-- some window sizes (which might look weird).
local widths = {}
for _, d in ipairs(specs) do
for _, m in ipairs(d:GetSupportedModes()) do
local w = m:GetWidth()
local h = m:GetHeight()
if math.abs((w/h) - r) < RATIO_EPSILON then
widths[w] = h
end
end
end
if table.itemcount(widths) < 4 then
return GenerateFeasibleWindowSizesForRatio(specs, r)
else
local resolutions = {}
for w, h in pairs(widths) do
resolutions[#resolutions + 1] = {w=w, h=h}
end
table.sort(resolutions, function (a, b) return a.w < b.w end)
return resolutions
end
end
local function GetDisplayResolutionsForRatio(d, r)
local sizes = {}
for _, m in ipairs(d:GetSupportedModes()) do
local w = m:GetWidth()
local h = m:GetHeight()
local trueRatio = w/h
if math.abs(trueRatio - r) < RATIO_EPSILON then
-- This depends on the fact that GetSupportedModes returns
-- a *sorted* list
if #sizes == 0 or sizes[#sizes].w ~= w or sizes[#sizes].h ~= h then
sizes[#sizes + 1] = {w=m:GetWidth(), h=m:GetHeight()}
end
end
end
return sizes
end
function ConfDisplayResolution()
local effectsMask = 2^OE["OptEffect_SavePreferences"]
effectsMask = effectsMask + 2^OE["OptEffect_ApplyGraphics"]
effectsMask = effectsMask + 2^OE["OptEffect_ApplyAspectRatio"]
local specs = GetDisplaySpecs()
local origWidth = PREFSMAN:GetPreference("DisplayWidth")
local origHeight = PREFSMAN:GetPreference("DisplayHeight")
local t = {
Name= "DisplayResolution",
GoToFirstOnStart= false,
OneChoiceForAllPlayers= true,
ExportOnChange= false,
LayoutType= "ShowAllInRow",
SelectType= "SelectOne",
ReloadRowMessages= {"AspectRatioChoiceChanged"},
LoadSelections= function(self, list, pn)
local w = PREFSMAN:GetPreference("DisplayWidth")
local h = PREFSMAN:GetPreference("DisplayHeight")
for i, v in ipairs(self.ChoiceVals) do
if v.w == w and v.h == h then
list[i] = true
break
end
end
end,
SaveSelections= function(self, list, pn)
local i = FindSelection(list)
local w = self.ChoiceVals[i].w
local h = self.ChoiceVals[i].h
PREFSMAN:SetPreference("DisplayWidth", w)
PREFSMAN:SetPreference("DisplayHeight", h)
if w == origWidth and h == origHeight then
return 0
else
return effectsMask
end
end,
NotifyOfSelection= function(self, pn, choice)
local selRes = self.ChoiceVals[choice]
local curWidth = PREFSMAN:GetPreference("DisplayWidth")
local curHeight = PREFSMAN:GetPreference("DisplayHeight")
if selRes.w ~= curWidth or selRes.h ~= curHeight then
PREFSMAN:SetPreference("DisplayWidth", selRes.w)
PREFSMAN:SetPreference("DisplayHeight", selRes.h)
MESSAGEMAN:Broadcast("DisplayResolutionChoiceChanged")
end
end,
Reload= function(self)
local origVals = self.ChoiceVals
self:GenChoices()
-- Pass along the message to ConfRefreshRate() regardless of
-- what happens here. Do this *after* GenChoices() so that
-- *if* currently configured resolution needed to change in response
-- to change in configured AspectRatio, ConfRefreshRate() can change
-- accordingly
MESSAGEMAN:Broadcast("DisplayResolutionChoiceChanged")
if #origVals ~= #self.ChoiceVals then
return "ReloadChanged_All"
end
for i, v in ipairs(origVals) do
if origVals[i] ~= self.ChoiceVals[i] then
return "ReloadChanged_All"
end
end
return "ReloadChanged_None"
end,
GenChoices= function(self)
local isWindowed = PREFSMAN:GetPreference("Windowed")
local curDisplay = specs:ById(PREFSMAN:GetPreference("DisplayId"))
local curRatio = CurAspectRatio ~= 0 and CurAspectRatio or PREFSMAN:GetPreference("DisplayAspectRatio")
local resolutions = isWindowed and GetFeasibleWindowSizesForRatio(specs, curRatio)
or GetDisplayResolutionsForRatio(curDisplay, curRatio)
local choices = {}
local vals = {}
for i, r in ipairs(resolutions) do
vals[#vals + 1] = r
choices[#choices + 1] = tostring(r.w) .. 'x' .. tostring(r.h)
end
self.Choices = choices
self.ChoiceVals = vals
-- If the currently configured resolution isn't available at this
-- aspect ratio, then pick the closest resolution
local w = PREFSMAN:GetPreference("DisplayWidth")
local h = PREFSMAN:GetPreference("DisplayHeight")
local closest = nil
local mindist = -1
for i, v in ipairs(vals) do
local dist = math.sqrt((v.w - w)^2 + (v.h - h)^2)
if mindist == -1 or dist < mindist then
mindist = dist
closest = v
end
end
PREFSMAN:SetPreference("DisplayWidth", closest.w)
PREFSMAN:SetPreference("DisplayHeight", closest.h)
end
}
t:GenChoices()
return t
end
function GetDisplayRatesForResolution(d, w, h)
local rates = {}
for _, m in ipairs(d:GetSupportedModes()) do
if m:GetWidth() == w and m:GetHeight() == h then
rates[#rates + 1] = math.round(m:GetRefreshRate())
end
end
return rates
end
function ConfRefreshRate()
local effectsMask = 2^OE["OptEffect_SavePreferences"]
effectsMask = effectsMask + 2^OE["OptEffect_ApplyGraphics"]
local specs = GetDisplaySpecs()
local origRate = PREFSMAN:GetPreference("RefreshRate")
local t = {
Name= "RefreshRate",
GoToFirstOnStart= false,
OneChoiceForAllPlayers= true,
ExportOnChange= false,
LayoutType= "ShowAllInRow",
SelectType= "SelectOne",
ReloadRowMessages= {"DisplayResolutionChoiceChanged"},
LoadSelections= function(self, list, pn)
local r = PREFSMAN:GetPreference("RefreshRate")
local i = FindValue(self.ChoiceVals, r)
list[i] = true
end,
SaveSelections= function(self, list, pn)
local i = FindSelection(list)
local r = self.ChoiceVals[i]
PREFSMAN:SetPreference("RefreshRate", r)
if r ~= origRate then
return effectsMask
else
return 0
end
end,
NotifyOfSelection= function(self, pn, choice)
local selRate = self.ChoiceVals[choice]
local curRate = PREFSMAN:GetPreference("RefreshRate")
if selRate ~= curRate then
PREFSMAN:SetPreference("RefreshRate", selRate)
end
end,
Reload= function(self)
local origVals = self.ChoiceVals
self:GenChoices()
if #origVals ~= #self.ChoiceVals then
return "ReloadChanged_All"
end
for i, v in ipairs(origVals) do
if origVals[i] ~= self.ChoiceVals[i] then
return "ReloadChanged_All"
end
end
return "ReloadChanged_None"
end,
GenChoices= function(self)
local isWindowed = PREFSMAN:GetPreference("Windowed")
local d = specs:ById(PREFSMAN:GetPreference("DisplayId"))
local w = PREFSMAN:GetPreference("DisplayWidth")
local h = PREFSMAN:GetPreference("DisplayHeight")
local rates = isWindowed and {}
or GetDisplayRatesForResolution(d, w, h)
local choices = {"Default"}
local choiceVals = {REFRESH_DEFAULT}
for i, r in ipairs(rates) do
choiceVals[#choiceVals + 1] = math.round(r)
choices[#choices + 1] = tostring(math.round(r))
end
self.Choices = choices
self.ChoiceVals = choiceVals
-- If the currently configured refresh rate isn't available at this
-- resolution, then pick the closest one (so long as it's within
-- a fixed threshold)
local curRate = PREFSMAN:GetPreference("RefreshRate")
local threshold = 10
local closestIdx = nil
local mindist = -1
for i, r in ipairs(choiceVals) do
local dist = math.abs(r - curRate)
if mindist == -1 or dist < mindist then
mindist = dist
closestIdx = i
end
end
local curRateIdx = mindist < threshold and closestIdx or 1
PREFSMAN:SetPreference("RefreshRate", choiceVals[curRateIdx])
end
}
t:GenChoices()
return t
end
function ConfFullscreenType()
local effectsMask = 2^OE["OptEffect_SavePreferences"]
effectsMask = effectsMask + 2^OE["OptEffect_ApplyGraphics"]
local FULLSCREEN_EXCLUSIVE = 0
local FULLSCREEN_BORDERLESS_WIN = 1
local fsbwSupported = DISPLAY:SupportsFullscreenBorderlessWindow()
local choiceVals = {FULLSCREEN_EXCLUSIVE}
local choices = {"Default"}
if fsbwSupported then
choices = {"Exclusive", "Borderless Window"}
choiceVals = {FULLSCREEN_EXCLUSIVE, FULLSCREEN_BORDERLESS_WIN}
end
local origFSType = PREFSMAN:GetPreference("FullscreenIsBorderlessWindow") and FULLSCREEN_BORDERLESS_WIN or FULLSCREEN_EXCLUSIVE
local t = {
Name= "FullscreenType",
GoToFirstOnStart= false,
OneChoiceForAllPlayers= true,
ExportOnChange= false,
LayoutType= "ShowAllInRow",
SelectType= "SelectOne",
Choices= choices,
ChoiceVals= choiceVals,
LoadSelections= function(self, list, pn)
local fsType = PREFSMAN:GetPreference("FullscreenIsBorderlessWindow") and FULLSCREEN_BORDERLESS_WIN
or FULLSCREEN_EXCLUSIVE
local i = FindValue(self.ChoiceVals, fsType)
list[i] = true
end,
SaveSelections= function(self, list, pn)
local selFSType = self.ChoiceVals[FindSelection(list)]
PREFSMAN:SetPreference("FullscreenIsBorderlessWindow", selFSType == FULLSCREEN_BORDERLESS_WIN)
if selFSType ~= origFSType then
return effectsMask
else
return 0
end
end
}
return t
end
+7 -4
View File
@@ -3051,10 +3051,12 @@ Line22="conf,EventMode"
Fallback="ScreenOptionsServiceChild"
NextScreen="ScreenOptionsService"
PrevScreen="ScreenOptionsService"
LineNames="1,2,3,4,5,6,7,8,9,10,11,12,13,FNR,14,17,18,19,20"
Line1="conf,Windowed"
Line2="conf,DisplayResolution"
Line3="conf,DisplayAspectRatio"
LineNames="1,2,3,RefreshRate,FSType,4,5,6,7,8,9,10,11,13,FNR,14,17,18,19,20"
Line1="lua,ConfDisplayMode()"
Line2="lua,ConfAspectRatio()"
Line3="lua,ConfDisplayResolution()"
LineRefreshRate="lua,ConfRefreshRate()"
LineFSType="lua,ConfFullscreenType()"
Line4="conf,DisplayColorDepth"
Line5="conf,HighResolutionTextures"
Line6="conf,MaxTextureResolution"
@@ -3063,6 +3065,7 @@ Line8="conf,MovieColorDepth"
Line9="conf,SmoothLines"
Line10="conf,CelShadeModels"
Line11="conf,DelayedTextureDelete"
# RefreshRate ConfOption no longer used
Line12="conf,RefreshRate"
Line13="conf,Vsync"
LineFNR="conf,FastNoteRendering"
+26 -6
View File
@@ -33,9 +33,7 @@ AC_DEFUN([SM_X11],
fi
# Check for Xrandr
# Can someone fix this for me? This is producing bizarre warnings from
# configure... I have no clue what I'm doing -Ben
AC_CHECK_LIB(Xrandr, XRRSizes,
AC_CHECK_LIB(Xrandr, XRRQueryVersion,
have_xrandr=yes,
have_xrandr=no,
[$XLIBS])
@@ -45,14 +43,36 @@ AC_DEFUN([SM_X11],
have_xrandr=no
fi
if test "$have_xrandr" = "no"; then
# Check for Xxf86vm
AC_CHECK_LIB(Xxf86vm, XF86VidModeSwitchToMode,
have_xf86vm=yes,
have_xf86vm=no,
[$XLIBS])
AC_CHECK_HEADER(X11/extensions/xf86vmode.h, have_xf86vm_header=yes, have_xf86vm_header=no, [#include <X11/Xlib.h>])
if test "$have_xf86vm_header" = "no"; then
have_xf86vm=no
fi
no_modeset=yes
if test "$have_xrandr" = "yes"; then
XLIBS="$XLIBS -lXrandr"
AC_DEFINE(HAVE_XRANDR, 1, [Xrandr is available])
no_modeset=no
fi
if test "$have_xf86vm" = "yes"; then
XLIBS="$XLIBS -lXxf86vm"
AC_DEFINE(HAVE_XF86VIDMODE, 1, [XF86VidMode is available])
no_modeset=no
fi
if test "$no_modeset" = "yes"; then
if test "$unix" = "yes"; then
AC_MSG_ERROR("Couldn't find X11 libraries.")
else
no_x=yes
fi
else
XLIBS="$XLIBS -lXrandr"
fi
AM_CONDITIONAL(HAVE_X11, test "$no_x" != "yes")
+14 -4
View File
@@ -20,11 +20,21 @@ add_library("jsoncpp" STATIC ${JSON_SRC} ${JSON_HPP})
set_property(TARGET "jsoncpp" PROPERTY FOLDER "External Libraries")
if(MSVC)
sm_add_compile_definition("jsoncpp" _CRT_SECURE_NO_WARNINGS)
endif()
disable_project_warnings("jsoncpp")
target_include_directories("jsoncpp" PUBLIC "jsoncpp/include")
if(MSVC)
sm_add_compile_definition("jsoncpp" _CRT_SECURE_NO_WARNINGS)
elseif(APPLE)
set_target_properties("jsoncpp" PROPERTIES
XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "gnu++14"
XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++"
)
else() # Unix/Linux
sm_add_compile_flag("jsoncpp" "-std=gnu++11")
if (CMAKE_CXX_COMPILER MATCHES "clang")
sm_add_compile_flag("jsoncpp" "-stdlib=libc++")
endif()
endif()
+73
View File
@@ -258,6 +258,79 @@ Actor::Actor( const Actor &cpy ):
#undef CPY
}
Actor &Actor::operator=(Actor other)
{
/* Don't copy an Actor in the middle of rendering. */
ASSERT( other.m_pTempState == nullptr );
m_pTempState = nullptr;
using std::swap;
#define SWAP(x) swap(x, other.x)
SWAP( m_sName );
SWAP( m_pParent );
SWAP( m_FakeParent );
SWAP( m_pLuaInstance );
SWAP( m_WrapperStates );
SWAP( m_baseRotation );
SWAP( m_baseScale );
SWAP( m_fBaseAlpha );
SWAP( m_internalDiffuse );
SWAP( m_internalGlow );
SWAP( m_size );
SWAP( m_current );
SWAP( m_start );
SWAP( m_Tweens );
SWAP( m_bFirstUpdate );
SWAP( m_fHorizAlign );
SWAP( m_fVertAlign );
#if defined(SSC_FUTURES)
SWAP( M_Effects );
#else
SWAP( m_Effect );
#endif
SWAP( m_fSecsIntoEffect );
SWAP( m_fEffectDelta );
SWAP(m_effect_ramp_to_half);
SWAP(m_effect_hold_at_half);
SWAP(m_effect_ramp_to_full);
SWAP(m_effect_hold_at_full);
SWAP(m_effect_hold_at_zero);
SWAP(m_effect_period);
SWAP( m_fEffectOffset );
SWAP( m_EffectClock );
SWAP( m_effectColor1 );
SWAP( m_effectColor2 );
SWAP( m_vEffectMagnitude );
SWAP( m_bVisible );
SWAP( m_fHibernateSecondsLeft );
SWAP( m_fShadowLengthX );
SWAP( m_fShadowLengthY );
SWAP( m_ShadowColor );
SWAP( m_bIsAnimating );
SWAP( m_iDrawOrder );
SWAP( m_bTextureWrapping );
SWAP( m_bTextureFiltering );
SWAP( m_BlendMode );
SWAP( m_bClearZBuffer );
SWAP( m_ZTestMode );
SWAP( m_bZWrite );
SWAP( m_fZBias );
SWAP( m_CullMode );
SWAP( m_mapNameToCommands );
#undef SWAP
return *this;
}
/* XXX: This calls InitCommand, which must happen after all other
* initialization (eg. ActorFrame loading children). However, it
* also loads input variables, which should happen first. The
+1
View File
@@ -111,6 +111,7 @@ public:
* @brief Copy a new Actor to the old one.
* @param cpy the new Actor to use in place of this one. */
Actor( const Actor &cpy );
Actor &operator=( Actor other );
virtual ~Actor();
virtual Actor *Copy() const;
virtual void InitState();
+2 -1
View File
@@ -167,6 +167,7 @@ list(APPEND SM_DATA_REST_SRC
"CryptHelpers.cpp"
"DateTime.cpp"
"Difficulty.cpp"
"DisplaySpec.cpp"
"EnumHelper.cpp"
"FileDownload.cpp"
"Game.cpp"
@@ -218,7 +219,7 @@ list(APPEND SM_DATA_REST_HPP
"CryptHelpers.h"
"CubicSpline.h"
"DateTime.h"
"DisplayResolutions.h"
"DisplaySpec.h"
"Difficulty.h"
"EnumHelper.h"
"FileDownload.h"
+6
View File
@@ -8,6 +8,12 @@ add_library("GtkModule"
"arch/LoadingWindow/LoadingWindow_GtkModule.h"
)
sm_add_compile_flag("GtkModule" "-std=${SM_CPP_STANDARD}")
if (CMAKE_CXX_COMPILER MATCHES "clang")
sm_add_compile_flag("GtkModule" "-stdlib=libc++")
set_target_properties("GtkModule" PROPERTIES LINK_FLAGS "-stdlib=libc++")
endif()
# It is normally not appropriate to set the prefix to the empty string.
# This is to maintain compatibility with the current source.
# At some point, it may be worth being more flexible.
+25 -8
View File
@@ -165,7 +165,7 @@ if(WIN32)
sm_add_compile_definition("${SM_EXE_NAME}" _CRT_SECURE_NO_WARNINGS)
sm_add_compile_definition("${SM_EXE_NAME}" _WINSOCK_DEPRECATED_NO_WARNINGS)
sm_add_compile_definition("${SM_EXE_NAME}" GLEW_STATIC)
set_target_properties("${SM_EXE_NAME}" PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${SM_PROGRAM_DIR}"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${SM_PROGRAM_DIR}"
@@ -173,7 +173,7 @@ if(WIN32)
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${SM_PROGRAM_DIR}"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${SM_PROGRAM_DIR}"
)
if(MSVC)
# Allow for getting a virtualdub stack trace.
add_custom_command(TARGET "${SM_EXE_NAME}"
@@ -190,6 +190,8 @@ elseif(APPLE)
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${SM_ROOT_DIR}"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${SM_ROOT_DIR}"
MACOSX_BUNDLE_INFO_PLIST "${SM_XCODE_DIR}/Info.StepMania.plist"
XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "${SM_CPP_STANDARD}"
XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++"
XCODE_ATTRIBUTE_INFOPLIST_FILE "${SM_XCODE_DIR}/Info.StepMania.plist"
XCODE_ATTRIBUTE_INFOPLIST_PREPROCESS "YES"
XCODE_ATTRIBUTE_INFOPLIST_PREPROCESSOR_DEFINITIONS[variant=Release] "RELEASE"
@@ -198,6 +200,7 @@ elseif(APPLE)
XCODE_ATTRIBUTE_INFOPLIST_PREPROCESSOR_DEFINITIONS[variant=RelWithDebInfo] "RELWITHDEBINFO"
XCODE_ATTRIBUTE_INFOPLIST_PREFIX_HEADER "${SM_XCODE_DIR}/plistHelper.hpp"
XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/archutils/Darwin/StepMania.pch"
XCODE_ATTRIBUTE_GCC_ENABLE_CPP_EXCEPTIONS "NO"
XCODE_ATTRIBUTE_LIBRARY_SEARCH_PATHS "${SM_XCODE_DIR}/Libraries"
)
@@ -304,6 +307,13 @@ else() # Linux
if (${HAS_FFMPEG})
sm_add_compile_definition("${SM_EXE_NAME}" HAVE_FFMPEG)
endif()
if (${HAS_XRANDR})
sm_add_compile_definition("${SM_EXE_NAME}" HAVE_XRANDR)
endif()
if (${HAS_XINERAMA} AND WITH_XINERAMA)
sm_add_compile_definition("${SM_EXE_NAME}" HAVE_XINERAMA)
endif()
if (BSD)
sm_add_compile_definition("${SM_EXE_NAME}" BSD)
endif()
@@ -316,6 +326,12 @@ else() # Linux
if("${CMAKE_SYSTEM}" MATCHES "Linux")
sm_add_compile_definition("${SM_EXE_NAME}" LINUX)
endif()
sm_add_compile_flag("${SM_EXE_NAME}" "-std=${SM_CPP_STANDARD}")
if (CMAKE_CXX_COMPILER MATCHES "clang")
sm_add_compile_flag("${SM_EXE_NAME}" "-stdlib=libc++")
set_target_properties("${SM_EXE_NAME}" PROPERTIES LINK_FLAGS "-stdlib=libc++")
endif()
endif()
set_property(TARGET "${SM_EXE_NAME}" PROPERTY FOLDER "Internal Libraries")
@@ -346,7 +362,7 @@ if (NOT SYSTEM_PCRE_FOUND)
endif()
if (WIN32)
list(APPEND SMDATA_LINK_LIB
# The misc libraries are here.
"${LIB_SWSCALE}"
@@ -354,7 +370,7 @@ if (WIN32)
"${LIB_AVFORMAT}"
"${LIB_AVUTIL}"
)
if (WITH_OGG)
list(APPEND SMDATA_LINK_LIB
"ogg"
@@ -362,15 +378,15 @@ if (WIN32)
"vorbisfile"
)
endif()
list(APPEND SMDATA_LINK_LIB
"dbghelp.lib"
"setupapi.lib"
"hid.lib"
)
get_filename_component(DIRECTX_LIBRARY_DIR "${DIRECTX_LIBRARIES}" DIRECTORY)
sm_add_link_flag("${SM_EXE_NAME}" "/LIBPATH:\"${DIRECTX_LIBRARY_DIR}\"")
sm_add_link_flag("${SM_EXE_NAME}" "/LIBPATH:\"${SM_EXTERN_DIR}/ffmpeg/lib\"")
sm_add_link_flag("${SM_EXE_NAME}" "/LIBPATH:\"${SM_SRC_DIR}/archutils/Win32/ddk\"")
@@ -385,7 +401,7 @@ if (WIN32)
set_target_properties("${SM_EXE_NAME}" PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:msvcrt.lib")
set_target_properties("${SM_EXE_NAME}" PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS")
set_target_properties("${SM_EXE_NAME}" PROPERTIES LINK_FLAGS_MINSIZEREL "/SUBSYSTEM:WINDOWS")
elseif(APPLE)
# The following were removed from SMDATA_LINK_LIB to "match" StepMania's pbxproj.
# ${MAC_FRAME_APPKIT}
@@ -508,6 +524,7 @@ else() # Unix / Linux
list(APPEND SMDATA_LINK_LIB
${XRANDR_LIBRARIES}
${XINERAMA_LIBRARIES}
)
list(REMOVE_ITEM SMDATA_LINK_LIB "zlib")
+149
View File
@@ -0,0 +1,149 @@
#include "global.h"
#include "DisplaySpec.h"
#include "LuaBinding.h"
#include "RageLog.h"
class LunaDisplayMode: public Luna<DisplayMode>
{
public:
DEFINE_METHOD( GetWidth, width );
DEFINE_METHOD( GetHeight, height );
DEFINE_METHOD( GetRefreshRate, refreshRate );
LunaDisplayMode()
{
ADD_METHOD( GetWidth );
ADD_METHOD( GetHeight );
ADD_METHOD( GetRefreshRate );
}
};
LUA_REGISTER_CLASS( DisplayMode )
class LunaDisplaySpec: public Luna<DisplaySpec>
{
public:
DEFINE_METHOD( GetId, id() );
DEFINE_METHOD( GetName, name() );
DEFINE_METHOD( IsVirtual, isVirtual() );
static int GetSupportedModes( T* p, lua_State *L)
{
std::vector<DisplayMode*> v;
for (auto const &m: p->supportedModes())
{
v.push_back( const_cast<DisplayMode*>(&m));
}
LuaHelpers::CreateTableFromArray( v, L );
return 1;
}
static int GetCurrentMode( T *p, lua_State *L )
{
if (p->currentMode() != NULL) {
DisplayMode *m = const_cast<DisplayMode *>( p->currentMode() );
m->PushSelf( L );
} else {
lua_pushnil( L );
}
return 1;
}
LunaDisplaySpec()
{
ADD_METHOD( GetId );
ADD_METHOD( GetName );
ADD_METHOD( GetSupportedModes );
ADD_METHOD( IsVirtual );
ADD_METHOD( GetCurrentMode );
}
};
LUA_REGISTER_CLASS( DisplaySpec )
namespace
{
const char *DISPLAYSPECS = "DisplaySpecs";
DisplaySpecs *check_DisplaySpecs(lua_State *L)
{
void *ud = luaL_checkudata( L, 1, DISPLAYSPECS );
luaL_argcheck( L, ud != NULL, 1, "`DisplaySpecs` expected" );
return static_cast<DisplaySpecs *>(ud);
}
int DisplaySpecs_gc(lua_State *L)
{
DisplaySpecs *specs = static_cast<DisplaySpecs *> (lua_touserdata( L, 1 ));
if (specs)
{
specs->~DisplaySpecs();
}
return 0;
}
int DisplaySpecs_len(lua_State *L)
{
DisplaySpecs *specs = check_DisplaySpecs( L );
if (specs)
{
lua_pushinteger( L, specs->size() );
} else
{
lua_pushinteger( L, 0 );
}
return 1;
}
int DisplaySpecs_tostring(lua_State *L)
{
DisplaySpecs *specs = check_DisplaySpecs( L );
lua_pushfstring( L, "DisplaySpecs: %p", specs );
return 1;
}
int DisplaySpecs_get(lua_State *L)
{
DisplaySpecs *specs = check_DisplaySpecs( L );
if (specs)
{
int index = luaL_checkint( L, 2 );
luaL_argcheck( L, 1 <= index && static_cast<unsigned int> (index) <= specs->size(), 2,
"index out of range" );
DisplaySpecs::iterator it = specs->begin();
std::advance( it, index - 1 );
DisplaySpec *s = const_cast<DisplaySpec *>(&(*it));
s->PushSelf( L );
}
else
{
lua_pushnil( L );
}
return 1;
}
const luaL_Reg DisplaySpecs_meta[] =
{
{"__gc", DisplaySpecs_gc},
{"__index", DisplaySpecs_get},
{"__len", DisplaySpecs_len},
{"__tostring", DisplaySpecs_tostring},
{NULL, NULL}
};
void register_DisplaySpecs(lua_State *L)
{
luaL_newmetatable( L, DISPLAYSPECS );
luaL_openlib( L, 0, DisplaySpecs_meta, 0 );
lua_pop( L, 1 );
}
}
REGISTER_WITH_LUA_FUNCTION(register_DisplaySpecs);
DisplaySpecs *pushDisplaySpecs(lua_State *L, const DisplaySpecs &specs)
{
void *vpSpecs = lua_newuserdata( L, sizeof( DisplaySpecs ));
DisplaySpecs *pspecs = new( vpSpecs ) DisplaySpecs( specs );
luaL_getmetatable( L, DISPLAYSPECS );
lua_setmetatable( L, -2 );
return pspecs;
}
+203
View File
@@ -0,0 +1,203 @@
#ifndef DisplaySpec_H
#define DisplaySpec_H
#include <set>
#include "RageTypes.h"
struct DisplayMode {
// Width (in pixels) of the display in this mode
unsigned int width;
// Height (in pixels) of the display in this mode
unsigned int height;
/** Refresh rate, in hz, of the display for the given mode
* MacOS Quartz Display Services provides rate as double,
* winapi DEVMODE uses 32bit unsigned int (still gives rate as hz),
* RandR gives 32 bit pixel clock, which is then divided by width*height.
*/
double refreshRate;
/*
* Bits-per-pixel is *not* a property of the DisplayMode for our purposes:
* bit-depth is going to be a property of the OpenGL/D3D context, not a display
* configuration
*/
bool operator<( const DisplayMode &other ) const
{
/** @brief A quick way to compare the two DisplayResolutions. */
#define COMPARE(x) if( x != other.x ) return x < other.x;
COMPARE( width );
COMPARE( height );
COMPARE( refreshRate );
#undef COMPARE
return false;
}
// Lua
void PushSelf( lua_State *L );
};
/** @brief The dimensions of the program. */
class DisplaySpec
{
public:
/*
* Construct a specification for the display with the given ID, which supports the given modes,
* and is currently using the specified mode with the specified logical screen bounds
*/
DisplaySpec(const std::string &id,
const std::string &name,
const std::set<DisplayMode> &modes,
const DisplayMode &curMode,
const RectI &curBounds,
const bool isVirtual=false):
m_sId( id ), m_sName( name ),
m_sModes( modes ), m_bCurModeActive( true ), m_CurMode( curMode ),
m_rectBounds( curBounds ), m_bIsVirtual( isVirtual )
{
if ( m_sModes.find( curMode ) == m_sModes.end() )
{
// This is an error, make a failing assertion with a descriptive error message
std::stringstream msgStream;
msgStream << "DisplaySpec current mode (" << curMode.width << "x" <<
curMode.height << "@" << curMode.refreshRate << ") not in given list of supported modes: ";
for ( auto &m : modes )
{
msgStream << m.width << "x" << m.height << "@" << m.refreshRate << ", ";
}
auto msg = msgStream.str();
// Drop the trailing ", "
msg.resize( msg.size() - 2 );
ASSERT_M( false, msg.c_str() );
}
}
/*
* Construct a specification for the display with the given ID, which supports the given modes,
* and is currently disabled (has no active mode)
*/
DisplaySpec(const std::string id,
const std::string name,
const std::set<DisplayMode> modes,
const bool isVirtual=false):
m_sId( id ), m_sName( name ),
m_sModes( modes ), m_bCurModeActive( false ), m_CurMode( { } ),
m_bIsVirtual( isVirtual )
{
}
// Create a specification for a display supporting a single (and currently active) mode
DisplaySpec(std::string id, std::string name, DisplayMode mode) : m_sId( id ), m_sName( name ), m_bIsVirtual( false ),
m_bCurModeActive( true ), m_CurMode( mode )
{
m_sModes.insert( mode );
m_rectBounds = RectI( 0, 0, mode.width, mode.height );
}
DisplaySpec( const DisplaySpec &other ) = default;
std::string name() const
{
return m_sName;
}
std::string id() const
{
return m_sId;
}
const std::set<DisplayMode> &supportedModes() const
{
return m_sModes;
}
/*
* Return a pointer to the currently active display mode, or NULL if
* display is inactive
*
* Note that inactive *does not* necessarily mean unusable. E.g., in X11,
* an output can be enabled/disabled by an application by connecting/disconnecting
* a crtc
*/
const DisplayMode *currentMode() const
{
return m_bCurModeActive ? &m_CurMode : nullptr;
}
const RectI &currentBounds() const
{
return m_rectBounds;
}
bool isVirtual() const
{
return m_bIsVirtual;
}
/**
* @brief Determine if one DisplaySpec compares less than the other.
*
* Used to enforce a consistent ordering of displays, e.g. for consistent option
* presentation. Also allows DisplaySpec to be placed in a std::set
*
* @param other the other DisplaySpec to check.
* @return true if this DisplaySpec is less than the other, or false otherwise. */
bool operator<( const DisplaySpec &other ) const
{
return m_sId < other.id();
}
// Lua
void PushSelf( lua_State *L );
private:
// Unique identifier of the display
std::string m_sId;
// "Human-readable" display name
std::string m_sName;
// Modes supported by this display
std::set<DisplayMode> m_sModes;
// currently configured mode, if available
bool m_bCurModeActive;
DisplayMode m_CurMode;
// The current bounds of this display in global display coordinate space
RectI m_rectBounds;
// Flag is "true" when display represents a logical display like an X screen
// or the Win32 "Virtual screen"
bool m_bIsVirtual;
};
/** @brief The collection of DisplaySpec available within the program. */
typedef std::set<DisplaySpec> DisplaySpecs;
//Lua
DisplaySpecs *pushDisplaySpecs(lua_State *L, const DisplaySpecs &specs);
#endif
/**
* @file
* @author Chris Danford (c) 2001-2005
* @section LICENSE
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
+2
View File
@@ -82,9 +82,11 @@ namespace LuaHelpers
{
template<> void Push<bool>( lua_State *L, const bool &Object ) { lua_pushboolean( L, Object ); }
template<> void Push<float>( lua_State *L, const float &Object ) { lua_pushnumber( L, Object ); }
template<> void Push<double>( lua_State *L, const double &Object ) { lua_pushnumber( L, Object ); }
template<> void Push<int>( lua_State *L, const int &Object ) { lua_pushinteger( L, Object ); }
template<> void Push<unsigned int>( lua_State *L, const unsigned int &Object ) { lua_pushnumber( L, double(Object) ); }
template<> void Push<RString>( lua_State *L, const RString &Object ) { lua_pushlstring( L, Object.data(), Object.size() ); }
template<> void Push<std::string>( lua_State *L, std::string const& object ) { lua_pushlstring( L, object.data(), object.size() ); }
template<> bool FromStack<bool>( Lua *L, bool &Object, int iOffset ) { Object = !!lua_toboolean( L, iOffset ); return true; }
template<> bool FromStack<float>( Lua *L, float &Object, int iOffset ) { Object = (float)lua_tonumber( L, iOffset ); return true; }
+1 -1
View File
@@ -145,7 +145,7 @@ CreateZip.cpp CreateZip.h \
CryptHelpers.cpp CryptHelpers.h \
DateTime.cpp DateTime.h \
Difficulty.cpp Difficulty.h \
DisplayResolutions.h \
DisplaySpec.h \
EnumHelper.cpp EnumHelper.h \
FileDownload.cpp FileDownload.h \
Font.cpp Font.h \
+3 -3
View File
@@ -845,10 +845,10 @@ void OptionRow::Reload()
switch( m_pHand->Reload() )
{
case OptionRowHandler::RELOAD_CHANGED_NONE:
case RELOAD_CHANGED_NONE:
break;
case OptionRowHandler::RELOAD_CHANGED_ALL:
case RELOAD_CHANGED_ALL:
{
ChoicesChanged( m_RowType );
@@ -861,7 +861,7 @@ void OptionRow::Reload()
// fall through
}
case OptionRowHandler::RELOAD_CHANGED_ENABLED:
case RELOAD_CHANGED_ENABLED:
UpdateEnabledDisabled();
FOREACH_HumanPlayer( pn )
PositionUnderlines( pn );
+93 -17
View File
@@ -834,6 +834,7 @@ class OptionRowHandlerLua : public OptionRowHandler
public:
LuaReference *m_pLuaTable;
LuaReference m_EnabledForPlayersFunc;
LuaReference m_ReloadFunc;
bool m_TableIsSane;
bool m_GoToFirstOnStart;
@@ -941,6 +942,17 @@ public:
}
lua_pop(L, 1);
lua_getfield(L, -1, "Reload");
if(!lua_isnil(L, -1))
{
if(!lua_isfunction(L, -1))
{
LuaHelpers::ReportScriptErrorFmt("LUA_ERROR: \"%s\" \"Reload\" entry is not a function.", RowName.c_str());
return false;
}
}
lua_pop(L, 1);
lua_getfield(L, -1, "LoadSelections");
if(!lua_isfunction(L, -1))
{
@@ -1007,6 +1019,22 @@ public:
LUA->Release(L);
}
void LoadChoices( Lua *L )
{
// Iterate over the "Choices" table.
lua_getfield(L, -1, "Choices");
lua_pushnil( L );
while( lua_next(L, -2) != 0 )
{
// `key' is at index -2 and `value' at index -1
const char *pValue = lua_tostring( L, -1 );
//LOG->Trace( "choice: '%s'", pValue);
m_Def.m_vsChoices.push_back( pValue );
lua_pop( L, 1 ); // removes `value'; keeps `key' for next iteration
}
lua_pop( L, 1 ); // pop choices table
}
virtual bool LoadInternal( const Commands &cmds )
{
const Command &command = cmds.v[0];
@@ -1059,18 +1087,7 @@ public:
m_Def.m_selectType = StringToSelectType( pStr );
lua_pop( L, 1 );
// Iterate over the "Choices" table.
lua_getfield(L, -1, "Choices");
lua_pushnil( L );
while( lua_next(L, -2) != 0 )
{
// `key' is at index -2 and `value' at index -1
const char *pValue = lua_tostring( L, -1 );
//LOG->Trace( "choice: '%s'", pValue);
m_Def.m_vsChoices.push_back( pValue );
lua_pop( L, 1 ); // removes `value'; keeps `key' for next iteration
}
lua_pop( L, 1 ); // pop choices table
LoadChoices( L );
// Set the EnabledForPlayers function.
lua_getfield(L, -1, "EnabledForPlayers");
@@ -1093,6 +1110,10 @@ public:
}
lua_pop( L, 1 ); // pop ReloadRowMessages table
// Set the Reload function
lua_getfield(L, -1, "Reload");
m_ReloadFunc.SetFromStack( L );
lua_pop( L, 1 ); // pop main table
ASSERT( lua_gettop(L) == 0 );
@@ -1102,8 +1123,47 @@ public:
virtual ReloadChanged Reload()
{
if (!m_TableIsSane)
{
return RELOAD_CHANGED_NONE;
}
/* We'll always call SetEnabledForPlayers, and
* return at least RELOAD_CHANGED_ENABLED,
* to preserve original OptionRowHandlerLua behavior.
*
* Will also call the standard OptionRowHandler::Reload
* function to determine whether we should declare a full
* RELOAD_CHANGED_ALL
*/
ReloadChanged effect = RELOAD_CHANGED_ENABLED;
if (!m_ReloadFunc.IsNil())
{
Lua *L = LUA->Get();
m_ReloadFunc.PushSelf( L );
// Argument 1: (self)
m_pLuaTable->PushSelf( L );
RString error = "Reload: ";
LuaHelpers::RunScriptOnStack( L, error, 1, 1, true );
effect = std::max( effect, Enum::Check<ReloadChanged>( L, -1 ));
lua_pop( L, 1 );
if (effect == RELOAD_CHANGED_ALL)
{
m_Def.m_vsChoices.clear();
m_pLuaTable->PushSelf( L );
LoadChoices( L );
lua_pop( L, 1 );
ASSERT( lua_gettop(L) == 0 );
}
LUA->Release( L );
}
SetEnabledForPlayers();
return RELOAD_CHANGED_ENABLED;
return effect;
}
virtual void ImportOption( OptionRow *pRow, const vector<PlayerNumber> &vpns, vector<bool> vbSelectedOut[NUM_PLAYERS] ) const
@@ -1174,6 +1234,7 @@ public:
ASSERT( lua_gettop(L) == 0 );
int effects = 0;
FOREACH_CONST( PlayerNumber, vpns, pn )
{
PlayerNumber p = *pn;
@@ -1206,9 +1267,14 @@ public:
ASSERT( lua_gettop(L) == 6 ); // vbSelectedOut, m_iLuaTable, function, self, arg, arg
RString error= "SaveSelections: ";
LuaHelpers::RunScriptOnStack( L, error, 3, 0, true );
ASSERT( lua_gettop(L) == 2 );
LuaHelpers::RunScriptOnStack( L, error, 3, 1, true );
ASSERT( lua_gettop(L) == 3 ); // SaveSelections *may* return effects flags, otherwise nil
double ret = lua_tonumber( L, -1 );
ASSERT_M( (lua_isnumber( L, -1 ) && std::floor( ret ) == ret) || lua_isnil( L, -1 ),
"SaveSelections must return integer flags, or nill" );
effects |= static_cast<int>( ret );
lua_pop( L, 1 ); // pop effects
lua_pop( L, 1 ); // pop option table
lua_pop( L, 1 ); // pop vbSelected table
@@ -1217,8 +1283,7 @@ public:
LUA->Release(L);
// XXX: allow specifying the mask
return 0;
return effects;
}
virtual bool NotifyOfSelection(PlayerNumber pn, int choice)
{
@@ -1593,6 +1658,17 @@ OptionRowHandler* OptionRowHandlerUtil::MakeSimple( const MenuRowDef &mr )
return pHand;
}
// Expose ReloadChanged to Lua
static const char *ReloadChangedNames[] =
{
"None",
"Enabled",
"All"
};
XToString( ReloadChanged );
StringToX( ReloadChanged );
LuaXType( ReloadChanged );
/*
* (c) 2002-2004 Chris Danford
* All rights reserved.
+7 -1
View File
@@ -31,6 +31,13 @@ enum LayoutType
const RString& LayoutTypeToString( LayoutType pm );
LayoutType StringToLayoutType( const RString& s );
LuaDeclareType( LayoutType );
enum ReloadChanged
{
RELOAD_CHANGED_NONE, RELOAD_CHANGED_ENABLED, RELOAD_CHANGED_ALL, NUM_ReloadChanged, ReloadChanged_Invalid
};
const RString& ReloadChangedToString( ReloadChanged rc );
ReloadChanged StringToReloadChanged( const std::string& rc );
LuaDeclareType( ReloadChanged );
/** @brief Define the purpose of the OptionRow. */
struct OptionRowDefinition
@@ -169,7 +176,6 @@ public:
* graphic elements will also be reinitialized. If only m_vEnabledForPlayers
* has been changed, return RELOAD_CHANGED_ENABLED. If the row is static, and
* nothing has changed, return RELOAD_CHANGED_NONE. */
enum ReloadChanged { RELOAD_CHANGED_NONE, RELOAD_CHANGED_ENABLED, RELOAD_CHANGED_ALL };
virtual ReloadChanged Reload() { return RELOAD_CHANGED_NONE; }
virtual int GetDefaultOption() const { return -1; }
+3 -1
View File
@@ -166,6 +166,7 @@ PrefsManager::PrefsManager() :
m_sDefaultModifiers ( "DefaultModifiers", "" ),
m_bWindowed ( "Windowed", true ),
m_sDisplayId ( "DisplayId", "" ),
m_iDisplayWidth ( "DisplayWidth", 854 ),
m_iDisplayHeight ( "DisplayHeight", 480 ),
m_fDisplayAspectRatio ( "DisplayAspectRatio", 16/9.f, ValidateDisplayAspectRatio ),
@@ -178,6 +179,7 @@ PrefsManager::PrefsManager() :
m_iMaxTextureResolution ( "MaxTextureResolution", 2048 ),
m_iRefreshRate ( "RefreshRate", REFRESH_DEFAULT ),
m_bAllowMultitexture ( "AllowMultitexture", true ),
m_bFullscreenIsBorderlessWindow( "FullscreenIsBorderlessWindow", false ),
m_bShowStats ( "ShowStats", TRUE_IF_DEBUG),
m_bShowBanners ( "ShowBanners", true ),
m_bShowMouseCursor ( "ShowMouseCursor", true ),
@@ -379,7 +381,7 @@ void PrefsManager::StoreGamePrefs()
ASSERT( !m_sCurrentGame.Get().empty() );
// save off old values
GamePrefs &gp = m_mapGameNameToGamePrefs[m_sCurrentGame];
GamePrefs &gp = m_mapGameNameToGamePrefs[m_sCurrentGame.Get()];
gp.m_sAnnouncer = m_sAnnouncer;
gp.m_sTheme = m_sTheme;
gp.m_sDefaultModifiers = m_sDefaultModifiers;
+2
View File
@@ -158,6 +158,7 @@ protected:
public:
Preference<bool> m_bWindowed;
Preference<RString> m_sDisplayId;
Preference<int> m_iDisplayWidth;
Preference<int> m_iDisplayHeight;
Preference<float> m_fDisplayAspectRatio;
@@ -170,6 +171,7 @@ public:
Preference<int> m_iMaxTextureResolution;
Preference<int> m_iRefreshRate;
Preference<bool> m_bAllowMultitexture;
Preference<bool> m_bFullscreenIsBorderlessWindow;
Preference<bool> m_bShowStats;
Preference<bool> m_bShowBanners;
Preference<bool> m_bShowMouseCursor;
+1 -1
View File
@@ -230,7 +230,7 @@ bool ProfileManager::LoadLocalProfileFromMachine( PlayerNumber pn )
void ProfileManager::GetMemoryCardProfileDirectoriesToTry( vector<RString> &asDirsToTry )
{
/* Try to load the preferred profile. */
asDirsToTry.push_back( PREFSMAN->m_sMemoryCardProfileSubdir );
asDirsToTry.push_back( PREFSMAN->m_sMemoryCardProfileSubdir.Get() );
/* If that failed, try loading from all fallback directories. */
split( g_sMemoryCardProfileImportSubdirs, ";", asDirsToTry, true );
+57 -8
View File
@@ -12,7 +12,7 @@
#include "RageSurface.h"
#include "Preference.h"
#include "LocalizedString.h"
#include "DisplayResolutions.h"
#include "DisplaySpec.h"
#include "arch/ArchHooks/ArchHooks.h"
// Statistics stuff
@@ -90,17 +90,33 @@ RString RageDisplay::SetVideoMode( VideoModeParams p, bool &bNeedReloadTextures
vs.push_back( err );
// Fall back on a known resolution good rather than 640 x 480.
DisplayResolutions dr;
this->GetDisplayResolutions( dr );
DisplaySpecs dr;
this->GetDisplaySpecs(dr);
if( dr.empty() )
{
vs.push_back( "No display resolutions" );
return SETVIDEOMODE_FAILED.GetValue() + " " + join(";",vs);
}
const DisplayResolution &d = *dr.begin();
p.width = d.iWidth;
p.height = d.iHeight;
DisplaySpec d = *dr.begin();
// Try to find DisplaySpec corresponding to requested display
for (const auto &candidate: dr)
{
if (candidate.currentMode() != nullptr)
{
d = candidate;
if (candidate.id() == p.sDisplayId)
{
break;
}
}
}
p.sDisplayId = d.id();
const DisplayMode supported = d.currentMode() != nullptr ? *d.currentMode() : *d.supportedModes().begin();
p.width = supported.width;
p.height = supported.height;
p.rate = static_cast<int> (round(supported.refreshRate));
if( (err = this->TryVideoMode(p,bNeedReloadTextures)) == "" )
return RString();
vs.push_back( err );
@@ -714,8 +730,8 @@ void RageDisplay::ChangeCentering( int iTranslateX, int iTranslateY, int iAddWid
RageMatrix RageDisplay::GetCenteringMatrix( float fTranslateX, float fTranslateY, float fAddWidth, float fAddHeight ) const
{
// in screen space, left edge = -1, right edge = 1, bottom edge = -1. top edge = 1
float fWidth = (float) GetActualVideoModeParams().width;
float fHeight = (float) GetActualVideoModeParams().height;
float fWidth = (float) GetActualVideoModeParams().windowWidth;
float fHeight = (float) GetActualVideoModeParams().windowHeight;
float fPercentShiftX = SCALE( fTranslateX, 0, fWidth, 0, +2.0f );
float fPercentShiftY = SCALE( fTranslateY, 0, fHeight, 0, -2.0f );
float fPercentScaleX = SCALE( fAddWidth, 0, fWidth, 1.0f, 2.0f );
@@ -990,6 +1006,16 @@ void RageCompiledGeometry::Set( const vector<msMesh> &vMeshes, bool bNeedsNormal
// lua start
#include "LuaBinding.h"
// Register with Lua.
static void register_REFRESH_DEFAULT(lua_State *L)
{
lua_pushstring( L, "REFRESH_DEFAULT" );
lua_pushinteger( L, REFRESH_DEFAULT );
lua_settable( L, LUA_GLOBALSINDEX);
}
REGISTER_WITH_LUA_FUNCTION( register_REFRESH_DEFAULT );
/** @brief Allow Lua to have access to the RageDisplay. */
class LunaRageDisplay: public Luna<RageDisplay>
{
@@ -1026,6 +1052,26 @@ public:
return 1;
}
static int GetDisplaySpecs( T* p, lua_State *L )
{
DisplaySpecs s;
p->GetDisplaySpecs(s);
pushDisplaySpecs(L, s);
return 1;
}
static int SupportsRenderToTexture( T* p, lua_State *L )
{
lua_pushboolean(L, p->SupportsRenderToTexture());
return 1;
}
static int SupportsFullscreenBorderlessWindow( T* p, lua_State *L )
{
lua_pushboolean(L, p->SupportsFullscreenBorderlessWindow());
return 1;
}
LunaRageDisplay()
{
ADD_METHOD( GetDisplayWidth );
@@ -1033,6 +1079,9 @@ public:
ADD_METHOD( GetFPS );
ADD_METHOD( GetVPF );
ADD_METHOD( GetCumFPS );
ADD_METHOD( GetDisplaySpecs );
ADD_METHOD( SupportsRenderToTexture );
ADD_METHOD( SupportsFullscreenBorderlessWindow);
}
};
+60 -9
View File
@@ -7,8 +7,8 @@
#include "ModelTypes.h"
#include <set>
class DisplayResolution;
typedef set<DisplayResolution> DisplayResolutions;
class DisplaySpec;
typedef std::set<DisplaySpec> DisplaySpecs;
const int REFRESH_DEFAULT = 0;
struct RageSurface;
@@ -79,6 +79,7 @@ public:
// are filled (in case new params are added).
VideoModeParams(
bool windowed_,
RString sDisplayId_,
int width_,
int height_,
int bpp_,
@@ -88,12 +89,14 @@ public:
bool bSmoothLines_,
bool bTrilinearFiltering_,
bool bAnisotropicFiltering_,
bool bWindowIsFullscreenBorderless_,
RString sWindowTitle_,
RString sIconFile_,
bool PAL_,
float fDisplayAspectRatio_
):
windowed(windowed_),
sDisplayId(sDisplayId_),
width(width_),
height(height_),
bpp(bpp_),
@@ -103,18 +106,36 @@ public:
bSmoothLines(bSmoothLines_),
bTrilinearFiltering(bTrilinearFiltering_),
bAnisotropicFiltering(bAnisotropicFiltering_),
bWindowIsFullscreenBorderless(bWindowIsFullscreenBorderless_),
sWindowTitle(sWindowTitle_),
sIconFile(sIconFile_),
PAL(PAL_),
fDisplayAspectRatio(fDisplayAspectRatio_) {}
VideoModeParams(const VideoModeParams &other):
windowed(other.windowed), sDisplayId(other.sDisplayId),
width(other.width), height(other.height),
bpp(other.bpp), rate(other.rate),
vsync(other.vsync), interlaced(other.interlaced),
bSmoothLines(other.bSmoothLines), bTrilinearFiltering(other.bTrilinearFiltering),
bAnisotropicFiltering(other.bAnisotropicFiltering), bWindowIsFullscreenBorderless(other.bWindowIsFullscreenBorderless),
sWindowTitle(other.sWindowTitle), sIconFile(other.sIconFile),
PAL(other.PAL), fDisplayAspectRatio(other.fDisplayAspectRatio)
{}
VideoModeParams(): windowed(false), width(0), height(0),
bpp(0), rate(0), vsync(false), interlaced(false),
bSmoothLines(false), bTrilinearFiltering(false),
bAnisotropicFiltering(false), sWindowTitle(RString()),
sIconFile(RString()), PAL(false), fDisplayAspectRatio(0.0f) {}
bpp(0), rate(0), vsync(false), interlaced(false),
bSmoothLines(false), bTrilinearFiltering(false),
bAnisotropicFiltering(false), bWindowIsFullscreenBorderless(false),
sWindowTitle(RString()), sIconFile(RString()),
PAL(false), fDisplayAspectRatio(0.0f) {}
// Subclassing VideoModeParams in ActualVideoModeParams. Make destructor virtual just in case
// someone tries to delete one of those through a pointer to base...
virtual ~VideoModeParams() {}
bool windowed;
RString sDisplayId;
int width;
int height;
int bpp;
@@ -124,12 +145,41 @@ public:
bool bSmoothLines;
bool bTrilinearFiltering;
bool bAnisotropicFiltering;
bool bWindowIsFullscreenBorderless;
RString sWindowTitle;
RString sIconFile;
bool PAL;
float fDisplayAspectRatio;
};
/**
* @brief The _actual_ VideoModeParams determined by the LowLevelWindow implementation.
* Contains all the attributes of VideoModeParams, plus the actual window width/height determined by
* LLW
*/
class ActualVideoModeParams: public VideoModeParams
{
public:
ActualVideoModeParams(): VideoModeParams(), windowWidth(0), windowHeight(0), renderOffscreen(false) {}
ActualVideoModeParams( const VideoModeParams &params ) : VideoModeParams( params ),
windowWidth( params.width ),
windowHeight( params.height ),
renderOffscreen( false )
{ }
ActualVideoModeParams( const VideoModeParams &params, int windowWidth, int windowHeight, bool renderOffscreen ) :
VideoModeParams( params ), windowWidth( windowWidth ), windowHeight( windowHeight ),
renderOffscreen( renderOffscreen )
{ }
ActualVideoModeParams (const ActualVideoModeParams &other) = default;
// If bWindowIsFullscreenBorderless is true,
// then these properties will differ from width/height (which describe the
// render size)
int windowWidth;
int windowHeight;
bool renderOffscreen;
};
struct RenderTargetParam
{
RenderTargetParam():
@@ -181,7 +231,7 @@ public:
virtual RString Init( const VideoModeParams &p, bool bAllowUnacceleratedRenderer ) = 0;
virtual RString GetApiDescription() const = 0;
virtual void GetDisplayResolutions( DisplayResolutions &out ) const = 0;
virtual void GetDisplaySpecs(DisplaySpecs &out) const = 0;
// Don't override this. Override TryVideoMode() instead.
// This will set the video mode to be as close as possible to params.
@@ -193,7 +243,7 @@ public:
virtual bool BeginFrame();
virtual void EndFrame();
virtual VideoModeParams GetActualVideoModeParams() const = 0;
virtual ActualVideoModeParams GetActualVideoModeParams() const = 0;
bool IsWindowed() const { return this->GetActualVideoModeParams().windowed; }
virtual void SetBlendMode( BlendMode mode ) = 0;
@@ -235,7 +285,8 @@ public:
virtual void SetEffectMode( EffectMode ) { }
virtual bool IsEffectModeSupported( EffectMode effect ) { return effect == EffectMode_Normal; }
bool SupportsRenderToTexture() const { return false; }
virtual bool SupportsRenderToTexture() const { return false; }
virtual bool SupportsFullscreenBorderlessWindow() const { return false; }
/* Create a render target, returning a texture handle. In addition to normal
* texture functions, this can be passed to SetRenderTarget. Delete with
+29 -10
View File
@@ -10,7 +10,7 @@
#include "RageSurface.h"
#include "RageSurfaceUtils.h"
#include "EnumHelper.h"
#include "DisplayResolutions.h"
#include "DisplaySpec.h"
#include "LocalizedString.h"
#include <D3dx9tex.h>
@@ -271,19 +271,39 @@ RageDisplay_D3D::~RageDisplay_D3D()
}
}
void RageDisplay_D3D::GetDisplayResolutions( DisplayResolutions &out ) const
void RageDisplay_D3D::GetDisplaySpecs( DisplaySpecs &out ) const
{
out.clear();
int iCnt = g_pd3d->GetAdapterModeCount( D3DADAPTER_DEFAULT, g_DefaultAdapterFormat );
for( int i = 0; i < iCnt; ++i )
std::set<DisplayMode> modes;
D3DDISPLAYMODE mode;
for ( int i = 0; i < iCnt; ++i )
{
D3DDISPLAYMODE mode;
g_pd3d->EnumAdapterModes( D3DADAPTER_DEFAULT, g_DefaultAdapterFormat, i, &mode );
DisplayResolution res = { mode.Width, mode.Height };
out.insert( res );
modes.insert( { mode.Width, mode.Height, static_cast<double> (mode.RefreshRate) } );
}
// Get the current display mode
if ( g_pd3d->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &mode ) == D3D_OK )
{
D3DADAPTER_IDENTIFIER9 ID;
g_pd3d->GetAdapterIdentifier( D3DADAPTER_DEFAULT, 0, &ID );
DisplayMode active = { mode.Width, mode.Height, static_cast<double> (mode.RefreshRate) };
RectI bounds( 0, 0, active.width, active.height );
out.insert( DisplaySpec( "", "Fullscreen", modes, active, bounds ) );
}
else
{
LOG->Warn( "Could not find active mode for default D3D adapter" );
if ( !modes.empty() )
{
const DisplayMode &m = *modes.begin();
RectI bounds( 0, 0, m.width, m.height );
out.insert( DisplaySpec( "", "Fullscreen", modes, m, bounds ) );
}
}
}
D3DFORMAT FindBackBufferType(bool bWindowed, int iBPP)
@@ -677,10 +697,9 @@ RageSurface* RageDisplay_D3D::CreateScreenshot()
return result;
}
VideoModeParams RageDisplay_D3D::GetActualVideoModeParams() const
ActualVideoModeParams RageDisplay_D3D::GetActualVideoModeParams() const
{
VideoModeParams p = GraphicsWindow::GetParams();
return p;
return GraphicsWindow::GetParams();
}
void RageDisplay_D3D::SendCurrentMatrices()
+2 -2
View File
@@ -11,13 +11,13 @@ public:
virtual RString Init( const VideoModeParams &p, bool bAllowUnacceleratedRenderer );
virtual RString GetApiDescription() const { return "D3D"; }
virtual void GetDisplayResolutions( DisplayResolutions &out ) const;
virtual void GetDisplaySpecs( DisplaySpecs &out ) const;
void ResolutionChanged();
const RagePixelFormatDesc *GetPixelFormatDesc(RagePixelFormat pf) const;
bool BeginFrame();
void EndFrame();
VideoModeParams GetActualVideoModeParams() const;
ActualVideoModeParams GetActualVideoModeParams() const;
void SetBlendMode( BlendMode mode );
bool SupportsTextureFormat( RagePixelFormat pixfmt, bool realtime=false );
bool SupportsThreadedRendering();
+4 -4
View File
@@ -11,7 +11,7 @@
#include "RageSurface.h"
#include "RageTextureManager.h"
#include "DisplayResolutions.h"
#include "DisplaySpec.h"
#include "arch/LowLevelWindow/LowLevelWindow.h"
@@ -444,10 +444,10 @@ RageDisplay_GLES2::~RageDisplay_GLES2()
}
void
RageDisplay_GLES2::GetDisplayResolutions( DisplayResolutions &out ) const
RageDisplay_GLES2::GetDisplaySpecs(DisplaySpecs &out) const
{
out.clear();
g_pWind->GetDisplayResolutions( out );
g_pWind->GetDisplaySpecs(out);
}
RageSurface*
@@ -517,7 +517,7 @@ RageDisplay_GLES2::GetApiDescription() const
return "OpenGL ES 2.0";
}
VideoModeParams
ActualVideoModeParams
RageDisplay_GLES2::GetActualVideoModeParams() const
{
return g_pWind->GetActualVideoModeParams();
+7 -7
View File
@@ -9,21 +9,21 @@ public:
virtual RString Init( const VideoModeParams &p, bool bAllowUnacceleratedRenderer );
virtual RString GetApiDescription() const;
virtual void GetDisplayResolutions( DisplayResolutions &out ) const;
virtual void GetDisplaySpecs(DisplaySpecs &out) const;
const RagePixelFormatDesc *GetPixelFormatDesc(RagePixelFormat pf) const;
bool BeginFrame();
void EndFrame();
VideoModeParams GetActualVideoModeParams() const;
ActualVideoModeParams GetActualVideoModeParams() const;
void SetBlendMode( BlendMode mode );
bool SupportsTextureFormat( RagePixelFormat pixfmt, bool realtime=false );
bool SupportsPerVertexMatrixScale();
unsigned CreateTexture(
RagePixelFormat pixfmt,
unsigned CreateTexture(
RagePixelFormat pixfmt,
RageSurface* img,
bool bGenerateMipMaps );
void UpdateTexture(
unsigned iTexHandle,
void UpdateTexture(
unsigned iTexHandle,
RageSurface* img,
int xoffset, int yoffset, int width, int height );
void DeleteTexture( unsigned iTexHandle );
@@ -42,7 +42,7 @@ public:
void ClearZBuffer();
void SetCullMode( CullMode mode );
void SetAlphaTest( bool b );
void SetMaterial(
void SetMaterial(
const RageColor &emissive,
const RageColor &ambient,
const RageColor &diffuse,
+5 -4
View File
@@ -9,7 +9,7 @@
#include "RageTypes.h"
#include "RageUtil.h"
#include "RageSurface.h"
#include "DisplayResolutions.h"
#include "DisplaySpec.h"
static RageDisplay::RagePixelFormatDesc PIXEL_FORMAT_DESC[NUM_RagePixelFormat] = {
{
@@ -81,11 +81,12 @@ RString RageDisplay_Null::Init( const VideoModeParams &p, bool /* bAllowUnaccele
return RString();
}
void RageDisplay_Null::GetDisplayResolutions( DisplayResolutions &out ) const
void RageDisplay_Null::GetDisplaySpecs(DisplaySpecs &out) const
{
out.clear();
DisplayResolution res = { 640, 480, true };
out.insert( res );
DisplayMode nullMode = {640U, 480U, 30.0};
DisplaySpec nullSpec("NullDisplay", "NullDisplay", nullMode);
out.insert( nullSpec );
}
RageSurface* RageDisplay_Null::CreateScreenshot()
+8 -8
View File
@@ -10,23 +10,23 @@ public:
virtual RString Init( const VideoModeParams &p, bool bAllowUnacceleratedRenderer );
virtual RString GetApiDescription() const { return "Null"; }
virtual void GetDisplayResolutions( DisplayResolutions &out ) const;
virtual void GetDisplaySpecs(DisplaySpecs &out) const;
const RagePixelFormatDesc *GetPixelFormatDesc(RagePixelFormat pf) const;
bool BeginFrame() { return true; }
void EndFrame();
VideoModeParams GetActualVideoModeParams() const { return m_Params; }
ActualVideoModeParams GetActualVideoModeParams() const { return m_Params; }
void SetBlendMode( BlendMode ) { }
bool SupportsTextureFormat( RagePixelFormat, bool /* realtime */ =false ) { return true; }
bool SupportsPerVertexMatrixScale() { return false; }
unsigned CreateTexture(
RagePixelFormat,
unsigned CreateTexture(
RagePixelFormat,
RageSurface* /* img */,
bool /* bGenerateMipMaps */ ) { return 1; }
void UpdateTexture(
unsigned /* iTexHandle */,
void UpdateTexture(
unsigned /* iTexHandle */,
RageSurface* /* img */,
int /* xoffset */, int /* yoffset */, int /* width */, int /* height */
int /* xoffset */, int /* yoffset */, int /* width */, int /* height */
) { }
void DeleteTexture( unsigned /* iTexHandle */ ) { }
void ClearAllTextures() { }
@@ -44,7 +44,7 @@ public:
void ClearZBuffer() { }
void SetCullMode( CullMode ) { }
void SetAlphaTest( bool ) { }
void SetMaterial(
void SetMaterial(
const RageColor & /* unreferenced: emissive */,
const RageColor & /* unreferenced: ambient */,
const RageColor & /* unreferenced: diffuse */,
+106 -20
View File
@@ -15,7 +15,7 @@ using namespace RageDisplay_Legacy_Helpers;
#include "RageUtil.h"
#include "EnumHelper.h"
#include "Foreach.h"
#include "DisplayResolutions.h"
#include "DisplaySpec.h"
#include "LocalizedString.h"
#include "arch/LowLevelWindow/LowLevelWindow.h"
@@ -63,7 +63,7 @@ static const GLenum RageSpriteVertexFormat = GL_T2F_C4F_N3F_V3F;
/* If we support texture matrix scaling, a handle to the vertex program: */
static GLhandleARB g_bTextureMatrixShader = 0;
static map<unsigned, RenderTarget *> g_mapRenderTargets;
static std::map<unsigned, RenderTarget *> g_mapRenderTargets;
static RenderTarget *g_pCurrentRenderTarget = NULL;
static LowLevelWindow *g_pWind;
@@ -264,6 +264,7 @@ RageDisplay_Legacy::RageDisplay_Legacy()
g_pWind = NULL;
g_bTextureMatrixShader = 0;
offscreenRenderTarget = nullptr;
}
RString GetInfoLog( GLhandleARB h )
@@ -561,10 +562,10 @@ RageDisplay_Legacy::~RageDisplay_Legacy()
delete g_pWind;
}
void RageDisplay_Legacy::GetDisplayResolutions( DisplayResolutions &out ) const
void RageDisplay_Legacy::GetDisplaySpecs(DisplaySpecs &out) const
{
out.clear();
g_pWind->GetDisplayResolutions( out );
g_pWind->GetDisplaySpecs(out);
}
static void CheckPalettedTextures()
@@ -685,7 +686,9 @@ void SetupExtensions()
const float fGLUVersion = StringToFloat( (const char *) gluGetString(GLU_VERSION) );
g_gluVersion = lrintf( fGLUVersion * 10 );
#ifndef HAVE_X11 // LLW_X11 needs to init GLEW early for GLX exts
glewInit();
#endif
g_iMaxTextureUnits = 1;
if (GLEW_ARB_multitexture)
@@ -712,6 +715,39 @@ void SetupExtensions()
}
}
bool RageDisplay_Legacy::UseOffscreenRenderTarget()
{
if ( !GetActualVideoModeParams().renderOffscreen || !TEXTUREMAN )
{
return false;
}
if ( !offscreenRenderTarget )
{
RenderTargetParam param;
param.bWithDepthBuffer = true;
param.bWithAlpha = true;
param.bFloat = false;
param.iWidth = GetActualVideoModeParams().width;
param.iHeight = GetActualVideoModeParams().height;
RageTextureID id( ssprintf( "FullscreenTexture%dx%d", param.iWidth,
param.iHeight ) );
// See if we have this texture loaded already
// (not GC'd yet). If it exists and we try to recreate
// it, we'll get an error
if ( TEXTUREMAN->IsTextureRegistered( id ) )
{
offscreenRenderTarget = static_cast<RageTextureRenderTarget*>( TEXTUREMAN->LoadTexture( id ) );
}
else
{
offscreenRenderTarget = new RageTextureRenderTarget( id, param );
TEXTUREMAN->RegisterTexture( id, offscreenRenderTarget );
}
}
return true;
}
void RageDisplay_Legacy::ResolutionChanged()
{
//LOG->Warn( "RageDisplay_Legacy::ResolutionChanged" );
@@ -721,6 +757,13 @@ void RageDisplay_Legacy::ResolutionChanged()
EndFrame();
RageDisplay::ResolutionChanged();
if (offscreenRenderTarget && TEXTUREMAN)
{
TEXTUREMAN->UnloadTexture( offscreenRenderTarget );
offscreenRenderTarget = NULL;
}
}
// Return true if mode change was successful.
@@ -746,6 +789,7 @@ RString RageDisplay_Legacy::TryVideoMode( const VideoModeParams &p, bool &bNewDe
if (TEXTUREMAN)
TEXTUREMAN->InvalidateTextures();
/* Delete all render targets. They may have associated resources other than
* the texture itself. */
FOREACHM( unsigned, RenderTarget *, g_mapRenderTargets, rt )
@@ -784,8 +828,8 @@ bool RageDisplay_Legacy::BeginFrame()
{
/* We do this in here, rather than ResolutionChanged, or we won't update the
* viewport for the concurrent rendering context. */
int fWidth = g_pWind->GetActualVideoModeParams().width;
int fHeight = g_pWind->GetActualVideoModeParams().height;
int fWidth = g_pWind->GetActualVideoModeParams().windowWidth;
int fHeight = g_pWind->GetActualVideoModeParams().windowHeight;
glViewport( 0, 0, fWidth, fHeight );
@@ -793,11 +837,34 @@ bool RageDisplay_Legacy::BeginFrame()
SetZWrite( true );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
return RageDisplay::BeginFrame();
bool beginFrame = RageDisplay::BeginFrame();
if (beginFrame && UseOffscreenRenderTarget()) {
offscreenRenderTarget->BeginRenderingTo( false );
}
return beginFrame;
}
void RageDisplay_Legacy::EndFrame()
{
if (UseOffscreenRenderTarget())
{
offscreenRenderTarget->FinishRenderingTo();
Sprite fullscreenSprite;
// We've got a hold of this, don't want sprite deleting it when
// it's deleted
offscreenRenderTarget->m_iRefCount++;
fullscreenSprite.SetTexture(offscreenRenderTarget);
fullscreenSprite.SetHorizAlign(align_left);
fullscreenSprite.SetVertAlign(align_top);
CameraPushMatrix();
LoadMenuPerspective( 0, GetActualVideoModeParams().width, GetActualVideoModeParams().height,
static_cast<float> (GetActualVideoModeParams().width) / 2.f,
static_cast<float> (GetActualVideoModeParams().height) / 2.f );
fullscreenSprite.Draw();
CameraPopMatrix();
}
FrameLimitBeforeVsync( g_pWind->GetActualVideoModeParams().rate );
g_pWind->SwapBuffers();
FrameLimitAfterVsync();
@@ -823,20 +890,31 @@ RageSurface* RageDisplay_Legacy::CreateScreenshot()
int width = g_pWind->GetActualVideoModeParams().width;
int height = g_pWind->GetActualVideoModeParams().height;
const RagePixelFormatDesc &desc = PIXEL_FORMAT_DESC[RagePixelFormat_RGBA8];
RageSurface *image = CreateSurface( width, height, desc.bpp,
desc.masks[0], desc.masks[1], desc.masks[2], 0 );
RageSurface *image = NULL;
if (offscreenRenderTarget) {
RageSurface *raw = GetTexture(offscreenRenderTarget->GetTexHandle());
image = CreateSurface( offscreenRenderTarget->GetImageWidth(), offscreenRenderTarget->GetImageHeight(),
raw->fmt.BitsPerPixel, raw->fmt.Rmask, raw->fmt.Gmask, raw->fmt.Bmask,
raw->fmt.Amask );
RageSurfaceUtils::Blit(raw, image);
delete raw;
} else {
const RagePixelFormatDesc &desc = PIXEL_FORMAT_DESC[RagePixelFormat_RGBA8];
image = CreateSurface( width, height, desc.bpp,
desc.masks[0], desc.masks[1], desc.masks[2], 0 );
DebugFlushGLErrors();
DebugFlushGLErrors();
glReadBuffer( GL_FRONT );
DebugAssertNoGLError();
//TODO: revisit for MacOS, where backbuffer size can be less than window size
glReadBuffer( GL_FRONT );
DebugAssertNoGLError();
glReadPixels( 0, 0, g_pWind->GetActualVideoModeParams().width, g_pWind->GetActualVideoModeParams().height, GL_RGBA,
GL_UNSIGNED_BYTE, image->pixels );
DebugAssertNoGLError();
glReadPixels( 0, 0, g_pWind->GetActualVideoModeParams().width, g_pWind->GetActualVideoModeParams().height, GL_RGBA,
GL_UNSIGNED_BYTE, image->pixels );
DebugAssertNoGLError();
RageSurfaceUtils::FlipVertically( image );
RageSurfaceUtils::FlipVertically( image );
}
return image;
}
@@ -865,7 +943,7 @@ RageSurface *RageDisplay_Legacy::GetTexture( unsigned iTexture )
return pImage;
}
VideoModeParams RageDisplay_Legacy::GetActualVideoModeParams() const
ActualVideoModeParams RageDisplay_Legacy::GetActualVideoModeParams() const
{
return g_pWind->GetActualVideoModeParams();
}
@@ -2483,6 +2561,14 @@ bool RageDisplay_Legacy::SupportsRenderToTexture() const
return GLEW_EXT_framebuffer_object || g_pWind->SupportsRenderToTexture();
}
bool RageDisplay_Legacy::SupportsFullscreenBorderlessWindow() const
{
// In order to support FSBW, we're going to need the LowLevelWindow implementation
// to support creating a fullscreen borderless window, and we're going to need
// RenderToTexture support in order to render in alternative resolutions
return g_pWind->SupportsFullscreenBorderlessWindow() && SupportsRenderToTexture();
}
/*
* Render-to-texture can be implemented in several ways: the generic GL_ARB_pixel_buffer_object,
* or platform-specifically. PBO is not available on all hardware that supports RTT,
@@ -2525,8 +2611,8 @@ void RageDisplay_Legacy::SetRenderTarget( unsigned iTexture, bool bPreserveTextu
DISPLAY->CameraPopMatrix();
/* Reset the viewport. */
int fWidth = g_pWind->GetActualVideoModeParams().width;
int fHeight = g_pWind->GetActualVideoModeParams().height;
int fWidth = g_pWind->GetActualVideoModeParams().windowWidth;
int fHeight = g_pWind->GetActualVideoModeParams().windowHeight;
glViewport( 0, 0, fWidth, fHeight );
if (g_pCurrentRenderTarget)
+17 -10
View File
@@ -4,9 +4,11 @@
#define RAGE_DISPLAY_OGL_H
#include "RageDisplay.h"
#include "RageTextureRenderTarget.h"
#include "Sprite.h"
/* Making an OpenGL call doesn't also flush the error state; if we happen
* to have an error from a previous call, then the assert below will fail.
* to have an error from a previous call, then the assert below will fail.
* Flush it. */
#define FlushGLErrors() do { } while( glGetError() != GL_NO_ERROR )
#define AssertNoGLError() \
@@ -31,7 +33,7 @@ public:
virtual RString Init( const VideoModeParams &p, bool bAllowUnacceleratedRenderer );
virtual RString GetApiDescription() const { return "OpenGL"; }
virtual void GetDisplayResolutions( DisplayResolutions &out ) const;
virtual void GetDisplaySpecs(DisplaySpecs &out) const;
void ResolutionChanged();
const RagePixelFormatDesc *GetPixelFormatDesc(RagePixelFormat pf) const;
@@ -41,22 +43,23 @@ public:
void BeginConcurrentRendering();
void EndConcurrentRendering();
bool BeginFrame();
bool BeginFrame();
void EndFrame();
VideoModeParams GetActualVideoModeParams() const;
ActualVideoModeParams GetActualVideoModeParams() const;
void SetBlendMode( BlendMode mode );
bool SupportsTextureFormat( RagePixelFormat pixfmt, bool realtime=false );
bool SupportsPerVertexMatrixScale();
unsigned CreateTexture(
RagePixelFormat pixfmt,
unsigned CreateTexture(
RagePixelFormat pixfmt,
RageSurface* img,
bool bGenerateMipMaps );
void UpdateTexture(
unsigned iTexHandle,
void UpdateTexture(
unsigned iTexHandle,
RageSurface* img,
int xoffset, int yoffset, int width, int height
int xoffset, int yoffset, int width, int height
);
void DeleteTexture( unsigned iTexHandle );
bool UseOffscreenRenderTarget();
RageSurface *GetTexture( unsigned iTexture );
RageTextureLock *CreateTextureLock();
@@ -70,6 +73,7 @@ public:
void SetEffectMode( EffectMode effect );
bool IsEffectModeSupported( EffectMode effect );
bool SupportsRenderToTexture() const;
bool SupportsFullscreenBorderlessWindow() const;
unsigned CreateRenderTarget( const RenderTargetParam &param, int &iTextureWidthOut, int &iTextureHeightOut );
unsigned GetRenderTarget();
void SetRenderTarget( unsigned iHandle, bool bPreserveTexture );
@@ -81,7 +85,7 @@ public:
void ClearZBuffer();
void SetCullMode( CullMode mode );
void SetAlphaTest( bool b );
void SetMaterial(
void SetMaterial(
const RageColor &emissive,
const RageColor &ambient,
const RageColor &diffuse,
@@ -125,6 +129,9 @@ protected:
bool SupportsSurfaceFormat( RagePixelFormat pixfmt );
void SendCurrentMatrices();
private:
RageTextureRenderTarget *offscreenRenderTarget;
};
#endif
+1
View File
@@ -247,6 +247,7 @@ ScreenManager::ScreenManager()
m_bZeroNextUpdate = false;
m_PopTopScreen = SM_Invalid;
m_OnDonePreparingScreen = SM_Invalid;
}
+35 -15
View File
@@ -14,7 +14,7 @@
#include "Game.h"
#include "Foreach.h"
#include "GameConstantsAndTypes.h"
#include "DisplayResolutions.h"
#include "DisplaySpec.h"
#include "LocalizedString.h"
#include "SpecialFiles.h"
#include "RageLog.h"
@@ -248,22 +248,25 @@ static void ThemeChoices( vector<RString> &out )
*s = THEME->GetThemeDisplayName( *s );
}
static DisplayResolutions display_resolution_list;
static void cache_display_resolution_list()
static DisplaySpecs display_specs;
static void cache_display_specs()
{
if(display_resolution_list.empty())
if(display_specs.empty())
{
DISPLAY->GetDisplayResolutions(display_resolution_list);
DISPLAY->GetDisplaySpecs(display_specs);
}
}
static void DisplayResolutionChoices( vector<RString> &out )
{
cache_display_resolution_list();
FOREACHS_CONST( DisplayResolution, display_resolution_list, iter )
cache_display_specs();
FOREACHS_CONST( DisplaySpec, display_specs, iter )
{
RString s = ssprintf("%dx%d", iter->iWidth, iter->iHeight);
out.push_back( s );
if (iter->currentMode() != NULL)
{
RString s = ssprintf("%dx%d", iter->currentMode()->width, iter->currentMode()->height);
out.push_back(s);
}
}
}
@@ -516,6 +519,8 @@ static void MaxHighScoresPerListForPlayer(int& sel, bool to_sel, ConfOption cons
#include "LuaManager.h"
#include "LuaBinding.h"
static int GetTimingDifficulty()
{
int iTimingDifficulty = 0;
@@ -583,12 +588,14 @@ static void DisplayResolutionM( int &sel, bool ToSel, const ConfOption *pConfOpt
{
static vector<res_t> res_choices;
if(res_choices.empty())
if( res_choices.empty() )
{
cache_display_resolution_list();
FOREACHS_CONST(DisplayResolution, display_resolution_list, iter)
FOREACHS_CONST( DisplaySpec, display_specs, iter )
{
res_choices.push_back(res_t(iter->iWidth, iter->iHeight));
if ( iter->currentMode() != NULL )
{
res_choices.push_back( res_t( iter->currentMode()->width, iter->currentMode()->height ) );
}
}
}
@@ -733,10 +740,10 @@ static void InitializeConfOptions()
if( !g_ConfOptions.empty() )
return;
// Clear the display_resolution_list so that we don't get problems from
// Clear the display_specs so that we don't get problems from
// caching it. If the DisplayResolution option row is on the screen, it'll
// recache the list. -Kyz
display_resolution_list.clear();
display_specs.clear();
// There are a couple ways of getting the current preference column or turning
// a new choice in the interface into a new preference. The easiest is when
@@ -958,6 +965,19 @@ void ConfOption::MakeOptionsList( vector<RString> &out ) const
out = names;
}
static const char *OptEffectNames[] = {
"SavePreferences",
"ApplyGraphics",
"ApplyTheme",
"ChangeGame",
"ApplySound",
"ApplySong",
"ApplyAspectRatio"
};
XToString( OptEffect );
StringToX( OptEffect );
LuaXType( OptEffect );
/**
* @file
* @author Glenn Maynard (c) 2003-2004
+17 -7
View File
@@ -1,14 +1,24 @@
#ifndef SCREEN_OPTIONS_MASTER_PREFS_H
#define SCREEN_OPTIONS_MASTER_PREFS_H
#include "EnumHelper.h"
static const int MAX_OPTIONS=16;
#define OPT_SAVE_PREFERENCES (1<<0)
#define OPT_APPLY_GRAPHICS (1<<1)
#define OPT_APPLY_THEME (1<<2)
#define OPT_CHANGE_GAME (1<<3)
#define OPT_APPLY_SOUND (1<<4)
#define OPT_APPLY_SONG (1<<5)
#define OPT_APPLY_ASPECT_RATIO (1<<6)
enum OptEffect
{
OPT_SAVE_PREFERENCES = (1<<0),
OPT_APPLY_GRAPHICS = (1<<1),
OPT_APPLY_THEME = (1<<2),
OPT_CHANGE_GAME = (1<<3),
OPT_APPLY_SOUND = (1<<4),
OPT_APPLY_SONG = (1<<5),
OPT_APPLY_ASPECT_RATIO = (1<<6),
NUM_OptEffect = 7,
OptEffect_Invalid = MAX_OPTIONS+1
};
const RString& OptEffectToString( OptEffect e );
OptEffect StringToOptEffect( const std::string &e );
LuaDeclareType( OptEffect );
struct ConfOption
{
+37 -12
View File
@@ -84,6 +84,31 @@ Sprite::Sprite( const Sprite &cpy ):
m_pTexture = NULL;
}
Sprite &Sprite::operator=( Sprite other )
{
using std::swap;
#define SWAP(a) swap(a, other.a)
SWAP( m_States );
SWAP(m_animation_length_seconds);
SWAP( m_iCurState );
SWAP( m_fSecsIntoState );
SWAP( m_bUsingCustomTexCoords );
SWAP( m_bUsingCustomPosCoords );
SWAP( m_bSkipNextUpdate );
SWAP( m_DecodeMovie );
SWAP( m_EffectMode );
memcpy( m_CustomTexCoords, other.m_CustomTexCoords, sizeof(m_CustomTexCoords) );
memcpy( m_CustomPosCoords, other.m_CustomPosCoords, sizeof(m_CustomPosCoords) );
SWAP( m_fRememberedClipWidth );
SWAP( m_fRememberedClipHeight );
SWAP( m_fTexCoordVelocityX );
SWAP( m_fTexCoordVelocityY );
SWAP(m_use_effect_clock_for_texcoords);
SWAP(m_pTexture);
#undef SWAP
return *this;
}
void Sprite::InitState()
{
Actor::InitState();
@@ -934,7 +959,7 @@ void Sprite::ScaleToClipped( float fWidth, float fHeight )
Sprite::ScaleToCover( RectF(0, 0, fWidth, fHeight) );
// find which dimension is larger
bool bXDimNeedsToBeCropped = GetZoomedWidth() > fWidth+0.01;
if( bXDimNeedsToBeCropped ) // crop X
{
float fPercentageToCutOff = (this->GetZoomedWidth() - fWidth) / this->GetZoomedWidth();
@@ -943,9 +968,9 @@ void Sprite::ScaleToClipped( float fWidth, float fHeight )
// generate a rectangle with new texture coordinates
RectF fCustomImageRect(
fPercentageToCutOffEachSide,
0,
1 - fPercentageToCutOffEachSide,
fPercentageToCutOffEachSide,
0,
1 - fPercentageToCutOffEachSide,
1 );
SetCustomImageRect( fCustomImageRect );
}
@@ -957,9 +982,9 @@ void Sprite::ScaleToClipped( float fWidth, float fHeight )
// generate a rectangle with new texture coordinates
RectF fCustomImageRect(
0,
0,
fPercentageToCutOffEachSide,
1,
1,
1 - fPercentageToCutOffEachSide );
SetCustomImageRect( fCustomImageRect );
}
@@ -995,7 +1020,7 @@ void Sprite::CropTo( float fWidth, float fHeight )
Sprite::ScaleToCover( RectF(0, 0, fWidth, fHeight) );
// find which dimension is larger
bool bXDimNeedsToBeCropped = GetZoomedWidth() > fWidth+0.01;
if( bXDimNeedsToBeCropped ) // crop X
{
float fPercentageToCutOff = (this->GetZoomedWidth() - fWidth) / this->GetZoomedWidth();
@@ -1003,9 +1028,9 @@ void Sprite::CropTo( float fWidth, float fHeight )
// generate a rectangle with new texture coordinates
RectF fCustomImageRect(
fPercentageToCutOffEachSide,
0,
1 - fPercentageToCutOffEachSide,
fPercentageToCutOffEachSide,
0,
1 - fPercentageToCutOffEachSide,
1 );
SetCustomImageRect( fCustomImageRect );
}
@@ -1016,9 +1041,9 @@ void Sprite::CropTo( float fWidth, float fHeight )
// generate a rectangle with new texture coordinates
RectF fCustomImageRect(
0,
0,
fPercentageToCutOffEachSide,
1,
1,
1 - fPercentageToCutOffEachSide );
SetCustomImageRect( fCustomImageRect );
}
+1
View File
@@ -21,6 +21,7 @@ public:
Sprite();
Sprite( const Sprite &cpy );
Sprite &operator=( Sprite other );
virtual ~Sprite();
// See explanation in source.
+10 -2
View File
@@ -96,7 +96,8 @@ void StepMania::GetPreferredVideoModeParams( VideoModeParams &paramsOut )
}
paramsOut = VideoModeParams(
PREFSMAN->m_bWindowed,
PREFSMAN->m_bWindowed || PREFSMAN->m_bFullscreenIsBorderlessWindow,
PREFSMAN->m_sDisplayId,
iWidth,
PREFSMAN->m_iDisplayHeight,
PREFSMAN->m_iDisplayColorDepth,
@@ -106,6 +107,7 @@ void StepMania::GetPreferredVideoModeParams( VideoModeParams &paramsOut )
PREFSMAN->m_bSmoothLines,
PREFSMAN->m_bTrilinearFiltering,
PREFSMAN->m_bAnisotropicFiltering,
!PREFSMAN->m_bWindowed && PREFSMAN->m_bFullscreenIsBorderlessWindow,
CommonMetrics::WINDOW_TITLE,
THEME->GetPathG("Common","window icon"),
PREFSMAN->m_bPAL,
@@ -146,7 +148,13 @@ static void StoreActualGraphicOptions()
* we don't go through the process of auto-detecting a usable video mode
* every time. */
const VideoModeParams &params = DISPLAY->GetActualVideoModeParams();
PREFSMAN->m_bWindowed.Set( params.windowed );
PREFSMAN->m_bWindowed.Set( params.windowed && !params.bWindowIsFullscreenBorderless );
if (!params.windowed && !params.bWindowIsFullscreenBorderless) {
// In all other cases, want to preserve the value of this preference,
// but if DISPLAY decides to go fullscreen exclusive, we'll persist that decision
PREFSMAN->m_bFullscreenIsBorderlessWindow.Set( false );
}
/* If we're windowed, we may have tweaked the width based on the aspect ratio.
* Don't save this new value over the preferred value. */
+7 -4
View File
@@ -3,9 +3,10 @@
#include <set>
class DisplayResolution;
typedef set<DisplayResolution> DisplayResolutions;
class DisplaySpec;
typedef std::set<DisplaySpec> DisplaySpecs;
class VideoModeParams;
class ActualVideoModeParams;
class RenderTarget;
struct RenderTargetParam;
/** @brief Handle low-level operations that OGL 1.x doesn't give us. */
@@ -22,7 +23,7 @@ public:
// bNewDeviceOut is set true if a new device was created and textures
// need to be reloaded.
virtual RString TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut ) = 0;
virtual void GetDisplayResolutions( DisplayResolutions &out ) const = 0;
virtual void GetDisplaySpecs(DisplaySpecs &out) const = 0;
virtual void LogDebugInformation() const { }
virtual bool IsSoftwareRenderer( RString & /* sError */ ) { return false; }
@@ -30,11 +31,13 @@ public:
virtual void SwapBuffers() = 0;
virtual void Update() { }
virtual const VideoModeParams &GetActualVideoModeParams() const = 0;
virtual const ActualVideoModeParams GetActualVideoModeParams() const = 0;
virtual bool SupportsRenderToTexture() const { return false; }
virtual RenderTarget *CreateRenderTarget() { return NULL; }
virtual bool SupportsFullscreenBorderlessWindow() const { return false; };
virtual bool SupportsThreadedRendering() { return false; }
virtual void BeginConcurrentRenderingMainThread() { }
virtual void EndConcurrentRenderingMainThread() { }
@@ -22,12 +22,12 @@ public:
~LowLevelWindow_MacOSX();
void *GetProcAddress( RString s );
RString TryVideoMode( const VideoModeParams& p, bool& newDeviceOut );
void GetDisplayResolutions( DisplayResolutions &dr ) const;
void GetDisplaySpecs( DisplaySpecs &specs ) const;
void SwapBuffers();
void Update();
const VideoModeParams &GetActualVideoModeParams() const { return m_CurrentParams; }
const ActualVideoModeParams GetActualVideoModeParams() const { return m_CurrentParams; }
bool SupportsRenderToTexture() const { return true; }
RenderTarget *CreateRenderTarget();
@@ -1,6 +1,6 @@
#import "global.h"
#import "LowLevelWindow_MacOSX.h"
#import "DisplayResolutions.h"
#import "DisplaySpec.h"
#import "RageUtil.h"
#import "RageThreads.h"
#import "RageDisplay_OGL_Helpers.h"
@@ -109,7 +109,7 @@ public:
- (void) setParams:(NSValue *)params
{
const VideoModeParams &p = *(const VideoModeParams *)[params pointerValue];
NSRect contentRect = { { 0, 0 }, { p.width, p.height } };
NSRect contentRect = { { 0, 0 }, { static_cast<CGFloat>(p.width), static_cast<CGFloat>(p.height) } };
[m_Window setContentSize:contentRect.size];
[m_Window setTitle:[NSString stringWithUTF8String:p.sWindowTitle.c_str()]];
@@ -564,28 +564,50 @@ static bool GetBoolValue( CFTypeRef r )
return r && CFGetTypeID( r ) == CFBooleanGetTypeID() && CFBooleanGetValue( CFBooleanRef(r) );
}
void LowLevelWindow_MacOSX::GetDisplayResolutions( DisplayResolutions &dr ) const
static double GetDoubleValue( CFTypeRef r )
{
double ret;
if( !r || CFGetTypeID(r) != CFNumberGetTypeID() || !CFNumberGetValue(CFNumberRef(r), kCFNumberDoubleType, &ret) )
return 0;
return ret;
}
static DisplayMode ConvertDisplayMode( CFDictionaryRef dict )
{
int width = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayWidth) );
int height = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayHeight) );
double rate = GetDoubleValue( CFDictionaryGetValue(dict, kCGDisplayRefreshRate) );
return { static_cast<unsigned int> (width), static_cast<unsigned int> (height), rate};
}
void LowLevelWindow_MacOSX::GetDisplaySpecs( DisplaySpecs &specs ) const
{
CFArrayRef modes = CGDisplayAvailableModes( kCGDirectMainDisplay );
ASSERT( modes );
const CFIndex count = CFArrayGetCount( modes );
std::set<DisplayMode> available;
CFDictionaryRef currentModeDict = CGDisplayCurrentMode( kCGDirectMainDisplay );
DisplayMode current = ConvertDisplayMode( currentModeDict );
for( CFIndex i = 0; i < count; ++i )
{
CFDictionaryRef dict = (CFDictionaryRef)CFArrayGetValueAtIndex( modes, i );
int width = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayWidth) );
int height = GetIntValue( CFDictionaryGetValue(dict, kCGDisplayHeight) );
CFTypeRef safe = CFDictionaryGetValue( dict, kCGDisplayModeIsSafeForHardware );
bool stretched = GetBoolValue( CFDictionaryGetValue(dict, kCGDisplayModeIsStretched) );
if( !width || !height )
DisplayMode mode = ConvertDisplayMode( dict );
if( !mode.width || !mode.height )
continue;
if( safe && !GetBoolValue( safe ) )
continue;
DisplayResolution res = { width, height, stretched };
dr.insert( res );
available.insert( mode );
}
// Do not release modes! We don't own them here.
RectI bounds( 0, 0, current.width, current.height );
DisplaySpec s( "", "Fullscreen", available, current, bounds );
specs.insert( s );
}
void LowLevelWindow_MacOSX::SwapBuffers()
@@ -59,9 +59,9 @@ LowLevelWindow_Win32::~LowLevelWindow_Win32()
GraphicsWindow::Shutdown();
}
void LowLevelWindow_Win32::GetDisplayResolutions( DisplayResolutions &out ) const
void LowLevelWindow_Win32::GetDisplaySpecs( DisplaySpecs &out ) const
{
GraphicsWindow::GetDisplayResolutions( out );
GraphicsWindow::GetDisplaySpecs( out );
}
int ChooseWindowPixelFormat( const VideoModeParams &p, PIXELFORMATDESCRIPTOR *pixfmt )
@@ -292,7 +292,7 @@ void LowLevelWindow_Win32::Update()
GraphicsWindow::Update();
}
const VideoModeParams &LowLevelWindow_Win32::GetActualVideoModeParams() const
const ActualVideoModeParams LowLevelWindow_Win32::GetActualVideoModeParams() const
{
return GraphicsWindow::GetParams();
}
@@ -10,7 +10,7 @@ public:
~LowLevelWindow_Win32();
void *GetProcAddress( RString s );
RString TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut );
void GetDisplayResolutions( DisplayResolutions &out ) const;
void GetDisplaySpecs( DisplaySpecs &out ) const;
bool IsSoftwareRenderer( RString &sError );
void SwapBuffers();
void Update();
@@ -20,7 +20,7 @@ public:
virtual bool SupportsRenderToTexture() const { return true; }
virtual RenderTarget *CreateRenderTarget();
const VideoModeParams &GetActualVideoModeParams() const;
const ActualVideoModeParams GetActualVideoModeParams() const;
};
#ifdef ARCH_LOW_LEVEL_WINDOW
+545 -116
View File
@@ -5,31 +5,54 @@
#include "archutils/Unix/X11Helper.h"
#include "PrefsManager.h" // XXX
#include "RageDisplay.h" // VideoModeParams
#include "DisplayResolutions.h"
#include "DisplaySpec.h"
#include "LocalizedString.h"
#include "RageDisplay_OGL_Helpers.h"
using namespace RageDisplay_Legacy_Helpers;
using namespace X11Helper;
#include <stack>
#include <set>
#include <math.h> // ceil()
#include <GL/glxew.h>
#define GLX_GLXEXT_PROTOTYPES
#include <GL/glx.h> // All sorts of stuff...
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrandr.h>
#if defined(HAVE_XINERAMA)
#include <X11/extensions/Xinerama.h>
#endif
#if defined(HAVE_LIBXTST)
#include <X11/extensions/XTest.h>
#endif
// Display ID for treating the entire X screen as the display
const std::string ID_XSCREEN = "XSCREEN_RANDR";
static GLXContext g_pContext = NULL;
static GLXContext g_pBackgroundContext = NULL;
static Window g_AltWindow = None;
static Rotation g_OldRotation;
static int g_iOldSize;
XRRScreenConfiguration *g_pScreenConfig = NULL;
static bool g_bChangedScreenSize = false;
static SizeID g_iOldSize = None;
static Rotation g_OldRotation = RR_Rotate_0;
static XRRScreenConfiguration *g_pScreenConfig = nullptr;
static RRMode g_originalRandRMode = None;
static RROutput g_usedCrtc = None;
static int g_iRandRVerMinor = 0;
static int g_iRandRVerMajor = 0;
static bool g_bUseXRandR12 = false;
static bool g_bUseXinerama = false;
inline float calcRandRRefresh( unsigned long iPixelClock, int iHTotal, int iVTotal )
{
// Pixel Clock divided by total pixels in mode,
// not just those onscreen!
return ( iPixelClock ) / ( iHTotal * iVTotal );
}
bool NetWMSupported(Display *Dpy, Atom feature);
static LocalizedString FAILED_CONNECTION_XSERVER( "LowLevelWindow_X11", "Failed to establish a connection with the X server" );
LowLevelWindow_X11::LowLevelWindow_X11()
@@ -37,6 +60,18 @@ LowLevelWindow_X11::LowLevelWindow_X11()
if( !OpenXConnection() )
RageException::Throw( "%s", FAILED_CONNECTION_XSERVER.GetValue().c_str() );
if( XRRQueryVersion( Dpy, &g_iRandRVerMajor, &g_iRandRVerMinor ) && g_iRandRVerMajor >= 1 && g_iRandRVerMinor >= 2) g_bUseXRandR12 = true;
#ifdef HAVE_XINERAMA
int xinerama_event_base = 0;
int xinerama_error_base = 0;
Atom fullscreen_monitors = XInternAtom( Dpy, "_NET_WM_FULLSCREEN_MONITORS", False );
if (XineramaQueryExtension( Dpy, &xinerama_event_base, &xinerama_error_base ) &&
NetWMSupported( Dpy, fullscreen_monitors ))
{
g_bUseXinerama = true;
}
#endif
const int iScreen = DefaultScreen( Dpy );
int iXServerVersion = XVendorRelease( Dpy ); /* eg. 40201001 */
int iMajor = iXServerVersion / 10000000; iXServerVersion %= 10000000;
@@ -50,7 +85,6 @@ LowLevelWindow_X11::LowLevelWindow_X11()
LOG->Info( "Client GLX vendor: %s [%s]", glXGetClientString( Dpy, GLX_VENDOR ), glXGetClientString( Dpy, GLX_VERSION ) );
m_bWasWindowed = true;
g_pScreenConfig = XRRGetScreenInfo( Dpy, RootWindow(Dpy, DefaultScreen(Dpy)) );
g_iOldSize = XRRConfigCurrentConfiguration( g_pScreenConfig, &g_OldRotation );
}
LowLevelWindow_X11::~LowLevelWindow_X11()
@@ -58,8 +92,7 @@ LowLevelWindow_X11::~LowLevelWindow_X11()
// Reset the display
if( !m_bWasWindowed )
{
XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), g_iOldSize, g_OldRotation, CurrentTime );
RestoreOutputConfig();
XUngrabKeyboard( Dpy, CurrentTime );
}
if( g_pContext )
@@ -72,9 +105,6 @@ LowLevelWindow_X11::~LowLevelWindow_X11()
glXDestroyContext( Dpy, g_pBackgroundContext );
g_pBackgroundContext = NULL;
}
XRRFreeScreenConfigInfo( g_pScreenConfig );
g_pScreenConfig = NULL;
XDestroyWindow( Dpy, Win );
Win = None;
XDestroyWindow( Dpy, g_AltWindow );
@@ -82,6 +112,29 @@ LowLevelWindow_X11::~LowLevelWindow_X11()
CloseXConnection();
}
/*
* Restore saved X screen/CRTC configuration
*/
void LowLevelWindow_X11::RestoreOutputConfig() {
if (g_bChangedScreenSize) {
XRRSetScreenConfig(Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), g_iOldSize, g_OldRotation,
CurrentTime);
}
if (g_usedCrtc != None) {
ASSERT(g_bUseXRandR12);
XRRScreenResources *res = XRRGetScreenResources(Dpy, Win);
XRRCrtcInfo *conf = XRRGetCrtcInfo(Dpy, res, g_usedCrtc);
XRRSetCrtcConfig(Dpy, res, g_usedCrtc, conf->timestamp, conf->x, conf->y, g_originalRandRMode, conf->rotation,
conf->outputs, conf->noutput);
XRRFreeScreenResources(res);
XRRFreeCrtcInfo(conf);
}
g_iOldSize = None;
g_bChangedScreenSize = false;
g_usedCrtc = None;
g_OldRotation = RR_Rotate_0;
}
void *LowLevelWindow_X11::GetProcAddress( RString s )
{
// XXX: We should check whether glXGetProcAddress or
@@ -92,19 +145,20 @@ void *LowLevelWindow_X11::GetProcAddress( RString s )
RString LowLevelWindow_X11::TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut )
{
#if defined(UNIX)
/* nVidia cards:
* This only works the first time we set up a window; after that, the
* drivers appear to cache the value, so you have to actually restart
* the program to change it again. */
static char buf[128];
strcpy( buf, "__GL_SYNC_TO_VBLANK=" );
strcat( buf, p.vsync?"1":"0" );
putenv( buf );
#endif
// We're going to be interested in MapNotify/ConfigureNotify events in this routine,
// so ensure our event mask includes these, restore it on exit
XWindowAttributes winAttrib;
auto restore = [&](XWindowAttributes *attr) { XSelectInput( Dpy, Win, attr->your_event_mask );};
auto restoreAttrib = std::unique_ptr<XWindowAttributes, decltype(restore)>(&winAttrib, restore);
// These might change if we're rendering at different resolution than window
int windowWidth = p.width;
int windowHeight = p.height;
bool renderOffscreen = false;
if( g_pContext == NULL || p.bpp != CurrentParams.bpp || m_bWasWindowed != p.windowed )
{
bool bFirstRun = g_pContext == NULL;
// Different depth, or we didn't make a window before. New context.
bNewDeviceOut = true;
@@ -156,128 +210,377 @@ RString LowLevelWindow_X11::TryVideoMode( const VideoModeParams &p, bool &bNewDe
glXMakeCurrent( Dpy, Win, g_pContext );
// Map the window, ensuring we get the MapNotify event
XWindowAttributes winAttrib;
XGetWindowAttributes( Dpy, Win, &winAttrib );
XSelectInput( Dpy, Win, winAttrib.your_event_mask | StructureNotifyMask );
XSelectInput( Dpy, Win, winAttrib.your_event_mask | StructureNotifyMask | PropertyChangeMask );
XMapWindow( Dpy, Win );
// Wait until we actually have a mapped window before trying to
// use it!
XEvent event;
do
{
XNextEvent( Dpy, &event );
} while (event.type != MapNotify);
XEvent ev;
do {XWindowEvent( Dpy, Win, StructureNotifyMask, &ev );}
while ( ev.type != MapNotify);
// Set the event mask back to what it was
XSelectInput( Dpy, Win, winAttrib.your_event_mask );
// I can't find official docs saying what happens if you re-init GLEW.
// I'll just assume the behavior is undefined.
if(bFirstRun)
{
GLenum err = glewInit();
ASSERT( err == GLEW_OK );
}
}
else
{
// We're remodeling the existing window, and not touching the context.
bNewDeviceOut = false;
XGetWindowAttributes( Dpy, Win, &winAttrib );
XSelectInput( Dpy, Win, winAttrib.your_event_mask | StructureNotifyMask | PropertyChangeMask );
if( !p.windowed )
{
// X11 is an asynchronous beast. If we're resizing an existing
// window directly (i.e. override-redirect as opposed to asking the
// WM to do it) and don't wait for the window to actually be
// resized, we'll get unexpected results from glViewport() etc. I
// don't know why, or why it *doesn't* break in the slower process
// of waiting for the WM to resize the window.
// So, set the event mask so we're notified when the window is resized...
// Send the resize command...
XResizeWindow( Dpy, Win, static_cast<unsigned int> (p.width), static_cast<unsigned int> (p.height) );
// We'll wait for the notification once we've done everything else,
// to save time.
}
}
float rate = 60; // Will be unchanged if windowed. Not sure I care.
if( !p.windowed )
{
if( m_bWasWindowed )
{
RestoreOutputConfig();
if (p.sDisplayId == ID_XSCREEN || p.sDisplayId.empty()) {
// If the user changed the resolution while StepMania was windowed we overwrite the resolution to restore with it at exit.
g_iOldSize = XRRConfigCurrentConfiguration( g_pScreenConfig, &g_OldRotation );
m_bWasWindowed = false;
}
// Find a matching mode.
int iSizesXct;
XRRScreenSize *pSizesX = XRRSizes( Dpy, DefaultScreen(Dpy), &iSizesXct );
ASSERT_M( iSizesXct != 0, "Couldn't get resolution list from X server" );
// Find a matching mode.
int iSizesXct;
XRRScreenSize *pSizesX = XRRSizes( Dpy, DefaultScreen(Dpy), &iSizesXct );
ASSERT_M( iSizesXct != 0, "Couldn't get resolution list from X server" );
int iSizeMatch = -1;
int iSizeMatch = -1;
for( int i = 0; i < iSizesXct; ++i )
{
if( pSizesX[i].width == p.width && pSizesX[i].height == p.height )
{
iSizeMatch = i;
break;
for (int i = 0; i < iSizesXct; ++i) {
if (pSizesX[i].width == p.width && pSizesX[i].height == p.height) {
iSizeMatch = i;
break;
}
}
if (iSizeMatch != g_iOldSize) {
g_bChangedScreenSize = true;
}
}
// Set this mode.
// XXX: This doesn't handle if the config has changed since we queried it (see man Xrandr)
XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), iSizeMatch, 1, CurrentTime );
// Set this mode.
// XXX: This doesn't handle if the config has changed since we queried it (see man Xrandr)
Status s = XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), iSizeMatch, 1, CurrentTime );
if (s)
{
return "Failed to set screen config";
}
XMoveWindow( Dpy, Win, 0, 0 );
XRaiseWindow( Dpy, Win );
// We want to prevent the WM from catching anything that comes from the keyboard.
// We should do this every time on fullscreen and not only we entering from windowed mode because we could lose focus at resolution change and that will leave the user input locked.
while (XGrabKeyboard( Dpy, Win, True, GrabModeAsync, GrabModeAsync, CurrentTime ));
} else {
ASSERT(g_bUseXRandR12);
/* === Configuring a specific CRTC === */
// Arcane and undocumented but PROPER XRandR 1.2 method.
// What we do is directly reconfigure the CRTC of the primary display,
// Which prevents the (RandR) screen itself from resizing, and therefore
// leaving user's desktop unmolested.
LOG->Info("LowLevelWindow_X11: Using XRandR");
XRRScreenResources *scrRes = XRRGetScreenResources(Dpy, Win);
ASSERT(scrRes != NULL);
ASSERT(scrRes->ncrtc > 0);
ASSERT(scrRes->noutput > 0);
ASSERT(scrRes->nmode > 0);
// If an output name has been specified, search for it
RROutput targetOut = None;
if (p.sDisplayId.length() > 0) {
for (unsigned int i = 0; i < scrRes->noutput && targetOut == None; ++i) {
XRROutputInfo *outInfo = XRRGetOutputInfo(Dpy, scrRes, scrRes->outputs[i]);
std::string outName = std::string(outInfo->name, static_cast<unsigned int> (outInfo->nameLen));
if (p.sDisplayId == outName) {
targetOut = scrRes->outputs[i];
}
XRRFreeOutputInfo(outInfo);
}
}
if (targetOut == None) {
LOG->Info("Did not find display output %s, trying another", p.sDisplayId.c_str());
// didn't find named output, pick primary/or at least one that works
if (g_iRandRVerMajor >= 1 && g_iRandRVerMinor >= 3) {
// RandR 1.3 can tell us what the primary display is.
targetOut = XRRGetOutputPrimary(Dpy, Win);
} else {
// Only RandR 1.2. We'll look for a "Connected" output, or if we can't find that,
// (it is possible the connection state could be unknown), we'll at least
// look for an output with a CRTC driving it
RROutput connected = None, hasCrtc = None;
for (unsigned int i = 0; i < scrRes->noutput; ++i) {
XRROutputInfo *outInfo = XRRGetOutputInfo(Dpy, scrRes, scrRes->outputs[i]);
if (outInfo->connection == RR_Connected) { // Check for CONNECTED state: Connected == 0
connected = scrRes->outputs[i];
}
if (outInfo->crtc != None) {
hasCrtc = outInfo->crtc;
}
XRRFreeOutputInfo(outInfo);
}
targetOut = connected != None ? connected : hasCrtc;
ASSERT(targetOut != None);
}
}
// if the target output is not currently being driven by a crtc,
// find an unused crtc that can be connected to it
XRROutputInfo *tgtOutInfo = XRRGetOutputInfo( Dpy, scrRes, targetOut );
if (tgtOutInfo == NULL)
{
XRRFreeScreenResources(scrRes);
return "Failed to find XRROutput";
}
RRCrtc tgtOutCrtc = tgtOutInfo->crtc;
if (tgtOutCrtc == None)
{
for (unsigned int i = 0; i < tgtOutInfo->ncrtc; ++i)
{
XRRCrtcInfo *crtcInfo = XRRGetCrtcInfo( Dpy, scrRes, tgtOutInfo->crtcs[i] );
if (crtcInfo->mode == None)
{
tgtOutCrtc = tgtOutInfo->crtcs[i];
}
XRRFreeCrtcInfo( crtcInfo );
}
}
ASSERT(tgtOutCrtc != None);
XRRCrtcInfo *oldConf = XRRGetCrtcInfo( Dpy, scrRes, tgtOutCrtc );
float fRefreshDiff = 99999;
float fRefreshRate = 0;
RRMode mode = None;
// A quirk of XRandR is that the width and height are as the display
// controller ("CRTC") sees it, which means height and width are
// flipped if there's rotation going on.
const bool bPortrait = (oldConf->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0;
// Find a mode that matches our exact wanted resolution,
// with as close to our desired refresh rate as possible.
for (int i = 0; i < scrRes->nmode; i++) {
const XRRModeInfo &thisMI = scrRes->modes[i];
const unsigned int modeWidth = bPortrait ? thisMI.height : thisMI.width;
const unsigned int modeHeight = bPortrait ? thisMI.width : thisMI.height;
if (modeWidth == p.width && modeHeight == p.height) {
float fTempRefresh = calcRandRRefresh(thisMI.dotClock, thisMI.hTotal, thisMI.vTotal);
float fTempDiff = std::abs(p.rate - fTempRefresh);
if ((p.rate != REFRESH_DEFAULT && fTempDiff < fRefreshDiff) ||
(p.rate == REFRESH_DEFAULT && fTempRefresh > fRefreshRate)) {
int j;
// Ensure that the output supports the mode
for (j = 0; j < tgtOutInfo->nmode; j++)
if (tgtOutInfo->modes[j] == scrRes->modes[i].id) {
mode = tgtOutInfo->modes[j];
break;
}
if (j < tgtOutInfo->nmode) {
fRefreshRate = fTempRefresh;
fRefreshDiff = fTempDiff;
}
}
}
}
rate = roundf(fRefreshRate);
g_usedCrtc = tgtOutCrtc;
g_originalRandRMode = oldConf->mode;
const std::string tgtOutName = std::string(tgtOutInfo->name, static_cast<unsigned int> (tgtOutInfo->nameLen));
LOG->Info("XRandR output config using CRTC %lu in mode %lu, driving output %s",
g_usedCrtc, mode, tgtOutName.c_str());
// and FIRE!
Status s = XRRSetCrtcConfig(Dpy, scrRes, g_usedCrtc, oldConf->timestamp, oldConf->x, oldConf->y, mode,
oldConf->rotation, oldConf->outputs, oldConf->noutput);
if (s) {
XRRFreeCrtcInfo(oldConf);
XRRFreeOutputInfo(tgtOutInfo);
XRRFreeScreenResources(scrRes);
return "Failed to set CRTC config";
}
// We don't move to absolute 0,0 because that may be in the area of a different output.
// Instead we preserved the corner of our CRTC; go to that.
XMoveWindow(Dpy, Win, oldConf->x, oldConf->y);
// Final cleanup
XRRFreeCrtcInfo(oldConf);
XRRFreeOutputInfo(tgtOutInfo);
XRRFreeScreenResources(scrRes);
}
m_bWasWindowed = false;
XRaiseWindow( Dpy, Win );
// We want to prevent the WM from catching anything that comes from the keyboard.
// We should do this every time on fullscreen and not only we entering from windowed mode because we could lose focus at resolution change and that will leave the user input locked.
XGrabKeyboard( Dpy, Win, True, GrabModeAsync, GrabModeAsync, CurrentTime );
while (XGrabKeyboard( Dpy, Win, True, GrabModeAsync, GrabModeAsync, CurrentTime ));
}
else
else // if(p.windowed)
{
if( !m_bWasWindowed )
{
XRRSetScreenConfig( Dpy, g_pScreenConfig, RootWindow(Dpy, DefaultScreen(Dpy)), g_iOldSize, g_OldRotation, CurrentTime );
// In windowed mode, we actually want the WM to function normally.
// Release any previous grab.
// Return the display to the mode it was in before we fullscreened.
RestoreOutputConfig();
XUngrabKeyboard( Dpy, CurrentTime );
m_bWasWindowed = true;
}
Atom net_wm_state = XInternAtom( Dpy, "_NET_WM_STATE", False );
Atom fullscreen_state = XInternAtom( Dpy, "_NET_WM_STATE_FULLSCREEN", False );
Atom maximized_vert = XInternAtom( Dpy, "_NET_WM_STATE_MAXIMIZED_VERT", False );
Atom maximized_horz = XInternAtom( Dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", False );
// if FSBW, find matching monitor, move window to its origin,
// then set fullscreen hint, and set the CurrentParams.outWidth, CurrentParams.outHeight to the values of that display
// otherwise set the size hints and disable MAXIMIZED_*
if (p.bWindowIsFullscreenBorderless)
{
auto specs = DisplaySpecs{};
GetDisplaySpecs( specs );
auto target = std::find_if( specs.begin(), specs.end(), [&]( const DisplaySpec &spec ) {
return p.sDisplayId == spec.id() && spec.currentMode() != nullptr;
} );
// If we didn't find a matching DisplaySpec for the requested ID, pick the first one with a current mode
if (target == specs.end())
{
target = std::find_if( specs.begin(), specs.end(), [&]( const DisplaySpec &spec ) {
return spec.currentMode() != nullptr;
} );
}
// If we _still_ haven't found anything (unlikely), then just give up
if (target == specs.end())
{
return "Unable to find destination monitor for fullscreen borderless";
}
windowWidth = target->currentMode()->width;
windowHeight = target->currentMode()->height;
if (windowWidth != p.width || windowHeight != p.height)
{
renderOffscreen = true;
}
// Reset anything that might've been set previously:
// (1) Undo Min/Max size bounds
// (2) Remove FULLSCREEN/MAXIMIZED_{HORIZ,VERT} hints
// Without doing this, WM may not let us move/resize window to new display
// Give Window manager the chance to react to changes (otherwise, Mutter had problems
// properly reacting to moving a _NET_WM_STATE_FULLSCREEN window to a different output
// and fullscreen resetting FULLSCREEN hint.
XSizeHints hints;
hints.flags = 0;
XSetWMNormalHints( Dpy, Win, &hints );
#if defined(HAVE_XINERAMA)
if (!g_bUseXinerama || !SetWMFullscreenMonitors( *target ))
#endif
{
SetWMState( winAttrib.root, Win, 0, maximized_horz );
SetWMState( winAttrib.root, Win, 0, maximized_vert );
SetWMState( winAttrib.root, Win, 0, fullscreen_state );
XFlush( Dpy );
XResizeWindow( Dpy, Win, static_cast<unsigned int> (windowWidth), static_cast<unsigned int> (windowHeight) );
XMoveWindow( Dpy, Win, target->currentBounds().left, target->currentBounds().top );
XRaiseWindow( Dpy, Win );
SetWMState( winAttrib.root, Win, 1, fullscreen_state );
SetWMState( winAttrib.root, Win, 1, maximized_horz );
SetWMState( winAttrib.root, Win, 1, maximized_vert );
}
} else
{
windowWidth = p.width;
windowHeight = p.height;
SetWMState( winAttrib.root, Win, 0, fullscreen_state );
// Make a window fixed size, don't let resize it or maximize it.
// Do this before resizing the window so that pane-style WMs (Ion,
// ratpoison) don't resize us back inappropriately.
{
XSizeHints hints;
hints.flags = PMinSize|PMaxSize|PWinGravity;
hints.min_width = hints.max_width = windowWidth;
hints.min_height = hints.max_height = windowHeight;
hints.win_gravity = CenterGravity;
XSetWMNormalHints( Dpy, Win, &hints );
}
/* Workaround for metacity and compiz: if the window have the same
* resolution or higher than the screen, it gets automaximized even
* when the window is set to not let it happen. This happens when
* changing from fullscreen to window mode and our screen resolution
* is bigger. */
{
SetWMState( winAttrib.root, Win, 1, maximized_vert );
SetWMState( winAttrib.root, Win, 1, maximized_horz );
// This one is needed for compiz, if the window reaches out of bounds of the screen it becames destroyed, only the window, the program is left running.
// Commented out per the patch at http://ssc.ajworld.net/sm-ssc/bugtracker/view.php?id=398
//XMoveWindow( Dpy, Win, 0, 0 );
}
}
}
// NOTE: nVidia's implementation of this is broken by default.
// The only ways around this are mucking with xorg.conf or querying
// nvidia-settings with "$ nvidia-settings -t -q RefreshRate".
int rate = XRRConfigCurrentRate( g_pScreenConfig );
// Make a window fixed size, don't let resize it or maximize it.
// Do this before resizing the window so that pane-style WMs (Ion,
// ratpoison) don't resize us back inappropriately.
{
XSizeHints hints;
hints.flags = PMinSize|PMaxSize|PWinGravity;
hints.min_width = hints.max_width = p.width;
hints.min_height = hints.max_height = p.height;
hints.win_gravity = CenterGravity;
XSetWMNormalHints( Dpy, Win, &hints );
}
/* Workaround for metacity and compiz: if the window have the same
* resolution or higher than the screen, it gets automaximized even
* when the window is set to not let it happen. This happens when
* changing from fullscreen to window mode and our screen resolution
* is bigger. */
{
XEvent xev;
Atom wm_state = XInternAtom(Dpy, "_NET_WM_STATE", False);
Atom maximized_vert = XInternAtom(Dpy, "_NET_WM_STATE_MAXIMIZED_VERT", False);
Atom maximized_horz = XInternAtom(Dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
xev.xclient.window = Win;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = 1;
xev.xclient.data.l[1] = maximized_vert;
xev.xclient.data.l[2] = 0;
XSendEvent(Dpy, DefaultRootWindow(Dpy), False, SubstructureNotifyMask, &xev);
xev.xclient.data.l[1] = maximized_horz;
XSendEvent(Dpy, DefaultRootWindow(Dpy), False, SubstructureNotifyMask, &xev);
// This one is needed for compiz, if the window reaches out of bounds of the screen it becames destroyed, only the window, the program is left running.
// Commented out per the patch at http://ssc.ajworld.net/sm-ssc/bugtracker/view.php?id=398
//XMoveWindow( Dpy, Win, 0, 0 );
}
// Resize the window.
XResizeWindow( Dpy, Win, p.width, p.height );
CurrentParams = p;
CurrentParams.rate = rate;
CurrentParams.windowWidth = windowWidth;
CurrentParams.windowHeight = windowHeight;
CurrentParams.renderOffscreen = renderOffscreen;
ASSERT( rate > 0 );
CurrentParams.rate = static_cast<int> (roundf(rate));
if (!p.windowed)
{
// Set our V-sync hint.
if (GLXEW_EXT_swap_control) // I haven't seen this actually implemented yet, but why not.
glXSwapIntervalEXT( Dpy, Win, CurrentParams.vsync ? 1 : 0 );
// XXX: These two might be server-global. I should look into whether
// to try to preserve the original value on exit.
#ifdef GLXEW_MESA_swap_control // Added in 1.7. 1.6 is still common out there apparently.
else if(GLXEW_MESA_swap_control) // Haven't seen this NOT implemented yet
glXSwapIntervalMESA( CurrentParams.vsync ? 1 : 0 );
#endif
else if (GLXEW_SGI_swap_control) // But old GLEW.
glXSwapIntervalSGI( CurrentParams.vsync ? 1 : 0 );
else
CurrentParams.vsync = false; // Assuming it's not on
}
return ""; // Success
}
@@ -332,16 +635,114 @@ void LowLevelWindow_X11::SwapBuffers()
}
}
void LowLevelWindow_X11::GetDisplayResolutions( DisplayResolutions &out ) const
{
int iSizesXct;
XRRScreenSize *pSizesX = XRRSizes( Dpy, DefaultScreen( Dpy ), &iSizesXct );
ASSERT_M( iSizesXct != 0, "Couldn't get resolution list from X server" );
void LowLevelWindow_X11::GetDisplaySpecs(DisplaySpecs &out) const {
int screenNum = DefaultScreen(Dpy);
Screen *screen = ScreenOfDisplay(Dpy, screenNum);
for( int i = 0; i < iSizesXct; ++i )
{
DisplayResolution res = { pSizesX[i].width, pSizesX[i].height, true };
out.insert( res );
XWindowAttributes winAttr = XWindowAttributes();
if (XGetWindowAttributes(Dpy, Win, &winAttr)) {
screen = winAttr.screen;
screenNum = XScreenNumberOfScreen(screen);
}
// Create a display spec for the entire X screen itself
// First get current config
Rotation curRotation;
XRRScreenConfiguration *screenConf = XRRGetScreenInfo(Dpy, Win);
const short curRate = XRRConfigCurrentRate(screenConf);
SizeID curSizeId = XRRConfigCurrentConfiguration(screenConf, &curRotation);
// curRotation does not factor into how we report supported XScreen sizes:
// XRR reports the supported *screen* sizes with height/width swapped appropriately
// for currently configured rotation. Supported sizes for *output* modes (below)
// DO NOT account for screen rotation
std::set<DisplayMode> screenModes;
int nsizes = 0;
XRRScreenSize *screenSizes = XRRSizes( Dpy, screenNum, &nsizes);
DisplayMode screenCurMode = {0};
for (unsigned int szIdx = 0, mode_idx = 0; szIdx < nsizes; ++szIdx) {
XRRScreenSize &size = screenSizes[szIdx];
int nrates = 0;
short *rates = XRRRates(Dpy, screenNum, szIdx, &nrates);
for (unsigned int rIdx = 0; rIdx < nrates; ++rIdx, ++mode_idx) {
DisplayMode m = {static_cast<unsigned int> (size.width), static_cast<unsigned int> (size.height), static_cast<double> (rates[rIdx])};
screenModes.insert(m);
if (rates[rIdx] == curRate && szIdx == curSizeId) {
screenCurMode = m;
}
}
}
const RectI screenBounds( 0, 0, screenSizes[curSizeId].width, screenSizes[curSizeId].height);
const DisplaySpec screenSpec( ID_XSCREEN, "X Screen", screenModes, screenCurMode, screenBounds, true);
out.insert(screenSpec);
// XRRScreenSize array from XRRSizes does *not* have to be returned (valgrind said XFree was an invalid
// free in a small test program, there is no XRRFreeScreenSize, etc)
XRRFreeScreenConfigInfo(screenConf);
if (g_bUseXRandR12) {
// Build per-output DisplaySpecs
// First, get the list of resolutions that'll be referenced (by RRMode) in each
// OutputInfo
XRRScreenResources *scrRes = XRRGetScreenResources(Dpy, Win);
std::map<RRMode, DisplayMode> outputModes;
for (unsigned int i = 0; i < scrRes->nmode; ++i) {
const XRRModeInfo &mode = scrRes->modes[i];
DisplayMode m = {mode.width, mode.height,
calcRandRRefresh(mode.dotClock, mode.hTotal, mode.vTotal)};
outputModes[mode.id] = m;
}
// Now, for each output, build a corresponding DisplaySpec
for (unsigned int outIdx = 0; outIdx < scrRes->noutput; ++outIdx)
{
XRROutputInfo *outInfo = XRRGetOutputInfo( Dpy, scrRes, scrRes->outputs[outIdx] );
if (outInfo->nmode > 0)
{
// Get the current configuration of the Output, if it's being driven by
// a crtc
RRMode curRRMode = None;
bool bPortrait = false;
int crtcX = 0, crtcY = 0;
if (outInfo->crtc != None)
{
XRRCrtcInfo *conf = XRRGetCrtcInfo( Dpy, scrRes, outInfo->crtc );
curRRMode = conf->mode;
bPortrait = (conf->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0;
crtcX = conf->x;
crtcY = conf->y;
XRRFreeCrtcInfo( conf );
}
// Get all supported modes, noting which one, if any, is currently active
std::set<DisplayMode> outputSupported;
DisplayMode outputCurMode = {0};
RectI outBounds;
for (unsigned int modeIdx = 0; modeIdx < outInfo->nmode; ++modeIdx)
{
DisplayMode mode = outputModes[outInfo->modes[modeIdx]];
unsigned int modeWidth = bPortrait ? mode.height : mode.width;
unsigned int modeHeight = bPortrait ? mode.width : mode.height;
DisplayMode m = {modeWidth, modeHeight, mode.refreshRate};
outputSupported.insert( m );
if (curRRMode != None && outInfo->modes[modeIdx] == curRRMode)
{
outputCurMode = m;
outBounds = RectI( crtcX, crtcY, crtcX + modeWidth, crtcY + modeHeight);
}
}
const std::string outId( outInfo->name, static_cast<unsigned int> (outInfo->nameLen) );
const std::string outName( outId );
if (curRRMode != None)
{
out.insert( DisplaySpec( outId, outName, outputSupported, outputCurMode, outBounds ));
} else
{
out.insert( DisplaySpec( outId, outName, outputSupported ));
}
}
XRRFreeOutputInfo( outInfo );
}
XRRFreeScreenResources( scrRes );
}
}
@@ -512,6 +913,34 @@ bool LowLevelWindow_X11::SupportsRenderToTexture() const
return true;
}
bool NetWMSupported(Display *Dpy, Atom feature)
{
Atom net_supported = XInternAtom( Dpy, "_NET_SUPPORTED", False );
Atom actual_type_return = BadAtom;
int actual_format_return = 0;
unsigned long nitems_return = 0;
unsigned long bytes_after_return = 0;
Atom *prop_return;
Status status = XGetWindowProperty( Dpy, RootWindow( Dpy, DefaultScreen( Dpy )), net_supported, 0, 8192, False,
XA_ATOM, &actual_type_return,
&actual_format_return, &nitems_return, &bytes_after_return,
reinterpret_cast<unsigned char **> (&prop_return));
if (status != Success)
{
return false;
}
auto supported = std::find( prop_return, prop_return + nitems_return, feature ) != prop_return + nitems_return;
XFree( prop_return );
return supported;
}
bool LowLevelWindow_X11::SupportsFullscreenBorderlessWindow() const
{
Atom fullscreen = XInternAtom( Dpy, "_NET_WM_STATE_FULLSCREEN", False );
return NetWMSupported( Dpy, fullscreen );
}
RenderTarget *LowLevelWindow_X11::CreateRenderTarget()
{
return new RenderTarget_X11( this );
+7 -3
View File
@@ -18,13 +18,15 @@ public:
bool IsSoftwareRenderer( RString &sError );
void SwapBuffers();
const VideoModeParams &GetActualVideoModeParams() const { return CurrentParams; }
const ActualVideoModeParams GetActualVideoModeParams() const { return CurrentParams; }
void GetDisplayResolutions( DisplayResolutions &out ) const;
void GetDisplaySpecs(DisplaySpecs &out) const;
bool SupportsRenderToTexture() const;
RenderTarget *CreateRenderTarget();
bool SupportsFullscreenBorderlessWindow() const;
bool SupportsThreadedRendering();
void BeginConcurrentRenderingMainThread();
void EndConcurrentRenderingMainThread();
@@ -32,8 +34,10 @@ public:
void EndConcurrentRendering();
private:
void RestoreOutputConfig();
bool m_bWasWindowed;
VideoModeParams CurrentParams;
ActualVideoModeParams CurrentParams;
};
#ifdef ARCH_LOW_LEVEL_WINDOW
-1
View File
@@ -5,7 +5,6 @@
extern "C" int sm_main( int argc, char *argv[] );
#define HAVE_CXA_DEMANGLE
#define HAVE_PTHREAD_COND_TIMEDWAIT
/* This must be defined to 1 because autoconf's AC_CHECK_DECLS macro decides to define
* this in all cases. If only they could be consistent... */
#define HAVE_DECL_SIGUSR1 1
+96
View File
@@ -129,6 +129,23 @@ bool X11Helper::MakeWindow( Window &win, int screenNum, int depth, Visual *visua
return true;
}
void X11Helper::SetWMState( const Window &root, const Window &win, const long action, const Atom atom )
{
if ( !Dpy )
return;
Atom wm_state = XInternAtom(Dpy, "_NET_WM_STATE", False);
XEvent xev;
memset( &xev, 0, sizeof( xev ));
xev.type = ClientMessage;
xev.xclient.window = Win;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = action; // 0 = Remove, 1 = Add, 2 = Toggle
xev.xclient.data.l[1] = atom;
xev.xclient.data.l[2] = 0; // end list of Atoms
XSendEvent( Dpy, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev );
}
int ErrorCallback( Display *d, XErrorEvent *err )
{
char errText[512];
@@ -144,6 +161,85 @@ int FatalCallback( Display *d )
RageException::Throw( "Fatal I/O error communicating with X server." );
}
#ifdef HAVE_XINERAMA
#include <X11/extensions/Xinerama.h>
bool X11Helper::SetWMFullscreenMonitors( const DisplaySpec &target )
{
int num_screens = 0;
XineramaScreenInfo *screens = XineramaQueryScreens( Dpy, &num_screens );
if (screens == nullptr)
{
return false;
}
XineramaScreenInfo *end = screens + num_screens;
RectI monitors{};
bool found_bounds = false;
if (target.isVirtual())
{
auto topmost = std::min_element( screens, end, []( XineramaScreenInfo &a, XineramaScreenInfo &b ) {
return a.y_org < b.y_org;
} );
monitors.top = topmost->screen_number;
auto bottommost = std::max_element( screens, end, []( XineramaScreenInfo &a, XineramaScreenInfo &b ) {
return a.y_org < b.y_org;
} );
monitors.bottom = bottommost->screen_number;
auto leftmost = std::min_element( screens, end, []( XineramaScreenInfo &a, XineramaScreenInfo &b ) {
return a.x_org < b.x_org;
} );
monitors.left = leftmost->screen_number;
auto rightmost = std::max_element( screens, end, []( XineramaScreenInfo &a, XineramaScreenInfo &b ) {
return a.x_org < b.x_org;
} );
monitors.right = rightmost->screen_number;
found_bounds = true;
}
else if (target.currentMode() != nullptr)
{
auto mon = std::find_if( screens, end, [&]( XineramaScreenInfo &screen ) {
return screen.x_org == target.currentBounds().left && screen.y_org == target.currentBounds().top
&& screen.width == target.currentMode()->width && screen.height == target.currentMode()->height;
} );
if (mon != end)
{
monitors.left = monitors.right = monitors.top = monitors.bottom = mon->screen_number;
found_bounds = true;
}
}
XFree( screens );
XWindowAttributes attr = {0};
if (!found_bounds || !XGetWindowAttributes( Dpy, Win, &attr ))
{
return false;
}
SetWMState( attr.root, Win, 1, XInternAtom( Dpy, "_NET_WM_STATE_FULLSCREEN", False ));
XClientMessageEvent xclient = {0};
xclient.type = ClientMessage;
xclient.window = Win;
xclient.message_type = XInternAtom( Dpy, "_NET_WM_FULLSCREEN_MONITORS", False );
xclient.format = 32;
xclient.data.l[0] = monitors.top;
xclient.data.l[1] = monitors.bottom;
xclient.data.l[2] = monitors.left;
xclient.data.l[3] = monitors.right;
xclient.data.l[4] = 1;
XSendEvent( Dpy, attr.root, False, SubstructureRedirectMask | SubstructureNotifyMask,
reinterpret_cast<XEvent *> (&xclient));
XFlush( Dpy );
return true;
}
#endif
/*
* (c) 2005, 2006 Ben Anderson, Steve Checkoway
* All rights reserved.
+8
View File
@@ -4,6 +4,8 @@
#include <X11/Xlib.h> // Window
#include <X11/Xutil.h>
#include <DisplaySpec.h>
namespace X11Helper
{
// All functions in here that return a bool return true on success, and
@@ -25,6 +27,12 @@ namespace X11Helper
// (Re)create the Window win.
bool MakeWindow( Window &win, int screenNum, int depth, Visual *visual,
int width, int height, bool overrideRedirect );
void SetWMState( const Window &root, const Window &win, const long action, const Atom atom );
#ifdef HAVE_XINERAMA
bool SetWMFullscreenMonitors( const DisplaySpec &target );
#endif
};
#endif
+44 -20
View File
@@ -4,7 +4,7 @@
#include "RageLog.h"
#include "RageUtil.h"
#include "RageDisplay.h"
#include "DisplayResolutions.h"
#include "DisplaySpec.h"
#include "arch/ArchHooks/ArchHooks.h"
#include "archutils/Win32/AppInstance.h"
#include "archutils/Win32/Crash.h"
@@ -539,29 +539,53 @@ HWND GraphicsWindow::GetHwnd()
return g_hWndMain;
}
void GraphicsWindow::GetDisplayResolutions( DisplayResolutions &out )
void GraphicsWindow::GetDisplaySpecs( DisplaySpecs &out )
{
DEVMODE dm;
ZERO( dm );
dm.dmSize = sizeof(dm);
int i=0;
while(EnumDisplaySettings(NULL, i++, &dm))
{
const size_t DM_DRIVER_EXTRA_BYTES = 4096;
const size_t DMSIZE = sizeof( DEVMODE ) + DM_DRIVER_EXTRA_BYTES;
auto reset = [=]( std::unique_ptr<DEVMODE> &p ) {
::memset( p.get(), 0, DMSIZE );
p->dmSize = sizeof( DEVMODE );
p->dmDriverExtra = static_cast<WORD> (DM_DRIVER_EXTRA_BYTES);
};
auto isvalid = []( std::unique_ptr<DEVMODE> &dm ) {
// Windows 8 and later don't support less than 32bpp, so don't even test
// for them. GetDisplayResolutions is only for resolutions anyway. -Kyz
if(dm.dmBitsPerPel < 32)
// for them. GetDisplaySpecs only tracks resolution/refresh rate anyway. -Kyz, drewbarbs
return (dm->dmFields & DM_PELSWIDTH) && (dm->dmFields & DM_PELSHEIGHT) && (dm->dmFields & DM_DISPLAYFREQUENCY)
&& (dm->dmBitsPerPel >= 32 || !(dm->dmFields & DM_BITSPERPEL));
};
std::unique_ptr<DEVMODE> dm( static_cast<DEVMODE*> (operator new(DMSIZE)) );
reset( dm );
int i = 0;
std::set<DisplayMode> modes;
while ( EnumDisplaySettingsEx( nullptr, i++, dm.get(), 0 ) )
{
if ( isvalid( dm ) && ChangeDisplaySettingsEx( nullptr, dm.get(), nullptr, CDS_TEST, nullptr ) == DISP_CHANGE_SUCCESSFUL )
{
continue;
}
DisplayResolution res = { dm.dmPelsWidth, dm.dmPelsHeight };
std::set<DisplayResolution>::iterator entry= out.find(res);
if(entry == out.end())
{
if(ChangeDisplaySettings(&dm, CDS_TEST)==DISP_CHANGE_SUCCESSFUL)
{
out.insert(res);
}
DisplayMode m = { dm->dmPelsWidth, dm->dmPelsHeight, static_cast<double> (dm->dmDisplayFrequency) };
modes.insert(m);
}
reset( dm );
}
reset( dm );
// Get the current display mode
if ( EnumDisplaySettingsEx( nullptr, ENUM_CURRENT_SETTINGS, dm.get(), 0 ) && isvalid( dm ) )
{
DisplayMode m = { dm->dmPelsWidth, dm->dmPelsHeight, static_cast<double> (dm->dmDisplayFrequency) };
RectI bounds = { 0, 0, static_cast<int> (m.width), static_cast<int> (m.height) };
out.insert( DisplaySpec( "", "Fullscreen", modes, m, bounds ) );
}
else if ( !modes.empty() )
{
LOG->Warn( "Could not retrieve valid current display mode" );
out.insert( DisplaySpec( "", "Fullscreen", *modes.begin() ) );
}
else
{
LOG->Warn( "Could not retrieve *any* DisplaySpec's!" );
}
}
+2 -2
View File
@@ -2,7 +2,7 @@
#define GRAPHICS_WINDOW_H
#include <windows.h>
#include "DisplayResolutions.h"
#include "DisplaySpec.h"
class VideoModeParams;
class DisplayResolution;
@@ -28,7 +28,7 @@ namespace GraphicsWindow
void CreateGraphicsWindow( const VideoModeParams &p, bool bForceRecreateWindow = false );
void DestroyGraphicsWindow();
void GetDisplayResolutions( DisplayResolutions &out );
void GetDisplaySpecs( DisplaySpecs &out );
const VideoModeParams &GetParams();
HDC GetHDC();