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:
committed by
Colby Klein
parent
7ef14c340d
commit
557be7cf1b
@@ -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
|
||||
Reference in New Issue
Block a user