PDA

View Full Version : DirectX - How can I disable Alt+Tab / Crashes



Gadget
11-05-2003, 11:26 AM
Just wondered if anyone knew of a quick easy way to handle my DirectX app loosing fullscreen status? It crashes when it minimizes, ideally I would like to stop Alt+Tab, and make it fullscreen until closed. Can you stop other applications from popping up and minimizing it?

TheLion
11-05-2003, 11:36 AM
In theory... Yes, I have seen applications do it, however in most games I have seen it done it made the window lose the focus and therefor the mouse and keys.

Not sure if it can be handled in a good way. Knowing a bit of the windows API there's probably a way the upperhand for your window. Stay On Top could be a step in the right direction, but it will only solve 1% of what you want accomplished ! :)

Kim Friis
12-05-2003, 08:59 AM
You can do it, BUT the way I know, only works for Windows 9x/ME so it is not much of an option anymore?

Gadget
12-05-2003, 10:37 AM
OK, so I can't easily disable alt+tab, but can I detect that the app has lost focus? Checking if WindowState = wsMinimized doesn't work...

Kim Friis
12-05-2003, 02:26 PM
I surgestion could be that you ask Windows what the topmost windows is, and campare that handle with the handle of your application, or your mainwindow? I cannot remember the exact procedure calls, but I think it is something with: GetTopMostWindow or something like that?

Summers Gone
12-05-2003, 08:23 PM
Out of my head this would be done in VCL with something like

function ActivateApp(lparam, wparam : Integer) : Integer message WM_ACTIVATEAPP;

Alimonster
12-05-2003, 08:28 PM
You don't want to be disabling alt-tab, because it annoys users unnecessarily (like my brother, who's always alt-tabbing out to do other stuff in the middle of his games). The first question: are you using D3D or DirectDraw, because the way to restore stuff is different in each (and I can't say that I've used D3D, so someone else [e.g. Jarrod] will have to fill in the blanks).

In DirectDraw, all surfaces are messed up once you alt-tab and you have to regenerate them. This is easy enough. In addition, any surface that's sitting in video memory must have its picture reloaded because it'll be blanked out.

The trick here is to catch the WM_ACTIVATEAPP message. Windows passed this when your app is activated/deactivated (note that a similar message, WM_ACTIVATE, exists, but you don't deal with that - it handles individual windows in your app getting/losing focus, rather than the main app itself). So... you maintain whether the app has focus. Don't do too much that's hard work if deactivated (no CPU hogging!), minimal drawing if required (maybe, maybe not).

You could handle the WM_ACTIVATEAPP message in a couple of ways -- writing a specific message handler function for it or by catching Application.OnMessage (be careful to not do too much here, since that'll be called for any message that passes through your system).

I won't detail the above since I don't know what system you're using. Instead, I'll go over the general process that I've used and that seems to work (no idea if it doesn't deal with special cases, but never mind.).

Here's the message handling in my WndProc for WM_ACTIVATEAPP:

[background=#FFFFFF][comment=#0000FF][normal=#000000][number=#C00000][reserved=#000000][string=#00C000] WM_ACTIVATEAPP:
begin
bActive := LongBool(wParam);
Result := 0;

// we have to handle ALT-Tabbing from the user or other ways to lose
// the focus. DirectDraw gets rid of our surfaces when this happens
// so we have to restore them and then reload anything on the bitmaps
// themselves
//
// todo: double-check this works!
if bActive and (DD7 <> nil) then
begin
if g_FullScreen then
begin
DD7.SetCooperativeLevel(Globals.g_Handle, DDSCL_EXCLUSIVE or
DDSCL_FULLSCREEN or
DDSCL_ALLOWREBOOT);
end;

while DD7.TestCooperativeLevel <> DD_OK do
begin
Sleep(0);
end;

ReloadAllImages;
end;

//etc
DD7 is my IDirectDraw7 interface, bActive is a boolean that refers to whether the app's running (side note: I really should eliminate that hungarian notation, sigh), and Globals.g_Handle is the window handle for the window.

First, I take a note of whether the app's activation status -- it's passed as a boolean in wParam. A typecast is required to convert it into a boolean value, since it's passed as something of type wParam. Now we know whether the app is doing stuff.

If we're active then it means we've been switched back from an inactive state -- in this case, we have to restore everything!

It's often the case that we'll lose exclusive mode when running fullscreen once the user alt-tabs. Therefore, the next step is to set the cooperative level back to the wanted value (exclusive, fullscreen). This takes some time, unfortunately, so we have to sit around and wait to see whether it works. I added a sleep(0) in there while we're waiting. We can use the TestCooperativeLevel method of IDirectDraw7, which returns whether the exclusive mode is set yet. There may be a better method to waste time than Sleep(0); investigate this.

Once we've done that, we have to restore the surfaces. This is done in my function "ReloadAllImages." First, we can use IDirectDraw7.RestoreAllSurfaces, which does the hard part for us. The only task left here is reload images for surfaces that are in video memory (system memory ones won't be wiped out).

The above is how I do it and it seems to work fine. Any corrections are much appreciated. Of course, it's based on my non-component-based stuff rather than a specific set. Making this relevant to your app is left as an exercise (to be done in public so we all learn from it, of course :)).

Now, I haven't used D3D yet, so I can't answer that case. I'll let one of the other dudes answer it -- I think there's an equivalent TestCooperativeLevel method somewhere and I remember something about D3DPOOL_MANAGED (or something similar) textures being reloaded by the system. I also remember something about a reset method that must be called for something. This is all vague, but hopefully it's not wrong and will give someone an idea of where to begin.

Remember that you'll have to re-acquire DirectInput things. This should not be a problem if you check for the appropriate "not acquired" return values for the important functions.

Clootie
12-05-2003, 11:02 PM
1) First point - you should not prevent users from "Alt-Tab"-bing
2) Second isssue - WinNT based OS'es will not let you prevent user from AltTab

Now how to correcly handle Alt-Tabbing, Printer Box pop-up's, Fast User switching, Suspendig, etc...

Yes, WM_ACTIVATEAPP in most cased will do the trick, but to ensure what your suggestion is correct you should check result of Present/Blit/Flip function for "device lost" situation.

Code snippet below is from D3D9 render procedure code:
[background=#FFFFFF][normal=#000000][number=#0000FF][string=#0000FF][comment=#248F24][reserved=#000000] function Render3DEnvironment: HRESULT;
var
...
begin
if fDeviceLost then
begin
// Test the cooperative level to see if it's okay to render
Result := D3DDevice.TestCooperativeLevel;
if FAILED(Result) then
begin
// If the device was lost, do not render until we get it back
if (D3DERR_DEVICELOST = Result) then
begin
Result := S_OK;
Exit;
end;

// Check if the device needs to be reset.
if (D3DERR_DEVICENOTRESET = Result) then
begin
// If we are windowed, read the desktop mode and use the same format for
// the back buffer
if fWindowed then
begin
// Setup back buffer format
...
end;

Result:= Reset3DEnvironment; // inside this function we call to D3DDevice.Reset
if FAILED(Result) then Exit;
end;
Exit;
end;
fDeviceLost := False;
end;
...
// Render the scene as normal
Result := Render;
if FAILED(Result) then Exit;
...
// Show the frame on the primary surface.
Result:= m_pd3dDevice.Present(nil, nil, 0, nil);
if (D3DERR_DEVICELOST = Result) then fbDeviceLost := True;

Result := S_OK;
end;
Code from D3DFramework: http://clootie.narod.ru/delphi/download_dx90.html#D3Dex_Common

Gadget
14-05-2003, 01:40 PM
Thanks for that, it answers my question :) I am using DirectDraw, I will go away and make the changes and let you know how I get on...

:D

Gadget
18-05-2003, 11:25 AM
Thanks a lot for the help, it works (sort of...)

For the moment I have set it to close the application if they alt+tab, this isn't how I want to leave it, but it's stopped the crashing for the moment.

I seem to have a problem in the logic of my app. My main loop is in the AppOnIdle event, and I think that it may be causing the SetCoop to fail, or hang for a very long time when going back into the application. Spent a few hours trying to work out why it wouldn't go back into fullscreen. I suspect its that main loop that needs to be halted.

Another related question:-

What should I be using WndProc for other than this? Anything else that you would recommend using the message handler for?

Jimmy Valavanis
11-06-2004, 02:17 PM
Try this to enable or diable Alt + Tab nad solve screensaver problems:


...
var s_alttab_disabled: boolean;

procedure WIN_DisableAltTab;
var
old: Boolean;
begin
if s_alttab_disabled then
Exit;

if Win32Platform = VER_PLATFORM_WIN32_NT then
RegisterHotKey(0, 0, MOD_ALT, VK_TAB)
else
SystemParametersInfo(SPI_SCREENSAVERRUNNING, 1, @old, 0);

s_alttab_disabled := True;
end;

procedure WIN_EnableAltTab;
var
old: Boolean;
begin
if s_alttab_disabled then
begin
if Win32Platform = VER_PLATFORM_WIN32_NT then
UnregisterHotKey(0, 0)
else
SystemParametersInfo(SPI_SCREENSAVERRUNNING, 0, @old, 0);

s_alttab_disabled := False;
end;
end;

....

initialization

s_alttab_disabled := false;

end.