OSDN Git Service

ビュアーモードで起動された場合には、ビュアーステージに遷移するよう実装。
[strokestylet/CsWin10Desktop3.git] / FDK24 / ApplicationBase.cs
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Diagnostics;
5 using System.Linq;
6 using System.Windows.Forms;
7 using Microsoft.VisualBasic.ApplicationServices;
8
9 namespace FDK
10 {
11         /// <summary>
12         /// アプリケーションフォームの基礎クラス。デバイスリソースを持つ。
13         /// </summary>
14         public class ApplicationBase : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
15         {
16                 public bool 全画面モードである
17                 {
18                         get;
19                         protected set;
20                 } = false;
21                 public bool ウィンドウモードである
22                 {
23                         get { return !this.全画面モードである; }
24                         protected set { this.全画面モードである = !value; }
25                 }
26
27                 public ApplicationBase() : base()
28                 {
29                         this.EnableVisualStyles = true;
30                         this.IsSingleInstance = true;   // 単一インスタンスである
31                         this.MainForm = new Form();
32                         this.MainForm.Load += OnLoad;
33                         this.MainForm.FormClosing += OnClosing;
34                         this.MainForm.ClientSizeChanged += OnClientSizeChanged;
35                 }
36                 public void 全画面モードとウィンドウモードを切り替える()
37                 {
38                         this.スレッド排他領域.ReadLock( () => {
39
40                                 if( false == this.スレッド排他領域.アプリを終了せよ )
41                                 {
42                                         if( this.全画面モードである )
43                                         {
44                                                 this.スレッド排他領域.デバイスリソース.SwapChain.SetFullscreenState( false, null );  // ウィンドウモードへ。
45                                                 this.ウィンドウモードである = true;
46                                         }
47                                         else
48                                         {
49                                                 this.スレッド排他領域.デバイスリソース.SwapChain.SetFullscreenState( true, null );    // 全画面モードへ。
50                                                 this.全画面モードである = true;
51                                         }
52                                 }
53
54                         } );
55                 }
56
57                 protected SharpDX.Size2F 設計画面サイズdpx = SharpDX.Size2F.Empty;
58
59                 /// <summary>
60                 /// GUIスレッドと進行描画スレッド(Main) とで排他が必要なデータを集めたクラス。
61                 /// </summary>
62                 protected class Cスレッド排他領域 : FDK.同期.RWLockAction
63                 {
64                         public bool アプリを終了せよ
65                         {
66                                 get { return this.ReadLock( () => this.bs_アプリを終了せよ ); }
67                                 set { this.WriteLock( () => { this.bs_アプリを終了せよ = value; } ); }
68                         }
69                         public FDK.メディア.デバイスリソース デバイスリソース
70                         {
71                                 get { return this.ReadLock( () => this.bs_デバイスリソース ); }
72                                 set { this.WriteLock( () => { this.bs_デバイスリソース = value; } ); }
73                         }
74                         public FDK.同期.TriStateEvent 進行描画スレッド生存中
75                         {
76                                 get { return this.ReadLock( () => this.bs_進行描画スレッド生存中 ); }
77                                 set { this.WriteLock( () => { this.bs_進行描画スレッド生存中 = value; } ); }
78                         }
79
80                         #region " バックストア。"
81                         //----------------
82                         protected bool bs_アプリを終了せよ = false;
83                         protected FDK.メディア.デバイスリソース bs_デバイスリソース = null;
84                         protected FDK.同期.TriStateEvent bs_進行描画スレッド生存中 = new 同期.TriStateEvent( 同期.TriStateEvent.状態種別.OFF );
85                         //----------------
86                         #endregion
87                 };
88                 protected Cスレッド排他領域 スレッド排他領域 = new Cスレッド排他領域();
89
90                 protected virtual void 初期化する()
91                 {
92                         //----------------
93                         // 以下は実装例。
94                         //----------------
95                         Debug.Assert( null == this.スレッド排他領域.デバイスリソース, "デバイスリソースの作成前であること。" );
96                         this.設計画面サイズdpx = new SharpDX.Size2F( 640, 480 );
97                 }
98                 protected virtual void 終了する()
99                 {
100                         //----------------
101                         // 以下は実装例。
102                         //----------------
103                         Debug.Assert( null != this.スレッド排他領域.デバイスリソース, "デバイスリソースが解放される前であること。" );
104                 }
105                 protected virtual void シーンを描画する()
106                 {
107                         //----------------
108                         // 以下は実装例。
109                         // なお、このメソッドはGUIスレッドではなく進行描画スレッドから呼び出されるので注意。
110                         //----------------
111
112                         this.スレッド排他領域.WriteLock( () => {
113
114                                 // ここで、描画を行う。
115                                 // ...
116
117
118                                 // ここで、入力を行う。
119                                 // ...
120
121                         } );
122
123                         // 表示する。垂直帰線待ちなどで時間がかかるのでロックしないこと。
124                         if( false == this.スレッド排他領域.アプリを終了せよ )
125                                 this.スレッド排他領域.デバイスリソース.SwapChain.Present( 0, SharpDX.DXGI.PresentFlags.None );
126                 }
127                 protected virtual void デバイス依存リソースを解放する()
128                 {
129                         // デバイスリソースはまだ解放されていない。
130                 }
131                 protected virtual void デバイス依存リソースを再構築する()
132                 {
133                         // デバイスリソースはまだ再構築されていない。
134                 }
135                 /// <summary>
136                 /// アプリが二重起動されたときに発生するイベント。
137                 /// </summary>
138                 /// <remarks>
139                 /// 後続のインスタンスは起動せず、既存のインスタンスに対してこのイベントが発生する。
140                 /// eventArg.CommandLine で、後続のインスタンスのコマンドライン引数を確認することができる。
141                 /// </remarks>
142                 protected override void OnStartupNextInstance( StartupNextInstanceEventArgs eventArgs )
143                 {
144                         base.OnStartupNextInstance( eventArgs );
145                 }
146
147                 protected System.Threading.Thread 進行描画スレッド = null;
148
149                 private void OnLoad( object sender, EventArgs e )
150                 {
151                         FDK.Log.現在のスレッドに名前をつける( "GUI" );
152                         FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
153
154                         try
155                         {
156                                 FDK.Log.BeginInfo( "派生クラスを初期化します。" );
157                                 this.初期化する();
158                                 Debug.Assert( SharpDX.Size2F.Empty != this.設計画面サイズdpx, "初期化メソッド内で設計画面サイズを設定してあること。" );
159                                 FDK.Log.Info( $"設計画面サイズ: {this.設計画面サイズdpx}" );
160                                 FDK.Log.Info( $"物理画面サイズ: {this.MainForm.ClientSize}" );
161                         }
162                         finally
163                         {
164                                 FDK.Log.EndInfo( "派生クラスを初期化しました。" );
165                         }
166
167                         try
168                         {
169                                 FDK.Log.BeginInfo( "デバイスリソースを作成します。" );
170                                 this.スレッド排他領域.WriteLock( () => {
171                                         this.スレッド排他領域.デバイスリソース = new メディア.デバイスリソース();
172                                         this.スレッド排他領域.デバイスリソース.設計画面サイズdpx = this.設計画面サイズdpx;
173                                         this.スレッド排他領域.デバイスリソース.すべてのリソースを作成する( this.MainForm.ClientSize, this.MainForm.Handle );
174                                 } );
175                         }
176                         finally
177                         {
178                                 FDK.Log.EndInfo( "デバイスリソースを作成しました。" );
179                         }
180
181                         try
182                         {
183                                 FDK.Log.BeginInfo( "進行描画スレッドを開始します。" );
184                                 this.進行描画スレッド = new System.Threading.Thread( this.進行描画スレッド処理 ) {
185                                         Name = "進行描画スレッド",
186                                         Priority = System.Threading.ThreadPriority.AboveNormal, // 優先度: やや高
187                                 };
188                                 this.進行描画スレッド.Start();
189                                 this.スレッド排他領域.進行描画スレッド生存中.ONになるまでブロックする();
190                         }
191                         finally
192                         {
193                                 FDK.Log.EndInfo( "進行描画スレッドを開始しました。" );
194                         }
195
196                         FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
197                 }
198                 private void OnClosing( object sender, FormClosingEventArgs e )
199                 {
200                         FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
201
202                         // 終了フラグを立てる。
203                         this.スレッド排他領域.アプリを終了せよ = true;
204
205                         // 終了フラグをチェックした進行描画スレッドが終了し、このトライステートに OFF を通知してくるまで待つ。
206                         this.スレッド排他領域.進行描画スレッド生存中.OFFになるまでブロックする();
207                         FDK.Log.Info( "進行描画スレッドが終了したことを確認しました。" );
208
209                         // 派生クラスの終了処理を呼び出す。
210                         this.終了する();
211
212                         // デバイスリソースを解放する。
213                         this.スレッド排他領域.デバイスリソース?.Dispose();
214                         this.スレッド排他領域.デバイスリソース = null;
215
216                         FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
217                 }
218                 private void OnClientSizeChanged( object sender, EventArgs e )
219                 {
220                         try
221                         {
222                                 FDK.Log.BeginInfo( $"{FDK.Utilities.現在のメソッド名}" );
223                                 FDK.Log.Info( $"新しいクライアントサイズ = {this.MainForm.ClientSize}" );
224
225                                 if( null == this.スレッド排他領域.デバイスリソース )
226                                 {
227                                         FDK.Log.Info( " まだ初期化されてないので、何もしません。" );
228                                         return;
229                                 }
230                                 if( this.MainForm.WindowState == FormWindowState.Minimized )
231                                 {
232                                         FDK.Log.Info( "最小化されました。" );
233                                         return; // 何もしない
234                                 }
235
236                                 this.スレッド排他領域.ReadLock( () => {
237
238                                         var dr = this.スレッド排他領域.ReadLock( () => this.スレッド排他領域.デバイスリソース );
239                                         Debug.Assert( null != dr, "デバイスリソースが作成済みであること。" );
240
241                                         // 現在の画面モードを取得しておく。(Alt+TABなど、勝手に全画面を解除されることもあるので。)
242                                         SharpDX.Mathematics.Interop.RawBool fullscreen;
243                                         SharpDX.DXGI.Output outputTarget;
244                                         dr.SwapChain.GetFullscreenState( out fullscreen, out outputTarget );
245                                         this.全画面モードである = fullscreen;
246                                         outputTarget?.Dispose();
247                                         FDK.Log.Info( $"現在、全画面モードである = {this.全画面モードである}" );
248
249                                         // (1) リソースを解放して、
250                                         this.デバイス依存リソースを解放する();
251                                         dr.サイズに依存するリソースを解放する();
252
253                                         // (2) 物理画面サイズを変更して、
254                                         dr.物理画面サイズpx = new SharpDX.Size2F( this.MainForm.ClientSize.Width, this.MainForm.ClientSize.Height );
255
256                                         // (3) リソースを再構築する。
257                                         dr.サイズに依存するリソースを作成する();
258                                         this.デバイス依存リソースを再構築する();
259
260                                 } );
261                         }
262                         finally
263                         {
264                                 FDK.Log.EndInfo( $"{FDK.Utilities.現在のメソッド名}" );
265                         }
266                 }
267                 private void 進行描画スレッド処理()
268                 {
269                         this.スレッド排他領域.進行描画スレッド生存中.状態 = 同期.TriStateEvent.状態種別.ON;
270
271                         FDK.Log.現在のスレッドに名前をつける( "Main" );
272                         FDK.Log.Info( "進行描画スレッドを起動しました。" );
273
274                         while( true )
275                         {
276                                 // デバイスロストに対応する。
277                                 this.スレッド排他領域.ReadLock( () => {
278                                         bool 異常発生 = false;
279                                         this.スレッド排他領域.デバイスリソース.D3Dデバイスが消失していれば再構築する( out 異常発生 );
280                                         if( 異常発生 )
281                                                 this.スレッド排他領域.アプリを終了せよ = true;
282                                 } );
283
284                                 // フラグがセットされていれば、ループを抜けてスレッドを終了する。
285                                 if( this.スレッド排他領域.アプリを終了せよ )
286                                         break;
287
288                                 // それ以外なら、シーンを進行・描画する。
289                                 this.シーンを描画する();
290                         }
291
292                         this.スレッド排他領域.進行描画スレッド生存中.状態 = 同期.TriStateEvent.状態種別.OFF;
293
294                         FDK.Log.Info( "アプリウィンドウをクローズします(非同期)。" );
295                         this.MainForm.BeginInvoke( new Action( () => { this.MainForm.Close(); } ) );
296
297                         FDK.Log.Info( "進行描画スレッドを終了します。" );
298                 }
299         };
300 }