xcb correct window size - c++

I have a question regarding xcb Window size
I create a window using xcb_create_window function
xcb_create_window(mScreen->connection(),
XCB_COPY_FROM_PARENT,
mWindow,
mScreen->screen()->root,
x, // left corner of the window client area
y, // upper corner of the window client area
width, // width of the client area
height, // height of the client area
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
mScreen->screen()->root_visual,
value_mask,
value_list);
auto reply = XCB_REPLY(xcb_intern_atom, mScreen->connection(), true, strlen("WM_PROTOCOLS"), "WM_PROTOCOLS");
auto atomDelete = XCB_REPLY(xcb_intern_atom, mScreen->connection(), false, strlen("WM_DELETE_WINDOW"), "WM_DELETE_WINDOW");
xcb_change_property(mScreen->connection(), XCB_PROP_MODE_REPLACE, mWindow, reply->atom, 4, 32, 1, &atomDelete->atom);
xcb_change_property(mScreen->connection(), XCB_PROP_MODE_REPLACE, mWindow, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(windowName), windowName);
xcb_flush(mScreen->connection());
On Win32 API I have the possibilty to adjust a window rect by using AdjustWindowRect function which basically adds border and caption size to ensure client window does have the expected size.
My question how do I achieve this with xcb? Is there any way to compute the additonal size that is needed to ensure client window das have the expected size?

Extended Window Manager Hints
_NET_REQUEST_FRAME_EXTENTS (Other Root Window Messages)
Rationale: A client cannot calculate the dimensions of its window's frame before the window is mapped, but some toolkits need this information. Asking the window manager for an estimate of the extents is a workable solution. The estimate may depend on the current theme, font sizes or other window properties. The client can track changes to the frame's dimensions by listening for _NET_FRAME_EXTENTS PropertyNotify events.
_NET_FRAME_EXTENTS (Application Window Properties)
The Window Manager MUST set _NET_FRAME_EXTENTS to the extents of the window's frame. left, right, top and bottom are widths of the respective borders added by the Window Manager.
Examples can be found here:
xcb_intern_atom (api)
xcb_get_property (api)

The following code gets margins of a window
followed suggestion from Erdal Küçük:
create window
configure stuff (like title or close button)
wait for property message
in case _NET_FRAME_EXTENTS read data
uint32_t value_mask, value_list[32]{};
auto windowHandle = xcb_generate_id(xcb_connection());
value_mask = XCB_CW_EVENT_MASK;
value_list[0] = XCB_EVENT_MASK_PROPERTY_CHANGE;
xcb_create_window(screen->connection(),
XCB_COPY_FROM_PARENT,
windowHandle,
screen->root,
100,
100,
100,
100,
0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
value_mask,
value_list);
auto protocols = XCB_REPLY(xcb_intern_atom, screen->connection(), true, strlen("WM_PROTOCOLS"), "WM_PROTOCOLS");
auto atomDelete = XCB_REPLY(xcb_intern_atom, screen->connection(), false, strlen("WM_DELETE_WINDOW"), "WM_DELETE_WINDOW");
auto atomExtents = XCB_REPLY(xcb_intern_atom, screen->connection(), false, strlen("_NET_FRAME_EXTENTS"), "_NET_FRAME_EXTENTS");
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, windowHandle, protocols->atom, XCB_ATOM_ATOM, 32, 1, &atomDelete->atom);
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, windowHandle, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(""), "");
xcb_map_window(xcb_connection(), windowHandle);
xcb_flush(xcb_connection());
xcb_generic_event_t* event;
for (;;) {
while ((event = xcb_poll_for_event(screen->connection()))) {
switch (event->response_type & 0x7f) {
case XCB_PROPERTY_NOTIFY: {
auto propertyNotify = (const xcb_property_notify_event_t*)event;
if (propertyNotify->atom == atomExtents->atom) {
free(event);
goto end;
}
break;
}
default:
break;
}
free(event);
}
}
end:
auto extends = XCB_REPLY(xcb_get_property, xcb_connection(), false, windowHandle, atomExtents->atom, XCB_ATOM_CARDINAL, 0, 4);
if (extends && extends->type == XCB_ATOM_CARDINAL && extends->format == 32 && extends->value_len == 4) {
uint32_t* data = std::pointer_cast<uint32_t*>(xcb_get_property_value(extends.get()));
windowMargins.l = -data[0];
windowMargins.r = data[1];
windowMargins.t = -data[2];
windowMargins.b = data[3];
}
xcb_destroy_window(xcb_connection(), windowHandle);

Related

L_LineRemoveBitmap returns an empty HRGN

I'm trying to use Leadtools Version 20 to automatically cleanup some images (black border removal, line removal, deskew, ...).
Since some of the APIs only work with black and white images, I create a copy of the image in memory and turn it black and white using L_ColorResBitmap. My plan is to use this black and white image to do the processing and then process the colored image manually. For example I use L_BorderRemoveBitmap to figure out the region that needs to be wiped out and then wipe the same region on the color image or use L_DeskewBitmap to figure out the angle that black and white image needs to be turned and then use L_RotateBitmap to turn the colored image. But when I use L_LineRemoveBitmap, it returns an empty region. I even tried to use the callback function, but inside the callback function region is always NULL.I have made sure the image that is being loaded has a vertical line in it and if I save the black and white version the line is removed, but the correct region is not handed back.Here is a snippet of what I'm doing:
FILEINFO fi;
L_INT PageCount;
L_INT i;
L_UINT uFlags;
BITMAPHANDLE tBmp;
BITMAPHANDLE bwBmp;
BORDERREMOVE br = {sizeof(BORDERREMOVE), BORDER_SINGLE_REGION, BORDER_ALL, 25, 4, 10, NULL, nullptr, sizeof(BITMAPHANDLE)};
RECT r;
LINEREMOVE lr = {sizeof(LINEREMOVE), LINE_SINGLE_REGION, 400, 12, 15, 10, 2, 0, LINEREMOVE_VERTICAL, NULL, nullptr, sizeof(BITMAPHANDLE)};
memset(&fi, 0, sizeof(FILEINFO));
fi.uStructSize = sizeof(FILEINFO);
plo->PageNumber = 0; // plo is a LOADFILEOPTION*
L_FileInfo(FileName, &fi, sizeof(FILEINFO), FILEINFO_TOTALPAGES, plo); // ok
PageCount = fi.TotalPages;
for(i = 0; i < PageCount; i++)
{
memset(&fi, 0, sizeof(FILEINFO));
fi.uStructSize = sizeof(FILEINFO);
plo->PageNumber = i + 1;
memset(&tBmp, 0, sizeof(BITMAPHANDLE));
FileInfo(FileName, &fi, sizeof(FILEINFO), 0, plo); // OK
if(tBmp.Flags.Allocated)
L_FreeBitmap(&tBmp);
L_LoadBitmap(FileName, &tBmp, sizeof(BITMAPHANDLE), fi.BitsPerPixel > 24 ? 24 : fi.BitsPerPixel, ORDER_RGBORGRAY, plo, &fi); // OK
if(tBmp.Flags.Allocated)
{
if (TOP_LEFT != tBmp.ViewPerspective)
L_ChangeBitmapViewPerspective(NULL, &tBmp, sizeof(BITMAPHANDLE), TOP_LEFT);
uFlags = DSKW_PROCESS | DSKW_FILL | DSKW_DOCUMENTANDPICTURE | DSKW_BICUBIC | DSKW_NORMALSPEEDROTATE;
if(1 != fi.BitsPerPixel)
uFlags |= (DSKW_DONT_PERFORM_PREPROCESSING | DSKW_NORMAL_DETECTION);
memset(&BitmapRegion, 0, sizeof(BITMAPHANDLE));
BitmapRegion.uStructSize = sizeof(BITMAPHANDLE);
if(bwBmp.Flags.Allocated)
L_FreeBitmap(&bwBmp);
memset(&bwBmp, 0, sizeof(BITMAPHANDLE));
bwBmp.uStructSize = sizeof(BITMAPHANDLE);
L_CopyBitmap(&bwBmp, &tBmp, bwBmp.uStructSize); // OK
if(1 != tBmp.BitsPerPixel)
L_ColorResBitmap(&bwBmp, &bwBmp, sizeof(BITMAPHANDLE), 1, CRF_FIXEDPALETTE, NULL, NULL, 0, NULL, NULL); // OK
L_BorderRemoveBitmap(&bwBmp, &br, nullptr, nullptr, 0) // OK
if(NULL != br.hRgn)
{
L_SetBitmapRgnHandle(&tBmp, nullptr, br.hRgn, L_RGN_SET); // OK
L_FillBitmap(&tBmp, bkColor); // OK bkColor is White
L_FreeBitmapRgn(&tBmp);
}
L_LineRemoveBitmap(&bwBmp, &lr, lrCB, NULL, 0) // returns OK
if(NULL != lr.hRgn) // not null but empty
{
::GetRgnBox(lr.hRgn, &r); // it is always {0, 0, 0, 0}
L_SetBitmapRgnHandle(&tBmp, nullptr, lr.hRgn, L_RGN_SET);
L_FillBitmap(&tBmp, bkColor); // OK but fills nothing
L_FreeBitmapRgn(&tBmp);
}
// do other stuff and save
}
}
L_INT EXT_CALLBACK lrCB(HRGN hRgn, L_INT iStartRow, L_INT iStartCol, L_INT iLength, L_VOID* pUserData)
{
UNREFERENCED_PARAMETER(pUserData);
if(NULL != hRgn) // always null
{
RECT rcRect;
GetRgnBox(hRgn, &rcRect);
DeleteObject(hRgn);
}
return SUCCESS_REMOVE;
}
Sam,
If you want the Callback to set a Windows region, you need to also set the LINE_CALLBACK_REGION uFlag when defining the LINEREMOVE structure:
LINEREMOVE lr = {
sizeof(LINEREMOVE), // uStructSize
LINE_CALLBACK_REGION| LINE_SINGLE_REGION, // uFlags
400, // Minimum Length
12, // Maximum Width
15, // Wall size
10, // Max percent of line that can be a wall
2, // Maximum Gap
0, // Maximum Line Variance
LINEREMOVE_VERTICAL, // horizontal or vertical
NULL, // hRgn
nullptr, // pBitmapRegion
sizeof(BITMAPHANDLE) // uBitmapStructSize
};
The flag is documented in this page.
After the function finishes processing, you can then take the lines region set in the lr.hRgn property and set it in the original color bitmap using the following code:
L_SetBitmapRgnHandle(&OriginalBitmap, NULL, lr.hRgn, L_RGN_SET);
In addition to the response here, I have sent you a small code snippet in a reply to the email you sent to our support address.

If I have five Win32 static controls, how can I set one of them with a specific foreground color? [duplicate]

This question already has answers here:
Set static text color Win32
(1 answer)
In Win32, how can the colour of STATIC text be changed?
(2 answers)
Closed 4 years ago.
As far as I can tell, this is not a duplicate question because it's dealing with a collection of static (label) controls. I want to set a foreground color to a specific one that I call in my thin OOP library.
I call a static control a "Label" in my library. This is how I set the color:
void Label::setForeColor(const BYTE red, const BYTE green, const BYTE blue)
{
m_foreColor = RGB(red, green, blue);
}
This just sets a COLORRREF that the control should have. I'm having trouble finding a solution to send a message for that specific static control without affecting others.
Many say to use WM_CTLCOLORSTATIC, but I already am for transparency of controls:
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC:
{
char class_Name[100];
WNDCLASS lpcls{};
SetBkMode((HDC)wParam, TRANSPARENT);
// SetTextColor((HDC)wParam, RGB(0, 0, 255)); This works and can set all statics as blue, but I need just one control blue.
GetClassName(hWnd, class_Name, 100);
GetClassInfo(frm.getInstance(), class_Name, &lpcls);
return (LRESULT)lpcls.hbrBackground;
}
But here's the issue: I may have more than one label on a window, so this goes beyond than just setting a single label as most examples show. There may be 5 labels with 5 different colors.
This is the top layer:
Label lblName("This is a label.", 330, 303);
lblName.setVisible(true);
lblName.setForeColor(0, 0, 255);
lblName.setFont("Garamond", 24, false, false, false);
lblName.OnMouseOver(lblName_onMouseOver);
Ideally, I would like to set the color in my setFont() function by sending a message.
bool Label::setFont(const std::string &fontName, const int size, const bool bold,
const bool italic, const bool underlined)
{
DWORD dwItalic;
DWORD dwBold;
DWORD dwUnderlined;
SIZE linkSize;
HFONT old_font;
dwItalic = (italic) ? TRUE : FALSE;
dwBold = (bold) ? FW_BOLD : FW_DONTCARE;
dwUnderlined = (underlined) ? TRUE : FALSE;
m_font = CreateFont(size, 0, 0, 0, dwBold, dwItalic, dwUnderlined, FALSE,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, fontName.c_str());
SendMessage(m_handle, WM_SETFONT, WPARAM(m_font), TRUE);
// Calculate the correct width and height size
HDC hDC = GetDC(m_handle);
old_font = SelectFont(hDC, m_font);
GetTextExtentPoint32(hDC, m_text.c_str(), (int)m_text.length(), &linkSize);
setSize(linkSize.cx, size);
DeleteFont(old_font);
ReleaseDC(m_handle, hDC);
return true;
}
Finally, this is how I retrieve my labels that I'm interested in. I wonder if I need to set the font color similarly.
case WM_MOUSEMOVE:
{
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) dwRefData;
// Check if this is a X3D Label control.
X3D::Windows::Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
{
lbl->setHovering(true);
lbl->invokeOnMouseHover();
}
else
{
lbl->setHovering(false);
}
break;
}
Overall question: If I have five Win32 static controls, how can I set one of them with a specific foreground color?
Update:
This is my current code. Assert() is barking at me: Expression: map/set iterator not dereferencable
case WM_CTLCOLORBTN:
case WM_CTLCOLORSTATIC:
{
char class_Name[100];
WNDCLASS lpcls{};
SetBkMode((HDC)wParam, TRANSPARENT);
GetClassName(hWnd, class_Name, 100);
GetClassInfo(frm.getInstance(), class_Name, &lpcls);
for (int i = 0; i < frm.getControlCount(); i++)
{
if (frm.getControls().find(i)->second->getHandle() == (HWND)lParam)
{
// Obtain the control associated with the id.
X3D::Windows::Control *ctrl = frm.getControls().find(i)->second;
if (ctrl == NULL)
return 0;
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl != NULL)
{
SetTextColor((HDC)wParam, lbl->getForeColor());
break;
}
}
}
return (LRESULT)lpcls.hbrBackground;
}
Update:
App runs, but the font color isn't updating. Something wrong in this?
int id = GetDlgCtrlID((HWND)lParam);
// Obtain the control associated with the id.
X3D::Windows::Control *ctrl = frm.getControls().at(id);
if (ctrl == NULL)
return 0;
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl != NULL)
{
SetTextColor((HDC)wParam, lbl->getForeColor());
break;
}
Tried this too:
int id = GetDlgCtrlID((HWND)lParam);
// Obtain the control associated with the id.
X3D::Windows::Control *ctrl = frm.getControls().find(id)->second;
if (ctrl == NULL)
return 0;
If all of that looks correct, I'll just have to debug it tomorrow.
WM_CTLCOLORSTATIC is sent multiple times, you can choose to take different actions depending on which child window is generating it.
As shown on MSDN, the lParam that arrives with the message is the HWND for the control. Compare it directly, or GetDlgCtrlID() if you want to work with dialog item IDs.
No need to do anything special in the subclass wndproc, because WM_CTLCOLORSTATIC is sent to the parent window.
But there is no message to send to change the color or font, because the STATIC window class doesn't use a permanent device context per label (which is a good thing, because many programs have a lot of labels). So the text configuration like color and font need to be reapplied to the DC each time it is borrowed from the pool. WM_CTLCOLORSTATIC is sent to the parent window at the ideal time to do this.
If your setter for the color is called, be sure to use InvalidateRect() to trigger a repaint. That repaint will send WM_CTLCOLORSTATIC again, giving you the opportunity to act on your updated color.

SetWindowPos centers window when position is adjacent to working area border

I'm trying to place dialog window adjacent to desktop's top or bottom border.
Code:
CRect MonitorWorkingArea(HMONITOR monitor)
{
MONITORINFOEX monInfo;
zeroVar(monInfo);
monInfo.cbSize = sizeof(monInfo);
GetMonitorInfo(monitor, &monInfo);
return monInfo.rcWork;
}
CRect MonitorWorkingArea_Wnd(HWND hwnd)
{
return MonitorWorkingArea(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST));
}
CRect MonitorWorkArea_Wnd(CWnd& wnd)
{
return MonitorWorkingArea_Wnd(wnd.GetSafeHwnd());
}
void CMyDialog::SetDefaultWndPos(bool toBottom)
{
// Here we can be sure, that
// a) parent of this window is not null, is visible, not minimized
// b) this window smaller than working area of desktop
// c) it has WS_POPUP style
CRect rcThis;
GetWindowRect(&rcThis);
// Shift our rectangle to most upper or most bottom position
const CRect rcDesktop = MonitorWorkingArea_Wnd(*this);
rcThis.MoveToY(toBottom ? rcDesktop.bottom - rcThis.Height() : 0);
// Move window
SetWindowPos(NULL, rcThis.left, rcThis.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
Real code is bit more complex, I omit some checks and features for simplicity.
I have two monitors, both are 1920×1080 and in virtual coordinate space they are laying on:
Primary: (0,0) — (1920,1080)
Secondary: (1920,0) — (3840, 1080)
Code above works fine when window placed on primary monitor, but on secondary one it's works unexpectedly: it centers window based on it's parent.
For instance I'm calling SetDefaultWndPos(false) and rcThis being calculated as {left:2710, top:0, right:3423, bottom:550}. So SetWindowPos is called with zero y:
SetWindowPos(NULL, 2710, 0, 0, 0,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
But in handler of WM_WINDOWPOSCHANGING
void CMyDialog::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
__super::OnWindowPosChanging(lpwndpos);
}
I see in debug x = 2710 (unchanged) and y = 475 (unexpected). Next I check dialog's y-position in Spy utility, it's 475. Dialog window looks like it was centered on it's parent window.
All it looks like an OS errorneously detected requested window position as out-of-desktop and used some default positioning.
Only workarund I found is to decrease desktop working area on 1 pixel from each side:
CRect MonitorWorkingArea(HMONITOR monitor)
{
MONITORINFOEX monInfo;
zeroVar(monInfo);
monInfo.cbSize = sizeof(monInfo);
GetMonitorInfo(monitor, &monInfo);
CRect rc = monInfo.rcWork;
if( !(monInfo.dwFlags & MONITORINFOF_PRIMARY) )
{
++rc.left;
++rc.top;
--rc.right;
--rc.bottom;
}
return rc;
}
It works but looks as strange ugly hack.
Can anybody explain what happens here and help me with good decision?
Can it be OS version dependent? (I use Windows 7 Professional SP1).

xcb_poll_for_event does not detect XCB_CLIENT_MESSAGE event for closing window

I am porting my application to Linux, and I am using the XCB library for window handling. I need to detect when the window closes, so that the application can exit. However, the system cannot block on the main window loop due to the way the system is designed. This is easy in Windows, since you just use PeekMessage. However, xcb does not seem to work when I try to detect XCB_CLIENT_MESSAGE using xcb_poll_for_event. The close button on the window in fact has no functionality when I try to inject the WM_DELETE_WINDOW protocol.
Window setup:
// initialize XCB
this->connection = xcb_connect(NULL, NULL);
this->screen = xcb_setup_roots_iterator(xcb_get_setup(this->connection)).data;;
// create window
u32 mask = 0;
u32 values[1];
this->window = xcb_generate_id(this->connection);
mask = XCB_CW_EVENT_MASK;
values[0] = XCB_EVENT_MASK_EXPOSURE;
xcb_create_window(this->connection, 0, this->window, this->screen->root, 0, 0, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, this->screen->root_visual, mask, values);
// setup close handler event
xcb_intern_atom_cookie_t protocolCookie = xcb_intern_atom_unchecked(this->connection, 1, 12, "WM_PROTOCOLS");
xcb_intern_atom_reply_t* protocolReply = xcb_intern_atom_reply(this->connection, protocolCookie, 0);
xcb_intern_atom_cookie_t closeCookie = xcb_intern_atom_unchecked(this->connection, 0, 16, "WM_DELETE_WINDOW");
this->m_closeReply = xcb_intern_atom_reply(this->connection, closeCookie, 0);
xcb_change_property(this->connection, XCB_PROP_MODE_REPLACE, this->window, protocolReply->atom, 4, 32, 1, &(this->m_closeReply->atom));
free(protocolReply);
// map and flush
xcb_map_window(this->connection, this->window);
xcb_flush(this->connection);
Message loop:
// handle all incoming messages
xcb_generic_event_t* e;
while(e = xcb_poll_for_event(connection))
{
// take action from message
switch(e->response_type & ~0x80)
{
case XCB_EXPOSE:
// invalidated
xcb_flush(connection);
break;
case XCB_CLIENT_MESSAGE:
// close window
if(((xcb_client_message_event_t*)e)->data.data32[0] == (*this->m_closeReply).atom)
return false;
break;
}
// cleanup
free(e);
}
Window closing works flawlessly when I replace xcb_poll_for_event with xcb_wait_for_event, but then the window loop is blocked on waiting for messages. I just need to know what I am doing wrong for the event to never get detected when using xcb_poll_for_event.

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));