mirror of
https://github.com/SK-la/osu-framework.git
synced 2026-03-15 03:20:30 +00:00
Initial commit.
This commit is contained in:
19
.gitattributes
vendored
Normal file
19
.gitattributes
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# This won't normalise line endings, but it will ensure that merge drivers use CRLF
|
||||
* -text eol=crlf
|
||||
|
||||
# Currently in-use binary file extensions
|
||||
*.blend binary
|
||||
*.bmp binary
|
||||
*.dll binary
|
||||
*.exe binary
|
||||
*.icns binary
|
||||
*.ico binary
|
||||
*.jpg binary
|
||||
*.osz2 binary
|
||||
*.pdn binary
|
||||
*.psd binary
|
||||
*.PSD binary
|
||||
*.tga binary
|
||||
*.ttf binary
|
||||
*.wav binary
|
||||
*.xnb binary
|
||||
258
.gitignore
vendored
Normal file
258
.gitignore
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
2
.travis.yml
Normal file
2
.travis.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
language: csharp
|
||||
solution: osu-framework.sln
|
||||
21
LICENCE
Normal file
21
LICENCE
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2007-2016 ppy Pty Ltd https://osu.ppy.sh
|
||||
|
||||
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, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
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. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
19
README.md
Normal file
19
README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# osu-framework
|
||||
[dev chat](https://discord.gg/ppy)
|
||||
|
||||
A game framework written with osu! in mind.
|
||||
|
||||
# Requirements
|
||||
|
||||
- A desktop platform which can compile .NET 4.5.
|
||||
- Visual Studio or MonoDevelop is recommended.
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://goo.gl/nFdoyI). If you're unsure of what you can help with, check out the [list](https://github.com/ppy/osu-framework/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Abounty) of available issues with bounty.
|
||||
|
||||
Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible.
|
||||
|
||||
# Licence
|
||||
|
||||
This framework is licensed under the [MIT licence](https://opensource.org/licenses/MIT). Please see [the licence file](LICENCE) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source.
|
||||
9
osu-framework.licenseheader
Normal file
9
osu-framework.licenseheader
Normal file
@@ -0,0 +1,9 @@
|
||||
extensions: .cs
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
extensions: .xml .config .xsd
|
||||
<!--
|
||||
Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
-->
|
||||
33
osu-framework.sln
Normal file
33
osu-framework.sln
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework", "osu.Framework\osu.Framework.csproj", "{C76BF5B3-985E-4D39-95FE-97C9C879B83A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Framework.Desktop", "osu.Framework.Desktop\osu.Framework.Desktop.csproj", "{65DC628F-A640-4111-AB35-3A5652BC1E17}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Deploy|Any CPU = Deploy|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Deploy|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Deploy|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C76BF5B3-985E-4D39-95FE-97C9C879B83A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{65DC628F-A640-4111-AB35-3A5652BC1E17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{65DC628F-A640-4111-AB35-3A5652BC1E17}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{65DC628F-A640-4111-AB35-3A5652BC1E17}.Deploy|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{65DC628F-A640-4111-AB35-3A5652BC1E17}.Deploy|Any CPU.Build.0 = Debug|Any CPU
|
||||
{65DC628F-A640-4111-AB35-3A5652BC1E17}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{65DC628F-A640-4111-AB35-3A5652BC1E17}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
26
osu.Framework.Desktop/Host.cs
Normal file
26
osu.Framework.Desktop/Host.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Desktop.OS.Linux;
|
||||
using osu.Framework.Desktop.OS.Windows;
|
||||
using osu.Framework.Framework;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Framework.Desktop
|
||||
{
|
||||
public static class Host
|
||||
{
|
||||
public static BasicGameHost GetSuitableHost()
|
||||
{
|
||||
BasicGameHost host = null;
|
||||
|
||||
GraphicsContextFlags flags = GraphicsContextFlags.Default;
|
||||
if (RuntimeInfo.IsLinux)
|
||||
host = new LinuxGameHost(flags);
|
||||
else
|
||||
host = new WindowsGameHost(flags);
|
||||
|
||||
return host;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
osu.Framework.Desktop/ManagedBass.PInvoke.dll.config
Normal file
11
osu.Framework.Desktop/ManagedBass.PInvoke.dll.config
Normal file
@@ -0,0 +1,11 @@
|
||||
<!--
|
||||
Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<dllmap os="linux" dll="bass" target="libbass.x86.so" wordsize="32"/>
|
||||
<dllmap os="linux" dll="bass_fx" target="libbass_fx.x86.so" wordsize="32"/>
|
||||
<dllmap os="linux" dll="bass" target="libbass.x64.so" wordsize="64"/>
|
||||
<dllmap os="linux" dll="bass_fx" target="libbass_fx.x64.so" wordsize="64"/>
|
||||
</configuration>
|
||||
43
osu.Framework.Desktop/OS/DesktopGameWindow.cs
Normal file
43
osu.Framework.Desktop/OS/DesktopGameWindow.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using osu.Framework.Desktop.OS.Windows;
|
||||
using osu.Framework.Framework;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Framework.Desktop.OS
|
||||
{
|
||||
public abstract class DesktopGameWindow : BasicGameWindow
|
||||
{
|
||||
public override BasicGameForm Form { get; }
|
||||
|
||||
public override Rectangle ClientBounds => Form.ClientBounds;
|
||||
public override bool IsMinimized => Form.IsMinimized;
|
||||
public override IntPtr Handle => Form.Handle;
|
||||
|
||||
internal DesktopGameWindow(GraphicsContextFlags flags)
|
||||
{
|
||||
Form = CreateGameForm(flags);
|
||||
Form.ScreenChanged += delegate { OnScreenDeviceNameChanged(); };
|
||||
Form.ApplicationActivated += delegate { OnActivated(); };
|
||||
Form.ApplicationDeactivated += delegate { OnDeactivated(); };
|
||||
Form.SizeChanged += delegate { OnClientSizeChanged(); };
|
||||
Form.Closing += delegate { OnDeactivated(); };
|
||||
Form.Paint += delegate { OnPaint(); };
|
||||
}
|
||||
|
||||
protected abstract BasicGameForm CreateGameForm(GraphicsContextFlags flags);
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
Form.Close();
|
||||
}
|
||||
|
||||
protected override void SetTitle(string title)
|
||||
{
|
||||
Form.Text = title;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
osu.Framework.Desktop/OS/Linux/LinuxGameForm.cs
Normal file
65
osu.Framework.Desktop/OS/Linux/LinuxGameForm.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using osu.Framework.Framework;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Linux
|
||||
{
|
||||
public class LinuxGameForm : BasicGameForm
|
||||
{
|
||||
public override bool IsMinimized => ClientSize.Width != 0 || ClientSize.Height == 0;
|
||||
|
||||
public override event EventHandler ScreenChanged;
|
||||
|
||||
private Screen screen;
|
||||
|
||||
internal LinuxGameForm(GraphicsContextFlags flags) : base(flags)
|
||||
{
|
||||
SuspendLayout();
|
||||
CausesValidation = false;
|
||||
ClientSize = new Size(1, 1);
|
||||
BackColor = Color.Black;
|
||||
ResumeLayout(false);
|
||||
|
||||
LocationChanged += delegate { updateScreen(); };
|
||||
ClientSizeChanged += delegate { updateScreen(); };
|
||||
}
|
||||
|
||||
protected override bool ProcessDialogKey(Keys keyData)
|
||||
{
|
||||
//stop alt/f10 from freezing form rendering.
|
||||
if (((keyData & Keys.Alt) == Keys.Alt && (keyData & Keys.F4) != Keys.F4) || (keyData & Keys.F10) == Keys.F10)
|
||||
return true;
|
||||
return base.ProcessDialogKey(keyData);
|
||||
}
|
||||
|
||||
public override Rectangle ClientBounds
|
||||
{
|
||||
get
|
||||
{
|
||||
Point point = PointToScreen(Point.Empty);
|
||||
return new Rectangle(point.X, point.Y, ClientSize.Width, ClientSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
public override void CentreToScreen()
|
||||
{
|
||||
CenterToScreen();
|
||||
}
|
||||
|
||||
private void updateScreen()
|
||||
{
|
||||
var screen = Screen.FromHandle(Handle);
|
||||
if ((this.screen == null) || !this.screen.Equals(screen))
|
||||
{
|
||||
this.screen = screen;
|
||||
if (this.screen != null)
|
||||
ScreenChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
osu.Framework.Desktop/OS/Linux/LinuxGameHost.cs
Normal file
39
osu.Framework.Desktop/OS/Linux/LinuxGameHost.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using osu.Framework.Desktop.OS.Windows.Native;
|
||||
using osu.Framework.Framework;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Linux
|
||||
{
|
||||
public class LinuxGameHost : BasicGameHost
|
||||
{
|
||||
public override BasicGameWindow Window => window;
|
||||
public override GLControl GLControl => window.Form;
|
||||
public override bool IsActive => true; // TODO LINUX
|
||||
|
||||
private LinuxGameWindow window;
|
||||
|
||||
internal LinuxGameHost(GraphicsContextFlags flags)
|
||||
{
|
||||
window = new LinuxGameWindow(flags);
|
||||
|
||||
Window.Activated += OnActivated;
|
||||
Window.Deactivated += OnDeactivated;
|
||||
}
|
||||
|
||||
protected override void OnActivated(object sender, EventArgs args)
|
||||
{
|
||||
Execution.SetThreadExecutionState(Execution.ExecutionState.Continuous | Execution.ExecutionState.SystemRequired | Execution.ExecutionState.DisplayRequired);
|
||||
base.OnActivated(sender, args);
|
||||
}
|
||||
|
||||
protected override void OnDeactivated(object sender, EventArgs args)
|
||||
{
|
||||
base.OnDeactivated(sender, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
osu.Framework.Desktop/OS/Linux/LinuxGameWindow.cs
Normal file
21
osu.Framework.Desktop/OS/Linux/LinuxGameWindow.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Framework;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Linux
|
||||
{
|
||||
public class LinuxGameWindow : DesktopGameWindow
|
||||
{
|
||||
public LinuxGameWindow(GraphicsContextFlags flags)
|
||||
: base(flags)
|
||||
{
|
||||
}
|
||||
|
||||
protected override BasicGameForm CreateGameForm(GraphicsContextFlags flags)
|
||||
{
|
||||
return new LinuxGameForm(flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
osu.Framework.Desktop/OS/Windows/Native/Architecture.cs
Normal file
25
osu.Framework.Desktop/OS/Windows/Native/Architecture.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Windows.Native
|
||||
{
|
||||
internal static class Architecture
|
||||
{
|
||||
private static string nativeIncludePath => $@"{Environment.CurrentDirectory}/{arch}/";
|
||||
private static string arch => Is64Bit ? @"x64" : @"x86";
|
||||
|
||||
internal static bool Is64Bit => IntPtr.Size == 8;
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
static extern bool SetDllDirectory(string lpPathName);
|
||||
|
||||
internal static void SetIncludePath()
|
||||
{
|
||||
SetDllDirectory(nativeIncludePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
317
osu.Framework.Desktop/OS/Windows/Native/Desktop.cs
Normal file
317
osu.Framework.Desktop/OS/Windows/Native/Desktop.cs
Normal file
@@ -0,0 +1,317 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Runtime.InteropServices;
|
||||
using osu.Framework.Framework;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Windows.Native
|
||||
{
|
||||
static class Desktop
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool EnumDisplaySettings(string deviceName, int modeNum, ref DeviceMode devMode);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int ChangeDisplaySettingsEx(string deviceName, ref DeviceMode devMode, IntPtr hWnd, int flags, IntPtr lParam);
|
||||
|
||||
/// <summary>
|
||||
/// Prototype required for providing the null pointer as devmode for resolution reset.
|
||||
/// </summary>
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int ChangeDisplaySettingsEx(string deviceName, IntPtr devMode, IntPtr hWnd, int flags, IntPtr lParam);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern bool GetMonitorInfo(IntPtr hMonitor, ref MonitorInfo monitorInfo);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
|
||||
|
||||
const int MONITOR_DEFAULTTONULL = 0;
|
||||
const int MONITOR_DEFAULTTOPRIMARY = 1;
|
||||
const int MONITOR_DEFAULTTONEAREST = 2;
|
||||
|
||||
const int ENUM_REGISTRY_SETTINGS = -2;
|
||||
|
||||
internal const int CDS_FULLSCREEN = 0x04;
|
||||
internal const int CDS_TEST = 0x02;
|
||||
|
||||
internal const int DISP_CHANGE_FAILED = -1;
|
||||
internal const int DISP_CHANGE_RESTART = 1;
|
||||
internal const int DISP_CHANGE_SUCCESSFUL = 0;
|
||||
internal const int ENUM_CURRENT_SETTINGS = -1;
|
||||
|
||||
internal const int DM_BITSPERPEL = 0x00040000;
|
||||
internal const int DM_PELSWIDTH = 0x00080000;
|
||||
internal const int DM_PELSHEIGHT = 0x00100000;
|
||||
internal const int DM_DISPLAYFREQUENCY = 0x00400000;
|
||||
|
||||
private static Window window;
|
||||
|
||||
internal static void InitializeWindow(Window window)
|
||||
{
|
||||
Desktop.window = window;
|
||||
}
|
||||
|
||||
internal static bool ChangeResolution(int width, int height, int? refreshRate = null, bool testOnly = false)
|
||||
{
|
||||
MonitorInfo monitorInfo = new MonitorInfo() { Size = 72 };
|
||||
IntPtr monitor = MonitorFromWindow(Game.Window.Handle, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
GetMonitorInfo(monitor, ref monitorInfo);
|
||||
|
||||
DeviceMode dm = new DeviceMode();
|
||||
dm.dmSize = (short)Marshal.SizeOf(dm);
|
||||
|
||||
bool success = false;
|
||||
|
||||
if (!EnumDisplaySettings(monitorInfo.DeviceName, ENUM_CURRENT_SETTINGS, ref dm))
|
||||
return false;
|
||||
|
||||
// Are we already on the desired resolution? If yes we don't need to do anything
|
||||
if (dm.dmPelsWidth == width && dm.dmPelsHeight == height && (!refreshRate.HasValue || refreshRate.Value == dm.dmDisplayFrequency))
|
||||
return true;
|
||||
|
||||
// At this point we are sure we need a custom resolution change.
|
||||
dm.dmPelsWidth = width;
|
||||
dm.dmPelsHeight = height;
|
||||
|
||||
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
|
||||
|
||||
if (refreshRate.HasValue)
|
||||
{
|
||||
dm.dmDisplayFrequency = refreshRate.Value;
|
||||
dm.dmFields |= DM_DISPLAYFREQUENCY;
|
||||
}
|
||||
|
||||
success = ChangeDisplaySettingsEx(monitorInfo.DeviceName, ref dm, IntPtr.Zero, CDS_TEST, IntPtr.Zero) == DISP_CHANGE_SUCCESSFUL;
|
||||
|
||||
if (testOnly)
|
||||
return success;
|
||||
|
||||
success &= ChangeDisplaySettingsEx(monitorInfo.DeviceName, ref dm, IntPtr.Zero, CDS_FULLSCREEN, IntPtr.Zero) == DISP_CHANGE_SUCCESSFUL;
|
||||
return success;
|
||||
}
|
||||
|
||||
internal static bool ResetResolution()
|
||||
{
|
||||
MonitorInfo monitorInfo = new MonitorInfo() { Size = 72 };
|
||||
IntPtr monitor = MonitorFromWindow(window.Handle, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
GetMonitorInfo(monitor, ref monitorInfo);
|
||||
|
||||
DeviceMode dmCurrent = new DeviceMode();
|
||||
dmCurrent.dmSize = (short)Marshal.SizeOf(dmCurrent);
|
||||
|
||||
DeviceMode dmRegistry = new DeviceMode();
|
||||
dmRegistry.dmSize = (short)Marshal.SizeOf(dmRegistry);
|
||||
|
||||
if (!EnumDisplaySettings(monitorInfo.DeviceName, ENUM_CURRENT_SETTINGS, ref dmCurrent) || !EnumDisplaySettings(monitorInfo.DeviceName, ENUM_REGISTRY_SETTINGS, ref dmRegistry))
|
||||
return false;
|
||||
|
||||
// No need to reset if we already have the settings that we want.
|
||||
if (dmCurrent.dmPelsWidth == dmRegistry.dmPelsWidth && dmCurrent.dmPelsHeight == dmRegistry.dmPelsHeight && dmCurrent.dmDisplayFrequency == dmRegistry.dmDisplayFrequency)
|
||||
return true;
|
||||
|
||||
return ChangeDisplaySettingsEx(monitorInfo.DeviceName, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero) == DISP_CHANGE_SUCCESSFUL;
|
||||
}
|
||||
|
||||
internal static List<Size> GetResolutions()
|
||||
{
|
||||
MonitorInfo monitorInfo = new MonitorInfo() { Size = 72 };
|
||||
IntPtr monitor = MonitorFromWindow(window.Handle, MONITOR_DEFAULTTONEAREST);
|
||||
GetMonitorInfo(monitor, ref monitorInfo);
|
||||
|
||||
List<Size> results = new List<Size>();
|
||||
|
||||
DeviceMode vDevMode = new DeviceMode();
|
||||
|
||||
int i = 0;
|
||||
while (EnumDisplaySettings(monitorInfo.DeviceName, i++, ref vDevMode))
|
||||
results.Add(new Size(vDevMode.dmPelsWidth, vDevMode.dmPelsHeight));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
internal static Size FindNativeResolution()
|
||||
{
|
||||
List<Size> resolutions = GetResolutions();
|
||||
|
||||
Size native_res = new Size(640, 480);
|
||||
foreach (Size res in resolutions)
|
||||
if ((res.Width > native_res.Width) || ((res.Width == native_res.Width) && (res.Height > native_res.Height))) native_res = res;
|
||||
|
||||
return native_res;
|
||||
}
|
||||
|
||||
// http://www.dotnetspark.com/kb/1948-change-display-settings-programmatically.aspx
|
||||
private static DeviceMode? GetCurrentSettings()
|
||||
{
|
||||
MonitorInfo monitorInfo = new MonitorInfo() { Size = 72 };
|
||||
IntPtr monitor = MonitorFromWindow(window.Handle, MONITOR_DEFAULTTONEAREST);
|
||||
GetMonitorInfo(monitor, ref monitorInfo);
|
||||
|
||||
DeviceMode mode = new DeviceMode();
|
||||
mode.dmSize = (short)Marshal.SizeOf(mode);
|
||||
|
||||
if (EnumDisplaySettings(monitorInfo.DeviceName, ENUM_CURRENT_SETTINGS, ref mode) != true)
|
||||
return null;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
internal static int GetCurrentRefreshRate()
|
||||
{
|
||||
DeviceMode? mode = GetCurrentSettings();
|
||||
if (mode == null) return 0;
|
||||
|
||||
return ((DeviceMode)mode).dmDisplayFrequency;
|
||||
}
|
||||
|
||||
internal static Size GetDesktopResolution()
|
||||
{
|
||||
MonitorInfo monitorInfo = new MonitorInfo() { Size = 40 };
|
||||
|
||||
IntPtr monitor = MonitorFromWindow(window.Handle, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
GetMonitorInfo(monitor, ref monitorInfo);
|
||||
return new Size(monitorInfo.Monitor.Right - monitorInfo.Monitor.Left, monitorInfo.Monitor.Bottom - monitorInfo.Monitor.Top);
|
||||
}
|
||||
|
||||
internal static Point GetDesktopPosition()
|
||||
{
|
||||
MonitorInfo monitorInfo = new MonitorInfo() { Size = 40 };
|
||||
|
||||
IntPtr monitor = MonitorFromWindow(window.Handle, MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
GetMonitorInfo(monitor, ref monitorInfo);
|
||||
|
||||
return new Point(monitorInfo.Monitor.Left, monitorInfo.Monitor.Top);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct MonitorInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The size, in bytes, of the structure. Set this member to sizeof(MONITORINFO) (40) before calling the GetMonitorInfo function.
|
||||
/// Doing so lets the function determine the type of structure you are passing to it.
|
||||
/// </summary>
|
||||
internal int Size;
|
||||
|
||||
/// <summary>
|
||||
/// A RECT structure that specifies the display monitor rectangle, expressed in virtual-screen coordinates.
|
||||
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
|
||||
/// </summary>
|
||||
internal RectStruct Monitor;
|
||||
|
||||
/// <summary>
|
||||
/// A RECT structure that specifies the work area rectangle of the display monitor that can be used by applications,
|
||||
/// expressed in virtual-screen coordinates. Windows uses this rectangle to maximize an application on the monitor.
|
||||
/// The rest of the area in rcMonitor contains system windows such as the task bar and side bars.
|
||||
/// Note that if the monitor is not the primary display monitor, some of the rectangle's coordinates may be negative values.
|
||||
/// </summary>
|
||||
internal RectStruct WorkArea;
|
||||
|
||||
/// <summary>
|
||||
/// The attributes of the display monitor.
|
||||
///
|
||||
/// This member can be the following value:
|
||||
/// 1 : MONITORINFOF_PRIMARY
|
||||
/// </summary>
|
||||
internal uint Flags;
|
||||
|
||||
/// <summary>
|
||||
/// The monitor device name.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||
internal String DeviceName;
|
||||
|
||||
internal void Init()
|
||||
{
|
||||
this.Size = 72;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The RECT structure defines the coordinates of the upper-left and lower-right corners of a rectangle.
|
||||
/// </summary>
|
||||
/// <see cref="http://msdn.microsoft.com/en-us/library/dd162897%28VS.85%29.aspx"/>
|
||||
/// <remarks>
|
||||
/// By convention, the right and bottom edges of the rectangle are normally considered exclusive.
|
||||
/// In other words, the pixel whose coordinates are ( right, bottom ) lies immediately outside of the the rectangle.
|
||||
/// For example, when RECT is passed to the FillRect function, the rectangle is filled up to, but not including,
|
||||
/// the right column and bottom row of pixels. This structure is identical to the RECTL structure.
|
||||
/// </remarks>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct RectStruct
|
||||
{
|
||||
/// <summary>
|
||||
/// The x-coordinate of the upper-left corner of the rectangle.
|
||||
/// </summary>
|
||||
internal int Left;
|
||||
|
||||
/// <summary>
|
||||
/// The y-coordinate of the upper-left corner of the rectangle.
|
||||
/// </summary>
|
||||
internal int Top;
|
||||
|
||||
/// <summary>
|
||||
/// The x-coordinate of the lower-right corner of the rectangle.
|
||||
/// </summary>
|
||||
internal int Right;
|
||||
|
||||
/// <summary>
|
||||
/// The y-coordinate of the lower-right corner of the rectangle.
|
||||
/// </summary>
|
||||
internal int Bottom;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct DeviceMode
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||
internal string dmDeviceName;
|
||||
internal short dmSpecVersion;
|
||||
internal short dmDriverVersion;
|
||||
internal short dmSize;
|
||||
internal short dmDriverExtra;
|
||||
internal int dmFields;
|
||||
|
||||
internal short dmOrientation;
|
||||
internal short dmPaperSize;
|
||||
internal short dmPaperLength;
|
||||
internal short dmPaperWidth;
|
||||
|
||||
internal short dmScale;
|
||||
internal short dmCopies;
|
||||
internal short dmDefaultSource;
|
||||
internal short dmPrintQuality;
|
||||
internal short dmColor;
|
||||
internal short dmDuplex;
|
||||
internal short dmYResolution;
|
||||
internal short dmTTOption;
|
||||
internal short dmCollate;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
|
||||
internal string dmFormName;
|
||||
internal short dmLogPixels;
|
||||
internal short dmBitsPerPel;
|
||||
internal int dmPelsWidth;
|
||||
internal int dmPelsHeight;
|
||||
|
||||
internal int dmDisplayFlags;
|
||||
internal int dmDisplayFrequency;
|
||||
|
||||
internal int dmICMMethod;
|
||||
internal int dmICMIntent;
|
||||
internal int dmMediaType;
|
||||
internal int dmDitherType;
|
||||
internal int dmReserved1;
|
||||
internal int dmReserved2;
|
||||
|
||||
internal int dmPanningWidth;
|
||||
internal int dmPanningHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
osu.Framework.Desktop/OS/Windows/Native/Execution.cs
Normal file
24
osu.Framework.Desktop/OS/Windows/Native/Execution.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Windows.Native
|
||||
{
|
||||
static class Execution
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
internal static extern uint SetThreadExecutionState(ExecutionState state);
|
||||
|
||||
[Flags]
|
||||
internal enum ExecutionState : uint
|
||||
{
|
||||
AwaymodeRequired = 0x00000040,
|
||||
Continuous = 0x80000000,
|
||||
DisplayRequired = 0x00000002,
|
||||
SystemRequired = 0x00000001,
|
||||
UserPresent = 0x00000004,
|
||||
}
|
||||
}
|
||||
}
|
||||
2141
osu.Framework.Desktop/OS/Windows/Native/Input.cs
Normal file
2141
osu.Framework.Desktop/OS/Windows/Native/Input.cs
Normal file
File diff suppressed because it is too large
Load Diff
131
osu.Framework.Desktop/OS/Windows/Native/Registry.cs
Normal file
131
osu.Framework.Desktop/OS/Windows/Native/Registry.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System.IO;
|
||||
using System.Security.AccessControl;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Windows.Native
|
||||
{
|
||||
public static class Registry
|
||||
{
|
||||
public static bool Check(string progId, string executable)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (RegistryKey key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(progId))
|
||||
{
|
||||
if (key == null || key.OpenSubKey(@"shell\open\command").GetValue(string.Empty).ToString() != string.Format("\"{0}\" \"%1\"", executable))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool Add(string extension, string progId, string description, string executable, bool urlHandler = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(extension))
|
||||
{
|
||||
if (extension[0] != '.')
|
||||
extension = "." + extension;
|
||||
|
||||
// register the extension, if necessary
|
||||
using (RegistryKey key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(extension, true))
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
using (RegistryKey extKey = Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(extension))
|
||||
{
|
||||
extKey.SetValue(string.Empty, progId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
key.SetValue(string.Empty, progId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register the progId, if necessary
|
||||
using (RegistryKey key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(progId))
|
||||
{
|
||||
if (key == null || key.OpenSubKey("shell\\open\\command").GetValue(string.Empty).ToString() != string.Format("\"{0}\" \"%1\"", executable))
|
||||
{
|
||||
using (RegistryKey progIdKey = Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(progId))
|
||||
{
|
||||
progIdKey.SetValue(string.Empty, description);
|
||||
if (urlHandler) progIdKey.SetValue("URL Protocol", string.Empty);
|
||||
|
||||
using (RegistryKey command = progIdKey.CreateSubKey("shell\\open\\command"))
|
||||
{
|
||||
command.SetValue(string.Empty, string.Format("\"{0}\" \"%1\"", executable));
|
||||
}
|
||||
|
||||
using (RegistryKey command = progIdKey.CreateSubKey("DefaultIcon"))
|
||||
{
|
||||
command.SetValue(string.Empty, string.Format("\"{0}\",1", executable));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add new icon for existing osu! users anyway
|
||||
else if (key.OpenSubKey("DefaultIcon").GetValue(string.Empty) == null)
|
||||
{
|
||||
using (RegistryKey progIdKey = Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(progId))
|
||||
{
|
||||
using (RegistryKey command = progIdKey.CreateSubKey("DefaultIcon"))
|
||||
{
|
||||
command.SetValue(string.Empty, string.Format("\"{0}\",1", executable));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void Delete(string progId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (null != Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(progId))
|
||||
Microsoft.Win32.Registry.ClassesRoot.DeleteSubKeyTree(progId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Adds an ACL entry on the specified directory for the specified account.
|
||||
public static void AddDirectorySecurity(string FileName, string Account, FileSystemRights Rights,
|
||||
InheritanceFlags Inheritance, PropagationFlags Propogation,
|
||||
AccessControlType ControlType)
|
||||
{
|
||||
// Create a new DirectoryInfo object.
|
||||
DirectoryInfo dInfo = new DirectoryInfo(FileName);
|
||||
// Get a DirectorySecurity object that represents the
|
||||
// current security settings.
|
||||
DirectorySecurity dSecurity = dInfo.GetAccessControl();
|
||||
// Add the FileSystemAccessRule to the security settings.
|
||||
dSecurity.AddAccessRule(new FileSystemAccessRule(Account,
|
||||
Rights,
|
||||
Inheritance,
|
||||
Propogation,
|
||||
ControlType));
|
||||
// Set the new access settings.
|
||||
dInfo.SetAccessControl(dSecurity);
|
||||
}
|
||||
}
|
||||
}
|
||||
88
osu.Framework.Desktop/OS/Windows/WindowsGameForm.cs
Normal file
88
osu.Framework.Desktop/OS/Windows/WindowsGameForm.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
using osu.Framework.Framework;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Windows
|
||||
{
|
||||
public class WindowsGameForm : BasicGameForm
|
||||
{
|
||||
public delegate void WndProcDelegate(ref Message m);
|
||||
|
||||
public override event EventHandler ScreenChanged;
|
||||
|
||||
public event WndProcDelegate OnWndProc;
|
||||
|
||||
public override bool IsMinimized => ClientSize.Width != 0 || ClientSize.Height == 0;
|
||||
|
||||
private Screen screen;
|
||||
|
||||
internal WindowsGameForm(GraphicsContextFlags flags) : base(flags)
|
||||
{
|
||||
SuspendLayout();
|
||||
CausesValidation = false;
|
||||
ClientSize = new Size(1, 1);
|
||||
BackColor = Color.Black;
|
||||
ResumeLayout(false);
|
||||
|
||||
LocationChanged += delegate { updateScreen(); };
|
||||
ClientSizeChanged += delegate { updateScreen(); };
|
||||
}
|
||||
|
||||
protected override bool ProcessDialogKey(Keys keyData)
|
||||
{
|
||||
//stop alt/f10 from freezing form rendering.
|
||||
if (((keyData & Keys.Alt) == Keys.Alt && (keyData & Keys.F4) != Keys.F4) || (keyData & Keys.F10) == Keys.F10)
|
||||
return true;
|
||||
return base.ProcessDialogKey(keyData);
|
||||
}
|
||||
|
||||
public override Rectangle ClientBounds
|
||||
{
|
||||
get
|
||||
{
|
||||
Point point = PointToScreen(Point.Empty);
|
||||
return new Rectangle(point.X, point.Y, ClientSize.Width, ClientSize.Height);
|
||||
}
|
||||
}
|
||||
|
||||
public override void CentreToScreen()
|
||||
{
|
||||
CenterToScreen();
|
||||
}
|
||||
|
||||
private void updateScreen()
|
||||
{
|
||||
Screen screen = Screen.FromHandle(Handle);
|
||||
if ((this.screen == null) || !this.screen.Equals(screen))
|
||||
{
|
||||
this.screen = screen;
|
||||
if (this.screen != null)
|
||||
ScreenChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
if (m.Msg == 0x1c)
|
||||
{
|
||||
bool active = m.WParam != IntPtr.Zero;
|
||||
OnActivateApp(active);
|
||||
}
|
||||
|
||||
OnWndProc?.Invoke(ref m);
|
||||
|
||||
if (m.Result.ToInt32() < 0)
|
||||
{
|
||||
m.Result = new IntPtr(-m.Result.ToInt32() - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
}
|
||||
}
|
||||
119
osu.Framework.Desktop/OS/Windows/WindowsGameHost.cs
Normal file
119
osu.Framework.Desktop/OS/Windows/WindowsGameHost.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Windows.Forms;
|
||||
using osu.Framework.Desktop.OS.Windows.Native;
|
||||
using osu.Framework.Framework;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Windows
|
||||
{
|
||||
public class WindowsGameHost : BasicGameHost
|
||||
{
|
||||
public override BasicGameWindow Window => window;
|
||||
public override GLControl GLControl => window.Form;
|
||||
public override bool IsActive => Window != null && GetForegroundWindow().Equals(Window.Handle);
|
||||
|
||||
private WindowsGameWindow window;
|
||||
|
||||
internal WindowsGameHost(GraphicsContextFlags flags)
|
||||
{
|
||||
Architecture.SetIncludePath();
|
||||
window = new WindowsGameWindow(flags);
|
||||
|
||||
Application.EnableVisualStyles();
|
||||
|
||||
Window.Activated += OnActivated;
|
||||
Window.Deactivated += OnDeactivated;
|
||||
}
|
||||
|
||||
protected override void OnActivated(object sender, EventArgs args)
|
||||
{
|
||||
Execution.SetThreadExecutionState(Execution.ExecutionState.Continuous | Execution.ExecutionState.SystemRequired | Execution.ExecutionState.DisplayRequired);
|
||||
base.OnActivated(sender, args);
|
||||
}
|
||||
|
||||
protected override void OnDeactivated(object sender, EventArgs args)
|
||||
{
|
||||
Execution.SetThreadExecutionState(Execution.ExecutionState.Continuous);
|
||||
base.OnDeactivated(sender, args);
|
||||
}
|
||||
|
||||
protected override void OnApplicationIdle(object sender, EventArgs e)
|
||||
{
|
||||
MSG message;
|
||||
while (!PeekMessage(out message, IntPtr.Zero, 0, 0, 0))
|
||||
base.OnApplicationIdle(sender, e);
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
OpenTK.NativeWindow.OsuWindowHandle = window.Handle;
|
||||
|
||||
base.Run();
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
[SuppressUnmanagedCodeSecurity, DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
internal static extern bool PeekMessage(out MSG msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax,
|
||||
uint flags);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MSG
|
||||
{
|
||||
public IntPtr hWnd;
|
||||
public WindowMessage msg;
|
||||
public IntPtr wParam;
|
||||
public IntPtr lParam;
|
||||
public uint time;
|
||||
public System.Drawing.Point p;
|
||||
}
|
||||
|
||||
public enum WindowMessage : uint
|
||||
{
|
||||
ActivateApplication = 0x1c,
|
||||
Character = 0x102,
|
||||
Close = 0x10,
|
||||
Destroy = 2,
|
||||
EnterMenuLoop = 0x211,
|
||||
EnterSizeMove = 0x231,
|
||||
ExitMenuLoop = 530,
|
||||
ExitSizeMove = 0x232,
|
||||
GetMinMax = 0x24,
|
||||
KeyDown = 0x100,
|
||||
KeyUp = 0x101,
|
||||
LeftButtonDoubleClick = 0x203,
|
||||
LeftButtonDown = 0x201,
|
||||
LeftButtonUp = 0x202,
|
||||
MiddleButtonDoubleClick = 0x209,
|
||||
MiddleButtonDown = 0x207,
|
||||
MiddleButtonUp = 520,
|
||||
MouseFirst = 0x201,
|
||||
MouseLast = 0x20d,
|
||||
MouseMove = 0x200,
|
||||
MouseWheel = 0x20a,
|
||||
NonClientHitTest = 0x84,
|
||||
Paint = 15,
|
||||
PowerBroadcast = 0x218,
|
||||
Quit = 0x12,
|
||||
RightButtonDoubleClick = 0x206,
|
||||
RightButtonDown = 0x204,
|
||||
RightButtonUp = 0x205,
|
||||
SetCursor = 0x20,
|
||||
Size = 5,
|
||||
SystemCharacter = 0x106,
|
||||
SystemCommand = 0x112,
|
||||
SystemKeyDown = 260,
|
||||
SystemKeyUp = 0x105,
|
||||
XButtonDoubleClick = 0x20d,
|
||||
XButtonDown = 0x20b,
|
||||
XButtonUp = 0x20c
|
||||
}
|
||||
}
|
||||
}
|
||||
21
osu.Framework.Desktop/OS/Windows/WindowsGameWindow.cs
Normal file
21
osu.Framework.Desktop/OS/Windows/WindowsGameWindow.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Framework;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Framework.Desktop.OS.Windows
|
||||
{
|
||||
public class WindowsGameWindow : DesktopGameWindow
|
||||
{
|
||||
public WindowsGameWindow(GraphicsContextFlags flags)
|
||||
: base(flags)
|
||||
{
|
||||
}
|
||||
|
||||
protected override BasicGameForm CreateGameForm(GraphicsContextFlags flags)
|
||||
{
|
||||
return new WindowsGameForm(flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
osu.Framework.Desktop/OpenTK.dll.config
Normal file
30
osu.Framework.Desktop/OpenTK.dll.config
Normal file
@@ -0,0 +1,30 @@
|
||||
<!--
|
||||
Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<dllmap os="linux" dll="opengl32.dll" target="libGL.so.1"/>
|
||||
<dllmap os="linux" dll="glu32.dll" target="libGLU.so.1"/>
|
||||
<dllmap os="linux" dll="openal32.dll" target="libopenal.so.1"/>
|
||||
<dllmap os="linux" dll="alut.dll" target="libalut.so.0"/>
|
||||
<dllmap os="linux" dll="opencl.dll" target="libOpenCL.so"/>
|
||||
<dllmap os="linux" dll="libX11" target="libX11.so.6"/>
|
||||
<dllmap os="linux" dll="libXi" target="libXi.so.6"/>
|
||||
<dllmap os="linux" dll="SDL2.dll" target="libSDL2-2.0.so.0"/>
|
||||
<dllmap os="osx" dll="opengl32.dll" target="/System/Library/Frameworks/OpenGL.framework/OpenGL"/>
|
||||
<dllmap os="osx" dll="openal32.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
|
||||
<dllmap os="osx" dll="alut.dll" target="/System/Library/Frameworks/OpenAL.framework/OpenAL" />
|
||||
<dllmap os="osx" dll="libGLES.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
|
||||
<dllmap os="osx" dll="libGLESv1_CM.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
|
||||
<dllmap os="osx" dll="libGLESv2.dll" target="/System/Library/Frameworks/OpenGLES.framework/OpenGLES" />
|
||||
<dllmap os="osx" dll="opencl.dll" target="/System/Library/Frameworks/OpenCL.framework/OpenCL"/>
|
||||
<dllmap os="osx" dll="SDL2.dll" target="libSDL2.dylib"/>
|
||||
<!-- XQuartz compatibility (X11 on Mac) -->
|
||||
<dllmap os="osx" dll="libGL.so.1" target="/usr/X11/lib/libGL.dylib"/>
|
||||
<dllmap os="osx" dll="libX11" target="/usr/X11/lib/libX11.dylib"/>
|
||||
<dllmap os="osx" dll="libXcursor.so.1" target="/usr/X11/lib/libXcursor.dylib"/>
|
||||
<dllmap os="osx" dll="libXi" target="/usr/X11/lib/libXi.dylib"/>
|
||||
<dllmap os="osx" dll="libXinerama" target="/usr/X11/lib/libXinerama.dylib"/>
|
||||
<dllmap os="osx" dll="libXrandr.so.2" target="/usr/X11/lib/libXrandr.dylib"/>
|
||||
</configuration>
|
||||
39
osu.Framework.Desktop/Properties/AssemblyInfo.cs
Normal file
39
osu.Framework.Desktop/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("osu.Framework.Desktop")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("osu.Framework.Desktop")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("65dc628f-a640-4111-ab35-3a5652bc1e17")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
BIN
osu.Framework.Desktop/libbass.x64.so
Normal file
BIN
osu.Framework.Desktop/libbass.x64.so
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/libbass.x86.so
Normal file
BIN
osu.Framework.Desktop/libbass.x86.so
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/libbass_fx.x64.so
Normal file
BIN
osu.Framework.Desktop/libbass_fx.x64.so
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/libbass_fx.x86.so
Normal file
BIN
osu.Framework.Desktop/libbass_fx.x86.so
Normal file
Binary file not shown.
161
osu.Framework.Desktop/osu.Framework.Desktop.csproj
Normal file
161
osu.Framework.Desktop/osu.Framework.Desktop.csproj
Normal file
@@ -0,0 +1,161 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{65DC628F-A640-4111-AB35-3A5652BC1E17}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>osu.Framework.Desktop</RootNamespace>
|
||||
<AssemblyName>osu.Framework.Desktop</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="OpenTK, Version=1.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ppy.OpenTK.1.1.2225.2\lib\net20\OpenTK.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="OpenTK.GLControl, Version=1.1.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\ppy.OpenTK.GLControl.1.1.2225.0\lib\net40\OpenTK.GLControl.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Host.cs" />
|
||||
<Compile Include="OS\DesktopGameWindow.cs" />
|
||||
<Compile Include="OS\Linux\LinuxGameForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="OS\Linux\LinuxGameHost.cs" />
|
||||
<Compile Include="OS\Linux\LinuxGameWindow.cs" />
|
||||
<Compile Include="OS\Windows\Native\Architecture.cs" />
|
||||
<Compile Include="OS\Windows\Native\Desktop.cs" />
|
||||
<Compile Include="OS\Windows\Native\Execution.cs" />
|
||||
<Compile Include="OS\Windows\Native\Input.cs" />
|
||||
<Compile Include="OS\Windows\Native\Registry.cs" />
|
||||
<Compile Include="OS\Windows\WindowsGameForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="OS\Windows\WindowsGameHost.cs" />
|
||||
<Compile Include="OS\Windows\WindowsGameWindow.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\osu.Framework\osu.Framework.csproj">
|
||||
<Project>{c76bf5b3-985e-4d39-95fe-97c9c879b83a}</Project>
|
||||
<Name>osu.Framework</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\osu-framework.licenseheader">
|
||||
<Link>osu-framework.licenseheader</Link>
|
||||
</None>
|
||||
<None Include="libbass.x64.so">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="libbass.x86.so">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="libbass_fx.x64.so">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="libbass_fx.x86.so">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="ManagedBass.PInvoke.dll.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="OpenTK.dll.config">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="x64\avcodec-51.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x64\avformat-52.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x64\avutil-49.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x64\bass.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x64\bass_fx.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x64\d3dcompiler_47.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x64\libEGL.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x64\libGLESv2.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x64\pthreadGC2.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\avcodec-51.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\avformat-52.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\avutil-49.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\bass.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\bass_fx.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\d3dcompiler_47.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\libEGL.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\libGLESv2.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="x86\pthreadGC2.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
10
osu.Framework.Desktop/packages.config
Normal file
10
osu.Framework.Desktop/packages.config
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
-->
|
||||
|
||||
<packages>
|
||||
<package id="ppy.OpenTK" version="1.1.2225.2" targetFramework="net452" />
|
||||
<package id="ppy.OpenTK.GLControl" version="1.1.2225.0" targetFramework="net452" />
|
||||
</packages>
|
||||
BIN
osu.Framework.Desktop/x64/avcodec-51.dll
Normal file
BIN
osu.Framework.Desktop/x64/avcodec-51.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x64/avformat-52.dll
Normal file
BIN
osu.Framework.Desktop/x64/avformat-52.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x64/avutil-49.dll
Normal file
BIN
osu.Framework.Desktop/x64/avutil-49.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x64/bass.dll
Normal file
BIN
osu.Framework.Desktop/x64/bass.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x64/bass_fx.dll
Normal file
BIN
osu.Framework.Desktop/x64/bass_fx.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x64/d3dcompiler_47.dll
Normal file
BIN
osu.Framework.Desktop/x64/d3dcompiler_47.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x64/libEGL.dll
Normal file
BIN
osu.Framework.Desktop/x64/libEGL.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x64/libGLESv2.dll
Normal file
BIN
osu.Framework.Desktop/x64/libGLESv2.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x64/pthreadGC2.dll
Normal file
BIN
osu.Framework.Desktop/x64/pthreadGC2.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x86/avcodec-51.dll
Normal file
BIN
osu.Framework.Desktop/x86/avcodec-51.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x86/avformat-52.dll
Normal file
BIN
osu.Framework.Desktop/x86/avformat-52.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x86/avutil-49.dll
Normal file
BIN
osu.Framework.Desktop/x86/avutil-49.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x86/bass.dll
Normal file
BIN
osu.Framework.Desktop/x86/bass.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x86/bass_fx.dll
Normal file
BIN
osu.Framework.Desktop/x86/bass_fx.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x86/d3dcompiler_47.dll
Normal file
BIN
osu.Framework.Desktop/x86/d3dcompiler_47.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x86/libEGL.dll
Normal file
BIN
osu.Framework.Desktop/x86/libEGL.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x86/libGLESv2.dll
Normal file
BIN
osu.Framework.Desktop/x86/libGLESv2.dll
Normal file
Binary file not shown.
BIN
osu.Framework.Desktop/x86/pthreadGC2.dll
Normal file
BIN
osu.Framework.Desktop/x86/pthreadGC2.dll
Normal file
Binary file not shown.
155
osu.Framework/Audio/AdjustableAudioComponent.cs
Normal file
155
osu.Framework/Audio/AdjustableAudioComponent.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Cached;
|
||||
using osu.Framework.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Framework.Audio
|
||||
{
|
||||
public class AdjustableAudioComponent : IDisposable, IUpdateable
|
||||
{
|
||||
private List<BindableDouble> volumeAdjustments = new List<BindableDouble>();
|
||||
private List<BindableDouble> balanceAdjustments = new List<BindableDouble>();
|
||||
private List<BindableDouble> frequencyAdjustments = new List<BindableDouble>();
|
||||
|
||||
/// <summary>
|
||||
/// Global volume of this component.
|
||||
/// </summary>
|
||||
public readonly BindableDouble Volume = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
||||
|
||||
protected readonly BindableDouble VolumeCalculated = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
||||
|
||||
/// <summary>
|
||||
/// Playback balance of this sample (-1 .. 1 where 0 is centered)
|
||||
/// </summary>
|
||||
public readonly BindableDouble Balance = new BindableDouble(0) { MinValue = -1, MaxValue = 1 };
|
||||
|
||||
protected readonly BindableDouble BalanceCalculated = new BindableDouble(0) { MinValue = -1, MaxValue = 1 };
|
||||
|
||||
/// <summary>
|
||||
/// Rate at which the component is played back (affects pitch). 1 is 100% playback speed, or default frequency.
|
||||
/// </summary>
|
||||
public readonly BindableDouble Frequency = new BindableDouble(1);
|
||||
|
||||
protected readonly BindableDouble FrequencyCalculated = new BindableDouble(1);
|
||||
|
||||
/// <summary>
|
||||
/// Handles invalidation of the component's state.
|
||||
/// </summary>
|
||||
private Cached<double> componentState = new Cached<double>();
|
||||
|
||||
protected AdjustableAudioComponent()
|
||||
{
|
||||
Volume.ValueChanged += InvalidateState;
|
||||
Balance.ValueChanged += InvalidateState;
|
||||
Frequency.ValueChanged += InvalidateState;
|
||||
}
|
||||
|
||||
protected void InvalidateState(object sender = null, EventArgs e = null)
|
||||
{
|
||||
componentState.Invalidate();
|
||||
}
|
||||
|
||||
protected virtual void OnStateChanged(object sender, EventArgs e)
|
||||
{
|
||||
VolumeCalculated.Value = volumeAdjustments.Aggregate(Volume.Value, (current, adj) => current * adj);
|
||||
BalanceCalculated.Value = balanceAdjustments.Aggregate(Balance.Value, (current, adj) => current + adj);
|
||||
FrequencyCalculated.Value = frequencyAdjustments.Aggregate(Frequency.Value, (current, adj) => current * adj);
|
||||
}
|
||||
|
||||
public void AddAdjustmentDependency(AdjustableAudioComponent component)
|
||||
{
|
||||
AddAdjustment(AdjustableProperty.Balance, component.BalanceCalculated);
|
||||
AddAdjustment(AdjustableProperty.Frequency, component.FrequencyCalculated);
|
||||
AddAdjustment(AdjustableProperty.Volume, component.VolumeCalculated);
|
||||
}
|
||||
|
||||
public void RemoveAdjustmentDependency(AdjustableAudioComponent component)
|
||||
{
|
||||
RemoveAdjustment(component.BalanceCalculated);
|
||||
RemoveAdjustment(component.FrequencyCalculated);
|
||||
RemoveAdjustment(component.VolumeCalculated);
|
||||
}
|
||||
|
||||
public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case AdjustableProperty.Balance:
|
||||
balanceAdjustments.Add(adjustBindable);
|
||||
break;
|
||||
case AdjustableProperty.Frequency:
|
||||
frequencyAdjustments.Add(adjustBindable);
|
||||
break;
|
||||
case AdjustableProperty.Volume:
|
||||
volumeAdjustments.Add(adjustBindable);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
adjustBindable.ValueChanged += InvalidateState;
|
||||
|
||||
InvalidateState();
|
||||
}
|
||||
|
||||
public void RemoveAdjustment(BindableDouble adjustBindable)
|
||||
{
|
||||
balanceAdjustments.Remove(adjustBindable);
|
||||
frequencyAdjustments.Remove(adjustBindable);
|
||||
volumeAdjustments.Remove(adjustBindable);
|
||||
|
||||
adjustBindable.ValueChanged -= InvalidateState;
|
||||
|
||||
InvalidateState();
|
||||
}
|
||||
|
||||
public virtual void Update()
|
||||
{
|
||||
componentState.Refresh(delegate
|
||||
{
|
||||
OnStateChanged(this, null);
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
protected bool IsDisposed = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!IsDisposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
volumeAdjustments.ForEach(d => d.ValueChanged -= InvalidateState);
|
||||
balanceAdjustments.ForEach(d => d.ValueChanged -= InvalidateState);
|
||||
frequencyAdjustments.ForEach(d => d.ValueChanged -= InvalidateState);
|
||||
}
|
||||
|
||||
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||
// TODO: set large fields to null.
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum AdjustableProperty
|
||||
{
|
||||
Volume,
|
||||
Balance,
|
||||
Frequency
|
||||
}
|
||||
}
|
||||
40
osu.Framework/Audio/AudioCollectionManager.cs
Normal file
40
osu.Framework/Audio/AudioCollectionManager.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Configuration;
|
||||
|
||||
namespace osu.Framework.Audio
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of audio components which need central property control.
|
||||
/// </summary>
|
||||
public class AudioCollectionManager<T> : AdjustableAudioComponent
|
||||
where T : AdjustableAudioComponent
|
||||
{
|
||||
List<T> ActiveItems = new List<T>();
|
||||
|
||||
protected void AddItem(T item)
|
||||
{
|
||||
item.AddAdjustmentDependency(this);
|
||||
ActiveItems.Add(item);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
ActiveItems.ForEach(s => s.Update());
|
||||
|
||||
ActiveItems.FindAll(s => (s as IHasCompletedState)?.HasCompleted ?? false).ForEach(s =>
|
||||
{
|
||||
s.Dispose();
|
||||
ActiveItems.Remove(s);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
242
osu.Framework/Audio/AudioManager.cs
Normal file
242
osu.Framework/Audio/AudioManager.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using ManagedBass;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Resources;
|
||||
using osu.Framework.Threading;
|
||||
|
||||
namespace osu.Framework.Audio
|
||||
{
|
||||
public class AudioManager : AudioCollectionManager<AdjustableAudioComponent>
|
||||
{
|
||||
public TrackManager Track => GetTrackManager();
|
||||
public SampleManager Sample => GetSampleManager();
|
||||
|
||||
internal event VoidDelegate AvailableDevicesChanged;
|
||||
|
||||
internal List<DeviceInfo> AudioDevices = new List<DeviceInfo>();
|
||||
|
||||
internal string CurrentAudioDevice;
|
||||
|
||||
private string lastPreferredDevice;
|
||||
|
||||
/// <summary>
|
||||
/// Volume of all samples played game-wide.
|
||||
/// </summary>
|
||||
public readonly BindableDouble VolumeSample = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
||||
|
||||
/// <summary>
|
||||
/// Volume of all tracks played game-wide.
|
||||
/// </summary>
|
||||
public readonly BindableDouble VolumeTrack = new BindableDouble(1) { MinValue = 0, MaxValue = 1 };
|
||||
|
||||
private Scheduler scheduler = new Scheduler();
|
||||
|
||||
public AudioManager(IResourceStore<byte[]> trackStore, IResourceStore<byte[]> sampleStore)
|
||||
{
|
||||
globalTrackManager = GetTrackManager(trackStore);
|
||||
globalSampleManager = GetSampleManager(sampleStore);
|
||||
|
||||
SetAudioDevice();
|
||||
|
||||
scheduler.AddDelayed(checkAudioDeviceChanged, 1000, true);
|
||||
}
|
||||
|
||||
private TrackManager globalTrackManager;
|
||||
private SampleManager globalSampleManager;
|
||||
|
||||
public TrackManager GetTrackManager(IResourceStore<byte[]> store = null)
|
||||
{
|
||||
if (store == null) return globalTrackManager;
|
||||
|
||||
TrackManager tm = new TrackManager(store);
|
||||
AddItem(tm);
|
||||
tm.AddAdjustment(AdjustableProperty.Volume, VolumeTrack);
|
||||
|
||||
return tm;
|
||||
}
|
||||
|
||||
public SampleManager GetSampleManager(IResourceStore<byte[]> store = null)
|
||||
{
|
||||
if (store == null) return globalSampleManager;
|
||||
|
||||
SampleManager sm = new SampleManager(store);
|
||||
AddItem(sm);
|
||||
sm.AddAdjustment(AdjustableProperty.Volume, VolumeSample);
|
||||
|
||||
return sm;
|
||||
}
|
||||
|
||||
internal bool CheckAudioDevice()
|
||||
{
|
||||
if (CurrentAudioDevice != null)
|
||||
return true;
|
||||
|
||||
//NotificationManager.ShowMessage("No compatible audio device detected. You must plug in a valid audio device in order to play osu!", Color4.Red, 4000);
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<DeviceInfo> getAllDevices()
|
||||
{
|
||||
int deviceCount = Bass.DeviceCount;
|
||||
List<DeviceInfo> info = new List<DeviceInfo>();
|
||||
for (int i = 0; i < deviceCount; i++)
|
||||
info.Add(Bass.GetDeviceInfo(i));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public bool SetAudioDevice(string preferredDevice = null)
|
||||
{
|
||||
lastPreferredDevice = preferredDevice;
|
||||
|
||||
AudioDevices = new List<DeviceInfo>(getAllDevices());
|
||||
AvailableDevicesChanged?.Invoke();
|
||||
|
||||
string oldDevice = CurrentAudioDevice;
|
||||
string newDevice = preferredDevice;
|
||||
|
||||
if (string.IsNullOrEmpty(newDevice))
|
||||
newDevice = AudioDevices.Find(df => df.IsDefault).Name;
|
||||
|
||||
bool oldDeviceValid = Bass.CurrentDevice >= 0;
|
||||
if (oldDeviceValid)
|
||||
{
|
||||
DeviceInfo oldDeviceInfo = Bass.GetDeviceInfo(Bass.CurrentDevice);
|
||||
oldDeviceValid &= oldDeviceInfo.IsEnabled && oldDeviceInfo.IsInitialized;
|
||||
}
|
||||
|
||||
if (newDevice == oldDevice)
|
||||
{
|
||||
//check the old device is still valid
|
||||
if (oldDeviceValid)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(newDevice))
|
||||
return false;
|
||||
|
||||
int newDeviceIndex = AudioDevices.FindIndex(df => df.Name == newDevice);
|
||||
|
||||
|
||||
DeviceInfo newDeviceInfo = new DeviceInfo();
|
||||
|
||||
try
|
||||
{
|
||||
if (newDeviceIndex >= 0)
|
||||
newDeviceInfo = Bass.GetDeviceInfo(newDeviceIndex);
|
||||
//we may have previously initialised this device.
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
if (oldDeviceValid && (newDeviceInfo.Driver == null || !newDeviceInfo.IsEnabled))
|
||||
{
|
||||
//handles the case we are trying to load a user setting which is currently unavailable,
|
||||
//and we have already fallen back to a sane default.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (newDevice != null && oldDevice != null)
|
||||
{
|
||||
//we are preparing to load a new device, so let's clean up any existing device.
|
||||
clearAllCaches();
|
||||
Bass.Free();
|
||||
}
|
||||
|
||||
if (!Bass.Init(newDeviceIndex, 44100, 0, Game.Window.Handle))
|
||||
{
|
||||
//the new device didn't go as planned. we need another option.
|
||||
|
||||
if (preferredDevice == null)
|
||||
{
|
||||
//we're fucked. the default device won't initialise.
|
||||
CurrentAudioDevice = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
//let's try again using the default device.
|
||||
return SetAudioDevice();
|
||||
}
|
||||
|
||||
//we have successfully initialised a new device.
|
||||
CurrentAudioDevice = newDevice;
|
||||
|
||||
Bass.PlaybackBufferLength = 100;
|
||||
Bass.UpdatePeriod = 5;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void clearAllCaches()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private int lastDeviceCount;
|
||||
|
||||
private void checkAudioDeviceChanged()
|
||||
{
|
||||
bool useDefault = string.IsNullOrEmpty(lastPreferredDevice);
|
||||
|
||||
if (useDefault)
|
||||
{
|
||||
int currentDevice = Bass.CurrentDevice;
|
||||
try
|
||||
{
|
||||
DeviceInfo device = Bass.GetDeviceInfo(currentDevice);
|
||||
if (device.IsDefault && device.IsEnabled)
|
||||
return; //early return when nothing has changed.
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int availableDevices = 0;
|
||||
|
||||
foreach (DeviceInfo device in getAllDevices())
|
||||
{
|
||||
if (device.Driver == null) continue;
|
||||
|
||||
bool isCurrentDevice = device.Name == CurrentAudioDevice;
|
||||
|
||||
if (device.IsEnabled)
|
||||
{
|
||||
if (isCurrentDevice && !device.IsDefault && useDefault)
|
||||
//the default device on windows has changed, so we need to update.
|
||||
SetAudioDevice();
|
||||
availableDevices++;
|
||||
}
|
||||
else if (isCurrentDevice)
|
||||
SetAudioDevice(lastPreferredDevice);
|
||||
//the active device has been disabled.
|
||||
}
|
||||
|
||||
if (lastDeviceCount != availableDevices && lastDeviceCount > 0)
|
||||
{
|
||||
SetAudioDevice(lastPreferredDevice);
|
||||
|
||||
//just update the available devices.
|
||||
//if (availableDevices > lastDeviceCount)
|
||||
//NotificationManager.ShowMessage(LocalisationManager.GetString(OsuString.AudioEngine_NewDeviceDetected), Color4.YellowGreen, 5000);
|
||||
}
|
||||
|
||||
lastDeviceCount = availableDevices;
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
scheduler.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
19
osu.Framework/Audio/IHasCompletedState.cs
Normal file
19
osu.Framework/Audio/IHasCompletedState.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Framework.Audio
|
||||
{
|
||||
public interface IHasCompletedState
|
||||
{
|
||||
/// <summary>
|
||||
/// Becomes true when we are out and done with this object (and pending clean-up).
|
||||
/// </summary>
|
||||
bool HasCompleted { get; }
|
||||
}
|
||||
}
|
||||
41
osu.Framework/Audio/Sample/AudioSample.cs
Normal file
41
osu.Framework/Audio/Sample/AudioSample.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
namespace osu.Framework.Audio.Sample
|
||||
{
|
||||
public abstract class AudioSample : AdjustableAudioComponent, IHasCompletedState, IUpdateable
|
||||
{
|
||||
protected bool WasStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Makes this sample fire-and-forget (will be cleaned up automatically).
|
||||
/// </summary>
|
||||
public bool OneShot;
|
||||
|
||||
public virtual void Play(bool restart = true)
|
||||
{
|
||||
WasStarted = true;
|
||||
}
|
||||
|
||||
public virtual void Stop()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
Stop();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public abstract bool Playing { get; }
|
||||
|
||||
public virtual bool Played => WasStarted && !Playing;
|
||||
|
||||
public bool HasCompleted => Played && (OneShot || IsDisposed);
|
||||
|
||||
public virtual void Pause()
|
||||
{
|
||||
if (!Playing) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
104
osu.Framework/Audio/Sample/AudioSampleBass.cs
Normal file
104
osu.Framework/Audio/Sample/AudioSampleBass.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using ManagedBass;
|
||||
using System;
|
||||
|
||||
namespace osu.Framework.Audio.Sample
|
||||
{
|
||||
class AudioSampleBass : AudioSample
|
||||
{
|
||||
private int channel;
|
||||
|
||||
bool hasChannel => channel != 0;
|
||||
bool hasSample => sample != 0;
|
||||
|
||||
private int sample;
|
||||
|
||||
float initialFrequency;
|
||||
|
||||
private bool freeWhenDone;
|
||||
|
||||
public AudioSampleBass(byte[] data) : this(Bass.SampleLoad(data, 0, data.Length, 8, BassFlags.Default))
|
||||
{
|
||||
}
|
||||
|
||||
protected AudioSampleBass(int sampleId, bool freeWhenDone = false)
|
||||
{
|
||||
sample = sampleId;
|
||||
this.freeWhenDone = freeWhenDone;
|
||||
}
|
||||
|
||||
private int ensureChannel()
|
||||
{
|
||||
if (!hasSample) return 0;
|
||||
|
||||
if (!hasChannel)
|
||||
{
|
||||
InvalidateState();
|
||||
channel = Bass.SampleGetChannel(sample);
|
||||
Bass.ChannelGetAttribute(channel, ChannelAttribute.Frequency, out initialFrequency);
|
||||
Update();
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
void resetChannel()
|
||||
{
|
||||
channel = 0;
|
||||
}
|
||||
|
||||
protected override void OnStateChanged(object sender, EventArgs e)
|
||||
{
|
||||
base.OnStateChanged(sender, e);
|
||||
|
||||
if (hasChannel)
|
||||
{
|
||||
Bass.ChannelSetAttribute(channel, ChannelAttribute.Volume, VolumeCalculated);
|
||||
Bass.ChannelSetAttribute(channel, ChannelAttribute.Pan, BalanceCalculated);
|
||||
Bass.ChannelSetAttribute(channel, ChannelAttribute.Frequency, initialFrequency * FrequencyCalculated);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Play(bool restart = true)
|
||||
{
|
||||
if (!hasSample)
|
||||
return;
|
||||
|
||||
base.Play();
|
||||
|
||||
Bass.ChannelPlay(ensureChannel(), restart);
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (!hasChannel) return;
|
||||
|
||||
base.Stop();
|
||||
|
||||
Bass.ChannelStop(channel);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (freeWhenDone)
|
||||
{
|
||||
Bass.SampleFree(sample);
|
||||
sample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Pause()
|
||||
{
|
||||
if (!hasChannel) return;
|
||||
|
||||
base.Pause();
|
||||
Bass.ChannelPause(channel);
|
||||
}
|
||||
|
||||
public override bool Playing => hasChannel && Bass.ChannelIsActive(channel) != 0;
|
||||
}
|
||||
}
|
||||
26
osu.Framework/Audio/Sample/SampleManager.cs
Normal file
26
osu.Framework/Audio/Sample/SampleManager.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Resources;
|
||||
|
||||
namespace osu.Framework.Audio.Sample
|
||||
{
|
||||
public class SampleManager : AudioCollectionManager<AudioSample>
|
||||
{
|
||||
IResourceStore<byte[]> store;
|
||||
|
||||
public SampleManager(IResourceStore<byte[]> store)
|
||||
{
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public AudioSample GetSample(string name)
|
||||
{
|
||||
byte[] data = store.Get(name);
|
||||
|
||||
AudioSample sample = new AudioSampleBass(data);
|
||||
AddItem(sample);
|
||||
return sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
osu.Framework/Audio/Track/AudioTrack.cs
Normal file
75
osu.Framework/Audio/Track/AudioTrack.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Framework.Audio.Track
|
||||
{
|
||||
public abstract class AudioTrack : AdjustableAudioComponent, IAdjustableClock, IHasCompletedState, IUpdateable
|
||||
{
|
||||
/// <summary>
|
||||
/// Is this track capable of producing audio?
|
||||
/// </summary>
|
||||
public virtual bool IsDummyDevice => true;
|
||||
|
||||
/// <summary>
|
||||
/// The speed of track playback. Does not affect pitch, but will reduce playback quality due to skipped frames.
|
||||
/// </summary>
|
||||
public readonly BindableDouble Tempo = new BindableDouble(1);
|
||||
|
||||
public AudioTrack()
|
||||
{
|
||||
Tempo.ValueChanged += InvalidateState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset this track to a logical default state.
|
||||
/// </summary>
|
||||
public virtual void Reset()
|
||||
{
|
||||
Frequency.Value = 1;
|
||||
|
||||
Stop();
|
||||
Seek(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current position in milliseconds.
|
||||
/// </summary>
|
||||
public abstract double CurrentTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Lenth of the track in milliseconds.
|
||||
/// </summary>
|
||||
public double Length { get; protected set; }
|
||||
|
||||
public virtual int? Bitrate => null;
|
||||
|
||||
/// <summary>
|
||||
/// Seek to a new position.
|
||||
/// </summary>
|
||||
/// <param name="seek">New position in milliseconds</param>
|
||||
/// <returns>Whether the seek was successful.</returns>
|
||||
public abstract bool Seek(double seek);
|
||||
|
||||
public abstract void Start();
|
||||
|
||||
public abstract void Stop();
|
||||
|
||||
public abstract bool IsRunning { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Overall playback rate (1 is 100%, -1 is reversed at 100%).
|
||||
/// </summary>
|
||||
public virtual double Rate => Frequency * Tempo;
|
||||
|
||||
public bool IsReversed => Rate < 0;
|
||||
|
||||
/// <summary>
|
||||
/// todo: implement
|
||||
/// </summary>
|
||||
public bool HasCompleted => IsDisposed;
|
||||
}
|
||||
|
||||
}
|
||||
253
osu.Framework/Audio/Track/AudioTrackBass.cs
Normal file
253
osu.Framework/Audio/Track/AudioTrackBass.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using ManagedBass;
|
||||
using ManagedBass.Fx;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.IO;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Framework.Audio.Track
|
||||
{
|
||||
public class AudioTrackBass : AudioTrack
|
||||
{
|
||||
private float initialFrequency;
|
||||
|
||||
private int audioStreamPrefilter;
|
||||
|
||||
private AsyncBufferStream dataStream;
|
||||
|
||||
public bool Looping { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should this track only be used for preview purposes? This suggests it has not yet been fully loaded.
|
||||
/// </summary>
|
||||
public bool Preview { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The handle for this track, if there is one.
|
||||
/// </summary>
|
||||
private int activeStream;
|
||||
|
||||
//must keep a reference to this else it will be garbage collected early.
|
||||
private DataStreamFileProcedures procs;
|
||||
|
||||
public AudioTrackBass(Stream data, bool quick = false, bool loop = false)
|
||||
{
|
||||
Preview = quick;
|
||||
Looping = loop;
|
||||
|
||||
BassFlags flags = Preview ? 0 : (BassFlags.Decode | BassFlags.Prescan);
|
||||
|
||||
if (data == null)
|
||||
throw new Exception(@"Data couldn't be loaded!");
|
||||
else
|
||||
{
|
||||
//encapsulate incoming stream with async buffer if it isn't already.
|
||||
dataStream = data as AsyncBufferStream;
|
||||
if (dataStream == null) dataStream = new AsyncBufferStream(data, quick ? 8 : -1);
|
||||
|
||||
procs = new DataStreamFileProcedures(dataStream);
|
||||
|
||||
audioStreamPrefilter = Bass.CreateStream(StreamSystem.NoBuffer, flags, procs.BassProcedures, IntPtr.Zero);
|
||||
}
|
||||
|
||||
if (Preview)
|
||||
activeStream = audioStreamPrefilter;
|
||||
else
|
||||
{
|
||||
activeStream = BassFx.TempoCreate(audioStreamPrefilter, BassFlags.Decode);
|
||||
activeStream = BassFx.ReverseCreate(activeStream, 5f, BassFlags.Default);
|
||||
|
||||
Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoUseQuickAlgorithm, 1);
|
||||
Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoOverlapMilliseconds, 4);
|
||||
Bass.ChannelSetAttribute(activeStream, ChannelAttribute.TempoSequenceMilliseconds, 30);
|
||||
}
|
||||
|
||||
Length = (Bass.ChannelBytes2Seconds(activeStream, Bass.ChannelGetLength(activeStream)) * 1000);
|
||||
Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Frequency, out initialFrequency);
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
Stop();
|
||||
Seek(0);
|
||||
Volume.Value = 1;
|
||||
base.Reset();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (activeStream != 0) Bass.ChannelStop(activeStream);
|
||||
|
||||
if (audioStreamPrefilter != 0) Bass.StreamFree(audioStreamPrefilter);
|
||||
|
||||
activeStream = 0;
|
||||
audioStreamPrefilter = 0;
|
||||
|
||||
dataStream?.Dispose();
|
||||
dataStream = null;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public override bool IsDummyDevice => false;
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
if (IsRunning)
|
||||
togglePause();
|
||||
}
|
||||
|
||||
private bool togglePause()
|
||||
{
|
||||
//if (IsDisposed) return false;
|
||||
|
||||
if (PlaybackState.Playing == Bass.ChannelIsActive(activeStream))
|
||||
{
|
||||
Bass.ChannelPause(activeStream);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Bass.ChannelPlay(activeStream, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int direction = 0;
|
||||
|
||||
private void setDirection(bool reverse)
|
||||
{
|
||||
int newDirection = reverse ? -1 : 1;
|
||||
|
||||
if (direction == newDirection) return;
|
||||
|
||||
direction = newDirection;
|
||||
|
||||
Bass.ChannelSetAttribute(activeStream, ChannelAttribute.ReverseDirection, direction);
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
Update();
|
||||
Bass.ChannelPlay(activeStream);
|
||||
}
|
||||
|
||||
public override bool Seek(double seek)
|
||||
{
|
||||
double clamped = MathHelper.Clamp(seek, 0, Length);
|
||||
|
||||
if (clamped != CurrentTime)
|
||||
{
|
||||
long pos = Bass.ChannelSeconds2Bytes(activeStream, clamped / 1000d);
|
||||
Bass.ChannelSetPosition(activeStream, pos);
|
||||
}
|
||||
|
||||
return clamped == seek;
|
||||
}
|
||||
|
||||
public override double CurrentTime => Bass.ChannelBytes2Seconds(activeStream, Bass.ChannelGetPosition(activeStream)) * 1000;
|
||||
|
||||
public override bool IsRunning => Bass.ChannelIsActive(activeStream) == PlaybackState.Playing;
|
||||
|
||||
protected override void OnStateChanged(object sender, EventArgs e)
|
||||
{
|
||||
base.OnStateChanged(sender, e);
|
||||
|
||||
setDirection(FrequencyCalculated.Value < 0);
|
||||
|
||||
Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Volume, VolumeCalculated);
|
||||
Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Pan, BalanceCalculated);
|
||||
Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Frequency, bassFreq);
|
||||
Bass.ChannelSetAttribute(activeStream, ChannelAttribute.Tempo, (Math.Abs(Tempo) - 1) * 100);
|
||||
}
|
||||
|
||||
int bassFreq => (int)MathHelper.Clamp(Math.Abs(initialFrequency * FrequencyCalculated), 100, 100000);
|
||||
|
||||
public override double Rate => bassFreq / initialFrequency * Tempo * direction;
|
||||
|
||||
public override int? Bitrate => (int)Bass.ChannelGetAttribute(activeStream, ChannelAttribute.Bitrate);
|
||||
|
||||
private class DataStreamFileProcedures
|
||||
{
|
||||
private byte[] readBuffer = new byte[32768];
|
||||
|
||||
private AsyncBufferStream dataStream;
|
||||
|
||||
public FileProcedures BassProcedures => new FileProcedures()
|
||||
{
|
||||
Close = ac_Close,
|
||||
Length = ac_Length,
|
||||
Read = ac_Read,
|
||||
Seek = ac_Seek
|
||||
};
|
||||
|
||||
public DataStreamFileProcedures(AsyncBufferStream data)
|
||||
{
|
||||
dataStream = data;
|
||||
}
|
||||
|
||||
void ac_Close(IntPtr user)
|
||||
{
|
||||
//manually handle closing of stream
|
||||
}
|
||||
|
||||
long ac_Length(IntPtr user)
|
||||
{
|
||||
if (dataStream == null) return 0;
|
||||
|
||||
try
|
||||
{
|
||||
return dataStream.Length;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ac_Read(IntPtr buffer, int length, IntPtr user)
|
||||
{
|
||||
if (dataStream == null) return 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (length > readBuffer.Length)
|
||||
readBuffer = new byte[length];
|
||||
|
||||
if (!dataStream.CanRead)
|
||||
return 0;
|
||||
|
||||
int readBytes = dataStream.Read(readBuffer, 0, length);
|
||||
Marshal.Copy(readBuffer, 0, buffer, readBytes);
|
||||
return readBytes;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
bool ac_Seek(long offset, IntPtr user)
|
||||
{
|
||||
if (dataStream == null) return false;
|
||||
|
||||
try
|
||||
{
|
||||
return dataStream.Seek(offset, SeekOrigin.Begin) == offset;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
55
osu.Framework/Audio/Track/AudioTrackVirtual.cs
Normal file
55
osu.Framework/Audio/Track/AudioTrackVirtual.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Framework.Audio.Track
|
||||
{
|
||||
class AudioTrackVirtual : AudioTrack
|
||||
{
|
||||
StopwatchClock clock = new StopwatchClock();
|
||||
|
||||
double seekOffset;
|
||||
|
||||
public override bool Seek(double seek)
|
||||
{
|
||||
double current = CurrentTime;
|
||||
|
||||
seekOffset = seek;
|
||||
clock.Restart();
|
||||
|
||||
if (Length > 0 && seekOffset > Length)
|
||||
seekOffset = Length;
|
||||
|
||||
return current != seekOffset;
|
||||
}
|
||||
|
||||
public override void Start()
|
||||
{
|
||||
clock.Start();
|
||||
}
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
clock.Reset();
|
||||
seekOffset = 0;
|
||||
|
||||
base.Reset();
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
clock.Stop();
|
||||
}
|
||||
|
||||
public override bool IsRunning => clock.IsRunning;
|
||||
|
||||
public override double CurrentTime => seekOffset + clock.CurrentTime;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (Length > 0 && CurrentTime >= Length)
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
32
osu.Framework/Audio/Track/TrackManager.cs
Normal file
32
osu.Framework/Audio/Track/TrackManager.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedBass;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Resources;
|
||||
|
||||
namespace osu.Framework.Audio.Track
|
||||
{
|
||||
public class TrackManager : AudioCollectionManager<AudioTrack>
|
||||
{
|
||||
IResourceStore<byte[]> store;
|
||||
|
||||
public TrackManager(IResourceStore<byte[]> store)
|
||||
{
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public AudioTrack GetTrack(string name)
|
||||
{
|
||||
AudioTrackBass track = new AudioTrackBass(store.GetStream(name));
|
||||
AddItem(track);
|
||||
return track;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
osu.Framework/Cached/Cached.cs
Normal file
99
osu.Framework/Cached/Cached.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
namespace osu.Framework.Cached
|
||||
{
|
||||
public class Cached<T>
|
||||
{
|
||||
public delegate T PropertyUpdater<T>();
|
||||
|
||||
/// <summary>
|
||||
/// How often this property is refreshed.
|
||||
/// </summary>
|
||||
public readonly int RefreshInterval;
|
||||
|
||||
/// <summary>
|
||||
/// Whether we allow reads of stale values. If this is set to false, there may be a potential blocking delay when accessing the property.
|
||||
/// </summary>
|
||||
public bool AllowStaleReads = true;
|
||||
|
||||
private bool isStale => lastUpdateTime < 0 || (RefreshInterval >= 0 && clock?.CurrentTime > lastUpdateTime + RefreshInterval);
|
||||
public bool IsValid => !isStale;
|
||||
|
||||
private PropertyUpdater<T> updateDelegate;
|
||||
private readonly IClock clock;
|
||||
private double lastUpdateTime = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new cached property.
|
||||
/// </summary>
|
||||
/// <param name="updateDelegate">The delegate method which will perform future updates to this property.</param>
|
||||
/// <param name="refreshInterval">How often we should refresh this property. Set to -1 to never update. Set to 0 for once per frame.</param>
|
||||
public Cached(PropertyUpdater<T> updateDelegate = null, IClock clock = null, int refreshInterval = -1)
|
||||
{
|
||||
RefreshInterval = refreshInterval;
|
||||
this.updateDelegate = updateDelegate;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
public static implicit operator T(Cached<T> value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh this cached object with a custom delegate.
|
||||
/// </summary>
|
||||
/// <param name="providedDelegate"></param>
|
||||
public T Refresh(PropertyUpdater<T> providedDelegate)
|
||||
{
|
||||
if (isStale)
|
||||
{
|
||||
updateDelegate = updateDelegate ?? providedDelegate;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh this property.
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
if (updateDelegate == null)
|
||||
throw new Exception("No value cached and no update delegate prepared!");
|
||||
|
||||
value = updateDelegate();
|
||||
lastUpdateTime = clock?.CurrentTime ?? 0;
|
||||
}
|
||||
|
||||
public bool Invalidate()
|
||||
{
|
||||
if (lastUpdateTime < 0) return false;
|
||||
|
||||
lastUpdateTime = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
private T value;
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (isStale)
|
||||
Refresh();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
throw new Exception("Can't manually update value!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
97
osu.Framework/Configuration/Bindable.cs
Normal file
97
osu.Framework/Configuration/Bindable.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
|
||||
namespace osu.Framework.Configuration
|
||||
{
|
||||
public class Bindable<T> : IBindable
|
||||
where T : IComparable
|
||||
{
|
||||
private T value;
|
||||
|
||||
public T Default;
|
||||
|
||||
public virtual bool IsDefault => Equals(value, Default);
|
||||
|
||||
public event EventHandler ValueChanged;
|
||||
|
||||
public virtual T Value
|
||||
{
|
||||
get { return value; }
|
||||
set
|
||||
{
|
||||
if (this.value?.CompareTo(value) == 0) return;
|
||||
|
||||
this.value = value;
|
||||
|
||||
TriggerChange();
|
||||
}
|
||||
}
|
||||
|
||||
public Bindable(T value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator T(Bindable<T> value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public object ObjectValue
|
||||
{
|
||||
get
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
set
|
||||
{
|
||||
try
|
||||
{
|
||||
Value = (T)value;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool Parse(object s)
|
||||
{
|
||||
if (s is T)
|
||||
Value = (T)s;
|
||||
else if (typeof(T).IsEnum)
|
||||
Value = (T)Enum.Parse(typeof(T), s as string);
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void TriggerChange()
|
||||
{
|
||||
if (ValueChanged != null) ValueChanged(this, null);
|
||||
}
|
||||
|
||||
public void UnbindAll()
|
||||
{
|
||||
ValueChanged = null;
|
||||
}
|
||||
|
||||
string description;
|
||||
public string Description
|
||||
{
|
||||
get { return description; }
|
||||
set { description = value; }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
Value = Default;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
osu.Framework/Configuration/BindableBool.cs
Normal file
36
osu.Framework/Configuration/BindableBool.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
|
||||
namespace osu.Framework.Configuration
|
||||
{
|
||||
public class BindableBool : Bindable<bool>
|
||||
{
|
||||
public BindableBool(bool value = false) : base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public static implicit operator bool(BindableBool value)
|
||||
{
|
||||
return value == null ? false : value.Value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value ? @"true" : @"false";
|
||||
}
|
||||
|
||||
public override bool Parse(object s)
|
||||
{
|
||||
string str = s as string;
|
||||
Value = str == @"1" || str == @"true";
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
Value = !Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
osu.Framework/Configuration/BindableDouble.cs
Normal file
57
osu.Framework/Configuration/BindableDouble.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace osu.Framework.Configuration
|
||||
{
|
||||
public class BindableDouble : Bindable<double>
|
||||
{
|
||||
internal double MinValue = double.MinValue;
|
||||
internal double MaxValue = double.MaxValue;
|
||||
|
||||
public override double Value
|
||||
{
|
||||
get { return base.Value; }
|
||||
set
|
||||
{
|
||||
double boundValue = value;
|
||||
|
||||
if (boundValue > MaxValue)
|
||||
boundValue = MaxValue;
|
||||
else if (boundValue < MinValue)
|
||||
boundValue = MinValue;
|
||||
|
||||
if (Precision > double.Epsilon)
|
||||
boundValue = Math.Round(boundValue / Precision) * Precision;
|
||||
|
||||
base.Value = boundValue;
|
||||
}
|
||||
}
|
||||
|
||||
public BindableDouble(double value = 0) : base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public static implicit operator double(BindableDouble value)
|
||||
{
|
||||
return value == null ? 0 : value.Value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString("0.0###", NumberFormatInfo.InvariantInfo);
|
||||
}
|
||||
|
||||
public override bool Parse(object s)
|
||||
{
|
||||
Value = double.Parse(s as string, NumberFormatInfo.InvariantInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
public double Precision = double.Epsilon;
|
||||
|
||||
public override bool IsDefault => Math.Abs(Value - Default) < Precision;
|
||||
}
|
||||
}
|
||||
49
osu.Framework/Configuration/BindableInt.cs
Normal file
49
osu.Framework/Configuration/BindableInt.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace osu.Framework.Configuration
|
||||
{
|
||||
public class BindableInt : Bindable<int>
|
||||
{
|
||||
internal int MinValue = int.MinValue;
|
||||
internal int MaxValue = int.MaxValue;
|
||||
|
||||
public override int Value
|
||||
{
|
||||
get { return base.Value; }
|
||||
set
|
||||
{
|
||||
int boundValue = value;
|
||||
|
||||
if (boundValue > MaxValue)
|
||||
boundValue = MaxValue;
|
||||
else if (boundValue < MinValue)
|
||||
boundValue = MinValue;
|
||||
|
||||
base.Value = boundValue;
|
||||
}
|
||||
}
|
||||
|
||||
public BindableInt(int value = 0) : base(value)
|
||||
{
|
||||
}
|
||||
|
||||
public static implicit operator int(BindableInt value)
|
||||
{
|
||||
return value == null ? 0 : value.Value;
|
||||
}
|
||||
|
||||
public override bool Parse(object s)
|
||||
{
|
||||
Value = int.Parse(s as string, NumberFormatInfo.InvariantInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString(NumberFormatInfo.InvariantInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
181
osu.Framework/Configuration/ConfigManager.cs
Normal file
181
osu.Framework/Configuration/ConfigManager.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace osu.Framework.Configuration
|
||||
{
|
||||
public class ConfigManager<T> : IDisposable
|
||||
where T : struct
|
||||
{
|
||||
public string Filename = @"game.ini";
|
||||
|
||||
bool hasUnsavedChanges;
|
||||
|
||||
Dictionary<T, IBindable> configStore = new Dictionary<T, IBindable>();
|
||||
|
||||
public ConfigManager()
|
||||
{
|
||||
InitialiseDefaults();
|
||||
Load();
|
||||
}
|
||||
|
||||
protected virtual void InitialiseDefaults()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BindableDouble Set(T lookup, double value)
|
||||
{
|
||||
BindableDouble bindable = GetBindable<double>(lookup) as BindableDouble;
|
||||
|
||||
if (bindable == null)
|
||||
{
|
||||
bindable = new BindableDouble(value);
|
||||
addBindable(lookup, bindable);
|
||||
}
|
||||
else
|
||||
{
|
||||
bindable.Value = value;
|
||||
}
|
||||
|
||||
return bindable;
|
||||
}
|
||||
|
||||
private void addBindable(T lookup, IBindable bindable)
|
||||
{
|
||||
configStore[lookup] = bindable;
|
||||
bindable.ValueChanged += delegate { hasUnsavedChanges = true; };
|
||||
}
|
||||
|
||||
public BindableInt Set(T lookup, int value)
|
||||
{
|
||||
BindableInt bindable = GetBindable<int>(lookup) as BindableInt;
|
||||
|
||||
if (bindable == null)
|
||||
{
|
||||
bindable = new BindableInt(value);
|
||||
addBindable(lookup, bindable);
|
||||
}
|
||||
else
|
||||
{
|
||||
bindable.Value = value;
|
||||
}
|
||||
|
||||
return bindable;
|
||||
}
|
||||
|
||||
public Bindable<U> Set<U>(T lookup, U value) where U : IComparable
|
||||
{
|
||||
Bindable<U> bindable = GetBindable<U>(lookup);
|
||||
|
||||
if (bindable == null)
|
||||
bindable = set(lookup, value);
|
||||
else
|
||||
bindable.Value = value;
|
||||
|
||||
return bindable;
|
||||
}
|
||||
|
||||
private Bindable<U> set<U>(T lookup, U value) where U : IComparable
|
||||
{
|
||||
Bindable<U> bindable = new Bindable<U>(value);
|
||||
addBindable(lookup, bindable);
|
||||
return bindable;
|
||||
}
|
||||
|
||||
public U Get<U>(T lookup) where U : IComparable
|
||||
{
|
||||
return GetBindable<U>(lookup).Value;
|
||||
}
|
||||
|
||||
public Bindable<U> GetBindable<U>(T lookup) where U : IComparable
|
||||
{
|
||||
IBindable obj;
|
||||
|
||||
if (configStore.TryGetValue(lookup, out obj))
|
||||
{
|
||||
Bindable<U> bindable = obj as Bindable<U>;
|
||||
return bindable;
|
||||
}
|
||||
|
||||
return set(lookup, default(U));
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
if (!File.Exists(Filename)) return;
|
||||
|
||||
string[] lines = File.ReadAllLines(Filename);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
int equalsIndex = line.IndexOf('=');
|
||||
|
||||
if (line.Length == 0 || line[0] == '#' || equalsIndex < 0) continue;
|
||||
|
||||
string key = line.Substring(0, equalsIndex).Trim();
|
||||
string val = line.Remove(0, equalsIndex + 1).Trim();
|
||||
|
||||
T lookup;
|
||||
|
||||
if (!Enum.TryParse(key, out lookup))
|
||||
continue;
|
||||
|
||||
IBindable b;
|
||||
|
||||
if (!configStore.TryGetValue(lookup, out b))
|
||||
continue;
|
||||
|
||||
b.Parse(val);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Save()
|
||||
{
|
||||
if (!hasUnsavedChanges) return true;
|
||||
|
||||
try
|
||||
{
|
||||
using (Stream stream = new SafeWriteStream(Filename))
|
||||
using (StreamWriter w = new StreamWriter(stream))
|
||||
{
|
||||
foreach (KeyValuePair<T, IBindable> p in configStore)
|
||||
w.WriteLine(@"{0} = {1}", p.Key, p.Value);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
Save();
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
~ConfigManager()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
9
osu.Framework/Configuration/IBindable.cs
Normal file
9
osu.Framework/Configuration/IBindable.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
namespace osu.Framework.Configuration
|
||||
{
|
||||
public interface IBindable : IHasObjectValue, IValueChangedObservable
|
||||
{
|
||||
}
|
||||
}
|
||||
12
osu.Framework/Configuration/IHasObjectValue.cs
Normal file
12
osu.Framework/Configuration/IHasObjectValue.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
namespace osu.Framework.Configuration
|
||||
{
|
||||
public interface IHasObjectValue
|
||||
{
|
||||
object ObjectValue { get; set; }
|
||||
|
||||
bool Parse(object s);
|
||||
}
|
||||
}
|
||||
16
osu.Framework/Configuration/IValueChangedObservable.cs
Normal file
16
osu.Framework/Configuration/IValueChangedObservable.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
|
||||
namespace osu.Framework.Configuration
|
||||
{
|
||||
public interface IValueChangedObservable
|
||||
{
|
||||
event EventHandler ValueChanged;
|
||||
|
||||
void UnbindAll();
|
||||
|
||||
string Description { get; set; }
|
||||
}
|
||||
}
|
||||
68
osu.Framework/Configuration/SafeWriteStream.cs
Normal file
68
osu.Framework/Configuration/SafeWriteStream.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using osu.Framework.IO;
|
||||
|
||||
namespace osu.Framework.Configuration
|
||||
{
|
||||
class SafeWriteStream : FileStream
|
||||
{
|
||||
static object SafeLock = new object(); //ensure we are only ever writing one stream to disk at a time, application wide.
|
||||
|
||||
private bool aborted;
|
||||
|
||||
string finalFilename;
|
||||
string temporaryFilename => base.Name;
|
||||
|
||||
public SafeWriteStream(string filename) : base(filename + "." + DateTime.Now.Ticks, FileMode.Create)
|
||||
{
|
||||
finalFilename = filename;
|
||||
}
|
||||
|
||||
~SafeWriteStream()
|
||||
{
|
||||
if (!isDisposed) Dispose();
|
||||
}
|
||||
|
||||
internal void Abort()
|
||||
{
|
||||
aborted = true;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
lock (SafeLock)
|
||||
{
|
||||
base.Close();
|
||||
|
||||
if (!File.Exists(temporaryFilename)) return;
|
||||
|
||||
if (aborted)
|
||||
{
|
||||
FileSafety.FileDelete(temporaryFilename);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
FileSafety.FileMove(temporaryFilename, finalFilename);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isDisposed;
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (isDisposed) return;
|
||||
isDisposed = true;
|
||||
|
||||
base.Dispose(disposing);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
135
osu.Framework/Extensions/GeneralExtensions.cs
Normal file
135
osu.Framework/Extensions/GeneralExtensions.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// this is an abusive thing to do, but it increases the visibility of Extension Methods to virtually every file.
|
||||
namespace System
|
||||
{
|
||||
/// <summary>
|
||||
/// This class holds extension methods for various purposes and should not be used explicitly, ever.
|
||||
/// </summary>
|
||||
public static class ExtensionMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// Searches for an element that matches the conditions defined by the specified predicate.
|
||||
/// </summary>
|
||||
/// <param name="match">The predicate that needs to be matched.</param>
|
||||
/// <param name="startIndex">The index to start conditional search.</param>
|
||||
/// <returns>The matched item, or the default value for the type if no item was matched.</returns>
|
||||
public static T Find<T>(this List<T> list, Predicate<T> match, int startIndex)
|
||||
{
|
||||
if (!list.IsValidIndex(startIndex)) return default(T);
|
||||
|
||||
int val = list.FindIndex(startIndex, list.Count - startIndex - 1, match);
|
||||
|
||||
return list.ValueAtOrDefault(val);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given item to the list according to standard sorting rules. Do not use on unsorted lists.
|
||||
/// </summary>
|
||||
/// <param name="item">The item that should be added.</param>
|
||||
/// <returns>The index in the list where the item was inserted.</returns>
|
||||
public static int AddInPlace<T>(this List<T> list, T item)
|
||||
{
|
||||
int index = list.BinarySearch(item);
|
||||
if (index < 0) index = ~index; // BinarySearch hacks multiple return values with 2's complement.
|
||||
list.Insert(index, item);
|
||||
return index;
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds the given item to the list according to the comparers sorting rules. Do not use on unsorted lists.
|
||||
/// </summary>
|
||||
/// <param name="item">The item that should be added.</param>
|
||||
/// <param name="comparer">The comparer that should be used for sorting.</param>
|
||||
/// <returns>The index in the list where the item was inserted.</returns>
|
||||
public static int AddInPlace<T>(this List<T> list, T item, IComparer<T> comparer)
|
||||
{
|
||||
int index = list.BinarySearch(item, comparer);
|
||||
if (index < 0) index = ~index; // BinarySearch hacks multiple return values with 2's complement.
|
||||
list.Insert(index, item);
|
||||
return index;
|
||||
}
|
||||
|
||||
public static bool IsValidIndex<T>(this List<T> list, int index)
|
||||
{
|
||||
return index >= 0 && index < list.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates whether index is valid, before returning the value at the given index.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Probably should limit to nullable types.</typeparam>
|
||||
/// <param name="list">The list to take values</param>
|
||||
/// <param name="index">The index to request values from</param>
|
||||
/// <returns>Value at index, else the default value</returns>
|
||||
public static T ValueAtOrDefault<T>(this List<T> list, int index)
|
||||
{
|
||||
return list.IsValidIndex(index) ? list[index] : default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares every item in list to given list.
|
||||
/// </summary>
|
||||
public static bool CompareTo<T>(this List<T> list, List<T> list2)
|
||||
{
|
||||
if (list.Count != list2.Count) return false;
|
||||
|
||||
return !list.Where((t, i) => !t.Equals(list2[i])).Any();
|
||||
}
|
||||
|
||||
public static string ToResolutionString(this System.Drawing.Size size)
|
||||
{
|
||||
return size.Width.ToString() + 'x' + size.Height.ToString();
|
||||
}
|
||||
|
||||
public static void WriteLineExplicit(this Stream s, string str = @"")
|
||||
{
|
||||
byte[] data = Encoding.UTF8.GetBytes($"{str}\r\n");
|
||||
s.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
public static string UnsecureRepresentation(this SecureString s)
|
||||
{
|
||||
IntPtr bstr = Marshal.SecureStringToBSTR(s);
|
||||
|
||||
try
|
||||
{
|
||||
return Marshal.PtrToStringBSTR(bstr);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeBSTR(bstr);
|
||||
}
|
||||
}
|
||||
|
||||
public static long ToUnixTimestamp(this DateTime date)
|
||||
{
|
||||
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
return Convert.ToInt64((date - epoch).TotalSeconds);
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
|
||||
{
|
||||
if (assembly == null) throw new ArgumentNullException("assembly");
|
||||
try
|
||||
{
|
||||
return assembly.GetTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
return e.Types.Where(t => t != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Extensions.MatrixExtensions
|
||||
{
|
||||
public static class MatrixExtensions
|
||||
{
|
||||
public static Matrix3 TranslateTo(this Matrix3 m, Vector2 v)
|
||||
{
|
||||
m.Row2 += m.Row0 * v.X + m.Row1 * v.Y;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Matrix3 RotateTo(this Matrix3 m, float angle)
|
||||
{
|
||||
// Convert to radians
|
||||
angle = angle / (180 / MathHelper.Pi);
|
||||
float cos = (float)Math.Cos(angle);
|
||||
float sin = (float)Math.Sin(angle);
|
||||
|
||||
Vector3 temp = m.Row0 * cos + m.Row1 * sin;
|
||||
m.Row1 = m.Row1 * cos - m.Row0 * sin;
|
||||
m.Row0 = temp;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Matrix3 ScaleTo(this Matrix3 m, Vector2 v)
|
||||
{
|
||||
m.Row0 *= v.X;
|
||||
m.Row1 *= v.Y;
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Framework.Extensions.RectangleExtensions;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Extensions.PolygonExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Todo: Support segment containment and circles.
|
||||
/// Todo: Might be overkill, but possibly support convex decomposition?
|
||||
/// </summary>
|
||||
public static class IConvexPolygonExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether two convex polygons intersect.
|
||||
/// </summary>
|
||||
/// <param name="first">The first polygon.</param>
|
||||
/// <param name="second">The second polygon.</param>
|
||||
/// <returns>Whether the two polygons intersect.</returns>
|
||||
public static bool Intersects(this IConvexPolygon first, IConvexPolygon second)
|
||||
{
|
||||
Vector2[][] bothAxes = { first.GetAxes(), second.GetAxes() };
|
||||
|
||||
Vector2[] firstVertices = first.Vertices;
|
||||
Vector2[] secondVertices = second.Vertices;
|
||||
|
||||
foreach (Vector2[] axes in bothAxes)
|
||||
{
|
||||
foreach (Vector2 axis in axes)
|
||||
{
|
||||
ProjectionRange firstRange = new ProjectionRange(axis, firstVertices);
|
||||
ProjectionRange secondRange = new ProjectionRange(axis, secondVertices);
|
||||
|
||||
if (!firstRange.Overlaps(secondRange))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether two convex polygons intersect.
|
||||
/// </summary>
|
||||
/// <param name="first">The first polygon.</param>
|
||||
/// <param name="second">The second polygon.</param>
|
||||
/// <returns>Whether the two polygons intersect.</returns>
|
||||
public static bool Intersects(this IConvexPolygon first, Rectangle second)
|
||||
{
|
||||
Vector2[][] bothAxes = { first.GetAxes(), second.GetAxes() };
|
||||
|
||||
Vector2[] firstVertices = first.Vertices;
|
||||
Vector2[] secondVertices = second.GetVertices();
|
||||
|
||||
foreach (Vector2[] axes in bothAxes)
|
||||
{
|
||||
foreach (Vector2 axis in axes)
|
||||
{
|
||||
ProjectionRange firstRange = new ProjectionRange(axis, firstVertices);
|
||||
ProjectionRange secondRange = new ProjectionRange(axis, secondVertices);
|
||||
|
||||
if (!firstRange.Overlaps(secondRange))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Extensions.PolygonExtensions
|
||||
{
|
||||
public static class IPolygonExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the axes for each edge in a polygon.
|
||||
/// </summary>
|
||||
/// <param name="polygon">The polygon to return the axes of.</param>
|
||||
/// <param name="normalize">Whether the normals should be normalized. Allows computation of the exact intersection point.</param>
|
||||
/// <returns>The axes of the polygon.</returns>
|
||||
public static Vector2[] GetAxes(this IPolygon polygon, bool normalize = false)
|
||||
{
|
||||
Vector2[] axes = new Vector2[polygon.AxisVertices.Length];
|
||||
|
||||
for (int i = 0; i < polygon.AxisVertices.Length; i++)
|
||||
{
|
||||
// Construct an edge between two sequential points
|
||||
Vector2 v1 = polygon.AxisVertices[i];
|
||||
Vector2 v2 = polygon.AxisVertices[i == polygon.AxisVertices.Length - 1 ? 0 : i + 1];
|
||||
Vector2 edge = v2 - v1;
|
||||
|
||||
// Find the normal to the edge
|
||||
Vector2 normal = new Vector2(-edge.Y, edge.X);
|
||||
|
||||
if (normalize)
|
||||
normal = Vector2.Normalize(normal);
|
||||
|
||||
axes[i] = normal;
|
||||
}
|
||||
|
||||
return axes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Extensions.RectangleExtensions
|
||||
{
|
||||
public static class RectangleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the axes for each edge in a rectangle.
|
||||
/// <para>A rectangle has equal normals for opposing edges, so only two axes will be returned.</para>
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The rectangle to return the axes of.</param>
|
||||
/// <param name="normalize">Whether the normals should be normalized. Allows computation of the exact intersection point.</param>
|
||||
/// <returns>The axes of the rectangle.</returns>
|
||||
public static Vector2[] GetAxes(this Rectangle rectangle, bool normalize = false)
|
||||
{
|
||||
Vector2[] edges = { new Vector2(rectangle.Right - rectangle.Left, rectangle.Top), new Vector2(rectangle.Right, rectangle.Bottom - rectangle.Top) };
|
||||
|
||||
for (int i = 0; i < edges.Length; i++)
|
||||
{
|
||||
Vector2 normal = new Vector2(-edges[i].Y, edges[i].X);
|
||||
|
||||
if (normalize)
|
||||
normal = Vector2.Normalize(normal);
|
||||
|
||||
edges[i] = normal;
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
public static Vector2[] GetVertices(this Rectangle rectangle)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new Vector2(rectangle.Left, rectangle.Top),
|
||||
new Vector2(rectangle.Right, rectangle.Top),
|
||||
new Vector2(rectangle.Right, rectangle.Bottom),
|
||||
new Vector2(rectangle.Left, rectangle.Bottom)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
39
osu.Framework/Framework/BasicGameForm.cs
Normal file
39
osu.Framework/Framework/BasicGameForm.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
|
||||
namespace osu.Framework.Framework
|
||||
{
|
||||
public abstract class BasicGameForm : GLControl
|
||||
{
|
||||
public BasicGameForm(GraphicsContextFlags flags) : base(GraphicsMode.Default, 2, 0, flags)
|
||||
{
|
||||
}
|
||||
|
||||
public event EventHandler ApplicationActivated;
|
||||
|
||||
public event EventHandler ApplicationDeactivated;
|
||||
|
||||
public abstract event EventHandler ScreenChanged;
|
||||
|
||||
public event EventHandler UserResized;
|
||||
|
||||
public abstract Rectangle ClientBounds { get; }
|
||||
|
||||
public abstract bool IsMinimized { get; }
|
||||
|
||||
public abstract void CentreToScreen();
|
||||
|
||||
protected virtual void OnActivateApp(bool active)
|
||||
{
|
||||
if (active)
|
||||
ApplicationActivated?.Invoke(this, EventArgs.Empty);
|
||||
else
|
||||
ApplicationDeactivated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
103
osu.Framework/Framework/BasicGameHost.cs
Normal file
103
osu.Framework/Framework/BasicGameHost.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.OpenGL;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
|
||||
namespace osu.Framework.Framework
|
||||
{
|
||||
public abstract class BasicGameHost : Container
|
||||
{
|
||||
public abstract BasicGameWindow Window { get; }
|
||||
public abstract GLControl GLControl { get; }
|
||||
public abstract bool IsActive { get; }
|
||||
|
||||
public event EventHandler Activated;
|
||||
public event EventHandler Deactivated;
|
||||
public event EventHandler Exiting;
|
||||
public event EventHandler Idle;
|
||||
|
||||
public override bool IsVisible => true;
|
||||
|
||||
public override Vector2 Size => new Vector2(Window?.Form.ClientSize.Width ?? 0, Window?.Form.ClientSize.Height ?? 0);
|
||||
|
||||
protected virtual void OnActivated(object sender, EventArgs args)
|
||||
{
|
||||
Activated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnDeactivated(object sender, EventArgs args)
|
||||
{
|
||||
Deactivated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnExiting(object sender, EventArgs args)
|
||||
{
|
||||
Exiting?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnIdle(object sender, EventArgs args)
|
||||
{
|
||||
GLWrapper.Reset();
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
||||
|
||||
UpdateSubTree();
|
||||
DrawSubTree();
|
||||
|
||||
Idle?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
GLControl.SwapBuffers();
|
||||
}
|
||||
|
||||
private bool exitRequested;
|
||||
public void Exit()
|
||||
{
|
||||
exitRequested = true;
|
||||
}
|
||||
|
||||
public virtual void Run()
|
||||
{
|
||||
Window.ClientSizeChanged += delegate { Invalidate(); };
|
||||
|
||||
GLControl.Initialize();
|
||||
|
||||
Exception error = null;
|
||||
|
||||
try
|
||||
{
|
||||
Application.Idle += OnApplicationIdle;
|
||||
Application.Run(Window.Form);
|
||||
}
|
||||
catch (OutOfMemoryException e)
|
||||
{
|
||||
error = e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Application.Idle -= OnApplicationIdle;
|
||||
|
||||
if (error == null || !(error is OutOfMemoryException))
|
||||
//we don't want to attempt a safe shutdown is memory is low; it may corrupt database files.
|
||||
OnExiting(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnApplicationIdle(object sender, EventArgs e)
|
||||
{
|
||||
if (exitRequested)
|
||||
Window.Close();
|
||||
else
|
||||
OnIdle(sender, e);
|
||||
}
|
||||
|
||||
public void Load(Game game)
|
||||
{
|
||||
game.SetHost(this);
|
||||
Add(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
osu.Framework/Framework/BasicGameWindow.cs
Normal file
66
osu.Framework/Framework/BasicGameWindow.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace osu.Framework.Framework
|
||||
{
|
||||
public abstract class BasicGameWindow
|
||||
{
|
||||
public event EventHandler ClientSizeChanged;
|
||||
public event EventHandler ScreenDeviceNameChanged;
|
||||
public event EventHandler Activated;
|
||||
public event EventHandler Deactivated;
|
||||
public event EventHandler Paint;
|
||||
|
||||
public abstract Rectangle ClientBounds { get; }
|
||||
public abstract IntPtr Handle { get; }
|
||||
public abstract bool IsMinimized { get; }
|
||||
public abstract BasicGameForm Form { get; }
|
||||
|
||||
public BasicGameWindow() { }
|
||||
|
||||
public abstract void Close();
|
||||
|
||||
private string title;
|
||||
public string Title
|
||||
{
|
||||
get { return title; }
|
||||
set
|
||||
{
|
||||
if (value == null || title == value)
|
||||
return;
|
||||
|
||||
SetTitle(title = value);
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnActivated()
|
||||
{
|
||||
Activated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected void OnClientSizeChanged()
|
||||
{
|
||||
ClientSizeChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected void OnDeactivated()
|
||||
{
|
||||
Deactivated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected void OnPaint()
|
||||
{
|
||||
Paint?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected void OnScreenDeviceNameChanged()
|
||||
{
|
||||
ScreenDeviceNameChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected abstract void SetTitle(string title);
|
||||
}
|
||||
}
|
||||
99
osu.Framework/Framework/GLControl.cs
Normal file
99
osu.Framework/Framework/GLControl.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using OpenTK.Graphics.ES20;
|
||||
using OpenTK.Graphics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using OpenTK;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using osu.Framework.Graphics.Shaders;
|
||||
using osu.Framework.Logging;
|
||||
|
||||
namespace osu.Framework.Framework
|
||||
{
|
||||
public class GLControl : OpenTK.GLControl
|
||||
{
|
||||
private string SupportedExtensions;
|
||||
|
||||
internal Version GLVersion;
|
||||
internal Version GLSLVersion;
|
||||
|
||||
public GLControl(GraphicsMode mode, int major, int minor, GraphicsContextFlags flags)
|
||||
: base(mode, major, minor, flags)
|
||||
{
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
string version = GL.GetString(StringName.Version);
|
||||
GLVersion = new Version(version.Split(' ')[0]);
|
||||
version = GL.GetString(StringName.ShadingLanguageVersion);
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
{
|
||||
try
|
||||
{
|
||||
GLSLVersion = new Version(version.Split(' ')[0]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e, $@"couldn't set GLSL version using string '{version}'");
|
||||
}
|
||||
}
|
||||
|
||||
if (GLSLVersion == null)
|
||||
GLSLVersion = new Version();
|
||||
|
||||
//Set up OpenGL related characteristics
|
||||
GL.Disable(EnableCap.DepthTest);
|
||||
GL.Disable(EnableCap.StencilTest);
|
||||
GL.Enable(EnableCap.Blend);
|
||||
GL.Enable(EnableCap.ScissorTest);
|
||||
|
||||
Logger.Log($@"GL Initialized
|
||||
GL Version: { GL.GetString(StringName.Version)}
|
||||
GL Renderer: { GL.GetString(StringName.Renderer)}
|
||||
GL Shader Language version: { GL.GetString(StringName.ShadingLanguageVersion)}
|
||||
GL Vendor: { GL.GetString(StringName.Vendor)}
|
||||
GL Extensions: { GL.GetString(StringName.Extensions)}
|
||||
GL Context: { GraphicsMode}", LoggingTarget.Runtime, LogLevel.Important);
|
||||
}
|
||||
|
||||
protected override void OnMouseEnter(EventArgs e)
|
||||
{
|
||||
Cursor.Hide();
|
||||
base.OnMouseEnter(e);
|
||||
}
|
||||
|
||||
protected override void OnMouseLeave(EventArgs e)
|
||||
{
|
||||
Cursor.Show();
|
||||
base.OnMouseLeave(e);
|
||||
}
|
||||
|
||||
internal bool CheckExtension(string extensionName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(SupportedExtensions))
|
||||
SupportedExtensions = GL.GetString(StringName.Extensions);
|
||||
|
||||
return SupportedExtensions.Contains(extensionName);
|
||||
}
|
||||
catch { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
GL.Flush();
|
||||
GL.Finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
125
osu.Framework/Framework/Window.cs
Normal file
125
osu.Framework/Framework/Window.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using OpenTK;
|
||||
using System.Windows.Forms;
|
||||
using System.Runtime.InteropServices;
|
||||
using osu.Framework.Threading;
|
||||
|
||||
namespace osu.Framework.Framework
|
||||
{
|
||||
public class Window
|
||||
{
|
||||
public delegate void ResolutionChangeSucceededEventHandler(bool spriteResultionChanged);
|
||||
|
||||
private const int default_width = 1366;
|
||||
private const int default_height = 768;
|
||||
|
||||
public event VoidDelegate OnSizeChanged;
|
||||
public event BoolDelegate OnMinimizedStateChanged;
|
||||
|
||||
public BasicGameForm Form => host?.Window?.Form;
|
||||
public Size Size
|
||||
{
|
||||
get { return Form.ClientSize; }
|
||||
set { Form.ClientSize = value; }
|
||||
}
|
||||
|
||||
public int Width => Size.Width;
|
||||
|
||||
public int Height => Size.Height;
|
||||
|
||||
public bool IsMinimized => Form.IsMinimized;
|
||||
|
||||
public IntPtr Handle => gameWindow.Handle;
|
||||
|
||||
private BasicGameHost host;
|
||||
private BasicGameWindow gameWindow => host?.Window;
|
||||
|
||||
internal Window(BasicGameHost host)
|
||||
{
|
||||
this.host = host;
|
||||
|
||||
Form.AllowDrop = true;
|
||||
Form.SizeChanged += Form_SizeChanged;
|
||||
|
||||
Size = new Size(default_width, default_height);
|
||||
}
|
||||
|
||||
private void Form_SizeChanged(object sender, EventArgs e)
|
||||
{
|
||||
OnSizeChanged?.Invoke();
|
||||
}
|
||||
|
||||
public bool AllowDrop
|
||||
{
|
||||
get { return Form.AllowDrop; }
|
||||
set { Form.AllowDrop = value; }
|
||||
}
|
||||
|
||||
public string Title
|
||||
{
|
||||
get { return gameWindow.Title; }
|
||||
set { gameWindow.Title = value; }
|
||||
}
|
||||
|
||||
public void StealFocus()
|
||||
{
|
||||
Form.BringToFront();
|
||||
Form.Focus();
|
||||
}
|
||||
|
||||
public void BringToFront()
|
||||
{
|
||||
Form.BringToFront();
|
||||
SetForegroundWindow(Form.Handle);
|
||||
}
|
||||
|
||||
private NotifyIcon notifyIcon;
|
||||
private bool minimizedToTray;
|
||||
public bool MinimizedToTray
|
||||
{
|
||||
get { return minimizedToTray; }
|
||||
set
|
||||
{
|
||||
if (value == minimizedToTray)
|
||||
return;
|
||||
minimizedToTray = value;
|
||||
|
||||
if (minimizedToTray)
|
||||
{
|
||||
if (notifyIcon == null)
|
||||
{
|
||||
notifyIcon = new NotifyIcon();
|
||||
notifyIcon.Icon = Form.Icon;
|
||||
notifyIcon.Click += (obj, e) => { MinimizedToTray = false; };
|
||||
}
|
||||
|
||||
notifyIcon.Visible = true;
|
||||
|
||||
Form.WindowState = FormWindowState.Minimized;
|
||||
Form.Visible = false;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Form.Visible = true;
|
||||
Form.WindowState = FormWindowState.Normal;
|
||||
|
||||
Form.ShowInTaskbar = true;
|
||||
notifyIcon.Visible = false;
|
||||
|
||||
BringToFront();
|
||||
}
|
||||
|
||||
OnMinimizedStateChanged?.Invoke(minimizedToTray);
|
||||
}
|
||||
}
|
||||
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
[DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
|
||||
private static extern bool SetForegroundWindow(IntPtr hwnd);
|
||||
}
|
||||
}
|
||||
194
osu.Framework/Game.cs
Normal file
194
osu.Framework/Game.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using osu.Framework.Framework;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using System.Threading;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Resources;
|
||||
using osu.Framework.Graphics.Shaders;
|
||||
using Scheduler = osu.Framework.Threading.Scheduler;
|
||||
|
||||
namespace osu.Framework
|
||||
{
|
||||
public class Game : LargeContainer
|
||||
{
|
||||
public static Window Window { get; private set; }
|
||||
|
||||
internal Scheduler Scheduler;
|
||||
|
||||
private ThrottledFrameClock clock = new ThrottledFrameClock();
|
||||
|
||||
protected override IFrameBasedClock Clock => clock;
|
||||
|
||||
public DllResourceStore Resources;
|
||||
|
||||
public TextureStore Textures;
|
||||
|
||||
protected virtual string MainResourceFile => AppDomain.CurrentDomain.FriendlyName;
|
||||
|
||||
protected int MaximumFramesPerSecond
|
||||
{
|
||||
get { return clock.MaximumUpdateHz; }
|
||||
set { clock.MaximumUpdateHz = value; }
|
||||
}
|
||||
|
||||
internal Thread MainThread;
|
||||
|
||||
private BasicGameForm form => host?.Window?.Form;
|
||||
private BasicGameHost host;
|
||||
|
||||
private bool exitRequested;
|
||||
private bool isActive;
|
||||
|
||||
public AudioManager Audio;
|
||||
|
||||
public ShaderManager Shaders;
|
||||
|
||||
public Game()
|
||||
{
|
||||
Game = this;
|
||||
}
|
||||
|
||||
public void SetHost(BasicGameHost host)
|
||||
{
|
||||
MainThread = Thread.CurrentThread;
|
||||
|
||||
this.host = host;
|
||||
host.Exiting += (sender, args) => { OnExiting(this, args); };
|
||||
|
||||
Window = new Window(host);
|
||||
|
||||
form.FormClosing += OnFormClosing;
|
||||
form.DragEnter += dragEnter;
|
||||
form.DragDrop += dragDrop;
|
||||
}
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
base.Load();
|
||||
|
||||
Scheduler = new Scheduler();
|
||||
|
||||
Resources = new DllResourceStore(MainResourceFile);
|
||||
|
||||
Textures = Textures = new TextureStore(new NamespacedResourceStore<byte[]>(Resources, @"Textures"));
|
||||
|
||||
Audio = new AudioManager(new NamespacedResourceStore<byte[]>(Resources, @"Shaders"), new NamespacedResourceStore<byte[]>(Resources, @"Samples"));
|
||||
|
||||
Shaders = new ShaderManager(new NamespacedResourceStore<byte[]>(Resources, @"Shaders"));
|
||||
|
||||
AddProcessingContainer(new UserInputManager());
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
clock.ProcessFrame();
|
||||
Scheduler.Update();
|
||||
Audio.Update();
|
||||
|
||||
base.Update();
|
||||
}
|
||||
|
||||
private void dragDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
Array fileDrop = e.Data.GetData(DataFormats.FileDrop) as Array;
|
||||
string textDrop = e.Data.GetData(DataFormats.Text) as string;
|
||||
|
||||
if (fileDrop != null)
|
||||
{
|
||||
for (int i = 0; i < fileDrop.Length; i++)
|
||||
OnDroppedFile(fileDrop.GetValue(i).ToString());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(textDrop))
|
||||
OnDroppedText(textDrop);
|
||||
}
|
||||
|
||||
private void dragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
bool isFile = e.Data.GetDataPresent(DataFormats.FileDrop);
|
||||
bool isUrl = e.Data.GetDataPresent(DataFormats.Text);
|
||||
e.Effect = isFile || isUrl ? DragDropEffects.Copy : DragDropEffects.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the Game environment is active (in the foreground).
|
||||
/// </summary>
|
||||
public bool IsActive
|
||||
{
|
||||
get { return isActive; }
|
||||
private set
|
||||
{
|
||||
if (value == isActive)
|
||||
return;
|
||||
isActive = value;
|
||||
|
||||
if (isActive)
|
||||
OnActivated();
|
||||
else
|
||||
OnDeactivated();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDroppedText(string text)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnDroppedFile(string file)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnFormClosing(object sender, FormClosingEventArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnDragEnter(object sender, EventArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnActivated()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected virtual void OnDeactivated()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnExiting(object sender, EventArgs args)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called before a frame cycle has started (Update and Draw).
|
||||
/// </summary>
|
||||
protected virtual void PreFrame()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after a frame cycle has been completed (Update and Draw).
|
||||
/// </summary>
|
||||
protected virtual void PostFrame()
|
||||
{
|
||||
}
|
||||
|
||||
private void onWindowSizeChange()
|
||||
{
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
Window.OnSizeChanged -= onWindowSizeChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
osu.Framework/GameModes/GameMode.cs
Normal file
61
osu.Framework/GameModes/GameMode.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics.Containers;
|
||||
|
||||
namespace osu.Framework.GameModes
|
||||
{
|
||||
public class GameMode : LargeContainer
|
||||
{
|
||||
public virtual string Name => @"Unknown";
|
||||
|
||||
private GameMode lastGameMode;
|
||||
|
||||
private bool modePushed;
|
||||
|
||||
/// <summary>
|
||||
/// Called when this GameMode is being entered.
|
||||
/// </summary>
|
||||
/// <param name="last">The last GameMode.</param>
|
||||
protected virtual void EnterTransition(GameMode last)
|
||||
{
|
||||
FadeInFromZero(200);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when this GameMode is exiting.
|
||||
/// </summary>
|
||||
/// <param name="next">The next GameMode.</param>
|
||||
protected virtual void ExitTransition(GameMode next)
|
||||
{
|
||||
FadeOutFromOne(200);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes to a new GameMode.
|
||||
/// </summary>
|
||||
/// <param name="mode">The new GameMode.</param>
|
||||
protected void PushMode(GameMode mode)
|
||||
{
|
||||
if (modePushed)
|
||||
return;
|
||||
modePushed = true;
|
||||
|
||||
AddTopLevel(mode);
|
||||
|
||||
mode.lastGameMode = this;
|
||||
mode.EnterTransition(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exits this GameMode.
|
||||
/// </summary>
|
||||
protected void ExitMode()
|
||||
{
|
||||
lastGameMode.modePushed = false;
|
||||
|
||||
ExitTransition(lastGameMode);
|
||||
Delay(5000).Expire();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
osu.Framework/Graphics/Batches/IVertexBatch.cs
Normal file
10
osu.Framework/Graphics/Batches/IVertexBatch.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
namespace osu.Framework.Graphics.Batches
|
||||
{
|
||||
public interface IVertexBatch
|
||||
{
|
||||
void Draw();
|
||||
}
|
||||
}
|
||||
28
osu.Framework/Graphics/Batches/LinearBatch.cs
Normal file
28
osu.Framework/Graphics/Batches/LinearBatch.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics.ES20;
|
||||
using osu.Framework.Graphics.OpenGL.Buffers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Graphics.Batches
|
||||
{
|
||||
public class LinearBatch<T> : VertexBatch<T> where T : struct, IEquatable<T>
|
||||
{
|
||||
private BeginMode type;
|
||||
|
||||
public LinearBatch(int size, int fixedBufferAmount, BeginMode type)
|
||||
: base(size, fixedBufferAmount)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
protected override VertexBuffer<T> CreateVertexBuffer()
|
||||
{
|
||||
return new LinearVertexBuffer<T>(Size, type, BufferUsageHint.DynamicDraw);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
osu.Framework/Graphics/Batches/QuadBatch.cs
Normal file
25
osu.Framework/Graphics/Batches/QuadBatch.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics.ES20;
|
||||
using osu.Framework.Graphics.OpenGL.Buffers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Graphics.Batches
|
||||
{
|
||||
public class QuadBatch<T> : VertexBatch<T> where T : struct, IEquatable<T>
|
||||
{
|
||||
public QuadBatch(int size, int fixedBufferAmount)
|
||||
: base(size, fixedBufferAmount)
|
||||
{
|
||||
}
|
||||
|
||||
protected override VertexBuffer<T> CreateVertexBuffer()
|
||||
{
|
||||
return new QuadVertexBuffer<T>(Size, BufferUsageHint.DynamicDraw);
|
||||
}
|
||||
}
|
||||
}
|
||||
114
osu.Framework/Graphics/Batches/VertexBatch.cs
Normal file
114
osu.Framework/Graphics/Batches/VertexBatch.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics.OpenGL;
|
||||
using osu.Framework.Graphics.OpenGL.Buffers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace osu.Framework.Graphics.Batches
|
||||
{
|
||||
public abstract class VertexBatch<T> : IVertexBatch where T : struct, IEquatable<T>
|
||||
{
|
||||
public List<VertexBuffer<T>> VertexBuffers = new List<VertexBuffer<T>>();
|
||||
|
||||
public int Size { get; private set; }
|
||||
|
||||
private int changeBeginIndex = -1;
|
||||
private int changeEndIndex = -1;
|
||||
|
||||
private int currentVertexBuffer = 0;
|
||||
private int currentVertex = 0;
|
||||
private int lastVertex = 0;
|
||||
|
||||
private int fixedBufferAmount;
|
||||
|
||||
private VertexBuffer<T> CurrentVertexBuffer => VertexBuffers[currentVertexBuffer];
|
||||
|
||||
public VertexBatch(int size, int fixedBufferAmount)
|
||||
{
|
||||
// Vertex buffers of size 0 don't make any sense. Let's not blindly hope for good behavior of OpenGL.
|
||||
Debug.Assert(size > 0);
|
||||
|
||||
Size = size;
|
||||
this.fixedBufferAmount = fixedBufferAmount;
|
||||
}
|
||||
|
||||
#region Disposal
|
||||
~VertexBatch()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
foreach (VertexBuffer<T> vbo in VertexBuffers)
|
||||
vbo.Dispose();
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
changeBeginIndex = -1;
|
||||
currentVertexBuffer = 0;
|
||||
currentVertex = 0;
|
||||
lastVertex = 0;
|
||||
}
|
||||
|
||||
protected abstract VertexBuffer<T> CreateVertexBuffer();
|
||||
|
||||
public void Add(T v)
|
||||
{
|
||||
while (currentVertexBuffer >= VertexBuffers.Count)
|
||||
VertexBuffers.Add(CreateVertexBuffer());
|
||||
|
||||
VertexBuffer<T> vertexBuffer = CurrentVertexBuffer;
|
||||
|
||||
if (!vertexBuffer.Vertices[currentVertex].Equals(v))
|
||||
{
|
||||
if (changeBeginIndex == -1)
|
||||
changeBeginIndex = currentVertex;
|
||||
|
||||
changeEndIndex = currentVertex + 1;
|
||||
}
|
||||
|
||||
vertexBuffer.Vertices[currentVertex] = v;
|
||||
++currentVertex;
|
||||
|
||||
if (currentVertex >= vertexBuffer.Vertices.Length)
|
||||
{
|
||||
Draw();
|
||||
lastVertex = currentVertex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (currentVertex == lastVertex)
|
||||
return;
|
||||
|
||||
GLWrapper.SetActiveBatch(this);
|
||||
|
||||
VertexBuffer<T> vertexBuffer = CurrentVertexBuffer;
|
||||
if (changeBeginIndex >= 0)
|
||||
vertexBuffer.UpdateRange(changeBeginIndex, changeEndIndex);
|
||||
|
||||
vertexBuffer.DrawRange(lastVertex, currentVertex);
|
||||
|
||||
// When using multiple buffers we advance to the next one with every draw to prevent contention on the same buffer with future vertex updates.
|
||||
currentVertexBuffer = (currentVertexBuffer + 1) % fixedBufferAmount;
|
||||
currentVertex = 0;
|
||||
|
||||
lastVertex = currentVertex;
|
||||
changeBeginIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
osu.Framework/Graphics/Containers/AutoSizeContainer.cs
Normal file
90
osu.Framework/Graphics/Containers/AutoSizeContainer.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Cached;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Framework.Graphics.Containers
|
||||
{
|
||||
public class AutoSizeContainer : Container
|
||||
{
|
||||
protected bool RequireAutoSize => autoSizeUpdatePending;
|
||||
|
||||
private bool autoSizeUpdatePending;
|
||||
|
||||
public override bool Invalidate(bool affectsSize = true, bool affectsPosition = true, Drawable source = null)
|
||||
{
|
||||
if (affectsSize)
|
||||
autoSizeUpdatePending = true;
|
||||
|
||||
bool alreadyInvalidated = base.Invalidate(affectsSize, affectsPosition, source);
|
||||
|
||||
return !alreadyInvalidated;
|
||||
}
|
||||
|
||||
protected override Quad DrawQuadForBounds
|
||||
{
|
||||
get
|
||||
{
|
||||
Vector2 size = Vector2.Zero;
|
||||
|
||||
Vector2 maxInheritingSize = Vector2.One;
|
||||
|
||||
// Find the maximum width/height of children
|
||||
foreach (Drawable c in Children)
|
||||
{
|
||||
if (!c.IsVisible)
|
||||
continue;
|
||||
|
||||
Vector2 boundingSize = c.GetBoundingSize(this);
|
||||
Vector2 inheritingSize = c.Size * c.VectorScale * c.Scale;
|
||||
|
||||
if ((c.SizeMode & InheritMode.X) == 0)
|
||||
size.X = Math.Max(size.X, boundingSize.X);
|
||||
else
|
||||
maxInheritingSize.X = Math.Max(maxInheritingSize.X, inheritingSize.X);
|
||||
|
||||
if ((c.SizeMode & InheritMode.Y) == 0)
|
||||
size.Y = Math.Max(size.Y, boundingSize.Y);
|
||||
else
|
||||
maxInheritingSize.Y = Math.Max(maxInheritingSize.Y, inheritingSize.Y);
|
||||
}
|
||||
|
||||
return new Quad(0, 0, size.X * maxInheritingSize.X, size.Y * maxInheritingSize.Y);
|
||||
}
|
||||
}
|
||||
|
||||
internal override void UpdateSubTree()
|
||||
{
|
||||
base.UpdateSubTree();
|
||||
|
||||
if (RequireAutoSize)
|
||||
{
|
||||
Vector2 b = GetBoundingSize(this);
|
||||
if (!HasDefinedSize || b != Size)
|
||||
{
|
||||
Size = b;
|
||||
|
||||
Invalidate();
|
||||
UpdateDrawInfoSubtree();
|
||||
}
|
||||
|
||||
autoSizeUpdatePending = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal override float InheritableWidth => HasDefinedSize ? ActualSize.X : Parent?.InheritableWidth ?? 0;
|
||||
internal override float InheritableHeight => HasDefinedSize ? ActualSize.Y : Parent?.InheritableHeight ?? 0;
|
||||
|
||||
protected override bool HasDefinedSize => !autoSizeUpdatePending;
|
||||
|
||||
protected override bool ChildrenShouldInvalidate => true;
|
||||
}
|
||||
}
|
||||
76
osu.Framework/Graphics/Containers/BufferedContainer.cs
Normal file
76
osu.Framework/Graphics/Containers/BufferedContainer.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics.ES20;
|
||||
using osu.Framework.Graphics.OpenGL;
|
||||
using osu.Framework.Graphics.OpenGL.Buffers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using osu.Framework.Graphics.Batches;
|
||||
|
||||
namespace osu.Framework.Graphics.Containers
|
||||
{
|
||||
class BufferedContainer : Container
|
||||
{
|
||||
private FrameBuffer frameBuffer;
|
||||
|
||||
private QuadBatch<TexturedVertex2d> quadBatch = new QuadBatch<TexturedVertex2d>(1, 3);
|
||||
protected override IVertexBatch ActiveBatch => quadBatch;
|
||||
|
||||
internal BufferedContainer()
|
||||
{
|
||||
frameBuffer = new FrameBuffer();
|
||||
}
|
||||
|
||||
internal void Attach(RenderbufferInternalFormat format)
|
||||
{
|
||||
frameBuffer.Attach(format);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
frameBuffer.Size = new Vector2(ScreenSpaceDrawQuad.Width, ScreenSpaceDrawQuad.Height);
|
||||
|
||||
base.Update();
|
||||
}
|
||||
|
||||
protected override void PreDraw()
|
||||
{
|
||||
frameBuffer.Bind();
|
||||
|
||||
// Set viewport to the texture size
|
||||
GLWrapper.PushViewport(new Rectangle(0, 0, frameBuffer.Texture.Width, frameBuffer.Texture.Height));
|
||||
// We need to draw children as if they were zero-based to the top-left of the texture
|
||||
// so we make the new zero be this container's position without affecting children in any negative ways
|
||||
GLWrapper.PushOrtho(new Rectangle((int)ScreenSpaceDrawQuad.TopLeft.X, (int)ScreenSpaceDrawQuad.TopLeft.Y, frameBuffer.Texture.Width, frameBuffer.Texture.Height));
|
||||
|
||||
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit);
|
||||
}
|
||||
|
||||
protected override void PostDraw()
|
||||
{
|
||||
frameBuffer.Unbind();
|
||||
|
||||
GLWrapper.PopOrtho();
|
||||
GLWrapper.PopViewport();
|
||||
|
||||
GLWrapper.SetBlend(BlendingFactorSrc.One, BlendingFactorDest.OneMinusSrcAlpha);
|
||||
|
||||
Rectangle textureRect = new Rectangle(0, frameBuffer.Texture.Height, frameBuffer.Texture.Width, -frameBuffer.Texture.Height);
|
||||
frameBuffer.Texture.Draw(ScreenSpaceDrawQuad, textureRect, DrawInfo.Colour, quadBatch);
|
||||
|
||||
// In the case of nested framebuffer containerse we need to draw to
|
||||
// the last framebuffer container immediately, so let's force it
|
||||
ActiveBatch.Draw();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
frameBuffer.Dispose();
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
osu.Framework/Graphics/Containers/Container.cs
Normal file
48
osu.Framework/Graphics/Containers/Container.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics;
|
||||
|
||||
namespace osu.Framework.Graphics.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// A drawable which can have children added externally.
|
||||
/// </summary>
|
||||
public class Container : Drawable
|
||||
{
|
||||
public virtual Drawable Add(Drawable drawable)
|
||||
{
|
||||
return base.Add(drawable);
|
||||
}
|
||||
|
||||
public void Add(IEnumerable<Drawable> drawables)
|
||||
{
|
||||
base.Add(drawables);
|
||||
}
|
||||
|
||||
public virtual bool Remove(Drawable drawable, bool dispose = true)
|
||||
{
|
||||
return base.Remove(drawable, dispose);
|
||||
}
|
||||
|
||||
public void Remove(IEnumerable<Drawable> drawables, bool dispose = true)
|
||||
{
|
||||
base.Remove(drawables);
|
||||
}
|
||||
|
||||
public int RemoveAll(Predicate<Drawable> match)
|
||||
{
|
||||
return base.RemoveAll(match);
|
||||
}
|
||||
|
||||
public virtual void Clear(bool dispose = true)
|
||||
{
|
||||
base.Clear(dispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
152
osu.Framework/Graphics/Containers/FlowContainer.cs
Normal file
152
osu.Framework/Graphics/Containers/FlowContainer.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
|
||||
namespace osu.Framework.Graphics.Containers
|
||||
{
|
||||
public class FlowContainer : AutoSizeContainer
|
||||
{
|
||||
public EasingTypes LayoutEasing;
|
||||
public int LayoutDuration = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Even if we aren't autosizing, we need to ensure invalidation.
|
||||
/// </summary>
|
||||
protected override bool ChildrenShouldInvalidate => true;
|
||||
|
||||
private FlowDirection direction = FlowDirection.Full;
|
||||
public FlowDirection Direction
|
||||
{
|
||||
get { return direction; }
|
||||
set
|
||||
{
|
||||
if (value == direction) return;
|
||||
direction = value;
|
||||
|
||||
requiresLayout = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private double lastLayout;
|
||||
private bool requiresLayout = true;
|
||||
|
||||
/// <summary>
|
||||
/// When we are using a transformation for layouts, we want to ensure we don't trigger re-flow due to our own actions.
|
||||
/// </summary>
|
||||
private double nextLayout => lastLayout + LayoutDuration;
|
||||
|
||||
Vector2 maximumSize;
|
||||
/// <summary>
|
||||
/// Optional maximum dimensions for this container.
|
||||
/// </summary>
|
||||
public Vector2 MaximumSize
|
||||
{
|
||||
get { return maximumSize; }
|
||||
set
|
||||
{
|
||||
if (maximumSize == value) return;
|
||||
|
||||
maximumSize = value;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 padding;
|
||||
public Vector2 Padding
|
||||
{
|
||||
get { return padding; }
|
||||
set
|
||||
{
|
||||
if (padding == value) return;
|
||||
|
||||
padding = value;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Invalidate(bool affectsSize = true, bool affectsPosition = true, Drawable source = null)
|
||||
{
|
||||
if (affectsSize)
|
||||
requiresLayout = true;
|
||||
|
||||
return base.Invalidate(affectsSize, affectsPosition, source);
|
||||
}
|
||||
|
||||
public override Drawable Add(Drawable drawable)
|
||||
{
|
||||
//let's force an instant re-flow on adding a new drawable for now.
|
||||
lastLayout = 0;
|
||||
|
||||
return base.Add(drawable);
|
||||
}
|
||||
|
||||
protected override void UpdateLayout()
|
||||
{
|
||||
if (!requiresLayout || (nextLayout > 0 && Time < nextLayout)) return;
|
||||
|
||||
lastLayout = Time;
|
||||
requiresLayout = false;
|
||||
|
||||
base.UpdateLayout();
|
||||
|
||||
if (Children.Count == 0) return;
|
||||
|
||||
Vector2 current = new Vector2(Math.Max(0, Padding.X), Math.Max(0, Padding.Y));
|
||||
|
||||
Vector2 max = maximumSize;
|
||||
if (direction == FlowDirection.Full && maximumSize == Vector2.Zero)
|
||||
{
|
||||
Drawable sDrawable = Parent;
|
||||
while (sDrawable is AutoSizeContainer)
|
||||
sDrawable = sDrawable.Parent;
|
||||
|
||||
if (sDrawable != null)
|
||||
max = sDrawable.ActualSize * sDrawable.VectorScale * sDrawable.Scale;
|
||||
}
|
||||
|
||||
float rowMaxHeight = 0;
|
||||
foreach (Drawable d in Children)
|
||||
{
|
||||
if (!d.IsVisible) continue;
|
||||
|
||||
Vector2 size = d.ActualSize * d.VectorScale * d.Scale;
|
||||
|
||||
if (Direction != FlowDirection.HorizontalOnly && current.X + size.X > max.X)
|
||||
{
|
||||
current.X = Math.Max(0, Padding.X);
|
||||
current.Y += rowMaxHeight;
|
||||
|
||||
rowMaxHeight = 0;
|
||||
}
|
||||
|
||||
//todo: check this is correct
|
||||
if (size.X > 0) size.X = Math.Max(0, size.X + Padding.X);
|
||||
if (size.Y > 0) size.Y = Math.Max(0, size.Y + Padding.Y);
|
||||
|
||||
if (size.Y > rowMaxHeight) rowMaxHeight = size.Y;
|
||||
|
||||
if (current != d.Position)
|
||||
{
|
||||
d.MoveTo(current, LayoutDuration, LayoutEasing);
|
||||
d.UpdateSubTree();
|
||||
}
|
||||
|
||||
current.X += size.X;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum FlowDirection
|
||||
{
|
||||
VerticalOnly,
|
||||
HorizontalOnly,
|
||||
Full
|
||||
}
|
||||
}
|
||||
15
osu.Framework/Graphics/Containers/LargeContainer.cs
Normal file
15
osu.Framework/Graphics/Containers/LargeContainer.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Framework.Graphics.Containers
|
||||
{
|
||||
public class LargeContainer : ProcessingContainer
|
||||
{
|
||||
public LargeContainer()
|
||||
{
|
||||
SizeMode = InheritMode.XY;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
osu.Framework/Graphics/Containers/MaskingContainer.cs
Normal file
24
osu.Framework/Graphics/Containers/MaskingContainer.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics.OpenGL;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Graphics.Containers
|
||||
{
|
||||
public class MaskingContainer : Container
|
||||
{
|
||||
protected override void PreDraw()
|
||||
{
|
||||
GLWrapper.PushScissor(ScreenSpaceDrawQuad.BoundingRectangle);
|
||||
}
|
||||
|
||||
protected override void PostDraw()
|
||||
{
|
||||
GLWrapper.PopScissor();
|
||||
}
|
||||
}
|
||||
}
|
||||
85
osu.Framework/Graphics/Containers/ProcessingContainer.cs
Normal file
85
osu.Framework/Graphics/Containers/ProcessingContainer.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Resources;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace osu.Framework.Graphics.Containers
|
||||
{
|
||||
public class ProcessingContainer : Container
|
||||
{
|
||||
private Container processingContainer = new Container() { SizeMode = InheritMode.XY };
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
base.Load();
|
||||
|
||||
AddTopLevel(processingContainer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a container and sets it as the new processing container.
|
||||
/// Any .Add() calls will be redirected to this container.
|
||||
/// </summary>
|
||||
/// <param name="container">The container to be the new processing container.</param>
|
||||
protected void AddProcessingContainer(Container container)
|
||||
{
|
||||
Debug.Assert(container != null);
|
||||
|
||||
// If the current container is a processing conatiner then we only need
|
||||
// to add the new container to it (handled below)
|
||||
if (!(processingContainer is ProcessingContainer))
|
||||
{
|
||||
// Move existing children to the new container
|
||||
List<Drawable> existingChildren = processingContainer.Children.ToList();
|
||||
existingChildren.ForEach(child => container.Add(child));
|
||||
}
|
||||
|
||||
processingContainer.Add(container);
|
||||
processingContainer = container;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a container at the same level as the first processing container.
|
||||
/// </summary>
|
||||
/// <param name="drawable">The drawable to add.</param>
|
||||
/// <returns>The added drawable.</returns>
|
||||
protected Drawable AddTopLevel(Drawable drawable) => base.Add(drawable);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a drawable to the processing container.
|
||||
/// </summary>
|
||||
/// <param name="drawable">The drawable to add.</param>
|
||||
/// <returns>The added drawable.</returns>
|
||||
public override Drawable Add(Drawable drawable)
|
||||
{
|
||||
return processingContainer.Add(drawable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the processing container.
|
||||
/// </summary>
|
||||
/// <param name="dispose">Whether to dispose contained drawables.</param>
|
||||
public override void Clear(bool dispose = true)
|
||||
{
|
||||
processingContainer.Clear(dispose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a drawable from the processing container.
|
||||
/// </summary>
|
||||
/// <param name="drawable">The drawable to remove.</param>
|
||||
/// <param name="dispose">Whether to dispose the drawable.</param>
|
||||
/// <returns>Whether the drawable was removed.</returns>
|
||||
public override bool Remove(Drawable drawable, bool dispose = true)
|
||||
{
|
||||
return processingContainer.Remove(drawable, dispose);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
osu.Framework/Graphics/DrawInfo.cs
Normal file
75
osu.Framework/Graphics/DrawInfo.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Extensions.MatrixExtensions;
|
||||
|
||||
namespace osu.Framework.Graphics
|
||||
{
|
||||
public class DrawInfo : IEquatable<DrawInfo>
|
||||
{
|
||||
public Matrix3 Matrix = Matrix3.Identity;
|
||||
public Matrix3 MatrixInverse => matrixInverse ?? (matrixInverse = Matrix.Inverted()).Value;
|
||||
public Color4 Colour = Color4.White;
|
||||
|
||||
private Matrix3? matrixInverse;
|
||||
|
||||
public DrawInfo()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a transformation to the current DrawInfo.
|
||||
/// </summary>
|
||||
/// <param name="target">The DrawInfo instance to be filled with the result.</param>
|
||||
/// <param name="translation">The amount by which to translate the current position.</param>
|
||||
/// <param name="scale">The amount by which to scale.</param>
|
||||
/// <param name="rotation">The amount by which to rotate.</param>
|
||||
/// <param name="origin">The center of rotation and scale.</param>
|
||||
/// <param name="colour">An optional color to be applied multiplicatively.</param>
|
||||
/// <param name="viewport">An optional new viewport size.</param>
|
||||
public void ApplyTransform(DrawInfo target, Vector2 translation, Vector2 scale, float rotation, Vector2 origin, Color4? colour = null)
|
||||
{
|
||||
Matrix3 m = Matrix;
|
||||
|
||||
if (translation != Vector2.Zero)
|
||||
m = m.TranslateTo(translation);
|
||||
if (rotation != 0)
|
||||
m = m.RotateTo(rotation);
|
||||
if (scale != Vector2.One)
|
||||
m = m.ScaleTo(scale);
|
||||
if (origin != Vector2.Zero)
|
||||
m = m.TranslateTo(-origin);
|
||||
|
||||
target.Matrix = m;
|
||||
|
||||
target.Colour = Colour;
|
||||
|
||||
if (colour != null)
|
||||
{
|
||||
target.Colour.R *= colour.Value.R;
|
||||
target.Colour.G *= colour.Value.G;
|
||||
target.Colour.B *= colour.Value.B;
|
||||
target.Colour.A *= colour.Value.A;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the current DrawInfo into target.
|
||||
/// </summary>
|
||||
/// <param name="target">The DrawInfo to be filled with the copy.</param>
|
||||
public void Copy(DrawInfo target)
|
||||
{
|
||||
target.Matrix = Matrix;
|
||||
target.Colour = Colour;
|
||||
}
|
||||
|
||||
public bool Equals(DrawInfo other)
|
||||
{
|
||||
return Matrix.Equals(other.Matrix) && Colour.Equals(other.Colour);
|
||||
}
|
||||
}
|
||||
}
|
||||
1383
osu.Framework/Graphics/Drawable.cs
Normal file
1383
osu.Framework/Graphics/Drawable.cs
Normal file
File diff suppressed because it is too large
Load Diff
204
osu.Framework/Graphics/Drawable_Interaction.cs
Normal file
204
osu.Framework/Graphics/Drawable_Interaction.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using OpenTK.Input;
|
||||
using OpenTK;
|
||||
using osu.Framework.Lists;
|
||||
using osu.Framework.Input;
|
||||
|
||||
namespace osu.Framework.Graphics
|
||||
{
|
||||
public partial class Drawable : IDisposable, IHasLifetime
|
||||
{
|
||||
/// <summary>
|
||||
/// Find the first parent InputManager which this drawable is contained by.
|
||||
/// </summary>
|
||||
private InputManager ourInputManager => this as InputManager ?? Parent?.ourInputManager;
|
||||
|
||||
public bool TriggerHover(InputState state)
|
||||
{
|
||||
return OnHover(state);
|
||||
}
|
||||
|
||||
protected virtual bool OnHover(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void TriggerHoverLost(InputState state)
|
||||
{
|
||||
OnHoverLost(state);
|
||||
}
|
||||
|
||||
protected virtual void OnHoverLost(InputState state)
|
||||
{
|
||||
}
|
||||
|
||||
public bool TriggerMouseDown(InputState state = null, MouseDownEventArgs args = null) => OnMouseDown(state, args);
|
||||
|
||||
protected virtual bool OnMouseDown(InputState state, MouseDownEventArgs args)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerMouseUp(InputState state = null, MouseUpEventArgs args = null) => OnMouseUp(state, args);
|
||||
|
||||
protected virtual bool OnMouseUp(InputState state, MouseUpEventArgs args)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerClick(InputState state = null) => OnClick(state);
|
||||
protected virtual bool OnClick(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerDoubleClick(InputState state) => OnDoubleClick(state);
|
||||
protected virtual bool OnDoubleClick(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerDragStart(InputState state) => OnDragStart(state);
|
||||
protected virtual bool OnDragStart(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerDrag(InputState state) => OnDrag(state);
|
||||
protected virtual bool OnDrag(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerDragEnd(InputState state) => OnDragEnd(state);
|
||||
protected virtual bool OnDragEnd(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerWheelUp(InputState state) => OnWheelUp(state);
|
||||
protected virtual bool OnWheelUp(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerWheelDown(InputState state) => OnWheelDown(state);
|
||||
protected virtual bool OnWheelDown(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Focuses this drawable.
|
||||
/// </summary>
|
||||
/// <param name="state">The input state.</param>
|
||||
/// <param name="checkCanFocus">Whether we should check this Drawable's OnFocus returns true before actually providing focus.</param>
|
||||
public bool TriggerFocus(InputState state = null, bool checkCanFocus = false)
|
||||
{
|
||||
if (HasFocus)
|
||||
return true;
|
||||
|
||||
if (checkCanFocus & !OnFocus(state))
|
||||
return false;
|
||||
|
||||
ourInputManager?.ChangeFocus(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool OnFocus(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unfocuses this drawable.
|
||||
/// </summary>
|
||||
/// <param name="state">The input state.</param>
|
||||
internal void TriggerFocusLost(InputState state = null, bool isCallback = false)
|
||||
{
|
||||
if (!HasFocus)
|
||||
return;
|
||||
|
||||
if (state == null)
|
||||
state = new InputState();
|
||||
|
||||
if (!isCallback) ourInputManager.ChangeFocus(null);
|
||||
OnFocusLost(state);
|
||||
}
|
||||
|
||||
protected virtual void OnFocusLost(InputState state)
|
||||
{
|
||||
}
|
||||
|
||||
public bool TriggerKeyDown(InputState state, KeyDownEventArgs args) => OnKeyDown(state, args);
|
||||
protected virtual bool OnKeyDown(InputState state, KeyDownEventArgs args)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerKeyUp(InputState state, KeyUpEventArgs args) => OnKeyUp(state, args);
|
||||
protected virtual bool OnKeyUp(InputState state, KeyUpEventArgs args)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TriggerMouseMove(InputState state) => OnMouseMove(state);
|
||||
protected virtual bool OnMouseMove(InputState state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HandleInput = true;
|
||||
|
||||
internal virtual bool HasFocus => ourInputManager?.FocusedDrawable == this;
|
||||
|
||||
internal bool Hovering;
|
||||
|
||||
/// <summary>
|
||||
/// Sometimes we need to know the position of the mouse inside the drawable.
|
||||
/// </summary>
|
||||
/// <param name="screenSpacePos">A position in screen space (user input device).</param>
|
||||
/// <returns>The relative (0..1) position inside (or outside) the drawable.</returns>
|
||||
internal virtual Vector2? GetContainedPosition(Vector2 screenSpacePos)
|
||||
{
|
||||
return ScreenSpaceInputQuad.Contains(screenSpacePos);
|
||||
}
|
||||
|
||||
public virtual Vector2 GetLocalPosition(Vector2 screenSpacePos)
|
||||
{
|
||||
return screenSpacePos * DrawInfo.MatrixInverse;
|
||||
}
|
||||
|
||||
internal virtual bool Contains(Vector2 screenSpacePos)
|
||||
{
|
||||
return ScreenSpaceInputQuad.Contains(screenSpacePos).HasValue;
|
||||
}
|
||||
}
|
||||
|
||||
public class KeyDownEventArgs : EventArgs
|
||||
{
|
||||
public Key Key;
|
||||
public bool Repeat;
|
||||
}
|
||||
|
||||
public class MouseUpEventArgs : MouseEventArgs { }
|
||||
public class MouseDownEventArgs : MouseEventArgs { }
|
||||
|
||||
public class MouseEventArgs : EventArgs
|
||||
{
|
||||
public MouseButton Button;
|
||||
}
|
||||
|
||||
public class KeyUpEventArgs : EventArgs
|
||||
{
|
||||
public Key Key;
|
||||
}
|
||||
|
||||
public delegate bool MouseEventHandlerDelegate(object sender, InputState state);
|
||||
internal delegate bool KeyDownEventHandlerDelegate(object sender, KeyDownEventArgs e, InputState state);
|
||||
internal delegate bool KeyUpEventHandlerDelegate(object sender, KeyUpEventArgs e, InputState state);
|
||||
}
|
||||
302
osu.Framework/Graphics/Drawable_TransformationHelpers.cs
Normal file
302
osu.Framework/Graphics/Drawable_TransformationHelpers.cs
Normal file
@@ -0,0 +1,302 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OpenTK.Graphics;
|
||||
using System.Diagnostics;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics.Transformations;
|
||||
|
||||
namespace osu.Framework.Graphics
|
||||
{
|
||||
public partial class Drawable : IDisposable
|
||||
{
|
||||
private double transformationDelay;
|
||||
|
||||
public void ClearTransformations()
|
||||
{
|
||||
Transformations.Clear();
|
||||
DelayReset();
|
||||
}
|
||||
|
||||
public Drawable Delay(double duration, bool propagateChildren = false)
|
||||
{
|
||||
if (duration == 0) return this;
|
||||
|
||||
transformationDelay += duration;
|
||||
if (propagateChildren)
|
||||
Children.ForEach(c => c.Delay(duration, propagateChildren));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Drawable DelayReset()
|
||||
{
|
||||
Delay(-transformationDelay);
|
||||
Children.ForEach(c => c.DelayReset());
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Loop(int delay = 0)
|
||||
{
|
||||
Transformations.ForEach(t =>
|
||||
{
|
||||
t.Loop = true;
|
||||
t.LoopDelay = Math.Max(0, transformationDelay + delay - t.Duration);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make this drawable automatically clean itself up after all transformations have finished playing.
|
||||
/// Can be delayed using Delay().
|
||||
/// </summary>
|
||||
public Drawable Expire(bool calculateLifetimeStart = false)
|
||||
{
|
||||
//expiry should happen either at the end of the last transformation or using the current sequence delay (whichever is highest).
|
||||
double max = Time + transformationDelay;
|
||||
foreach (Transformation t in Transformations)
|
||||
if (t.Time2 > max) max = t.Time2 + 1; //adding 1ms here ensures we can expire on the current frame without issue.
|
||||
LifetimeEnd = max;
|
||||
|
||||
if (calculateLifetimeStart)
|
||||
{
|
||||
double min = double.MaxValue;
|
||||
foreach (Transformation t in Transformations)
|
||||
if (t.Time1 < min) min = t.Time1;
|
||||
LifetimeStart = min < Int32.MaxValue ? min : Int32.MinValue;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void TimeWarp(double change)
|
||||
{
|
||||
if (change == 0)
|
||||
return;
|
||||
|
||||
foreach (Transformation t in Transformations)
|
||||
{
|
||||
t.Time1 += change;
|
||||
t.Time2 += change;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide sprite instantly.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual void Hide()
|
||||
{
|
||||
FadeOut(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show sprite instantly.
|
||||
/// </summary>
|
||||
public virtual void Show()
|
||||
{
|
||||
FadeIn(0);
|
||||
}
|
||||
|
||||
public Drawable FadeIn(double duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
return FadeTo(1, duration, easing);
|
||||
}
|
||||
|
||||
public Transformation FadeInFromZero(double duration)
|
||||
{
|
||||
if (transformationDelay == 0)
|
||||
{
|
||||
Alpha = 0;
|
||||
Transformations.RemoveAll(t => t.Type == TransformationType.Fade);
|
||||
}
|
||||
|
||||
double startTime = Time + transformationDelay;
|
||||
|
||||
Transformation tr = new Transformation(TransformationType.Fade, 0, 1, startTime, startTime + duration);
|
||||
Transformations.Add(tr);
|
||||
return tr;
|
||||
}
|
||||
|
||||
public Drawable FadeOut(double duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
return FadeTo(0, duration, easing);
|
||||
}
|
||||
|
||||
public Transformation FadeOutFromOne(double duration)
|
||||
{
|
||||
if (transformationDelay == 0)
|
||||
{
|
||||
Alpha = 1;
|
||||
Transformations.RemoveAll(t => t.Type == TransformationType.Fade);
|
||||
}
|
||||
|
||||
double startTime = Time + transformationDelay;
|
||||
|
||||
Transformation tr =
|
||||
new Transformation(TransformationType.Fade, 1, 0, startTime, startTime + duration);
|
||||
Transformations.Add(tr);
|
||||
return tr;
|
||||
}
|
||||
|
||||
#region Float-based helpers
|
||||
private Drawable transformFloatTo(float startValue, float newValue, double duration, EasingTypes easing, TransformationType transform)
|
||||
{
|
||||
if (transformationDelay == 0)
|
||||
{
|
||||
Transformations.RemoveAll(t => t.Type == transform);
|
||||
if (startValue == newValue)
|
||||
return this;
|
||||
}
|
||||
else
|
||||
startValue = Transformations.FindLast(t => t.Type == transform)?.EndFloat ?? startValue;
|
||||
|
||||
double startTime = Time + transformationDelay;
|
||||
|
||||
Transformations.Add(new Transformation(transform, startValue, newValue, startTime, startTime + duration, easing));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Drawable FadeTo(float newAlpha, double duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
if (duration == 0)
|
||||
{
|
||||
Alpha = newAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
return transformFloatTo(Alpha, newAlpha, duration, easing, TransformationType.Fade);
|
||||
}
|
||||
|
||||
public Drawable ScaleTo(float newScale, double duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
if (duration == 0)
|
||||
{
|
||||
Scale = newScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
return transformFloatTo(Scale, newScale, duration, easing, TransformationType.Scale);
|
||||
}
|
||||
|
||||
public Drawable RotateTo(float newRotation, double duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
if (duration == 0)
|
||||
{
|
||||
Rotation = newRotation;
|
||||
return this;
|
||||
}
|
||||
|
||||
return transformFloatTo(Rotation, newRotation, duration, easing, TransformationType.Rotation);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public Drawable MoveToX(float destination, double duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
if (duration == 0)
|
||||
{
|
||||
Position = new Vector2(destination, Position.Y);
|
||||
return this;
|
||||
}
|
||||
|
||||
return transformFloatTo(Position.X, destination, duration, easing, TransformationType.MovementX);
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public Drawable MoveToY(float destination, double duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
if (duration == 0)
|
||||
{
|
||||
Position = new Vector2(Position.X, destination);
|
||||
return this;
|
||||
}
|
||||
|
||||
return transformFloatTo(Position.Y, destination, duration, easing, TransformationType.MovementY);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Vector2-based helpers
|
||||
private Drawable transformVectorTo(Vector2 startValue, Vector2 newValue, double duration, EasingTypes easing, TransformationType transform)
|
||||
{
|
||||
if (transformationDelay == 0)
|
||||
{
|
||||
Transformations.RemoveAll(t => t.Type == transform);
|
||||
if (startValue == newValue)
|
||||
return this;
|
||||
}
|
||||
else
|
||||
startValue = Transformations.FindLast(t => t.Type == transform)?.EndVector ?? startValue;
|
||||
|
||||
double startTime = Time + transformationDelay;
|
||||
|
||||
Transformations.Add(new Transformation(transform, startValue, newValue, startTime, startTime + duration, easing));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Drawable ScaleTo(Vector2 newScale, double duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
if (duration == 0)
|
||||
{
|
||||
VectorScale = newScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
return transformVectorTo(VectorScale, newScale, duration, easing, TransformationType.VectorScale);
|
||||
}
|
||||
|
||||
public Drawable MoveTo(Vector2 newPosition, double duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
if (duration == 0)
|
||||
{
|
||||
Position = newPosition;
|
||||
return this;
|
||||
}
|
||||
|
||||
return transformVectorTo(Position, newPosition, duration, easing, TransformationType.Movement);
|
||||
}
|
||||
|
||||
public Drawable MoveToRelative(Vector2 offset, int duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
return MoveTo(Transformations.FindLast(t => t.Type == TransformationType.Movement)?.EndVector ?? Position + offset, duration, easing);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Color4-based helpers
|
||||
public Drawable FadeColour(Color4 newColour, int duration, EasingTypes easing = EasingTypes.None)
|
||||
{
|
||||
Color4 startValue = Colour;
|
||||
if (transformationDelay == 0)
|
||||
{
|
||||
Transformations.RemoveAll(t => t.Type == TransformationType.Colour);
|
||||
if (startValue == newColour)
|
||||
return this;
|
||||
}
|
||||
else
|
||||
startValue = Transformations.FindLast(t => t.Type == TransformationType.Colour)?.EndColour ?? startValue;
|
||||
|
||||
double startTime = Time + transformationDelay;
|
||||
|
||||
Transformations.Add(new Transformation(startValue, newColour, startTime, startTime + duration, easing));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Drawable FlashColour(Color4 flashColour, int duration)
|
||||
{
|
||||
Debug.Assert(transformationDelay == 0, @"FlashColour doesn't support Delay() currently");
|
||||
|
||||
Color4 startValue = Transformations.FindLast(t => t.Type == TransformationType.Colour)?.EndColour ?? Colour;
|
||||
Transformations.RemoveAll(t => t.Type == TransformationType.Colour);
|
||||
|
||||
double startTime = Time + transformationDelay;
|
||||
|
||||
Transformations.Add(new Transformation(flashColour, startValue, startTime, startTime + duration));
|
||||
|
||||
return this;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
39
osu.Framework/Graphics/Drawables/Box.cs
Normal file
39
osu.Framework/Graphics/Drawables/Box.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Batches;
|
||||
using osu.Framework.Graphics.OpenGL;
|
||||
using osu.Framework.Graphics.Shaders;
|
||||
|
||||
namespace osu.Framework.Graphics.Drawables
|
||||
{
|
||||
public class Box : Drawable
|
||||
{
|
||||
private QuadBatch<Vertex2d> quadBatch = new QuadBatch<Vertex2d>(1, 3);
|
||||
protected override IVertexBatch ActiveBatch => quadBatch;
|
||||
|
||||
private static Shader shader;
|
||||
|
||||
protected override void Draw()
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
if (shader == null)
|
||||
shader = Game.Shaders.Load(VertexShader.Colour, FragmentShader.Colour);
|
||||
|
||||
shader.Bind();
|
||||
|
||||
quadBatch.Add(new Vertex2d() { Colour = DrawInfo.Colour, Position = ScreenSpaceDrawQuad.BottomLeft });
|
||||
quadBatch.Add(new Vertex2d() { Colour = DrawInfo.Colour, Position = ScreenSpaceDrawQuad.BottomRight });
|
||||
quadBatch.Add(new Vertex2d() { Colour = DrawInfo.Colour, Position = ScreenSpaceDrawQuad.TopRight });
|
||||
quadBatch.Add(new Vertex2d() { Colour = DrawInfo.Colour, Position = ScreenSpaceDrawQuad.TopLeft });
|
||||
quadBatch.Draw();
|
||||
|
||||
shader.Unbind();
|
||||
}
|
||||
}
|
||||
}
|
||||
132
osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs
Normal file
132
osu.Framework/Graphics/OpenGL/Buffers/FrameBuffer.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics.ES20;
|
||||
using osu.Framework.Graphics.OpenGL.Textures;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Graphics.OpenGL.Buffers
|
||||
{
|
||||
class FrameBuffer : IDisposable
|
||||
{
|
||||
private int lastFramebuffer;
|
||||
private int frameBuffer = -1;
|
||||
|
||||
internal TextureGL Texture { get; private set; }
|
||||
|
||||
private bool IsBound => lastFramebuffer != -1;
|
||||
|
||||
private List<RenderBuffer> attachedRenderBuffers = new List<RenderBuffer>();
|
||||
|
||||
internal FrameBuffer(bool withTexture = true)
|
||||
{
|
||||
frameBuffer = GL.GenFramebuffer();
|
||||
|
||||
if (withTexture)
|
||||
{
|
||||
Texture = new TextureGLSingle(1, 1);
|
||||
Texture.SetData(new byte[0]);
|
||||
Texture.Upload();
|
||||
|
||||
Bind();
|
||||
|
||||
GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0, TextureTarget.Texture2D, Texture.TextureId, 0);
|
||||
GLWrapper.BindTexture(0);
|
||||
|
||||
Unbind();
|
||||
}
|
||||
}
|
||||
|
||||
#region Disposal
|
||||
~FrameBuffer()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
private bool isDisposed;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (isDisposed)
|
||||
return;
|
||||
isDisposed = true;
|
||||
|
||||
Unbind();
|
||||
|
||||
GLWrapper.DeleteFramebuffer(frameBuffer);
|
||||
frameBuffer = -1;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private Vector2 size = Vector2.One;
|
||||
/// <summary>
|
||||
/// Sets the size of the texture of this framebuffer.
|
||||
/// </summary>
|
||||
internal Vector2 Size
|
||||
{
|
||||
get { return size; }
|
||||
set
|
||||
{
|
||||
if (value == size)
|
||||
return;
|
||||
size = value;
|
||||
|
||||
Texture.Width = (int)Math.Ceiling(size.X);
|
||||
Texture.Height = (int)Math.Ceiling(size.Y);
|
||||
Texture.SetData(new byte[0]);
|
||||
Texture.Upload();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a RenderBuffer to this framebuffer.
|
||||
/// </summary>
|
||||
/// <param name="format">The type of RenderBuffer to attach.</param>
|
||||
internal void Attach(RenderbufferInternalFormat format)
|
||||
{
|
||||
if (attachedRenderBuffers.Exists(r => r.Format == format))
|
||||
return;
|
||||
|
||||
attachedRenderBuffers.Add(new RenderBuffer(format));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds the framebuffer.
|
||||
/// <para>Does not clear the buffer or reset the viewport/ortho.</para>
|
||||
/// </summary>
|
||||
internal void Bind()
|
||||
{
|
||||
if (frameBuffer == -1)
|
||||
return;
|
||||
|
||||
if (lastFramebuffer == frameBuffer)
|
||||
return;
|
||||
|
||||
// Bind framebuffer and all its renderbuffers
|
||||
lastFramebuffer = GLWrapper.BindFrameBuffer(frameBuffer);
|
||||
attachedRenderBuffers.ForEach(r => r.Bind(frameBuffer));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unbinds the framebuffer.
|
||||
/// </summary>
|
||||
internal void Unbind()
|
||||
{
|
||||
if (!IsBound)
|
||||
return;
|
||||
|
||||
GLWrapper.BindFrameBuffer(lastFramebuffer);
|
||||
attachedRenderBuffers.ForEach(r => r.Unbind());
|
||||
|
||||
lastFramebuffer = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
69
osu.Framework/Graphics/OpenGL/Buffers/LinearVertexBuffer.cs
Normal file
69
osu.Framework/Graphics/OpenGL/Buffers/LinearVertexBuffer.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics.ES20;
|
||||
using osu.Framework.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Graphics.OpenGL.Buffers
|
||||
{
|
||||
static class LinearIndexData
|
||||
{
|
||||
static LinearIndexData()
|
||||
{
|
||||
GL.GenBuffers(1, out LinearIndexData.EboId);
|
||||
}
|
||||
|
||||
public static readonly int EboId;
|
||||
public static int MaxAmountIndices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This type of vertex buffer lets the ith vertex be referenced by the ith index.
|
||||
/// </summary>
|
||||
public class LinearVertexBuffer<T> : VertexBuffer<T> where T : struct, IEquatable<T>
|
||||
{
|
||||
private BeginMode type;
|
||||
|
||||
public LinearVertexBuffer(int amountVertices, BeginMode type, BufferUsageHint usage)
|
||||
: base(amountVertices, usage)
|
||||
{
|
||||
this.type = type;
|
||||
|
||||
if (amountVertices > LinearIndexData.MaxAmountIndices)
|
||||
{
|
||||
ushort[] indices = new ushort[amountVertices];
|
||||
|
||||
for (ushort i = 0; i < amountVertices; i++)
|
||||
indices[i] = i;
|
||||
|
||||
GLWrapper.BindBuffer(BufferTarget.ElementArrayBuffer, LinearIndexData.EboId);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(amountVertices * sizeof(ushort)), indices, BufferUsageHint.StaticDraw);
|
||||
|
||||
LinearIndexData.MaxAmountIndices = amountVertices;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Bind(bool forRendering)
|
||||
{
|
||||
base.Bind(forRendering);
|
||||
|
||||
if (forRendering)
|
||||
GLWrapper.BindBuffer(BufferTarget.ElementArrayBuffer, LinearIndexData.EboId);
|
||||
}
|
||||
|
||||
public override void Unbind()
|
||||
{
|
||||
base.Unbind();
|
||||
}
|
||||
|
||||
protected override BeginMode Type
|
||||
{
|
||||
get { return type; }
|
||||
}
|
||||
}
|
||||
}
|
||||
79
osu.Framework/Graphics/OpenGL/Buffers/QuadVertexBuffer.cs
Normal file
79
osu.Framework/Graphics/OpenGL/Buffers/QuadVertexBuffer.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics.ES20;
|
||||
using osu.Framework.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Graphics.OpenGL.Buffers
|
||||
{
|
||||
static class QuadIndexData
|
||||
{
|
||||
static QuadIndexData()
|
||||
{
|
||||
GL.GenBuffers(1, out QuadIndexData.EboId);
|
||||
}
|
||||
|
||||
public static readonly int EboId;
|
||||
public static int MaxAmountIndices;
|
||||
}
|
||||
|
||||
public class QuadVertexBuffer<T> : VertexBuffer<T> where T : struct, IEquatable<T>
|
||||
{
|
||||
public QuadVertexBuffer(int amountQuads, BufferUsageHint usage)
|
||||
: base(amountQuads * 4, usage)
|
||||
{
|
||||
int amountIndices = amountQuads * 6;
|
||||
if (amountIndices > QuadIndexData.MaxAmountIndices)
|
||||
{
|
||||
ushort[] indices = new ushort[amountIndices];
|
||||
|
||||
for (ushort i = 0, j = 0; j < amountIndices; i += 4, j += 6)
|
||||
{
|
||||
indices[j] = i;
|
||||
indices[j + 1] = (ushort)(i + 1);
|
||||
indices[j + 2] = (ushort)(i + 3);
|
||||
indices[j + 3] = (ushort)(i + 2);
|
||||
indices[j + 4] = (ushort)(i + 3);
|
||||
indices[j + 5] = (ushort)(i + 1);
|
||||
}
|
||||
|
||||
GLWrapper.BindBuffer(BufferTarget.ElementArrayBuffer, QuadIndexData.EboId);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(amountIndices * sizeof(ushort)), indices, BufferUsageHint.StaticDraw);
|
||||
|
||||
QuadIndexData.MaxAmountIndices = amountIndices;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Bind(bool forRendering)
|
||||
{
|
||||
base.Bind(forRendering);
|
||||
|
||||
if (forRendering)
|
||||
GLWrapper.BindBuffer(BufferTarget.ElementArrayBuffer, QuadIndexData.EboId);
|
||||
}
|
||||
|
||||
public override void Unbind()
|
||||
{
|
||||
base.Unbind();
|
||||
}
|
||||
|
||||
protected override int ToElements(int vertices)
|
||||
{
|
||||
return 3 * vertices / 2;
|
||||
}
|
||||
|
||||
protected override int ToElementIndex(int verticexIndex)
|
||||
{
|
||||
return 3 * verticexIndex / 2;
|
||||
}
|
||||
|
||||
protected override BeginMode Type
|
||||
{
|
||||
get { return BeginMode.Triangles; }
|
||||
}
|
||||
}
|
||||
}
|
||||
121
osu.Framework/Graphics/OpenGL/Buffers/RenderBuffer.cs
Normal file
121
osu.Framework/Graphics/OpenGL/Buffers/RenderBuffer.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
//Copyright (c) 2007-2016 ppy Pty Ltd <contact@ppy.sh>.
|
||||
//Licensed under the MIT License - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE
|
||||
|
||||
using OpenTK.Graphics.ES20;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace osu.Framework.Graphics.OpenGL.Buffers
|
||||
{
|
||||
class RenderBuffer : IDisposable
|
||||
{
|
||||
private static Dictionary<RenderbufferInternalFormat, ConcurrentStack<RenderBufferInfo>> renderBufferCache = new Dictionary<RenderbufferInternalFormat, ConcurrentStack<RenderBufferInfo>>();
|
||||
|
||||
private RenderBufferInfo info;
|
||||
private bool isDisposed;
|
||||
|
||||
internal RenderbufferInternalFormat Format { get; private set; }
|
||||
|
||||
internal RenderBuffer(RenderbufferInternalFormat format)
|
||||
{
|
||||
this.Format = format;
|
||||
|
||||
info.ID = -1;
|
||||
info.LastFramebuffer = -1;
|
||||
}
|
||||
|
||||
#region Disposal
|
||||
~RenderBuffer()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (isDisposed)
|
||||
return;
|
||||
isDisposed = true;
|
||||
|
||||
Unbind();
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Binds the renderbuffer to the specfied framebuffer.
|
||||
/// </summary>
|
||||
/// <param name="frameBuffer">The framebuffer this renderbuffer should be bound to.</param>
|
||||
internal void Bind(int frameBuffer)
|
||||
{
|
||||
if (info.ID != -1)
|
||||
return;
|
||||
|
||||
if (!renderBufferCache.ContainsKey(Format))
|
||||
renderBufferCache[Format] = new ConcurrentStack<RenderBufferInfo>();
|
||||
|
||||
// Make sure we have renderbuffers available
|
||||
if (renderBufferCache[Format].Count == 0)
|
||||
{
|
||||
int newBuffer = GL.GenRenderbuffer();
|
||||
GL.BindRenderbuffer(RenderbufferTarget.Renderbuffer, newBuffer);
|
||||
GL.RenderbufferStorage(RenderbufferTarget.Renderbuffer, Format, Game.Window.Width, Game.Window.Height);
|
||||
|
||||
renderBufferCache[Format].Push(new RenderBufferInfo() { ID = newBuffer, LastFramebuffer = -1 });
|
||||
}
|
||||
|
||||
// Get a renderbuffer from the cache
|
||||
renderBufferCache[Format].TryPop(out info);
|
||||
|
||||
// For performance reasons, we only need to re-bind the renderbuffer to
|
||||
// the framebuffer if it is not already attached to it
|
||||
if (info.LastFramebuffer != frameBuffer)
|
||||
{
|
||||
// Make sure the framebuffer we want to attach to is bound
|
||||
int lastFrameBuffer = GLWrapper.BindFrameBuffer(frameBuffer);
|
||||
|
||||
switch (Format)
|
||||
{
|
||||
case RenderbufferInternalFormat.DepthComponent16:
|
||||
GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferSlot.DepthAttachment, RenderbufferTarget.Renderbuffer, info.ID);
|
||||
break;
|
||||
case RenderbufferInternalFormat.Rgb565:
|
||||
case RenderbufferInternalFormat.Rgb5A1:
|
||||
case RenderbufferInternalFormat.Rgba4:
|
||||
GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferSlot.ColorAttachment0, RenderbufferTarget.Renderbuffer, info.ID);
|
||||
break;
|
||||
case RenderbufferInternalFormat.StencilIndex8:
|
||||
GL.FramebufferRenderbuffer(FramebufferTarget.Framebuffer, FramebufferSlot.DepthAttachment, RenderbufferTarget.Renderbuffer, info.ID);
|
||||
break;
|
||||
}
|
||||
|
||||
GLWrapper.BindFrameBuffer(lastFrameBuffer);
|
||||
}
|
||||
|
||||
info.LastFramebuffer = frameBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unbinds the renderbuffer.
|
||||
/// <para>The renderbuffer will remain internally attached to the framebuffer.</para>
|
||||
/// </summary>
|
||||
internal void Unbind()
|
||||
{
|
||||
// Return the renderbuffer to the cache
|
||||
renderBufferCache[Format].Push(info);
|
||||
info.ID = -1;
|
||||
}
|
||||
|
||||
private struct RenderBufferInfo
|
||||
{
|
||||
public int ID;
|
||||
public int LastFramebuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user