Полноэкранное окно DirectX для оконного дескриптора с использованием функций Windows API

Короткий рассказ

Игра запускается в полноэкранном режиме, но является полноэкранным окном DirectX. Игра поддерживает оконный режим, но я должен сделать это вручную. Я создал программу на Delphi, которая растягивает / изменяет размеры любого окна, чтобы соответствовать экрану (с использованием общесистемной горячей клавиши) без рамки и заголовка, чтобы она выглядела как полноэкранная, но не запускала прямой аппаратный доступ. Это важно, потому что я использую адаптер DisplayLink, который не поддерживает приемы, используемые прямым доступом к оборудованию, но хочет воспроизвести его на весь экран без безобразных границ. Я могу изменить размер любого окна, кроме полноэкранного окна DirectX, я должен вручную изменить полноэкранный режим на оконный, это то, что я хочу автоматизировать.

Длинная история (прокрутите вниз, если хотите узнать вопрос)

У меня есть адаптер DisplayLink с экраном в 5 метрах от компьютера. Я хочу использовать его для запуска игр, чтобы я мог играть в игры с дивана. Однако драйвер DisplayLink не может воспроизводить большинство игр в полноэкранном режиме, поскольку большинство игр не используют Диспетчер окон рабочего стола (DWM) для прямого доступа к графическому оборудованию в полноэкранном режиме.

Это распространенная / известная проблема с DisplayLink. В оконном режиме адаптер работает очень хорошо, поэтому я подумал написать небольшую программу на Delphi, чтобы максимизировать оконный экран DirectX до полного экрана, не переходя в полноэкранный режим путем его максимизации, вместо этого растягивая окно до полного экрана.

Созданная мною программа работает довольно хорошо, но только тогда, когда на экране DirectX уже есть окно (игра запускается в полноэкранном режиме, поэтому мне нужно один раз щелкнуть значок окна, чтобы сделать его оконным). В игре уже есть возможность сделать ее оконной при запуске, но с меньшими возможностями фиксированного разрешения. Я хочу автоматизировать этот процесс при запуске в полноэкранном режиме. Я хочу изменить экран DirectX на оконный, и после этого изменить его размер / растянуть на весь экран, не увеличивая его.

Как работает программа

Программа определяет общесистемную горячую клавишу клавиатуры. При нажатии горячей клавиши программа максимизирует любое активное окно переднего плана (Windows API getForeGroundWindow()), растягивая его на весь экран и делая его окном без границ, чтобы оно выглядело как полный экран. Это позволяет вам также запускать игру на любом экране, который вам нравится, а не только на главном экране вашей системы. При повторном нажатии горячей клавиши окно возвращается в предыдущее состояние (переключение). Перед применением "патча" он также проверяет тип окна, поэтому его нельзя использовать в окнах без изменения размера.

Вопрос

Я знаю дескриптор окна, которое должно быть растянуто / изменено до полного экрана. Когда это полноэкранное окно DirectX, я ничего не могу сделать, если оно не оконное. Как я могу изменить его состояние на оконное, отправив сообщения на этот дескриптор окна (sendMessage()). Это возможно в любом случае?

Некоторый код (чтобы дать вам некоторое представление о том, что происходит за кулисами)

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;

Некоторые скриншоты

Некоторые скриншоты

Обратите внимание: на этом рисунке изображена опечатка, ActiveX должен быть DirectX;-)

Дополнительная информация о проблеме DisplayLink: http://support.displaylink.com/knowledgebase/articles/543922-games-do-not-work-on-windows-with-displaylink-soft

0 ответов

Другие вопросы по тегам