Sign in to follow this  
martinboehme

Displaying popup menus in full screen mode

Recommended Posts

Hello everyone,just wanted to share a discovery I've made. I'm currently working on a C gauge where I want to display a context menu when the user right-clicks on the gauge. The API call to display this kind of menu is TrackPopupMenu(), and in windowed mode, this works just fine. In full screen mode, however, the menu only appears half of the time; the other half of the time, the menu seems to be "active" but is not displayed.After some searching on the Internet, I found that the reason for this is that Flight Simulator is constantly switching between two graphics buffers when it does its rendering, whereas GDI always draws to the same buffer. So if I get lucky, the GDI buffer is active when I open the menu, and it gets displayed; if I'm unlucky, the other buffer is active, and I don't get to see the menu.There are a couple of API calls in DirectX to make the GDI buffer active before trying to display a dialog or menu -- FlipToGDISurface in DirectDraw and SetDialogBoxMode in Direct3D. However, to be able to call these routines, I would need to have access to FS's Direct3D interface, which I don't.However, I've come up with a hack that does pretty much the same thing. Figuring that FS switches buffers every frame, I decided that I would just count the number of frames from the point when FS initializes its rendering surfaces. I should then display the menu only on even frames, when the right graphics buffer is active. To paraphrase the code:

case PANEL_SERVICE_PRE_INSTALL:	frameCounter=0;	break;case PANEL_SERVICE_PRE_DRAW:	if(wantToShowMenu && (frameCounter%2)==0)	{		wantToShowMenu=false;		ShowTheMenu();	}	frameCounter++;	break;

And then I set wantToShowMenu to true in the mouse handler when the user clicks the button. PANEL_SERVICE_PRE_INSTALL seems to be the right place to reset the frame counter because it gets called when switching between full screen and windowed mode and when the window gets resized.Admittedly, this is a monstrous hack, but it seems to work, and I can't think of any other way to do the same thing. I haven't tested to see if it's graphics-driver dependent, but I'm hoping that it's not.I believe a similar technique could maybe be used to draw overlaid windows in FS, similar to what FSNavigator and FSInn do, though I haven't tested this. The idea would be to respond to a WM_PAINT by validating the client area (without doing any actual painting) and setting a flag that would cause the actual painting to be done in PANEL_SERVICE_PRE_DRAW.Hope this helps some of you who may be grappling with a similar problem...Cheers,Martin

Share this post


Link to post
Share on other sites
Help AVSIM continue to serve you!
Please donate today!

Update: My previous post was a bit premature... Turns out the technique is not very robust. For example, if the user switches to a different view, the frame counter will stop counting... menu selections etc. can also cause the technique to go wrong.However, I've since discovered a different technique that doesn't seem to suffer from these problems. The idea is to change a single pixel on the screen, then on the next PANEL_SERVICE_PRE_DRAW call, check to see if that pixel has been overwritten by Flight Simulator. If so, the front buffer is currently being displayed on the screen, and we can open the menu:

case PANEL_SERVICE_PRE_DRAW:	if(wantToShowMenu)	{		if(!haveSetPixel)		{			// Change a pixel on the screen			hwndFS=FindWindowEx(NULL, NULL, L"FS98MAIN", NULL);			hdc=GetWindowDC(hwndFS);			colorref=GetPixel(hdc, 0, 0);			colorref=colorref ^ 0xffffff;			SetPixel(hdc, 0, 0, colorref);			ReleaseDC(hwndFS, hdc);			// Remember the color that we drew			colorrefDrawn=colorref;			// Remember that we've set the pixel			haveSetPixel=true;		}		else		{			// Check if the color of the pixel now is different than			// when we set it...			hwndFS=FindWindowEx(NULL, NULL, L"FS98MAIN", NULL);			hdc=GetWindowDC(hwndFS);			colorref=GetPixel(hdc, 0, 0);			ReleaseDC(hwndFS, hdc);			if(colorrefDrawn!=colorref)			{				wantToShowMenu=false;				ShowTheMenu();			}		}	}	break;

I'm hoping this code should be more stable -- I can't currently think of anything that could go wrong here, but who knows what further testing will uncover...MartinEdit: Switched off emoticon option

Share this post


Link to post
Share on other sites

Thanks for the update, Martin. I've nothing to add, but just wanted you to know that your post(s) are being read and appreciated! ;)

Share this post


Link to post
Share on other sites

Thanks for letting me know! By the way, my browser shows the text below the code snippets in blue and in a smaller font -- do you know how I can avoid this happening?

Share this post


Link to post
Share on other sites

Hi MartinYou wouldn't be kind enough to post some sample code showing how to create the menu as well...? I know of a document that would be very grateful for the information.... :-wink2 -Dai

Share this post


Link to post
Share on other sites

Presumably the code section is smaller and in blue because you used the {code} script tag to preserve the formatting:

case PANEL_SERVICE_PRE_DRAW:	if(wantToShowMenu)	{		if(!haveSetPixel)		{			// Change a pixel on the screen			hwndFS=FindWindowEx(NULL, NULL, L"FS98MAIN",NULL);			hdc=GetWindowDC(hwndFS);			colorref=GetPixel(hdc, 0, 0);			colorref=colorref ^ 0xffffff;			SetPixel(hdc, 0, 0, colorref);			ReleaseDC(hwndFS, hdc);			// Remember the color that we drew			colorrefDrawn=colorref;			// Remember that we've set the pixel			haveSetPixel=true;		}		else		{			// Check if the color of the pixel now is different than			// when we set it...			hwndFS=FindWindowEx(NULL, NULL, L"FS98MAIN",NULL);			hdc=GetWindowDC(hwndFS);			colorref=GetPixel(hdc, 0, 0);			ReleaseDC(hwndFS, hdc);			if(colorrefDrawn!=colorref)			{				wantToShowMenu=false;				ShowTheMenu();			}		}	}	break;

For some reason though, it doesn't seem to switch back to "normal text" even after the {/code} closing tag!

Share this post


Link to post
Share on other sites

Sure... as a simple example:

HMENU hmenu;UINT id;HWND hwndFS;// Let x and y be the position on screen where we want the popup menu// to appearint x=50, y=50;// Create the menuhmenu=CreatePopupMenu();AppendMenu(hmenu, MF_STRING, 1, "One");AppendMenu(hmenu, MF_STRING, 2, "Two");// Find window handle of the MSFS windowhwndFS=FindWindowEx(NULL, NULL, L"FS98MAIN", NULL);// Show the menu. The ID of the selected item will be returned in idid=TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | 	TPM_RETURNCMD | TPM_LEFTBUTTON, x, y, 0, hwndFS, NULL);// Destroy the menuDestroyMenu(hmenu);

This would then return either 1 or 2 in id (or 0 if the user cancelled the menu), and we can then perform the appropriate action.I just cobbled this up (my code constructs menus dynamically, so there isn't anything I could lift directly)... so the code isn't tested, but it should work, possibly with a few quickly corrected compiler errors.Cheers,Martin

Share this post


Link to post
Share on other sites

>Presumably the code section is smaller and in blue because>you used the {code} script tag to preserve the formatting:Yep, that's just what I did...>For some reason though, it doesn't seem to switch back to>"normal text" even after the {/code} closing tag!So I guess it's just a known bug and I wasn't doing anything wrong?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this