Support for switching to and from fullscreen mode in Windows during runtime.

This commit is contained in:
gogg 2010-01-10 16:54:09 +00:00
parent acb8427feb
commit b37d1191d5
2 changed files with 241 additions and 88 deletions

View File

@ -329,6 +329,26 @@ set_properties_now(WindowProperties &properties) {
properties.clear_foreground(); properties.clear_foreground();
} }
if (properties.has_fullscreen()) {
if (properties.get_fullscreen() && !is_fullscreen()) {
if (do_fullscreen_switch()){
_properties.set_fullscreen(true);
properties.clear_fullscreen();
} else {
windisplay_cat.warning()
<< "Switching to fullscreen mode failed!\n";
}
} else if (!properties.get_fullscreen() && is_fullscreen()){
if (do_windowed_switch()){
_properties.set_fullscreen(false);
properties.clear_fullscreen();
} else {
windisplay_cat.warning()
<< "Switching to windowed mode failed!\n";
}
}
}
} }
@ -345,7 +365,7 @@ close_window() {
if (is_fullscreen()) { if (is_fullscreen()) {
// revert to default display mode. // revert to default display mode.
ChangeDisplaySettings(NULL, 0x0); do_fullscreen_disable();
} }
// Remove the window handle from our global map. // Remove the window handle from our global map.
@ -722,6 +742,61 @@ do_fullscreen_resize(int x_size, int y_size) {
return true; return true;
} }
////////////////////////////////////////////////////////////////////
// Function: WinGraphicsWindow::do_fullscreen_switch
// Access: Protected, Virtual
// Description: Called in the set_properties_now function
// to switch to fullscreen.
////////////////////////////////////////////////////////////////////
bool WinGraphicsWindow::
do_fullscreen_switch() {
DWORD window_style = make_style(true);
SetWindowLong(_hWnd, GWL_STYLE, window_style);
WINDOW_METRICS metrics;
bool has_origin;
if (!calculate_metrics(true, window_style, metrics, has_origin)){
return false;
}
SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, metrics.width, metrics.height,
SWP_FRAMECHANGED | SWP_SHOWWINDOW);
do_fullscreen_enable();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: WinGraphicsWindow::do_windowed_switch
// Access: Protected, Virtual
// Description: Called in the set_properties_now function
// to switch to windowed mode.
////////////////////////////////////////////////////////////////////
bool WinGraphicsWindow::
do_windowed_switch() {
do_fullscreen_disable();
DWORD window_style = make_style(false);
SetWindowLong(_hWnd, GWL_STYLE, window_style);
WINDOW_METRICS metrics;
bool has_origin;
_properties.set_origin(-1, -1);
if (!calculate_metrics(false, window_style, metrics, has_origin)){
return false;
}
// We make an initial SetWindowPos call so that the new styles are taken into account,
// then call do_reshape_request which does the actual sizing and positioning.
SetWindowPos(_hWnd, HWND_NOTOPMOST, metrics.x, metrics.y,
metrics.width, metrics.height,
SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
do_reshape_request(0, 0, false, metrics.width, metrics.height);
return true;
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: WinGraphicsWindow::reconsider_fullscreen_size // Function: WinGraphicsWindow::reconsider_fullscreen_size
// Access: Protected, Virtual // Access: Protected, Virtual
@ -748,12 +823,13 @@ support_overlay_window(bool) {
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: WinGraphicsWindow::open_graphic_window // Function: WinGraphicsWindow::make_style
// Access: Private // Access: Private
// Description: Creates a regular or fullscreen window. // Description: Constructs a dwStyle for the specified mode,
// be it windowed or fullscreen.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool WinGraphicsWindow:: DWORD WinGraphicsWindow::
open_graphic_window(bool fullscreen) { make_style(bool fullscreen) {
// from MSDN: // from MSDN:
// An OpenGL window has its own pixel format. Because of this, only // An OpenGL window has its own pixel format. Because of this, only
// device contexts retrieved for the client area of an OpenGL // device contexts retrieved for the client area of an OpenGL
@ -761,8 +837,8 @@ open_graphic_window(bool fullscreen) {
// OpenGL window should be created with the WS_CLIPCHILDREN and // OpenGL window should be created with the WS_CLIPCHILDREN and
// WS_CLIPSIBLINGS styles. Additionally, the window class attribute // WS_CLIPSIBLINGS styles. Additionally, the window class attribute
// should not include the CS_PARENTDC style. // should not include the CS_PARENTDC style.
DWORD window_style =
WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; DWORD window_style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
if (fullscreen){ if (fullscreen){
window_style |= WS_SYSMENU; window_style |= WS_SYSMENU;
@ -775,50 +851,48 @@ open_graphic_window(bool fullscreen) {
window_style |= WS_BORDER; window_style |= WS_BORDER;
} }
} }
return window_style;
}
string title; ////////////////////////////////////////////////////////////////////
if (_properties.has_title()) { // Function: WinGraphicsWindow::calculate_metrics
title = _properties.get_title(); // Access: Private
} // Description: Calculates the metrics for the specified mode,
// be it windowed or fullscreen.
if (!_properties.has_size()) { ////////////////////////////////////////////////////////////////////
//Just pick a conservative default size if one isn't specified. bool WinGraphicsWindow::
_properties.set_size(640, 480); calculate_metrics(bool fullscreen, DWORD window_style, WINDOW_METRICS &metrics,
} bool &has_origin) {
metrics.x = 0;
int x_size = _properties.get_x_size(); metrics.y = 0;
int y_size = _properties.get_y_size(); has_origin = _properties.has_origin();
int x_origin = 0;
int y_origin = 0;
bool has_origin = _properties.has_origin();
if (!fullscreen && has_origin) { if (!fullscreen && has_origin) {
x_origin = _properties.get_x_origin(); metrics.x = _properties.get_x_origin();
y_origin = _properties.get_y_origin(); metrics.y = _properties.get_y_origin();
// A coordinate of -2 means to center the window in its client area. // A coordinate of -2 means to center the window in its client area.
if (x_origin == -2) { if (metrics.x == -2) {
x_origin = 0.5 * (_pipe->get_display_width() - x_size); metrics.x = 0.5 * (_pipe->get_display_width() - _properties.get_x_origin());
} }
if (y_origin == -2) { if (metrics.y == -2) {
y_origin = 0.5 * (_pipe->get_display_height() - y_size); metrics.y = 0.5 * (_pipe->get_display_height() - _properties.get_y_origin());
} }
_properties.set_origin(x_origin, y_origin); _properties.set_origin(metrics.x, metrics.y);
if (x_origin == -1 && y_origin == -1) { if (metrics.x == -1 && metrics.y == -1) {
x_origin = 0; metrics.x = 0;
y_origin = 0; metrics.y = 0;
has_origin = false; has_origin = false;
} }
} }
int clientAreaWidth = x_size; metrics.width = _properties.get_x_size();
int clientAreaHeight = y_size; metrics.height = _properties.get_y_size();
if (!fullscreen){ if (!fullscreen){
RECT win_rect; RECT win_rect;
SetRect(&win_rect, x_origin, y_origin, SetRect(&win_rect, metrics.x, metrics.y,
x_origin + x_size, y_origin + y_size); metrics.x + metrics.width, metrics.y + metrics.height);
// Compute window size based on desired client area size // Compute window size based on desired client area size
if (!AdjustWindowRect(&win_rect, window_style, FALSE)) { if (!AdjustWindowRect(&win_rect, window_style, FALSE)) {
@ -828,14 +902,42 @@ open_graphic_window(bool fullscreen) {
} }
if (has_origin) { if (has_origin) {
x_origin = win_rect.left; metrics.x = win_rect.left;
y_origin = win_rect.top; metrics.y = win_rect.top;
} else { } else {
x_origin = CW_USEDEFAULT; metrics.x = CW_USEDEFAULT;
y_origin = CW_USEDEFAULT; metrics.y = CW_USEDEFAULT;
} }
clientAreaWidth = win_rect.right - win_rect.left; metrics.width = win_rect.right - win_rect.left;
clientAreaHeight = win_rect.bottom - win_rect.top; metrics.height = win_rect.bottom - win_rect.top;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: WinGraphicsWindow::open_graphic_window
// Access: Private
// Description: Creates a regular or fullscreen window.
////////////////////////////////////////////////////////////////////
bool WinGraphicsWindow::
open_graphic_window(bool fullscreen) {
DWORD window_style = make_style(fullscreen);
string title;
if (_properties.has_title()) {
title = _properties.get_title();
}
if (!_properties.has_size()) {
//Just fill in a conservative default size if one isn't specified.
_properties.set_size(640, 480);
}
WINDOW_METRICS metrics;
bool has_origin;
if (!calculate_metrics(fullscreen, window_style, metrics, has_origin)){
return false;
} }
const WindowClass &wclass = register_window_class(_properties); const WindowClass &wclass = register_window_class(_properties);
@ -869,13 +971,13 @@ open_graphic_window(bool fullscreen) {
if (!_hparent) { // This can be a regular window or a fullscreen window if (!_hparent) { // This can be a regular window or a fullscreen window
_hWnd = CreateWindow(wclass._name.c_str(), title.c_str(), window_style, _hWnd = CreateWindow(wclass._name.c_str(), title.c_str(), window_style,
x_origin, y_origin, metrics.x, metrics.y,
clientAreaWidth, metrics.width,
clientAreaHeight, metrics.height,
NULL, NULL, hinstance, 0); NULL, NULL, hinstance, 0);
} else { // This is a regular window with a parent } else { // This is a regular window with a parent
x_origin = 0; int x_origin = 0;
y_origin = 0; int y_origin = 0;
if (!fullscreen && has_origin) { if (!fullscreen && has_origin) {
x_origin = _properties.get_x_origin(); x_origin = _properties.get_x_origin();
@ -885,7 +987,7 @@ open_graphic_window(bool fullscreen) {
_hWnd = CreateWindow(wclass._name.c_str(), title.c_str(), _hWnd = CreateWindow(wclass._name.c_str(), title.c_str(),
WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS , WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ,
x_origin, y_origin, x_origin, y_origin,
x_size, y_size, _properties.get_x_size(), _properties.get_y_size(),
_hparent, NULL, hinstance, 0); _hparent, NULL, hinstance, 0);
if (_hWnd) { if (_hWnd) {
@ -914,49 +1016,84 @@ open_graphic_window(bool fullscreen) {
// up the desktop during the mode change. // up the desktop during the mode change.
if (fullscreen){ if (fullscreen){
HWND hDesktopWindow = GetDesktopWindow(); if (!do_fullscreen_enable()){
HDC scrnDC = GetDC(hDesktopWindow);
DWORD cur_bitdepth = GetDeviceCaps(scrnDC, BITSPIXEL);
// DWORD drvr_ver = GetDeviceCaps(scrnDC, DRIVERVERSION);
// DWORD cur_scrnwidth = GetDeviceCaps(scrnDC, HORZRES);
// DWORD cur_scrnheight = GetDeviceCaps(scrnDC, VERTRES);
ReleaseDC(hDesktopWindow, scrnDC);
DWORD dwWidth = _properties.get_x_size();
DWORD dwHeight = _properties.get_y_size();
DWORD dwFullScreenBitDepth = cur_bitdepth;
DEVMODE dm;
reconsider_fullscreen_size(dwWidth, dwHeight, dwFullScreenBitDepth);
if (!find_acceptable_display_mode(dwWidth, dwHeight, dwFullScreenBitDepth, dm)) {
windisplay_cat.error()
<< "Videocard has no supported display resolutions at specified res ("
<< dwWidth << " x " << dwHeight << " x " << dwFullScreenBitDepth <<")\n";
return false; return false;
} }
dm.dmPelsWidth = dwWidth;
dm.dmPelsHeight = dwHeight;
dm.dmBitsPerPel = dwFullScreenBitDepth;
int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
if (chg_result != DISP_CHANGE_SUCCESSFUL) {
windisplay_cat.error()
<< "ChangeDisplaySettings failed (error code: "
<< chg_result << ") for specified res (" << dwWidth
<< " x " << dwHeight << " x " << dwFullScreenBitDepth
<< "), " << _fullscreen_display_mode.dmDisplayFrequency << "Hz\n";
return false;
}
_properties.set_origin(0, 0);
_properties.set_size(dwWidth, dwHeight);
} }
return true; return true;
} }
////////////////////////////////////////////////////////////////////
// Function: WinGraphicsWindow::do_fullscreen_enable
// Access: Private
// Description: This is a low-level function that just puts Windows
// in fullscreen mode. Not to confuse with
// do_fullscreen_switch().
////////////////////////////////////////////////////////////////////
bool WinGraphicsWindow::
do_fullscreen_enable() {
HWND hDesktopWindow = GetDesktopWindow();
HDC scrnDC = GetDC(hDesktopWindow);
DWORD cur_bitdepth = GetDeviceCaps(scrnDC, BITSPIXEL);
// DWORD drvr_ver = GetDeviceCaps(scrnDC, DRIVERVERSION);
// DWORD cur_scrnwidth = GetDeviceCaps(scrnDC, HORZRES);
// DWORD cur_scrnheight = GetDeviceCaps(scrnDC, VERTRES);
ReleaseDC(hDesktopWindow, scrnDC);
DWORD dwWidth = _properties.get_x_size();
DWORD dwHeight = _properties.get_y_size();
DWORD dwFullScreenBitDepth = cur_bitdepth;
DEVMODE dm;
reconsider_fullscreen_size(dwWidth, dwHeight, dwFullScreenBitDepth);
if (!find_acceptable_display_mode(dwWidth, dwHeight, dwFullScreenBitDepth, dm)) {
windisplay_cat.error()
<< "Videocard has no supported display resolutions at specified res ("
<< dwWidth << " x " << dwHeight << " x " << dwFullScreenBitDepth <<")\n";
return false;
}
dm.dmPelsWidth = dwWidth;
dm.dmPelsHeight = dwHeight;
dm.dmBitsPerPel = dwFullScreenBitDepth;
int chg_result = ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
if (chg_result != DISP_CHANGE_SUCCESSFUL) {
windisplay_cat.error()
<< "ChangeDisplaySettings failed (error code: "
<< chg_result << ") for specified res (" << dwWidth
<< " x " << dwHeight << " x " << dwFullScreenBitDepth
<< "), " << _fullscreen_display_mode.dmDisplayFrequency << "Hz\n";
return false;
}
_properties.set_origin(0, 0);
_properties.set_size(dwWidth, dwHeight);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: WinGraphicsWindow::do_fullscreen_disable
// Access: Private
// Description: This is a low-level function that just gets Windows
// out of fullscreen mode. Not to confuse with
// do_windowed_switch().
////////////////////////////////////////////////////////////////////
bool WinGraphicsWindow::
do_fullscreen_disable() {
int chg_result = ChangeDisplaySettings(NULL, 0x0);
if (chg_result != DISP_CHANGE_SUCCESSFUL) {
windisplay_cat.warning()
<< "ChangeDisplaySettings failed to restore Windowed mode\n";
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: WinGraphicsWindow::adjust_z_order // Function: WinGraphicsWindow::adjust_z_order
// Access: Private // Access: Private
@ -1241,7 +1378,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
// losing the graphics context. // losing the graphics context.
ShowWindow(_hWnd, SW_MINIMIZE); ShowWindow(_hWnd, SW_MINIMIZE);
GdiFlush(); GdiFlush();
ChangeDisplaySettings(NULL, 0x0); do_fullscreen_disable();
fullscreen_minimized(properties); fullscreen_minimized(properties);
} }
} }
@ -2165,7 +2302,7 @@ find_acceptable_display_mode(DWORD dwWidth, DWORD dwHeight, DWORD bpp,
if ((dm.dmPelsWidth == dwWidth) && (dm.dmPelsHeight == dwHeight) && if ((dm.dmPelsWidth == dwWidth) && (dm.dmPelsHeight == dwHeight) &&
(dm.dmBitsPerPel == bpp)) { (dm.dmBitsPerPel == bpp)) {
cout << "[FS FOUND] " << dwWidth << "x" << dwHeight << "@" << bpp << endl; // cout << "[FS FOUND] " << dwWidth << "x" << dwHeight << "@" << bpp << endl;
// We want to modify the current DEVMODE rather than using a fresh one in order // We want to modify the current DEVMODE rather than using a fresh one in order
// to work around a Windows 7 bug. // to work around a Windows 7 bug.
ZeroMemory(&dm, sizeof(dm)); ZeroMemory(&dm, sizeof(dm));

View File

@ -28,7 +28,13 @@ class WinGraphicsPipe;
#define PM_INACTIVE (WM_APP+124) #define PM_INACTIVE (WM_APP+124)
typedef struct {
int x;
int y;
int width;
int height;
}
WINDOW_METRICS;
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Class : WinGraphicsWindow // Class : WinGraphicsWindow
@ -83,6 +89,16 @@ protected:
virtual void handle_reshape(); virtual void handle_reshape();
virtual bool do_fullscreen_resize(int x_size, int y_size); virtual bool do_fullscreen_resize(int x_size, int y_size);
virtual bool do_fullscreen_switch();
virtual bool do_windowed_switch();
virtual bool do_fullscreen_enable();
virtual bool do_fullscreen_disable();
virtual bool calculate_metrics(bool fullscreen, DWORD style,
WINDOW_METRICS &metrics, bool &has_origin);
virtual DWORD make_style(bool fullscreen);
virtual void reconsider_fullscreen_size(DWORD &x_size, DWORD &y_size, virtual void reconsider_fullscreen_size(DWORD &x_size, DWORD &y_size,
DWORD &bitdepth); DWORD &bitdepth);