2 * Copyright (c) 2007-2009 SlimDX Group
\r
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
\r
5 * of this software and associated documentation files (the "Software"), to deal
\r
6 * in the Software without restriction, including without limitation the rights
\r
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
8 * copies of the Software, and to permit persons to whom the Software is
\r
9 * furnished to do so, subject to the following conditions:
\r
11 * The above copyright notice and this permission notice shall be included in
\r
12 * all copies or substantial portions of the Software.
\r
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
\r
23 using System.ComponentModel;
\r
24 using System.Drawing;
\r
26 using System.Reflection;
\r
27 using System.Security.Permissions;
\r
28 using System.Windows.Forms;
\r
29 using System.Runtime.InteropServices;
\r
30 using System.Diagnostics;
\r
31 using SampleFramework.Properties;
\r
33 namespace SampleFramework
\r
36 /// Implements a specialized window for games and rendering.
\r
38 public class GameWindow : Form
\r
40 const int DefaultWidth = 800;
\r
41 const int DefaultHeight = 600;
\r
42 const string DefaultTitle = "Game";
\r
50 /// Occurs when the application is suspended.
\r
52 public event EventHandler Suspend;
\r
55 /// Occurs when the application is resumed.
\r
57 public event EventHandler Resume;
\r
60 /// Occurs when the user resizes the window.
\r
62 public event EventHandler UserResized;
\r
65 /// Occurs when the screen on which the window resides is changed.
\r
67 public event EventHandler ScreenChanged;
\r
70 /// Occurs when the application is activated.
\r
72 public event EventHandler ApplicationActivated;
\r
75 /// Occurs when the application is deactivated.
\r
77 public event EventHandler ApplicationDeactivated;
\r
80 /// Occurs when the system is suspended.
\r
82 public event EventHandler SystemSuspend;
\r
85 /// Occurs when the system is resumed.
\r
87 public event EventHandler SystemResume;
\r
90 /// Occurs when a screen saver is about to be activated.
\r
92 public event CancelEventHandler Screensaver;
\r
95 /// Gets a value indicating whether this instance is minimized.
\r
98 /// <c>true</c> if this instance is minimized; otherwise, <c>false</c>.
\r
100 public bool IsMinimized
\r
102 get { return minimized; }
\r
106 /// Gets a value indicating whether this instance is maximized.
\r
109 /// <c>true</c> if this instance is maximized; otherwise, <c>false</c>.
\r
111 public bool IsMaximized
\r
113 get { return maximized; }
\r
117 /// Gets or sets a value indicating whether System Menu is enabled.
\r
119 /// <value><c>true</c> if System Menu is enabled; otherwise, <c>false</c>.</value>
\r
120 public bool EnableSystemMenu // #28200 2012.5.1 yyagi
\r
125 public string strMessage // #28821 2014.1.23 yyagi
\r
130 public bool IsReceivedMessage
\r
136 private Screen m_Screen;
\r
138 /// Gets the screen on which the window resides.
\r
140 /// <value>The screen.</value>
\r
141 public Screen Screen
\r
143 get { return m_Screen; }
\r
144 private set { m_Screen = value; }
\r
148 /// Initializes a new instance of the <see cref="GameWindow"/> class.
\r
150 public GameWindow()
\r
152 MinimumSize = new Size(200, 200);
\r
154 Screen = ScreenFromHandle(Handle);
\r
156 //Icon = GetDefaultIcon();
\r
157 Text = GetDefaultTitle();
\r
162 /// Raises the <see cref="E:Suspend"/> event.
\r
164 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
\r
165 protected virtual void OnSuspend(EventArgs e)
\r
167 if (Suspend != null)
\r
172 /// Raises the <see cref="E:Resume"/> event.
\r
174 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
\r
175 protected virtual void OnResume(EventArgs e)
\r
177 if (Resume != null)
\r
182 /// Raises the <see cref="E:UserResized"/> event.
\r
184 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
\r
185 protected virtual void OnUserResized(EventArgs e)
\r
187 if (UserResized != null)
\r
188 UserResized(this, e);
\r
192 /// Raises the <see cref="E:ScreenChanged"/> event.
\r
194 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
\r
195 protected virtual void OnScreenChanged(EventArgs e)
\r
197 if (ScreenChanged != null)
\r
198 ScreenChanged(this, e);
\r
202 /// Raises the <see cref="E:ApplicationActivated"/> event.
\r
204 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
\r
205 protected virtual void OnApplicationActivated(EventArgs e)
\r
207 if (ApplicationActivated != null)
\r
208 ApplicationActivated(this, e);
\r
212 /// Raises the <see cref="E:ApplicationDeactivated"/> event.
\r
214 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
\r
215 protected virtual void OnApplicationDeactivated(EventArgs e)
\r
217 if (ApplicationDeactivated != null)
\r
218 ApplicationDeactivated(this, e);
\r
222 /// Raises the <see cref="E:SystemSuspend"/> event.
\r
224 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
\r
225 protected virtual void OnSystemSuspend(EventArgs e)
\r
227 if (SystemSuspend != null)
\r
228 SystemSuspend(this, e);
\r
232 /// Raises the <see cref="E:SystemResume"/> event.
\r
234 /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
\r
235 protected virtual void OnSystemResume(EventArgs e)
\r
237 if (SystemResume != null)
\r
238 SystemResume(this, e);
\r
242 /// Raises the <see cref="E:Screensaver"/> event.
\r
244 /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
\r
245 protected virtual void OnScreensaver(CancelEventArgs e)
\r
247 if (Screensaver != null)
\r
248 Screensaver(this, e);
\r
252 /// Raises the <see cref="E:System.Windows.Forms.Form.Load"/> event.
\r
254 /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
\r
255 protected override void OnLoad(EventArgs e)
\r
263 /// Raises the <see cref="E:System.Windows.Forms.Form.ResizeBegin"/> event.
\r
265 /// <param name="e">A <see cref="T:System.EventArgs"/> that contains the event data.</param>
\r
266 protected override void OnResizeBegin(EventArgs e)
\r
268 base.OnResizeBegin(e);
\r
270 // suspend any processing until we are done being minimized
\r
273 OnSuspend(EventArgs.Empty);
\r
277 /// Raises the <see cref="E:System.Windows.Forms.Form.ResizeEnd"/> event.
\r
279 /// <param name="e">A <see cref="T:System.EventArgs"/> that contains the event data.</param>
\r
280 protected override void OnResizeEnd(EventArgs e)
\r
282 base.OnResizeEnd(e);
\r
284 // check for screen and size changes
\r
285 OnUserResized(EventArgs.Empty);
\r
287 inSizeMove = false;
\r
289 // resume application processing
\r
290 OnResume(EventArgs.Empty);
\r
294 #region #23510 2010.11.14 yyagi add: 縦横比固定でのウインドウサイズ変更 定数定義 from http://www.vcskicks.com/maintain-aspect-ratio.php
\r
295 //double so division keeps decimal points
\r
296 const double widthRatio = SampleFramework.GameWindowSize.Width;
\r
297 const double heightRatio = SampleFramework.GameWindowSize.Height;
\r
298 const int WM_SIZING = 0x214;
\r
299 const int WMSZ_LEFT = 1;
\r
300 const int WMSZ_RIGHT = 2;
\r
301 const int WMSZ_TOP = 3;
\r
302 const int WMSZ_TOPLEFT = 4;
\r
303 const int WMSZ_TOPRIGHT = 5;
\r
304 const int WMSZ_BOTTOM = 6;
\r
305 const int WMSZ_BOTTOMLEFT = 7;
\r
306 const int WMSZ_BOTTOMRIGHT = 8;
\r
320 /// Handles raw window messages.
\r
322 /// <param name="m">The raw message.</param>
\r
323 [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
\r
324 protected override void WndProc(ref Message m)
\r
326 if (m.Msg == WindowConstants.WM_SIZE)
\r
328 if (m.WParam == WindowConstants.SIZE_MINIMIZED)
\r
333 OnSuspend(EventArgs.Empty);
\r
337 Rectangle client = NativeMethods.GetClientRectangle(m.HWnd);
\r
338 if (client.Height == 0)
\r
340 // rapidly clicking the task bar to minimize and restore a window
\r
341 // can cause a WM_SIZE message with SIZE_RESTORED when
\r
342 // the window has actually become minimized due to rapid change
\r
343 // so just ignore this message
\r
345 else if (m.WParam == WindowConstants.SIZE_MAXIMIZED)
\r
348 OnResume(EventArgs.Empty);
\r
353 OnUserResized(EventArgs.Empty);
\r
356 else if (m.WParam == WindowConstants.SIZE_RESTORED)
\r
359 OnResume(EventArgs.Empty);
\r
364 if (!inSizeMove && Size != cachedSize)
\r
366 OnUserResized(EventArgs.Empty);
\r
373 else if (m.Msg == WindowConstants.WM_ACTIVATEAPP)
\r
375 if (m.WParam != IntPtr.Zero)
\r
376 OnApplicationActivated(EventArgs.Empty);
\r
378 OnApplicationDeactivated(EventArgs.Empty);
\r
380 else if (m.Msg == WindowConstants.WM_POWERBROADCAST)
\r
382 if (m.WParam == WindowConstants.PBT_APMQUERYSUSPEND)
\r
384 OnSystemSuspend(EventArgs.Empty);
\r
385 m.Result = (IntPtr)1;
\r
388 else if (m.WParam == WindowConstants.PBT_APMRESUMESUSPEND)
\r
390 OnSystemResume(EventArgs.Empty);
\r
391 m.Result = (IntPtr)1;
\r
395 else if (m.Msg == WindowConstants.WM_SYSCOMMAND)
\r
397 long wparam = m.WParam.ToInt64() & 0xFFF0;
\r
398 if (wparam == WindowConstants.SC_MONITORPOWER || wparam == WindowConstants.SC_SCREENSAVE)
\r
400 CancelEventArgs e = new CancelEventArgs();
\r
404 m.Result = IntPtr.Zero;
\r
408 #region #28200 2012.5.1 yyagi: Disable system menu
\r
409 if ( ( m.WParam.ToInt32() & 0xFFFF ) == 0xF100 && !EnableSystemMenu ) // SC_KEYMENU
\r
411 m.Result = IntPtr.Zero;
\r
415 #region #23510 2010.11.13 yyagi: reset to 640x480
\r
416 if ((m.WParam.ToInt32() & 0xFFFF) == MENU_VIEW)
\r
418 base.ClientSize = new Size(SampleFramework.GameWindowSize.Width, SampleFramework.GameWindowSize.Height);
\r
419 this.OnResizeEnd(new EventArgs()); // #23510 2010.11.20 yyagi: to set window size to Config.ini
\r
422 if ((m.WParam.ToInt32() & 0xFFFF) == MENU_VIEW - 1)
\r
424 base.ClientSize = new Size( 1280, 720 );
\r
425 this.OnResizeEnd(new EventArgs()); // #23510 2010.11.20 yyagi: to set window size to Config.ini
\r
428 if ((m.WParam.ToInt32() & 0xFFFF) == MENU_VIEW - 2)
\r
430 base.ClientSize = new Size( 640, 480 );
\r
431 this.OnResizeEnd(new EventArgs()); // #23510 2010.11.20 yyagi: to set window size to Config.ini
\r
435 #region #28821 2014.1.23 yyagi (WM_COPYDATA)
\r
436 else if ( m.Msg == WindowConstants.WM_COPYDATA )
\r
438 //Trace.WriteLine( "FDK;msg received" );
\r
439 COPYDATASTRUCT cds = (COPYDATASTRUCT) Marshal.PtrToStructure( m.LParam, typeof( COPYDATASTRUCT ) );
\r
440 strMessage = Marshal.PtrToStringUni( cds.lpData );
\r
441 IsReceivedMessage = true;
\r
442 //Trace.WriteLine( "FDK;msg=" + strMessage + ", len=" + strMessage.Length + ", truelen=" + cds.cbData );
\r
445 #region #23510 2010.11.16 yyagi add: 縦横比固定でのウインドウサイズ変更 from http://d.hatena.ne.jp/iselix/20080917/1221666614 http://hp.vector.co.jp/authors/VA016117/sizing.html
\r
446 else if ( m.Msg == WM_SIZING )
\r
448 RECT rc = (RECT) Marshal.PtrToStructure( m.LParam, typeof( RECT ) );
\r
449 int w = rc.Right - rc.Left - ( Size.Width - ClientSize.Width );
\r
450 int h = rc.Bottom - rc.Top - ( Size.Height - ClientSize.Height );
\r
451 int dw = (int) ( h * widthRatio / heightRatio + 0.5 ) - w;
\r
452 int dh = (int) ( w / ( widthRatio / heightRatio ) + 0.5 ) - h;
\r
454 switch ( m.WParam.ToInt32() )
\r
464 case WMSZ_BOTTOMRIGHT:
\r
484 case WMSZ_TOPRIGHT:
\r
494 case WMSZ_BOTTOMLEFT:
\r
504 case 9: // #32383 2013.11.2 yyagi; exitting maximized window by using Aero snap
\r
507 throw new ArgumentOutOfRangeException( "value", "Illegal WM_SIZING value." );
\r
509 Marshal.StructureToPtr( rc, m.LParam, true );
\r
514 base.WndProc(ref m);
\r
517 void UpdateScreen()
\r
519 Screen current = Screen.FromHandle(Handle);
\r
520 if (Screen == null || Screen.DeviceName != current.DeviceName)
\r
523 if (Screen != null)
\r
524 OnScreenChanged(EventArgs.Empty);
\r
528 static Screen ScreenFromHandle(IntPtr windowHandle)
\r
530 Rectangle rectangle = NativeMethods.GetWindowRectangle(windowHandle);
\r
532 Screen bestScreen = null;
\r
534 foreach (Screen screen in Screen.AllScreens)
\r
536 Rectangle r = Rectangle.Intersect(rectangle, screen.Bounds);
\r
537 int area = r.Width * r.Height;
\r
539 if (area > mostArea)
\r
542 bestScreen = screen;
\r
546 if (bestScreen == null)
\r
547 bestScreen = Screen.PrimaryScreen;
\r
551 static string GetAssemblyTitle(Assembly assembly)
\r
553 if (assembly == null)
\r
556 AssemblyTitleAttribute[] customAttributes = (AssemblyTitleAttribute[])assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), true);
\r
557 if (customAttributes != null && customAttributes.Length > 0)
\r
558 return customAttributes[0].Title;
\r
563 static string GetDefaultTitle()
\r
565 string assemblyTitle = GetAssemblyTitle(Assembly.GetEntryAssembly());
\r
566 if (!string.IsNullOrEmpty(assemblyTitle))
\r
567 return assemblyTitle;
\r
571 Uri uri = new Uri(Application.ExecutablePath);
\r
572 return Path.GetFileNameWithoutExtension(uri.LocalPath);
\r
574 catch (ArgumentNullException)
\r
576 // swallow the exception
\r
578 catch (UriFormatException)
\r
580 // swallow the exception
\r
583 return DefaultTitle;
\r
586 static Icon GetDefaultIcon()
\r
588 return (Icon)Resources.sdx_icon_black.Clone();
\r
591 #region システムメニューに"1920x1080", "1280x720", "640x480" を追加 #23510 2010.11.13 yyagi add: to set "1920x1020","1280x720","640x480" menu in systemmenu. See also http://cs2ch.blog123.fc2.com/blog-entry-80.html
\r
592 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
\r
593 private struct MENUITEMINFO
\r
595 public uint cbSize;
\r
598 public uint fState;
\r
600 public IntPtr hSubMenu;
\r
601 public IntPtr hbmpChecked;
\r
602 public IntPtr hbmpUnchecked;
\r
603 public IntPtr dwItemData;
\r
604 public string dwTypeData;
\r
606 public IntPtr hbmpItem;
\r
609 [DllImport("user32", ExactSpelling = true)]
\r
610 private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
\r
611 [DllImport("user32", CharSet = CharSet.Auto)]
\r
612 private static extern bool InsertMenuItem(IntPtr hMenu, uint uItem, bool fByPosition, ref MENUITEMINFO lpmii);
\r
614 private const uint MENU_VIEW = 0x9999;
\r
615 private const uint MFT_SEPARATOR = 0x00000800;
\r
616 private const uint MIIM_FTYPE = 0x00000100;
\r
617 private const uint MIIM_STRING = 0x00000040;
\r
618 private const uint MIIM_ID = 0x00000002;
\r
620 protected override void OnCreateControl()
\r
622 base.OnCreateControl();
\r
625 IntPtr hSysMenu = GetSystemMenu(this.Handle, false);
\r
628 MENUITEMINFO item1 = new MENUITEMINFO();
\r
629 item1.cbSize = (uint)Marshal.SizeOf(item1);
\r
630 item1.fMask = MIIM_FTYPE;
\r
631 item1.fType = MFT_SEPARATOR;
\r
632 InsertMenuItem(hSysMenu, 5, true, ref item1);
\r
634 //メニュー項目(1920x1080)の挿入
\r
635 MENUITEMINFO item2 = new MENUITEMINFO();
\r
636 item2.cbSize = (uint)Marshal.SizeOf(item2);
\r
637 item2.fMask = MIIM_STRING | MIIM_ID;
\r
638 item2.wID = MENU_VIEW;
\r
639 item2.dwTypeData = "&" + SampleFramework.GameWindowSize.Width.ToString() + "x" + SampleFramework.GameWindowSize.Height.ToString();
\r
640 InsertMenuItem(hSysMenu, 6, true, ref item2);
\r
642 //メニュー項目(1280x720)の挿入
\r
643 MENUITEMINFO item3 = new MENUITEMINFO();
\r
644 item3.cbSize = (uint) Marshal.SizeOf( item3 );
\r
645 item3.fMask = MIIM_STRING | MIIM_ID;
\r
646 item3.wID = MENU_VIEW - 1;
\r
647 item3.dwTypeData = "&1280x720";
\r
648 InsertMenuItem( hSysMenu, 6, true, ref item3 );
\r
650 //メニュー項目(640x480)の挿入
\r
651 MENUITEMINFO item4 = new MENUITEMINFO();
\r
652 item4.cbSize = (uint) Marshal.SizeOf( item4 );
\r
653 item4.fMask = MIIM_STRING | MIIM_ID;
\r
654 item4.wID = MENU_VIEW - 2;
\r
655 item4.dwTypeData = "&640x480";
\r
656 InsertMenuItem( hSysMenu, 6, true, ref item4 );
\r