Can't center my console window by using the following code - c++

void Initialize_Window(void)
{
RECT rConsole;
GetWindowRect(GetConsoleWindow(), &rConsole);
SetWindowPos(GetConsoleWindow(), NULL, 0, 0, 800, 700, 0);
SetWindowLong(GetConsoleWindow(), GWL_STYLE, GetWindowLong(GetConsoleWindow(), GWL_STYLE) & ~(WS_SIZEBOX | WS_MAXIMIZEBOX));
SetWindowPos(GetConsoleWindow(), NULL, (GetSystemMetrics(SM_CXSCREEN) - rConsole.right - rConsole.left) / 2, (GetSystemMetrics(SM_CYSCREEN) - rConsole.bottom - rConsole.top) / 2, 0, 0, SWP_NOSIZE);
}
I'm trying to center my console window by using the code above, but seems like the window just moved to a random position on my screen every time I execute the program, any idea how to fix it?

You need (GetSystemMetrics(SM_CXSCREEN) - (rConsole.right - rConsole.left))/2 to get center.
Side note: you can use one SetWindowPos instead of two (and do not need to get window Rect)
const int width = 800;
const int height = 700;
//SetWindowLong()...
SetWindowPos(GetConsoleWindow(), NULL,
GetSystemMetrics(SM_CXSCREEN)/2 - width/2,
GetSystemMetrics(SM_CYSCREEN)/2 - height/2,
width, height, SWP_SHOWWINDOW);

Don't use GetSystemMetrics() for this because it only returns the metrics of the primary monitor. Multi monitor setups are quite common these days so users will be rightly upset if you ignore that.
In addition, a window normally should not be aligned to the physical monitor surface, but to the work area which excludes the taskbar(s). Yes, there can be multiple taskbars (called "appbars" in Windows slang) on either side of the screen. An exception where you would actually use the full physical surface are full screen windows.
To cover both aspects we can use MonitorFromWindow() and GetMonitorInfo().
First we get the "nearest" monitor from the window handle. This is the monitor that either shows the window completely or that has the biggest area of the window on it:
HWND hConsoleWnd = ::GetConsoleWindow();
HMONITOR hMonitor = ::MonitorFromWindow( hConsoleWnd, MONITOR_DEFAULTTONEAREST );
Then we get the work area rectangle of that monitor and center the window relative to that:
if( hMonitor )
{
MONITORINFO info{ sizeof(info) }; // set cbSize member and fill the rest with zero
if( ::GetMonitorInfo( hMonitor, &info ) )
{
int width = 800;
int height = 700;
int x = ( info.rcWork.left + info.rcWork.right ) / 2 - width / 2;
int y = ( info.rcWork.top + info.rcWork.bottom ) / 2 - height / 2;
::SetWindowPos( hConsoleWnd, nullptr, x, y, width, height,
SWP_NOZORDER | SWP_NOOWNERZORDER );
}
}
That's it. In a real-world application you should of course not hardcode the window size because it is a user preference. For first launch a default size can be reasonable but even that should not be hardcoded but scaled according to the Windows DPI settings.

Related

StretchBlt doesn't draw a background window title bar

Here's my objective: a) capture the device context for the entire screen; b) capture a background window device context (including the title bar); c) draw the desktop content on a buffer device context; d) draw the captured window content on the same buffer device context; e) display the drawn device context on a top window, simulating that the desired window is on top.
The issue is that the title bar isn't drawn on the new window, even though the GetWindowRect function seems to be working fine. That's because some other stuff is drawn instead of the title bar (usually the content of windows on the background when the target window is created).
This is my code right now:
case __SourceDC:
{
HDC desktop_dc = GetDC(NULL);
HWND target_hwnd = FindWindowA(NULL, "Everything");
HDC target_dc = GetWindowDC(target_hwnd);
RECT rc{};
GetWindowRect(target_hwnd, &rc);
RECT clrc{};
GetClientRect(target_hwnd, &clrc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
int x_dst = ((double)rc.left / SCREEN_WIDTH) * m_area.width;
int y_dst = ((double)rc.top / SCREEN_HEIGHT) * m_area.height;
int w_dst = ((double)width / SCREEN_WIDTH) * m_area.width;
int h_dst = ((double)height / SCREEN_HEIGHT) * m_area.height;
SetStretchBltMode(hdc, HALFTONE);
StretchBlt(hdc, 0, 0, m_area.width, m_area.height, desktop_dc, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SRCCOPY);
StretchBlt(hdc, x_dst, y_dst, w_dst, h_dst, target_dc, 0, 0, width, height, SRCCOPY);
ReleaseDC(target_hwnd, target_dc);
ReleaseDC(NULL, desktop_dc);
}
break;
All the desktop content and the window content is correctly drawn, except for the title bar (and apparently the borders as well).
As you can see from the image, the myWnd window shows the desktop content, but the title bar is not displayed for the target window. Also, nothing changes when the target window is the foreground window.
What am I missing here? Is this behavior expected? Is StretchBlt working as intended by not capturing the content of the title bar?
Thanks in advance.

Windows Console Resizing ignored - Screen buffer messed up

I'm going to ask the same question as countless people before me. I've tried about every solution out there but I seam to get the same output every time. I'm creating a console game engine and want to improve fps by using the windows API. However, Now that I have rotating polygons and such implemented, I'm realizing that the console resizes itself when the requested size extends about 100 x 100 characters. When this happens, the console gets resized to about 50 x 25 and the screen buffer shows absolute gibberish when I scroll to the right.
Here are some pictures:
I will supply just the relevant code for now but if anyone needs anymore I'd be glad to add it.
m_width = width;
m_height = height;
m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
m_hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
if (m_hConsole == INVALID_HANDLE_VALUE)
log += "Invalid std output console handle\n";
COORD bottemRight = { (signed)width, (signed)height };
if (!SetConsoleScreenBufferSize(m_hConsole, bottemRight))
log += "Error setting screen size\n";
m_screenBound = { 0, 0, (signed)width - 1, (signed)height - 1 };
if (!SetConsoleWindowInfo(m_hConsole, TRUE, &m_screenBound))
log += "Error initializing window info\n";
if (!SetConsoleActiveScreenBuffer(m_hConsole))
log += "Error setting active screen buffer\n";
I've also tried the HWND solution:
HWND hwnd = GetConsoleWindow();
RECT rect = { 0, 0, width, height };
MoveWindow(hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
I tried switching the window info setter and the screen buffer size setter and changed the window info rect to {0, 0, 1, 1} to no prevail. The output is pretty much exactly what you see in the pictures above.
Thanks in advance!
SOLVED
After noodling around with my code a bit, it turns out (based on the Microsoft documentation) that I need to something like this:
SMALL_RECT screen = { 0, 0, 1, 1 };
SetConsoleWindowInfo(m_handle, true, &screen);
COORD buffer_size = { width, height };
SetConsoleScreenBufferSize(m_handle, &buffer_size);
screen = { 0, 0, width - 1, height - 1 };
SetConsoleWindowInfo(m_handle, &screen);
This works for me now but thanks for your suggestions!

Next-Gen Display Splash Screen | C++

Is there anyway to display a splash screen without the window? Like just the image pops up for a few seconds, then after that shows a dialog box asking the player if he/she wants to create a new project or open one. I know, two questions in one. I am using Visual Studio right now.
Thanks!
You mean like:
Code Blocks:
Photoshop:
Or even, the very program you're using, Visual Studio:
Other ones include anything in the Adobe suite, and anything in the office suite, just off the top of my head.
So, obviously the answer is yes. The way to do this is to create a borderless window. With C++, this seems to be a bit difficult. I would highly suggest sticking to a rectangle splash screen, unless you want to handle the paint events to draw whatever is below you, which is a pain.
I found a how-to over here at Programmers Heaven, which is below so it's also on this site per SO guidelines.
The other option, is to simply use C#, which makes this kind of thing really easy.
First Create a window with the WS_POPUP flag
Also - When creating your window take into account the fact that the system will likely still want to draw the border and title bar etc... so calculate that into your window dimensions as follows
#define WINDOW_WIDTH 300 // whatever size you need
#define WINDOW_HEIGHT 256
// Calculate the proper size for the window given a client
DWORD dwFrameWidth = GetSystemMetrics( SM_CXSIZEFRAME );
DWORD dwFrameHeight = GetSystemMetrics( SM_CYSIZEFRAME );
DWORD dwMenuHeight = GetSystemMetrics( SM_CYMENU );
DWORD gdwWindowXPos = ( GetSystemMetrics( SM_CXSCREEN ) - WINDOW_WIDTH ) / 2;
DWORD gdwWindowYPos = ( GetSystemMetrics( SM_CYSCREEN ) - WINDOW_HEIGHT ) / 2;
DWORD gdwWindowWidth = WINDOW_WIDTH + dwFrameWidth * 2;
DWORD gdwWindowHeight = WINDOW_HEIGHT + dwFrameHeight * 2 + dwMenuHeight;
You should then have something like this for your window creation:
HWND hwnd = CreateWindowEx(0,
"Class Name",
"Window Name",
WS_POPUP, // and any other flags you need
gdwWindowXPos, // these just center the
gdwWindowYPos, // window on the screen
gdwWindowWidth, // Calculated width
gdwWindowHeight, // and height
NULL, // Parent window
NULL, // Menu (don't want one)
hInst, // hInstance
NULL // Other Parameters
);
... Right ....
The next thing is to set the window region, so that you only draw the area you're interested in by using CreateRectRgn, and SetWindowRgn like so:
HRGN hrgn = CreateRectRgn( 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT );
SetWindowRgn( hwnd, hrgn, TRUE );
This clips the window to the size I wanted when I #defined WINDOW_WIDTH and WINDOW_HEIGHT

How to make background in MFC translucent?

I import a picture(.bmp or .jpeg) as the background of a client view in MFC.
When I click Open, function CDrawToolView::OnFileOpen() open a window to choose a picture, then I use ShowBitmap(CDC* pDC,CString strPicPath) and ShowPic(CDC* pDC,CString strPicPath) to load the picture as backgroud and adjust the size of client view to fit the picture.
I want to set the picture translucent, so the background looks softer. Could some one help me or give some suggestion, thanks.
Here is my code:
void CDrawToolView::ShowBitmap(CDC* pDC,CString strPicPath)
{
HBITMAP hBitmap=(HBITMAP)LoadImage(NULL,strPicPath,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION|LR_DEFAULTSIZE|LR_LOADFROMFILE);
m_bitmap.Detach();
m_bitmap.Attach(hBitmap);
CRect rect;
GetClientRect(&rect);
CDC dcImage;
if (!dcImage.CreateCompatibleDC(pDC))
{
return;
}
BITMAP bm;
m_bitmap.GetBitmap(&bm);
dcImage.SelectObject(&m_bitmap);
pDC->StretchBlt(0,0,rect.right,rect.bottom,&dcImage,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);
}
void CDrawToolView::ShowPic(CDC* pDC,CString strPicPath)
{
if(!m_MyImage.IsNull())
m_MyImage.Destroy();
HRESULT hResult=m_MyImage.Load(strPicPath);
int iWidth=m_MyImage.GetWidth();
int iHeight=m_MyImage.GetHeight();
m_MyImage.Draw(pDC->m_hDC,0,0,iWidth,iHeight);
CRect client(0, 0, iWidth, iHeight);
client.bottom=client.bottom+::GetSystemMetrics(SM_CYMENU)+::GetSystemMetrics(SM_CYEDGE)*2;
client.right=client.right+::GetSystemMetrics(SM_CXEDGE)*2;
CFrameWnd* pFrame = GetParentFrame();
pFrame->CalcWindowRect(&client);
int width = client.Width();
int height = client.Height();
int y = (::GetSystemMetrics(SM_CYSCREEN) - height) / 2 + 100;
int x = (::GetSystemMetrics(SM_CXSCREEN) - width) / 2;
pFrame->SetWindowPos( NULL, x, y, width, height, SWP_NOACTIVATE | SWP_NOZORDER );
}
Use AlphaBlend instead of StretchBlt
CDC::AlphaBlend
Set SourceConstantAlpha in your BLENDFUNCTION struct to something like 128 (halfway between transparent and opaque), then adjust until it looks good.
AlphaFormat should be zero unless your usa a 32-bit bitmap with an alpha channel.

Win32 Screensaver multiple monitors with main display not leftmost

I've made a screensaver that simply scrolls user-defined text from right to left, automatically jumping back to the right if it exceeds the left boundary.
It works with multiple monitors flawlessly, barring one exception: if the 'Main Display' is on the right (i.e. Monitor #2 is primary), then I do not get the scrolling text, however the monitor IS blacked out by the code. If the main display is #1, there's no problem.
I've been poring over the code for hours and cannot identify at what stage the issue arises; I can confirm the text is in the right position (I inserted logging code that verifies its current position), but it's as if one of the API calls simply erases it. I've read the documentation for them and all looks ok.
I create a custom DC in WM_CREATE via:
if (( hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL)) == NULL )
To prevent flicker, I create compatible objects to update:
void
TickerScreensaver::Paint_Prep(HDC hDC)
{
_devcon_mem = CreateCompatibleDC(hDC);
_devcon_orig = hDC;
_bmp_mem = CreateCompatibleBitmap(hDC, _width, _height);
}
and when painting in WM_PAINT (after BeginPaint, etc.), do a bit-block transfer to the actual device context:
void
TickerScreensaver::Paint(HDC hDC, RECT rect)
{
_bmp_orig = (HBITMAP)SelectObject(_devcon_mem, _bmp_mem);
FillRect(_devcon_mem, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
if ( _gdiplus_token != NULL )
{
Graphics graphics(_devcon_mem);
SolidBrush brush(cfg.display.font_colour);
FontFamily font_family(cfg.display.font_family.c_str());
Font font(&font_family, cfg.display.font_size, FontStyleRegular, UnitPixel);
PointF point_f((f32)cfg.display.text_pos.x, (f32)cfg.display.text_pos.y);
RectF layout_rect(0, 0, 0, 0);
RectF bound_rect;
graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
graphics.MeasureString(cfg.display.text.c_str(), cfg.display.text.length(), &font, layout_rect, &bound_rect);
cfg.display.offset.x = (DWORD)(0 - bound_rect.Width);
cfg.display.offset.y = (DWORD)(bound_rect.Height / 2);
graphics.DrawString(cfg.display.text.c_str(), cfg.display.text.length(), &font, point_f, &brush);
}
BitBlt(hDC, 0, 0, _width, _height, _devcon_mem, 0, 0, SRCCOPY);
SelectObject(_devcon_mem, _bmp_orig);
}
I calculate the dimensions like so:
void
TickerScreensaver::GetFullscreenRect(HDC hDC, RECT *rect)
{
RECT s = { 0, 0, 0, 0 };
if ( EnumDisplayMonitors(hDC, NULL, EnumMonitorCallback, (LPARAM)&s) )
{
CopyRect(rect, &s);
s.left < 0 ?
_width = s.right + (0 + -s.left) :
_width = s.right;
s.top < 0 ?
_height = s.bottom + (0 + -s.top) :
_height = s.bottom;
}
}
Please note that the calculated width, height, etc., are all 100% accurate; it is purely the drawing code that doesn't appear to be working on the main display, only when it is on the right (which sets the origin to {0,0}, monitor #1 then being negative values). It is also reproduceable on a tri-display, with the main being in the center.
Well, turns out it is nice and simple - in Paint(), we should use a rect using the real width and height, not the one retrieved containing the negative values (the one actually retrieved from the API functions):
RECT r = { 0, 0, _width, _height };
_bmp_orig = (HBITMAP)SelectObject(_devcon_mem, _bmp_mem);
FillRect(_devcon_mem, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));