{"id":564,"date":"2018-02-08T08:21:48","date_gmt":"2018-02-08T08:21:48","guid":{"rendered":"http:\/\/imalogic.com\/blog\/?p=564"},"modified":"2018-03-14T09:44:38","modified_gmt":"2018-03-14T09:44:38","slug":"remote-desktop-connection-and-painting","status":"publish","type":"post","link":"https:\/\/imalogic.com\/blog\/2018\/02\/08\/remote-desktop-connection-and-painting\/","title":{"rendered":"Remote Desktop Connection and Painting"},"content":{"rendered":"<body><p>An increasingly important developer task is supporting Remote Desktop Connection properly.<\/p>\n<p>When the user is connected via a Remote Desktop Connection, video operations are transferred over the network connection to the client for display. Since networks have high latency and nowhere near the bandwidth of a local PCI or AGP bus, you need to adapt to the changing cost of drawing to the screen.<\/p>\n<p>If you draw a line on the screen, the \u201cdraw line\u201d command is sent over the network to the client. If you draw text, a \u201cdraw text\u201d command is sent (along with the text to draw). So far so good. But if you copy a bitmap to the screen, the entire bitmap needs to be transferred over the network !<\/p>\n<p>Let\u2019s write a sample program that illustrates this point. Start with our new scratch program and make the following changes:<\/p>\n<pre>void Window::Register()\r\n{\r\n    WNDCLASS wc;\r\n    wc.style         = <span style=\"color: blue;\">CS_VREDRAW | CS_HREDRAW<\/span>;\r\n    wc.lpfnWndProc   = Window::s_WndProc;\r\n    ...\r\n}\r\n\r\nclass RootWindow : public Window\r\n{\r\npublic:\r\n virtual LPCTSTR ClassName() { return TEXT(\"Scratch\"); }\r\n static RootWindow *Create();\r\nprotected:\r\n LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);\r\n LRESULT OnCreate();\r\n <span style=\"color: blue;\">void PaintContent(PAINTSTRUCT *pps);\r\n void Draw(HDC hdc, PAINTSTRUCT *pps);<\/span>\r\nprivate:\r\n HWND m_hwndChild;\r\n};\r\n\r\n<span style=\"color: blue;\">void RootWindow::Draw(HDC hdc, PAINTSTRUCT *pps)\r\n{\r\n FillRect(hdc, &amp;pps-&gt;rcPaint, (HBRUSH)(COLOR_WINDOW + 1));\r\n RECT rc;\r\n GetClientRect(m_hwnd, &amp;rc);\r\n for (int i = -10; i &lt; 10; i++) {\r\n  TextOut(hdc, 0, i * 15 + rc.bottom \/ 2, TEXT(\"Blah blah\"), 9);\r\n }\r\n}\r\n\r\nvoid RootWindow::PaintContent(PAINTSTRUCT *pps)\r\n{\r\n Draw(pps-&gt;hdc, pps);\r\n}<\/span>\r\n\r\nLRESULT RootWindow::HandleMessage(\r\n                          UINT uMsg, WPARAM wParam, LPARAM lParam)\r\n{\r\n switch (uMsg) {\r\n ...\r\n <span style=\"color: blue;\">case WM_ERASEBKGND: return 1;<\/span>\r\n ...\r\n}\r\n<\/pre>\n<p>There is an odd division of labor here; the <code><span style=\"font-family: Courier New;\">PaintContent<\/span><\/code> method doesn\u2019t actually do anything aside from handing the work off to the <code><span style=\"font-family: Courier New;\">Draw<\/span><\/code> method to do the actual drawing. (You\u2019ll see why soon.) Make sure \u201cShow window contents while dragging\u201d is enabled and run this program and resize it vertically. Ugh, what ugly flicker. We fix this by the traditional technique of double-buffering.<\/p>\n<pre>void RootWindow::PaintContent(PAINTSTRUCT *pps)\r\n{\r\n <span style=\"color: blue;\">if (!IsRectEmpty(&amp;pps-&gt;rcPaint)) {\r\n  HDC hdc = CreateCompatibleDC(pps-&gt;hdc);\r\n  if (hdc) {\r\n   int x = pps-&gt;rcPaint.left;\r\n   int y = pps-&gt;rcPaint.top;\r\n   int cx = pps-&gt;rcPaint.right - pps-&gt;rcPaint.left;\r\n   int cy = pps-&gt;rcPaint.bottom - pps-&gt;rcPaint.top;\r\n   HBITMAP hbm = CreateCompatibleBitmap(pps-&gt;hdc, cx, cy);\r\n   if (hbm) {\r\n    HBITMAP hbmPrev = SelectBitmap(hdc, hbm);\r\n    SetWindowOrgEx(hdc, x, y, NULL);\r\n\r\n    Draw(hdc, pps);\r\n\r\n    BitBlt(pps-&gt;hdc, x, y, cx, cy, hdc, x, y, SRCCOPY);\r\n\r\n    SelectObject(hdc, hbmPrev);\r\n    DeleteObject(hbm);\r\n   }\r\n   DeleteDC(hdc);\r\n  }\r\n }<\/span>\r\n}\r\n<\/pre>\n<p>Our new <code><span style=\"font-family: Courier New;\">PaintContent<\/span><\/code> method creates an offscreen bitmap and asks the <code><span style=\"font-family: Courier New;\">Draw<\/span><\/code> method to draw into it. Once that\u2019s done, the results are copied to the screen at one go, thereby avoiding flicker. If you run this program, you\u2019ll see that it resizes nice and smooth.<\/p>\n<p>Now connect to the computer via a Remote Desktop Connection and run it again. Since Remote Desktop Connection disables \u201cShow window contents while dragging\u201d, you can\u2019t use resizing to trigger redraws, so instead maximize the program and restore it a few times. Notice the long delay before the window is resized when you maximize it. That\u2019s because we are pumping a huge bitmap across the Remote Desktop Connection as part of that <code><span style=\"font-family: Courier New;\">BitBlt<\/span><\/code> call.<\/p>\n<p>Go back to the old version of the <code><span style=\"font-family: Courier New;\">PaintContent<\/span><\/code> method, the one that just calls <code><span style=\"font-family: Courier New;\">Draw<\/span><\/code>, and run it over Remote Desktop Connection. Ah, this one is fast. That\u2019s because the simpler version doesn\u2019t transfer a huge bitmap over the Remote Desktop Connection; it just sends twenty <code><span style=\"font-family: Courier New;\">TextOut<\/span><\/code> calls on a pretty short string of text. These take up much less bandwidth than a 1024\u00d7768 bitmap.<\/p>\n<p>We have one method that is faster over a Remote Desktop Connection, and another method that is faster when run locally. Which should we use?<\/p>\n<p>We use both, choosing our drawing method based on whether the program is running over a Remote Desktop Connection.<\/p>\n<pre>void RootWindow::PaintContent(PAINTSTRUCT *pps)\r\n{\r\n <span style=\"color: blue;\">if (GetSystemMetrics(SM_REMOTESESSION)) {\r\n  Draw(pps-&gt;hdc, pps);\r\n } else<\/span> if (!IsRectEmpty(&amp;pps-&gt;rcPaint)) {\r\n  ... as before ...\r\n }\r\n}\r\n<\/pre>\n<p>Now we get the best of both worlds. When run locally, we use the double-buffered drawing which draws without flickering, but when run over a Remote Desktop Connection, we use the simple <code><span style=\"font-family: Courier New;\">Draw<\/span><\/code> method that draws directly to the screen rather than to an offscreen bitmap.<\/p>\n<p>This is a rather simple example of adapting to Remote Desktop Connection. In a more complex world, you may have more complicated data structures associated with the two styles of drawing, or you may have background activities related to drawing that you may want to turn on and off based on whether the program is running over a Remote Desktop Connection. Since the user can dynamically connect and disconnect, you can\u2019t just assume that the state of the Remote Desktop Connection when your program starts will be the state for the lifetime of the program.<\/p>\n<\/body>","protected":false},"excerpt":{"rendered":"<p>An increasingly important developer task is supporting Remote Desktop Connection properly. When the user is connected via a Remote Desktop<\/p>\n","protected":false},"author":1,"featured_media":579,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[7],"tags":[],"class_list":["post-564","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-coding"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/imalogic.com\/blog\/wp-content\/uploads\/2018\/02\/webinterf.jpg?fit=204%2C204&ssl=1","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8J21V-96","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/posts\/564","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/comments?post=564"}],"version-history":[{"count":1,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/posts\/564\/revisions"}],"predecessor-version":[{"id":565,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/posts\/564\/revisions\/565"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/media\/579"}],"wp:attachment":[{"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/media?parent=564"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/categories?post=564"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/imalogic.com\/blog\/wp-json\/wp\/v2\/tags?post=564"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}