OSDN Git Service

Logにフォルダ変数に変換せずにパスが出力されていたミスを修正。
[strokestylet/CsWin10Desktop3.git] / StrokeStyleT / App.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Drawing;
5 using System.Linq;
6 using System.ServiceModel;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
10 using SharpDX;
11 using FDK;
12 using FDK.メディア;
13 using FDK.同期;
14 using SSTFormat.v2;
15 using SST.入力;
16 using SST.設定;
17 using SST.Viewer;
18 using SST.曲;
19 using SST.ステージ;
20 using SST.ステージ.起動;
21 using SST.ステージ.タイトル;
22 using SST.ステージ.ユーザ;
23 using SST.ステージ.選曲;
24 using SST.ステージ.曲読込;
25 using SST.ステージ.演奏;
26 using SST.ステージ.クリア;
27 using SST.ステージ.結果;
28
29 namespace SST
30 {
31         [ServiceBehavior( InstanceContextMode = InstanceContextMode.Single )]   // サービスインターフェースをシングルスレッドで呼び出す。
32         class App : ApplicationForm, IStrokeStyleTService, IDisposable
33         {
34                 public static bool ビュアーモードである
35                 {
36                         get;
37                         set;
38                 } = false;
39                 public static bool ビュアーモードではない
40                 {
41                         get
42                                 => !( App.ビュアーモードである );
43
44                         set
45                                 => App.ビュアーモードである = !( value );
46                 }
47
48                 #region //////// グローバルリソース(static) ////////
49
50                 /// <remarks>
51                 ///             SharpDX.Mathematics パッケージを参照し、かつ SharpDX 名前空間を using しておくと、
52                 ///             SharpDX で定義する追加の拡張メソッド(NextFloatなど)を使えるようになる。
53                 /// </remarks>
54                 public static Random 乱数
55                 {
56                         get;
57                         protected set;
58                 } = null;
59
60                 public static システム設定 システム設定
61                 {
62                         get;
63                         protected set;
64                 } = null;
65
66                 public static 入力管理 入力管理
67                 {
68                         get;
69                         protected set;
70                 } = null;
71
72                 public static FDK.メディア.サウンド.WASAPI.Device サウンドデバイス
73                 {
74                         get;
75                         protected set;
76                 } = null;
77
78                 public static FDK.メディア.サウンド.WASAPI.SoundTimer サウンドタイマ
79                 {
80                         get;
81                         protected set;
82                 } = null;
83
84                 /// <remarks>
85                 ///             デバイス依存リソースあり。
86                 /// </remarks>
87                 public static ユーザ管理 ユーザ管理
88                 {
89                         get;
90                         protected set;
91                 } = null;
92
93                 /// <remarks>
94                 ///             デバイス依存リソースあり。
95                 /// </remarks>
96                 public static ステージ管理 ステージ管理
97                 {
98                         get;
99                         protected set;
100                 } = null;
101
102                 public static スコア 演奏スコア
103                 {
104                         get;
105                         set;
106                 } = null;
107
108                 public static ViewerMessageQueue ビュアーメッセージキュー
109                 {
110                         get;
111                         protected set;
112                 }
113
114                 public static ViewerMessage 最後に取得したビュアーメッセージ
115                 {
116                         get;
117                         protected set;
118                 } = null;
119
120                 public static MusicNode ビュアー用ノード
121                 {
122                         get;
123                         protected set;
124                 }
125
126                 #endregion
127
128
129                 /// <summary>
130                 ///             static コンストラクタ。
131                 ///             インスタンスのコンストラクタに先駆けて必要となる処理を行う。
132                 /// </summary>
133                 static App()
134                 {
135                         SST.IO.Folder.初期化する();
136                 }
137
138                 /// <summary>
139                 ///             初期化処理。
140                 /// </summary>
141                 public App( string[] args )
142                         : base( 設計画面サイズ: new SizeF( 1920f, 1080f ), 物理画面サイズ: new SizeF( 960f, 540f ) )
143                 {
144                         using( Log.Block( FDKUtilities.現在のメソッド名 ) )
145                         {
146                                 Log.現在のスレッドに名前をつける( "描画" );
147
148                                 // 高解像度タイマを使えないならエラー。
149                                 if( !( Stopwatch.IsHighResolution ) )
150                                         throw new Exception( "このシステムは、高解像度タイマをサポートしていません。" );
151
152                                 //Sleep 精度を上げる。
153                                 timeBeginPeriod( 1 );
154
155                                 #region " ビュアーモードかどうかを確認する。"
156                                 //----------------
157                                 foreach( var arg in args )
158                                 {
159                                         if( ( "-v" == arg.ToLower() ) || ( "-viewer" == arg.ToLower() ) )
160                                         {
161                                                 App.ビュアーモードである = true;
162                                                 break;
163                                         }
164                                 }
165                                 //----------------
166                                 #endregion
167
168                                 // グローバルリソースを初期化する (1)フォーム初期化前
169
170                                 App.乱数 = new Random( DateTime.Now.Millisecond );
171
172                                 App.システム設定 = システム設定.復元する();
173
174                                 if( App.ビュアーモードである )
175                                         App.システム設定.全画面モードである = false;   // ビュアーモードでは常にウィンドウモード。
176
177                                 App.ビュアーメッセージキュー = new ViewerMessageQueue();
178
179                                 App.最後に取得したビュアーメッセージ = null;
180
181                                 App.ビュアー用ノード = null;
182
183                                 #region " メインフォームを初期化する。"
184                                 //----------------
185                                 this.Text = $"{Application.ProductName} {Application.ProductVersion}";
186                                 if( App.ビュアーモードである )
187                                         this.Text += " (Viewer)";
188
189                                 // ユーザはフォームサイズを変更できない。
190                                 //this.AllowUserResizing = false;
191                                 //----------------
192                                 #endregion
193
194                                 // グローバルリソースを初期化する (2)フォーム初期化後
195
196                                 App.入力管理 = new 入力管理( this.Handle, 32 );
197
198                                 App.サウンドデバイス = new FDK.メディア.サウンド.WASAPI.Device( CSCore.CoreAudioAPI.AudioClientShareMode.Shared );
199                                 App.サウンドタイマ = new FDK.メディア.サウンド.WASAPI.SoundTimer( App.サウンドデバイス );
200
201                                 App.ユーザ管理 = ユーザ管理.復元する( App.グラフィックデバイス ); // この中で活性化も行われる。
202                                 App.ユーザ管理.最初のユーザを選択する();
203
204                                 App.ステージ管理 = new ステージ管理();
205
206                                 App.演奏スコア = null;
207
208
209                                 this._活性化する();
210
211                                 #region " 最初のステージへ遷移する。"
212                                 //----------------
213                                 if( App.ビュアーモードではない )
214                                 {
215                                         // (A) 通常モード
216                                         App.ステージ管理.ステージを遷移する( App.グラフィックデバイス, App.ステージ管理.最初のステージ名 );
217                                 }
218                                 else
219                                 {
220                                         // (B) ビュアーモード
221                                         App.ステージ管理.ステージを遷移する( App.グラフィックデバイス, nameof( ユーザ選択ステージ ) );
222                                 }
223                                 //----------------
224                                 #endregion
225                         }
226                 }
227
228                 /// <summary>
229                 ///             終了処理。
230                 /// </summary>
231                 public new void Dispose()
232                 {
233                         using( Log.Block( FDKUtilities.現在のメソッド名 ) )
234                         {
235                                 this._非活性化する();
236
237                                 App.ビュアー用ノード = null;
238                                 App.最後に取得したビュアーメッセージ = null;
239                                 App.ビュアーメッセージキュー = null;
240
241                                 App.演奏スコア?.Dispose();
242                                 App.演奏スコア = null;
243
244                                 App.ステージ管理.Dispose( App.グラフィックデバイス );
245                                 App.ステージ管理 = null;
246
247                                 //App.ユーザ管理.保存する();   --> 必要時に更新する。
248                                 App.ユーザ管理 = null;
249
250                                 //App.システム設定.保存する();        --> 必要時に更新する。
251                                 App.システム設定?.Dispose();
252                                 App.システム設定 = null;
253
254                                 App.サウンドタイマ?.Dispose();
255                                 App.サウンドタイマ = null;
256
257                                 App.サウンドデバイス?.Dispose();
258                                 App.サウンドデバイス = null;
259
260                                 App.入力管理?.キーバインディングを保存する();     // 同上。
261                                 App.入力管理?.Dispose();
262                                 App.入力管理 = null;
263
264                                 base.Dispose();
265                         }
266                 }
267
268                 /// <summary>
269                 ///             メインループ。
270                 /// </summary>
271                 public sealed override void Run()
272                 {
273                         Debug.Assert( ( null != App.ステージ管理.現在のステージ ), "最初のステージが設定されていません。" );
274
275                         SharpDX.Windows.RenderLoop.Run( this, () => {
276
277                                 switch( this._AppStatus )
278                                 {
279                                         case AppStatus.開始:
280
281                                                 // 高速進行タスク起動。
282                                                 this._高速進行ステータス = new TriStateEvent( TriStateEvent.状態種別.OFF );
283                                                 Task.Factory.StartNew( this._高速進行タスクエントリ );
284
285                                                 // 描画タスク開始。
286                                                 this._AppStatus = AppStatus.実行中;
287                                                 break;
288
289                                         case AppStatus.実行中:
290                                                 this._進行と描画を行う();
291                                                 break;
292
293                                         case AppStatus.終了:
294                                                 Thread.Sleep( 500 );    // 終了待機中。
295                                                 break;
296                                 }
297
298                         } );
299                 }
300
301                 /// <summary>
302                 ///             アプリフォームが読み込まれた。
303                 /// </summary>
304                 protected override void OnLoad( EventArgs e )
305                 {
306                         using( Log.Block( FDKUtilities.現在のメソッド名 ) )
307                         {
308                                 lock( this._高速進行と描画の同期 )
309                                 {
310                                         if( App.システム設定.全画面モードである )
311                                                 this.全画面モード = true;
312                                 }
313                         }
314                 }
315
316                 /// <summary>
317                 ///             アプリフォームが閉じられる。
318                 /// </summary>
319                 protected override void OnClosing( System.ComponentModel.CancelEventArgs e )
320                 {
321                         using( Log.Block( FDKUtilities.現在のメソッド名 ) )
322                         {
323                                 lock( this._高速進行と描画の同期 )
324                                 {
325                                         // 通常は進行タスクから終了するが、Alt+F4でここに来た場合はそれが行われてないので、行う。
326                                         if( this._AppStatus != AppStatus.終了 )
327                                         {
328                                                 this._アプリを終了する();
329                                         }
330
331                                         base.OnClosing( e );
332                                 }
333                         }
334                 }
335
336                 protected override void スワップチェーンに依存するグラフィックリソースを作成する( SharpDX.Direct3D11.Device d3dDevice )
337                 {
338                         using( Log.Block( FDKUtilities.現在のメソッド名 ) )
339                         {
340                                 Debug.Assert( null != d3dDevice );
341                         }
342                 }
343
344                 protected override void スワップチェーンに依存するグラフィックリソースを解放する()
345                 {
346                         using( Log.Block( FDKUtilities.現在のメソッド名 ) )
347                         {
348                         }
349                 }
350
351                 #region " IStrokeStyleT の実装 "
352                 //----------------
353                 // このアセンブリ(exe)は、WCF で IStrokeStyleTService を公開する。(基本的に、SSTFViewer 向け。)
354                 // ・このサービスインターフェースは、シングルスレッド(GUIスレッド)で同期実行される。(Appクラスの ServiceBehavior属性を参照。)
355                 // ・このサービスホストはシングルトンであり、すべてのクライアントセッションは同一(単一)のサービスインスタンスへ接続される。(Program.Main() を参照。)
356
357                 /// <summary>
358                 ///             曲を読み込み、演奏を開始する。
359                 ///             ビュアーモードのときのみ有効。
360                 /// </summary>
361                 /// <param name="path">曲ファイルパス</param>
362                 /// <param name="startPart">演奏開始小節番号(0~)</param>
363                 /// <param name="drumsSound">ドラムチップ音を発声させるなら true。</param>
364                 public void ViewerPlay( string path, int startPart = 0, bool drumsSound = true )
365                 {
366                         if( App.ビュアーモードではない )
367                                 return;
368
369                         App.ビュアーメッセージキュー.格納する( new ViewerMessage() {
370                                 種別 = ViewerMessage.指示種別.演奏開始,
371                                 演奏対象曲のファイルパス = path,
372                                 演奏を開始する小節番号 = startPart,
373                                 ドラムチップのヒット時に発声する = drumsSound,
374                         } );
375                 }
376
377                 /// <summary>
378                 ///             現在の演奏を停止する。
379                 ///             ビュアーモードのときのみ有効。
380                 /// </summary>
381                 public void ViewerStop()
382                 {
383                         if( App.ビュアーモードではない )
384                                 return;
385
386                         App.ビュアーメッセージキュー.格納する( new ViewerMessage() {
387                                 種別 = ViewerMessage.指示種別.演奏停止,
388                         } );
389                 }
390
391                 /// <summary>
392                 ///             サウンドデバイスの発声遅延[ms]を返す。
393                 /// </summary>
394                 /// <returns>遅延量[ms]</returns>
395                 public float GetSoundDelay()
396                 {
397                         if( App.ビュアーモードではない )
398                                 return 0f;
399
400                         return (float) ( App.サウンドデバイス?.遅延sec ?? 0.0 ) * 1000.0f;
401                 }
402                 //----------------
403                 #endregion
404
405
406                 private enum AppStatus { 開始, 実行中, 終了 };
407
408                 /// <summary>
409                 ///             アプリケーションの状態。
410                 /// </summary>
411                 private AppStatus _AppStatus = AppStatus.開始;
412
413
414                 // ※ Form イベントの override メソッドは描画スレッドで実行されるため、処理中に進行タスクが呼び出されると困る場合には、進行タスクとの lock を忘れないこと。
415                 private readonly object _高速進行と描画の同期 = new object();
416
417                 /// <summary>
418                 ///             進行タスクの状態。
419                 ///             OFF:タスク起動前、ON:タスク実行中、OFF:タスク終了済み
420                 /// </summary>
421                 private TriStateEvent _高速進行ステータス;
422
423
424                 /// <summary>
425                 ///             高速進行ループの処理内容。
426                 /// </summary>
427                 private void _高速進行タスクエントリ()
428                 {
429                         Log.現在のスレッドに名前をつける( "高速進行" );
430                         Log.Info( "高速進行タスクを開始します。" );
431
432                         this._高速進行ステータス.現在の状態 = TriStateEvent.状態種別.ON;
433
434                         while( true )
435                         {
436                                 lock( this._高速進行と描画の同期 )
437                                 {
438                                         if( this._高速進行ステータス.現在の状態 != TriStateEvent.状態種別.ON )        // lock してる間に状態が変わることがあるので注意。
439                                                 break;
440
441                                         //App.入力管理.すべての入力デバイスをポーリングする();
442                                         // --> 入力ポーリングの挙動はステージごとに異なるので、それぞれのステージ内で行う。
443
444                                         App.ステージ管理.現在のステージ.高速進行する();
445                                 }
446
447                                 Thread.Sleep( 1 );  // ウェイト。
448                         }
449
450                         this._高速進行ステータス.現在の状態 = TriStateEvent.状態種別.無効;
451
452                         Log.Info( "高速進行タスクを終了しました。" );
453                 }
454
455                 /// <summary>
456                 ///             進行描画ループの処理内容。
457                 /// </summary>
458                 private void _進行と描画を行う()
459                 {
460                         var gd = App.グラフィックデバイス;
461                         bool vsync = false;
462
463                         lock( this._高速進行と描画の同期 )
464                         {
465                                 if( this._AppStatus != AppStatus.実行中 )  // 上記lock中に終了されている場合があればそれをはじく。
466                                         return;
467
468                                 gd.D3DDeviceを取得する( ( d3dDevice ) => {
469
470                                         #region " (1) D3Dレンダリングの前処理を行う。"
471                                         //----------------
472                                         // 既定のD3Dレンダーターゲットビューを黒でクリアする。
473                                         d3dDevice.ImmediateContext.ClearRenderTargetView( gd.D3DRenderTargetView, Color4.Black );
474
475                                         // 深度バッファを 1.0f でクリアする。
476                                         d3dDevice.ImmediateContext.ClearDepthStencilView(
477                                                         gd.D3DDepthStencilView,
478                                                         SharpDX.Direct3D11.DepthStencilClearFlags.Depth,
479                                                         depth: 1.0f,
480                                                         stencil: 0 );
481                                         //----------------
482                                         #endregion
483
484                                         #region " (2) 現在のステージの進行描画を行う。"
485                                         //----------------
486                                         App.ステージ管理.現在のステージ.進行描画する( gd );
487                                         //----------------
488                                         #endregion
489
490                                         #region " (3) ステージの状態をチェックし、遷移処理を行う。(必要に応じてビュアーメッセージキューの処理も行う。)"
491                                         //----------------
492                                         switch( App.ステージ管理.現在のステージ )
493                                         {
494                                                 case 起動ステージ stage:
495                                                         #region " 完了 → タイトルステージへ "
496                                                         //----------------
497                                                         if( stage.現在のフェーズ == 起動ステージ.フェーズ.完了 )
498                                                         {
499                                                                 App.ステージ管理.ステージを遷移する( gd, nameof( タイトルステージ ) );
500                                                         }
501                                                         //----------------
502                                                         #endregion
503                                                         break;
504
505                                                 case タイトルステージ stage:
506                                                         #region " 確定 → ユーザステージへ "
507                                                         //----------------
508                                                         if( stage.現在のフェーズ == タイトルステージ.フェーズ.確定 )
509                                                         {
510                                                                 App.ステージ管理.ステージを遷移する( gd, nameof( ユーザ選択ステージ ) );
511                                                         }
512                                                         //----------------
513                                                         #endregion
514                                                         #region " キャンセル → アプリを終了する。"
515                                                         //----------------
516                                                         else if( stage.現在のフェーズ == タイトルステージ.フェーズ.キャンセル )
517                                                         {
518                                                                 App.ステージ管理.ステージを遷移する( gd, null );
519                                                                 this._アプリを終了する();
520                                                         }
521                                                         //----------------
522                                                         #endregion
523                                                         break;
524
525                                                 case ユーザ選択ステージ stage:
526                                                         #region " キャンセル → タイトルステージへ "
527                                                         //----------------
528                                                         if( stage.現在のフェーズ == ユーザ選択ステージ.フェーズ.キャンセル )
529                                                         {
530                                                                 App.ステージ管理.ステージを遷移する( gd, nameof( タイトルステージ ) );
531                                                         }
532                                                         //----------------
533                                                         #endregion
534                                                         #region " 完了 → 通常モード時は選曲ステージ、ビュアーモード時は演奏ステージへ "
535                                                         //----------------
536                                                         else if( stage.現在のフェーズ == ユーザ選択ステージ.フェーズ.完了 )
537                                                         {
538                                                                 if( App.ビュアーモードではない )
539                                                                 {
540                                                                         App.ステージ管理.ステージを遷移する( gd, nameof( 選曲ステージ ) );
541                                                                 }
542                                                                 else
543                                                                 {
544                                                                         App.ステージ管理.ステージを遷移する( gd, nameof( 演奏ステージ ) );
545                                                                 }
546                                                         }
547                                                         //----------------
548                                                         #endregion
549                                                         break;
550
551                                                 case 選曲ステージ stage:
552                                                         #region " キャンセル → ユーザステージへ "
553                                                         //----------------
554                                                         if( stage.現在のフェーズ == 選曲ステージ.フェーズ.キャンセル )
555                                                         {
556                                                                 App.ステージ管理.ステージを遷移する( gd, nameof( ユーザ選択ステージ ) );
557                                                         }
558                                                         //----------------
559                                                         #endregion
560                                                         #region " 曲決定 → 曲読込ステージへ "
561                                                         //----------------
562                                                         else if( stage.現在のフェーズ == 選曲ステージ.フェーズ.曲決定 )
563                                                         {
564                                                                 App.ステージ管理.ステージを遷移する( gd, nameof( 曲読込ステージ ) );
565                                                         }
566                                                         //----------------
567                                                         #endregion
568                                                         break;
569
570                                                 case 曲読込ステージ stage:
571                                                         #region " 完了 → 演奏ステージへ "
572                                                         //----------------
573                                                         if( stage.現在のフェーズ == 曲読込ステージ.フェーズ.完了 )
574                                                         {
575                                                                 App.ステージ管理.ステージを遷移する( gd, nameof( 演奏ステージ ) );
576                                                         }
577                                                         //----------------
578                                                         #endregion
579                                                         break;
580
581                                                 case 演奏ステージ stage:
582                                                         if( App.ビュアーモードではない )
583                                                         {
584                                                                 // (A) 通常モード時
585                                                                 #region " Failed, キャンセル → 選曲ステージへ。"
586                                                                 //--------------------
587                                                                 if( ( stage.現在のフェーズ == 演奏ステージ.フェーズ.キャンセル ) ||
588                                                                         ( stage.現在のフェーズ == 演奏ステージ.フェーズ.Failed ) )
589                                                                 {
590                                                                         App.ステージ管理.ステージを遷移する( gd, nameof( 選曲ステージ ) );
591                                                                 }
592                                                                 //--------------------
593                                                                 #endregion
594                                                                 #region " クリア → クリアステージへ。"
595                                                                 //--------------------
596                                                                 else if( stage.現在のフェーズ == 演奏ステージ.フェーズ.クリア )
597                                                                 {
598                                                                         App.ステージ管理.ステージを遷移する( gd, nameof( クリアステージ ) );
599                                                                 }
600                                                                 //--------------------
601                                                                 #endregion
602                                                         }
603                                                         else
604                                                         {
605                                                                 // (B) ビュアーモード時
606                                                                 #region " ビュアーメッセージが届いていれば、処理する。"
607                                                                 //----------------
608                                                                 var msg
609                                                                         = App.最後に取得したビュアーメッセージ
610                                                                         = App.ビュアーメッセージキュー.取得する();
611
612                                                                 if( null != msg )
613                                                                 {
614                                                                         Log.Info( $"{msg.ToString()}" );
615
616                                                                         switch( msg.種別 )
617                                                                         {
618                                                                                 case ViewerMessage.指示種別.指示なし:
619                                                                                         break;
620
621                                                                                 case ViewerMessage.指示種別.演奏開始:
622                                                                                         if( stage.現在のフェーズ == 演奏ステージ.フェーズ.演奏中 ||
623                                                                                                 stage.現在のフェーズ == 演奏ステージ.フェーズ.ビュアーメッセージ待機 )
624                                                                                         {
625                                                                                                 stage.非活性化する( gd );
626
627                                                                                                 stage.BGMを停止する();
628
629                                                                                                 if( ( null != App.ビュアー用ノード ) && App.ビュアー用ノード.活性化している )
630                                                                                                         App.ビュアー用ノード.非活性化する( gd );
631
632                                                                                                 App.ビュアー用ノード = new MusicNode( msg.演奏対象曲のファイルパス, null );
633
634                                                                                                 // 曲読込ステージへ。
635                                                                                                 App.ステージ管理.ステージを遷移する( gd, nameof( 曲読込ステージ ) );
636                                                                                         }
637                                                                                         break;
638
639                                                                                 case ViewerMessage.指示種別.演奏停止:
640                                                                                         if( stage.現在のフェーズ == 演奏ステージ.フェーズ.演奏中 ||
641                                                                                                 stage.現在のフェーズ == 演奏ステージ.フェーズ.ビュアーメッセージ待機 )  // 待機中もまだBGMが鳴ってるかもしれない
642                                                                                         {
643                                                                                                 stage.演奏を停止する();
644                                                                                                 stage.現在のフェーズ = 演奏ステージ.フェーズ.ビュアーメッセージ待機;
645                                                                                         }
646                                                                                         break;
647
648                                                                                 default:
649                                                                                         Log.ERROR( "未対応のViewerMessageです。" );
650                                                                                         break;
651                                                                         }
652                                                                 }
653                                                                 //----------------
654                                                                 #endregion
655                                                                 #region " クリア, Failed, キャンセル → メッセージ待機へ。"
656                                                                 //----------------
657                                                                 if( stage.現在のフェーズ == 演奏ステージ.フェーズ.クリア ||
658                                                                         stage.現在のフェーズ == 演奏ステージ.フェーズ.Failed ||
659                                                                         stage.現在のフェーズ == 演奏ステージ.フェーズ.キャンセル )
660                                                                 {
661                                                                         // フェーズのみ変更し、BGM は止めない。
662                                                                         stage.現在のフェーズ = 演奏ステージ.フェーズ.ビュアーメッセージ待機;
663                                                                 }
664                                                                 //----------------
665                                                                 #endregion
666                                                         }
667                                                         break;
668
669                                                 case クリアステージ stage:
670                                                         #region " 完了 → 結果ステージへ "
671                                                         //----------------
672                                                         if( stage.現在のフェーズ == クリアステージ.フェーズ.完了 )
673                                                         {
674                                                                 App.ステージ管理.ステージを遷移する( gd, nameof( 結果ステージ ) );
675                                                         }
676                                                         //----------------
677                                                         #endregion
678                                                         break;
679
680                                                 case 結果ステージ stage:
681                                                         #region " 完了 → 選曲ステージへ。"
682                                                         //--------------------
683                                                         if( stage.現在のフェーズ == 結果ステージ.フェーズ.完了 )
684                                                         {
685                                                                 App.ステージ管理.ステージを遷移する( gd, nameof( 選曲ステージ ) );
686                                                         }
687                                                         //--------------------
688                                                         #endregion
689                                                         break;
690                                         }
691                                         //----------------
692                                         #endregion
693
694                                         #region " (4) D3Dコマンドをフラッシュする。 "
695                                         //----------------
696                                         if( App.システム設定.垂直帰線待ちを行う )
697                                         {
698                                                 // ID3D11DeviceContext::Flush
699                                                 // <https://msdn.microsoft.com/ja-jp/library/windows/desktop/ff476425(v=vs.85).aspx>
700                                                 // > We recommend that you use Flush when the CPU waits for an arbitrary amount of time
701                                                 // > (such as when you call the Sleep function). 
702                                                 // > CPUが(Sleep関数の呼び出しなどで)任意の時間 wait するのであれば、Flush の使用を推奨します。
703                                                 d3dDevice.ImmediateContext.Flush();
704                                         }
705                                         //----------------
706                                         #endregion
707                                 } );
708
709                                 vsync = App.システム設定.垂直帰線待ちを行う;
710                         }
711
712                         #region " (5) スワップチェーンを表示する。"
713                         //----------------
714                         gd.SwapChain.Present( ( vsync ) ? 1 : 0, SharpDX.DXGI.PresentFlags.None );
715                         //----------------
716                         #endregion
717                 }
718
719                 /// <summary>
720                 ///             グローバルリソースのうち、グラフィックリソースを持つものについて、活性化がまだなら活性化する。
721                 /// </summary>
722                 private void _活性化する()
723                 {
724                         using( Log.Block( FDKUtilities.現在のメソッド名 ) )
725                         {
726                                 if( App.ユーザ管理.活性化していない )
727                                         App.ユーザ管理.活性化する( App.グラフィックデバイス );
728
729                                 if( App.ステージ管理.現在のステージ?.活性化していない ?? false )
730                                         App.ステージ管理.現在のステージ?.活性化する( App.グラフィックデバイス );
731                         }
732                 }
733
734                 /// <summary>
735                 ///             グローバルリソースのうち、グラフィックリソースを持つものについて、活性化中なら非活性化する。
736                 /// </summary>
737                 private void _非活性化する()
738                 {
739                         using( Log.Block( FDKUtilities.現在のメソッド名 ) )
740                         {
741                                 if( App.ステージ管理.現在のステージ?.活性化している ?? false )
742                                         App.ステージ管理.現在のステージ?.非活性化する( App.グラフィックデバイス );
743
744                                 if( App.ユーザ管理.活性化している )
745                                         App.ユーザ管理.非活性化する( App.グラフィックデバイス );
746                         }
747                 }
748
749                 /// <summary>
750                 ///             進行タスクを終了し、ウィンドウを閉じ、アプリを終了する。
751                 /// </summary>
752                 private void _アプリを終了する()
753                 {
754                         using( Log.Block( FDKUtilities.現在のメソッド名 ) )
755                         {
756                                 if( this._AppStatus != AppStatus.終了 )
757                                 {
758                                         this._高速進行ステータス.現在の状態 = TriStateEvent.状態種別.OFF;
759                                         this._AppStatus = AppStatus.終了;
760
761                                         // _AppStatus を変更したあとに、GUI スレッドで非同期実行を指示する。
762                                         this.BeginInvoke( new Action( () => { this.Close(); } ) );
763                                 }
764                         }
765                 }
766
767                 protected override void OnKeyDown( KeyEventArgs e )
768                 {
769                         if( e.KeyCode == Keys.F11 )
770                                 this.全画面モード = !( this.全画面モード );
771                 }
772
773                 #region " Win32 "
774                 //----------------
775                 [System.Runtime.InteropServices.DllImport( "winmm.dll", EntryPoint = "timeBeginPeriod" )]
776                 public static extern uint timeBeginPeriod( uint uMilliseconds );
777
778                 [System.Runtime.InteropServices.DllImport( "winmm.dll", EntryPoint = "timeEndPeriod" )]
779                 public static extern uint timeEndPeriod( uint uMilliseconds );
780                 //----------------
781                 #endregion
782         }
783 }