Utilizo el ChangeDisplaySettings() para pasar a fullscreen y salir de él, pero una vez de cada muchas me encuentro que al regresar al escritorio los iconos han cambiado de lugar. He buscado por san Google, y parece que es algo más o menos común, pero curiosamente no encuentro por ningún lugar la forma correcta de llamar a ChangeDisplaySettings() para que no haga tal fechoría.
Puede alguien decirme por qué ocurre y como evitarlo?!?!?
Para entrar en fullscreen hago esto:
// Pone la ventana a pantalla completa segun GDI
bool Window::SetFullScreenGDI(int x,int y,ColorMode cm,int hz){ // GDI
bool creada;
DEVMODE ModoPantalla;
// Si no esta creada, la crea
if(!GetProperty(CREATE)){
Create(true);
creada=true;
} else creada=false;
// Pone pantalla completa
ModoPantalla.dmSize = sizeof(DEVMODE); // Tamaño de la estructura
ModoPantalla.dmBitsPerPel = (cm==COLOR8INDEX)?8:(cm==COLOR16RGB)?16:(cm==COLOR16XRGB)?16:(cm==COLOR24RGB)?24:(cm==COLOR32XRGB)?32:(cm==COLOR32ARGB)?32:0;// Profundidad de color
ModoPantalla.dmPelsWidth = x; // Ancho
ModoPantalla.dmPelsHeight = y; // Alto
ModoPantalla.dmDisplayFrequency = hz; // Refresco
ModoPantalla.dmFields = DM_PELSWIDTH | // VALORES A USAR DE LA ESTRUCTURA
DM_PELSHEIGHT |
DM_BITSPERPEL |
DM_DISPLAYFREQUENCY;
// Pone pantalla completa
if(ChangeDisplaySettings(&ModoPantalla,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL){
// No pudo poner pantalla completa
if(creada) // Se creo en la funcion, la destruye
Destroy();
logMsg("( ~ %d ) ( %s ) Window::SetFullScreen(int x,int y,ColorMode cm,int hz) : fallo el ChangeDisplaySettings() con %dx%dx%s %dHz.",
__LINE__,__FILE__,x,y, (cm==COLOR8INDEX)?"8":(cm==COLOR16RGB)?"16RGB":(cm==COLOR16XRGB)?"16XRGB":(cm==COLOR24RGB)?"24":(cm==COLOR32XRGB)?"32XRGB":(cm==COLOR32XRGB)?"32ARGB":"?(solo son validos COLOR8INDEX,COLOR16RGB,COLOR24RGB,COLOR32XRGB)", hz);
return false;
}
// Guarda valor de Stickykeys
m_skOriginal.cbSize= sizeof (STICKYKEYS);
m_skOriginal.dwFlags = 0;
SystemParametersInfo(SPI_GETSTICKYKEYS,sizeof (STICKYKEYS),&m_skOriginal,0);
// Desactiva Stickykeys
STICKYKEYS stickyOff = m_skOriginal;
stickyOff.dwFlags &=SKF_STICKYKEYSON;
SystemParametersInfo(SPI_SETSTICKYKEYS,sizeof (STICKYKEYS),&stickyOff,0);
// Fuerza estilo WS_POPUP, necesario para ventana a pantalla completa
m_bFullScreen=true;
SetWindowLong(m_hWnd,GWL_STYLE,WS_POPUP);
m_nXFullScreen=x;
m_nYFullScreen=y;
m_cmFullScreen=cm;
m_nHZ=hz;
SetWindowPos(m_hWnd,HWND_TOPMOST/*HWND_TOP*/,0,0,x,y,SWP_SHOWWINDOW); // Se asegura que sea la visible
return true;
}
He puesto también el codigo del Stikykeys y demás por si tiene algo que ver... todo y que lo dudo.
Y para salir de fullscreen hago esto otro:
// Sale de pantalla completa segun GDI
void Window::FullScreenExitGDI(){
if(!m_bFullScreen) return;
// sale de fullscreen
ChangeDisplaySettings(NULL,0);
// Desactiva flag m_bFullScreen(para que funcionen SetSize() y SetPosition())
m_bFullScreen=false;
// Restaura la ventana normalmente
if(GetProperty(CREATE)){
// Restaura estilo,posicion y tamaño de ventana
SetWindowLong(m_hWnd,GWL_STYLE,GetStyleWindow(m_wtWindowType));
SetWindowPos(m_hWnd,HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW); // Se asegura que sea la visible
SetPosition(m_uX,m_uY);
SetSize(m_uWidth,m_uHeight);
}
// Restaura valor de stickykeys
SystemParametersInfo(SPI_SETSTICKYKEYS,sizeof (STICKYKEYS),&m_skOriginal,0);
}
Gracias.
Si no se me olvida esta noche o mañana te miro un codigo que tengo en VB6 donde hago el cambio a pantalla completa con GDI repetando la configuracion del escritorio. Asi de memoria creo que habia que agregar un par de flags a la funcion para evitar el problema o quizas usar otra funcion del GDI. Aun asi, podrias usar DirectDraw para aplicar unicamnete el modo de video a pantalla completa y evitar este fallo. Aun estando en sintaxis VB, echa un ojo al codigo de este ejemplo (http://www.stratos-ad.com/forums/index.php?topic=11701.msg125848#msg125848) a ver si te sirviera. De todas formas cuando pueda te subo el codigo via GDI (tambien en sintaxis VB6, sorry ^_^')
Salu2...
La verdad es que no estoy seguro, pero fijate estas son las propiedades de la ventana que yo uso:
if( Get_FullScreen() )
{
dwExStyle = WS_EX_APPWINDOW;
dwStyle = WS_POPUP | WS_VISIBLE| WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
}
else
{
dwExStyle = WS_EX_CLIENTEDGE;
dwStyle = WS_MINIMIZEBOX | WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_VISIBLE| [b]WS_CLIPSIBLINGS | WS_CLIPCHILDREN[/b];
}
Ademas yo pongo las propiedades de la ventana antes de cambiar a modo fullscreen cosa que tape todo, no se si eso influira.
Seguramente nada de esto solucionara tu problema :(
Suerte :P
Por lo poco que se es cosa de ChangeDisplaySettings(), no de los flags de la ventana, pero no encuentro la forma correcta de llamarla. Es más, en mucho codigo que se puede encontrar por internet se llama exactamente como yo hago, lo que me recuerda que más de uno y de dos programillas freeware o incluso share te hace la "broma" de trastocarte el escritorio.
Respecto a hacerlo con las DirectX... bueno, también trabaja a nivel de Dx sin tal problema, pero lo tengo hecho de tal manera que pueda funcionar sin Dx usando rutinas por soft y GDI, y claro, ahí no puedo usar SetDisplayMode() sino las funciones de la API de Windows.
Supongo que el código de [EX3] podra ayudar, que esté en VB6 no debería ser problema mientra se vea la llamada y demás.
Gracias a los dos por vuestro tiempo.
Wenas. Aqui traigo el codigo que mencionaba, este tambien usa ChangeDisplaySettings():
Option Explicit
Const WM_DISPLAYCHANGE = &H7E
Const HWND_BROADCAST = &HFFFF&
Const CCDEVICENAME = 32
Const CCFORMNAME = 32
Const DM_BITSPERPEL = &H40000
Const DM_PELSWIDTH = &H80000
Const DM_PELSHEIGHT = &H100000
Const CDS_TEST = &H4
Const CDS_FULLSCREEN As Long = &H4
Const DISP_CHANGE_SUCCESSFUL = 0
Const DISP_CHANGE_RESTART = 1
Const BITSPIXEL = 12
Private Type DEVMODE
dmDeviceName As String * CCDEVICENAME
dmSpecVersion As Integer
dmDriverVersion As Integer
dmSize As Integer
dmDriverExtra As Integer
dmFields As Long
dmOrientation As Integer
dmPaperSize As Integer
dmPaperLength As Integer
dmPaperWidth As Integer
dmScale As Integer
dmCopies As Integer
dmDefaultSource As Integer
dmPrintQuality As Integer
dmColor As Integer
dmDuplex As Integer
dmYResolution As Integer
dmTTOption As Integer
dmCollate As Integer
dmFormName As String * CCFORMNAME
dmUnusedPadding As Integer
dmBitsPerPel As Integer
dmPelsWidth As Long
dmPelsHeight As Long
dmDisplayFlags As Long
dmDisplayFrequency As Long
End Type
Private Declare Function EnumDisplaySettings Lib "user32" Alias "EnumDisplaySettingsA" (ByVal lpszDeviceName As Long, ByVal iModeNum As Long, lpDevMode As Any) As Boolean
Private Declare Function ChangeDisplaySettings Lib "user32" Alias "ChangeDisplaySettingsA" (lpDevMode As Any, ByVal dwFlags As Long) As Long
Private Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As Long, ByVal nIndex As Long) As Long
Private Declare Function CreateDC Lib "gdi32" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As Any) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Dim OldX As Long, OldY As Long, nDC As Long
Dim Bpp As Long
Dim IsRestore As Boolean
Public Width As Long, Height As Long
Public Function ChangeRes(X As Long, Y As Long) As Boolean
Dim DevM As DEVMODE, ScInfo As Long, erg As Long, an As VbMsgBoxResult
'Get the info into DevM
erg = EnumDisplaySettings(0&, 0&, DevM)
'This is what we're going to change
DevM.dmFields = DM_PELSWIDTH Or DM_PELSHEIGHT Or DM_BITSPERPEL
DevM.dmPelsWidth = X 'ScreenWidth
DevM.dmPelsHeight = Y 'ScreenHeight
DevM.dmBitsPerPel = Bpp 'Bits '(can be 8, 16, 24, 32 or even 4)
'Now change the display and check if possible
erg = ChangeDisplaySettings(DevM, CDS_TEST)
'Check if succesfull
Select Case erg&
Case DISP_CHANGE_SUCCESSFUL
erg = ChangeDisplaySettings(DevM, CDS_FULLSCREEN)
ScInfo = Y * 2 ^ 16 + X
'Notify all the windows of the screen resolution change
SendMessage HWND_BROADCAST, WM_DISPLAYCHANGE, ByVal Bpp, ByVal ScInfo
' Everything's ok:
ChangeRes = True
Case Else
' Mode not supported:
ChangeRes = False
End Select
End Function
Private Sub Class_Initialize()
'KPD-Team 1999
'URL: http://www.allapi.net/
'E-Mail: KPDTeam@Allapi.net
Dim nDC As Long
'retrieve the screen's resolution
OldX = Screen.Width / Screen.TwipsPerPixelX
OldY = Screen.Height / Screen.TwipsPerPixelY
'Create a device context, compatible with the screen
nDC = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0&)
Bpp = GetDeviceCaps(nDC, BITSPIXEL)
End Sub
Private Sub class_Terminate()
If Not IsRestore Then Restore
End Sub
Public Sub Restore()
'restore the screen resolution
ChangeRes OldX, OldY ', GetDeviceCaps(nDC, BITSPIXEL)
'delete our device context
DeleteDC nDC
IsRestore = True
End Sub
No soy experto en la materia pero me imagino que el truco estara en la parte en que llama a SendMessage para notificar a Windows del cambio de resolucion de pantalla. Una cosa. Acabo de probar el codigo para ver que exactamente respeta la configuracion de iconos del escritorio, cosa que cumple, pero el programa se me queda colgado. No se si es cosa de Visual Basic o quizas de alguna llamada mal hecha en el codigo.
Cita de: _Grey en 06 de Febrero de 2009, 10:04:15 PM
Respecto a hacerlo con las DirectX... bueno, también trabaja a nivel de Dx sin tal problema, pero lo tengo hecho de tal manera que pueda funcionar sin Dx usando rutinas por soft y GDI, y claro, ahí no puedo usar SetDisplayMode() sino las funciones de la API de Windows.
Hombre, el codigo que te decia por DDraw solo aplica el modo de video, pero no te impide renderizar con Direct3D, OpenGL o GDI por lo que podria ser igualmente valido para la parte por soft que quieres implementar.
Ya me cuentas si te funciono :)
Salu2...
Me cuesta creer, pero por más que fuerzo el programa con cambios de fullscreen a ventana y viciversa ¡me respeta los iconos!.
El problema me estaba minando en cantidad, sólo encontraba gente desahogándose contra la documentación de Microsoft, o comformándose con que era problema de la API de Win. En la documentación advierten del problema, invitándote a llamar a la función desde otro Thread sin HWND asociado a él, pero después de hacerlo seguía ocurriendo. Hasta ahora!
Tus sospechas parecen ser ciertas, ha bastado con el SendMessage para despistar a Windows y que no hiciera de las suyas.
Gracias mil !!.
De nada. Me alegro de que te sirviera ^_^
Salu2...