问题
Short story
A game is started fullscreen but is a fullscreen directX window. The game supports windowed but I have to do it manually. I made a program in Delphi that stretch/resize any window to fit the screen (with use of a system-wide hotkey) without border and caption so it looks like a fullscreen but doesn't trigger direct hardware access. This is important because I'm using a DisplayLink adapter that doesn't support the tricks used by direct hardware access but want to play it full screen without ugly borders. I can resize any window except a fullscreen DirectX window, I have to manually change the full screen mode to windowed, this is what I want to automate.
Long story (scroll down if you want to know the question)
I have a DisplayLink adapter with TV-screen 5 Meters away from my computer. I want to use it to run games so I can play games from the couch. The DisplayLink driver however cannot play most games full screen because most games bypass the Desktop Window Manager (DWM) to access the graphics hardware directly when full screen.
This is a common/known problem with DisplayLink. In windowed mode the adapter performs very well so I thought to write a little program in Delphi to maximize a windowed directx screen to full screen without going full screen by maximizing it, stretching the window to full screen instead.
The program I made works pretty well however only when the directx screen is already windowed (the game starts full screen so I have to click once the windowed icon to make it windowed). The game already has an option to make it windowed at launch but with less fixed resolution possibilities. I want to automate this process when launched full screen. I want to change the directx screen to windowed and after this resize/stretch it to full screen without maximizing it.
How the program works
The program defines a system wide keyboard hotkey. When pressing the hotkey the program maximize any active foreground window (windows API getForeGroundWindow()
) by stretching it to full screen and makes it a borderless window so it looks like full screen. This enable you also to run the game on any screen you like and not only on the main screen of your system. When pressing the hotkey again, the window returns to it's previous state (toggle). Before applying the 'patch', it also checks the type of window, so it cannot be used on non-resizable windows.
Question
I know the handle of the window that must be stretched/resized to fullscreen. When it is a fullscreen DirectX window I can't do anything with unless it is windowed. How can I change it's state to windowed by sending messages to this window handle (sendMessage()
). Is this possible anyway?
Some code (to give you some idea what's happening behind the scenes)
function TWinSpread.setWindowStyleBounds( h : hWnd; lStyle : LongInt = 0; pR : PRect = nil ) : LongInt;
var
bRestore : Boolean;
r : TRect;
pMouse : TPoint;
rStyle : TStyleStruct;
begin
Result:=0;
if( h <= 0 ) then
Exit;
bRestore:=( lStyle > 0 );
if( NOT bRestore ) then
begin
lStyle:=getWindowLong( h, GWL_STYLE );
Result:=lStyle;
lStyle:=lStyle and not WS_BORDER and not WS_SIZEBOX and not WS_DLGFRAME;
if( Assigned( pR )) then
begin
r:=pR^;
end
else begin
getWindowRect( h, r );
r:=getDisplayRect( getDisplayNumFromPoint( Point( r.left+2, r.top+2 ) ) );
end;
end
else begin
Result:=lStyle;
end;
rStyle.styleOld:=Result;
rStyle.styleNew:=lStyle;
if( Result = lStyle ) then
begin
rStyle.styleOld:=getWindowLong( h, GWL_STYLE );
end;
sendMessage( h, WM_ENTERSIZEMOVE, 0, 0 );
__restoreWindow( h );
setWindowLong( h, GWL_STYLE, lStyle );
if( NOT bRestore ) then
begin
setWindowPos( h, HWND_TOP, r.left,r.top,r.right-r.left,r.bottom-r.top, SWP_FRAMECHANGED and WS_SIZEBOX );
moveWindow( h, r.left,r.top,r.right-r.left,r.bottom-r.top, TRUE );
sendMessage( h, WM_SIZE, SIZE_RESTORED, makeLong( r.right-r.left,r.bottom-r.top ));
end
else begin
// updateWindowFrame( h );
setWindowPos( h, 0, 0,0,0,0, SWP_FRAMECHANGED or SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER or SWP_NOOWNERZORDER );
getWindowRect( h, r );
end;
sendMessage( h, WM_EXITSIZEMOVE, 0, 0 );
sendMessage( h, WM_STYLECHANGED, GWL_STYLE, longInt( Pointer( @rStyle )));
activateWindow( h );
windows.setFocus( h );
if( __mousePresent() ) then
begin
pMouse:=__getMousePos();
// Simulate click to focus window
__setMousePos( Point( r.left+2, r.top+2 ));
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
// restore mouse
__setMousePos( pMouse );
end;
end;
procedure TWinSpread.shHotKeyHotKey(Sender: TObject; Index: Integer);
var
h : hWnd;
sHandle : string;
lStyle : LongInt;
bNoBorder : Boolean;
begin
h:=getForeGroundWindow();
if( h <= 0 ) then
exit;
if( h = handle ) then
begin
showMessage( 'It works!' );
exit;
end;
sHandle:=ItoA( h );
if( scWinStyles.Count > 0 ) then
begin
lStyle:=AtoI( scWinStyles.list.Values[sHandle] );
if( lStyle > 0 ) then
begin
scWinStyles.list.delete( scWinStyles.list.IndexOfName(sHandle));
setWindowStyleBounds( h, lStyle );
Exit;
end;
end;
bNoBorder:=windowIsBorderless( h );
if( isMaximized( h ) or bNoBorder ) then
begin
// does not work for ActiveX fullscreen window :-(
//if( bNoBorder ) then
// setWindowSizeable( h, TRUE );
__maximizeWindow( h );
__restoreWindow( h );
showWindow( h, SW_SHOWNORMAL );
end;
if( windowIsSizeable( h ) ) then
begin
lStyle:=setWindowStyleBounds( h );
scWinStyles.list.Values[sHandle]:=ItoA( lStyle );
end
else Windows.Beep( 600, 200 );
end;
Some screenshots
Notice: In this picture there is a typo, ActiveX must be DirectX ;-)
More info about the DisplayLink problem: http://support.displaylink.com/knowledgebase/articles/543922-games-do-not-work-on-windows-with-displaylink-soft
来源:https://stackoverflow.com/questions/32062929/directx-full-screen-window-to-windowed-by-window-handle-with-use-windows-api-fun