From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Po Lu Newsgroups: gmane.emacs.devel Subject: Re: MS Windows double buffering Date: Fri, 29 Apr 2022 11:36:11 +0800 Message-ID: <87mtg4zhlg.fsf@yahoo.com> References: <877d791thh.fsf.ref@yahoo.com> <877d791thh.fsf@yahoo.com> <83ilqtbl3p.fsf@gnu.org> <87sfpxz8a7.fsf@yahoo.com> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="13641"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.91 (gnu/linux) Cc: emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Fri Apr 29 05:40:02 2022 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nkHUA-0003Gr-D0 for ged-emacs-devel@m.gmane-mx.org; Fri, 29 Apr 2022 05:40:02 +0200 Original-Received: from localhost ([::1]:42944 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nkHU8-0000zP-M7 for ged-emacs-devel@m.gmane-mx.org; Thu, 28 Apr 2022 23:40:00 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:47640) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nkHQg-00058l-Pj for emacs-devel@gnu.org; Thu, 28 Apr 2022 23:36:26 -0400 Original-Received: from sonic307-10.consmr.mail.ne1.yahoo.com ([66.163.190.33]:42904) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1nkHQc-0002po-Vc for emacs-devel@gnu.org; Thu, 28 Apr 2022 23:36:25 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1651203381; bh=3ZK7G2QT3GfTpJfbm4TY607X2RN+3FWEv8eOTk0opJk=; h=From:To:Cc:Subject:References:Date:In-Reply-To:From:Subject:Reply-To; b=KImcCJI2OLtICjibJgVYwtz3RhdJTmcy8SB2p+peLVYFFcCEqXCc/eRanJ7aEUstWFHqnoCy9pzAZSi03BFX3xCG9E9iiQu4l7oWdPP+45f3EW4IB5N3z8JjcHAIn0CdKOOwkzptx+ASTuRqaIvcEIgAubiFfsGmjRr7Kf8gY+atftK5d6T8pZ0UpzaNHw4lOp9eXeDSpWMkPEJbmiZbZTkGSc7jDx8FQkAYy/F6mzcQ1qBVmwzQAKrdNsMNCAb3W4YsnuxTupeexDXuwh6bVD9TooQo/KmU1p4zQW7bQgSfuRIhFbtQNOXqI4+u3cECVaXjHqkUMzf0QPsOSBLuSg== X-SONIC-DKIM-SIGN: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1651203381; bh=K+eJ2iNfTJcVoa1MOotBVK8bI4uPUqBEyoj1YYjCmpX=; h=X-Sonic-MF:From:To:Subject:Date:From:Subject; b=URc2jGr9rHP2iatycY1GaXr3Gob3r+AXzqbdCN2j8FTWRXQzoIBh6r8AkC7rE/PzGeKqA1oH/haLhi/l7Iz8DT4+k448AeXTK8JyDtiR7zvN0kwKNOvJ+CgEv+WSAxgOsk1UMhD8MjmdUqOlPG3/hUH4hKAESgL/YsYaZLP8DjkQj9R/epOqtpTZyZmKl23xJdvGaLE+nfZTqyckXlI8OYO2llUg4fosor6AHnyFjf9FI0P4npQSlSO0cyWGrIpYxkTyLlNwnVW2yTDwxsScYr0h4vWWghCA1auyR4UwurL4/HdRRP7NwWfYISLG7uOAG78f2MHoMHXOBR/r3sXDkg== X-YMail-OSG: KZkOv2oVM1ndATAcnjwCVTexgVKObSsV9VheV14hwRDzs6n.6rq5Mw1MArjnuB8 dagUC5rXGy6J8RrNksxy_sRqa8oeAqRZsNucUjfpvbP4pWpnSzlAP9ETC6TsoY63JJ.q8I7QCyww PYCeytHb8fH7k8xsZjcpoh6B3wA1iCEDjDE48SCB7FH3e384NJVus0WFQYTE2WArWXZJh1Nqz7h4 dpucBNs3NHlmdXC_0wGhZr7fsTB_KUyKJPFMYZSyMwgsmSh6sV2Gc4HItrqqjjvtXMkhxcO4xlnV 83AVaCouHzoJ.OcRDDflqMgayd9kD78nhR5M3jknHRYFOMBo7lPz2Fwed2ytH6xUfREHtyIq3b7v 6jSXgO4Sr1n5ekAeBGlySZvGm7LPJ5hYiLADXEEgKuXbEvyER.Xyd42wdULyMQXLz6.MtIlRSyiG IUbcKwDes1kb8nsD6RivszzGgFlqcN4apaO1NDBbV2.2t5iYq06E8oCWJ8fnmtfcR_J1h9smrg8F wGRWHFnLp5eZw7VpiSMfySQ79aGBOXOC.GnWB_IEWv1xBEJxpu_XuWHVRTydXpBlQDNr2k2D2gJK UbL36cstFGIabci0rceGOXrn3hqdj4ra3nQD2eh..bhql3eKkYixOAo1R315pMhfpIV.iNAMYj.m RPRVqbj_kHzbaDtpYGqtXAkbCtEZg6EciQ0ikmauVD3wfpv_PRRxDDhhvD9Hqdl7uxr8o5a1AD5V yxv4AxSsElULJ0Q79IMCQMNDUMJ0b7MHNK0_a91NR0smLiAslSKc2FfOHpYe2jbgcnQ4Qe4SKAkb RG6bO1C8l5I_LEFeG9acFNfoHwwdr2zRQL3XjXPWOD X-Sonic-MF: Original-Received: from sonic.gate.mail.ne1.yahoo.com by sonic307.consmr.mail.ne1.yahoo.com with HTTP; Fri, 29 Apr 2022 03:36:21 +0000 Original-Received: by hermes--canary-production-sg3-795d7b4d54-zfnfv (Yahoo Inc. Hermes SMTP Server) with ESMTPA ID 036e060c181e871fac07fb05969949fb; Fri, 29 Apr 2022 03:36:16 +0000 (UTC) In-Reply-To: <87sfpxz8a7.fsf@yahoo.com> (Po Lu's message of "Thu, 28 Apr 2022 20:45:04 +0800") X-Mailer: WebService/1.1.20118 mail.backend.jedi.jws.acl:role.jedi.acl.token.atz.jws.hermes.yahoo Received-SPF: pass client-ip=66.163.190.33; envelope-from=luangruo@yahoo.com; helo=sonic307-10.consmr.mail.ne1.yahoo.com X-Spam_score_int: 0 X-Spam_score: -0.1 X-Spam_bar: / X-Spam_report: (-0.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, PDS_OTHER_BAD_TLD=1.997, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:288955 Archived-At: Po Lu writes: > Right... since the best way to get something done fast is to do it > yourself, I tried my best to fix all the issues mentioned above. It > should work on old versions of MS Windows now as well. If this works > for you, I'll install this after implementing the > `inhibit-double-buffering' frame parameter. Thanks. Here's the patch with that implemented as well: >From ee8cc91559c04a4f21a1ad14ff5461453638145e Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 29 Apr 2022 11:33:41 +0800 Subject: [PATCH] Implement double buffering on MS Windows * etc/NEWS: Announce changes. * src/w32fns.c (w32_set_inhibit_double_buffering): New function. (w32_wnd_proc): (Fx_create_frame): (w32_create_tip_frame): Set `inhibit-double-buffering' parameter. (w32_frame_parm_handlers): Add new handler. * src/w32term.c (w32_show_back_buffer): (w32_release_paint_buffer): New functions. (w32_frame_up_to_date): Show back buffer if applicable. (w32_buffer_flipping_unblocked_hook): New hook. (w32_scroll_run): Use BitBlt to scroll instead of window scrolling functions. (w32_scroll_bar_clear): Don't clear scroll bars when double buffered. (w32_read_socket): Flip buffers after reading input events in some cases. (w32_free_frame_resources): Free back buffer. (w32_create_terminal): Add new hook. * src/w32term.h (struct w32_output): New fields for handling back buffers. * src/w32xfns.c (select_palette): Fix indentation. (get_frame_dc, release_frame_dc): Return back buffer when appropriate and set dirty flag. --- etc/NEWS | 7 +++ src/w32fns.c | 33 +++++++++- src/w32term.c | 167 ++++++++++++++++++++++++++++++++++---------------- src/w32term.h | 22 +++++++ src/w32xfns.c | 76 ++++++++++++++++++++--- 5 files changed, 240 insertions(+), 65 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 70087f2629..fe8697e28f 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2134,6 +2134,13 @@ to preserve the old behavior, apply ** MS-Windows +--- +*** Emacs now supports double buffering on MS Windows to reduce flicker. +This leads to a noticable reduction in the amount of graphics flicker +during redisplay on many systems, but can also make painting slower. +If that happens, it can be disabled by setting the +'inhibit-double-buffering' frame parameter. + +++ *** Emacs now supports system dark mode. On Windows 10 (version 1809 and higher) and Windows 11, Emacs will now diff --git a/src/w32fns.c b/src/w32fns.c index a880136d0a..d4e4b2a30b 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -1802,6 +1802,25 @@ w32_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) w32_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f)); } +static void +w32_set_inhibit_double_buffering (struct frame *f, + Lisp_Object new_value, + Lisp_Object old_value) +{ + block_input (); + + if (NILP (new_value)) + FRAME_OUTPUT_DATA (f)->want_paint_buffer = 1; + else + { + FRAME_OUTPUT_DATA (f)->want_paint_buffer = 0; + w32_release_paint_buffer (f); + + SET_FRAME_GARBAGED (f); + } + + unblock_input (); +} /* Set the pixel height of the tool bar of frame F to HEIGHT. */ void @@ -4093,7 +4112,9 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { case WM_ERASEBKGND: f = w32_window_to_frame (dpyinfo, hwnd); - if (f) + + enter_crit (); + if (f && !FRAME_OUTPUT_DATA (f)->paint_buffer) { HDC hdc = get_frame_dc (f); GetUpdateRect (hwnd, &wmsg.rect, FALSE); @@ -4107,6 +4128,7 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) wmsg.rect.right, wmsg.rect.bottom)); #endif /* W32_DEBUG_DISPLAY */ } + leave_crit (); return 1; case WM_PALETTECHANGED: /* ignore our own changes */ @@ -6080,6 +6102,10 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, ? make_fixnum (0) : make_fixnum (1), NULL, NULL, RES_TYPE_NUMBER); + gui_default_parameter (f, parameters, Qinhibit_double_buffering, Qnil, + "inhibitDoubleBuffering", "InhibitDoubleBuffering", + RES_TYPE_BOOLEAN); + gui_default_parameter (f, parameters, Qbuffer_predicate, Qnil, "bufferPredicate", "BufferPredicate", RES_TYPE_SYMBOL); gui_default_parameter (f, parameters, Qtitle, Qnil, @@ -7096,6 +7122,9 @@ w32_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms) "alpha", "Alpha", RES_TYPE_NUMBER); gui_default_parameter (f, parms, Qalpha_background, Qnil, "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER); + gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil, + "inhibitDoubleBuffering", "InhibitDoubleBuffering", + RES_TYPE_BOOLEAN); /* Add `tooltip' frame parameter's default value. */ if (NILP (Fframe_parameter (frame, Qtooltip))) @@ -10432,7 +10461,7 @@ w32_get_resource (const char *key, const char *name, LPDWORD lpdwtype) gui_set_alpha, 0, /* x_set_sticky */ 0, /* x_set_tool_bar_position */ - 0, /* x_set_inhibit_double_buffering */ + w32_set_inhibit_double_buffering, w32_set_undecorated, w32_set_parent_frame, w32_set_skip_taskbar, diff --git a/src/w32term.c b/src/w32term.c index 7837032304..ca96320a5e 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -275,6 +275,57 @@ XGetGCValues (void *ignore, XGCValues *gc, } #endif +static void +w32_show_back_buffer (struct frame *f) +{ + struct w32_output *output; + HDC raw_dc; + + output = FRAME_OUTPUT_DATA (f); + + enter_crit (); + + if (output->paint_buffer) + { + raw_dc = GetDC (output->window_desc); + + if (!raw_dc) + emacs_abort (); + + BitBlt (raw_dc, 0, 0, FRAME_PIXEL_WIDTH (f), + FRAME_PIXEL_HEIGHT (f), + output->paint_dc, 0, 0, SRCCOPY); + ReleaseDC (output->window_desc, raw_dc); + + output->paint_buffer_dirty = 0; + } + + leave_crit (); +} + +void +w32_release_paint_buffer (struct frame *f) +{ + /* Delete the back buffer so it gets created + again the next time we ask for the DC. */ + + enter_crit (); + if (FRAME_OUTPUT_DATA (f)->paint_buffer) + { + SelectObject (FRAME_OUTPUT_DATA (f)->paint_dc, + FRAME_OUTPUT_DATA (f)->paint_dc_object); + ReleaseDC (FRAME_OUTPUT_DATA (f)->window_desc, + FRAME_OUTPUT_DATA (f)->paint_buffer_handle); + DeleteDC (FRAME_OUTPUT_DATA (f)->paint_dc); + DeleteObject (FRAME_OUTPUT_DATA (f)->paint_buffer); + + FRAME_OUTPUT_DATA (f)->paint_buffer = NULL; + FRAME_OUTPUT_DATA (f)->paint_dc = NULL; + FRAME_OUTPUT_DATA (f)->paint_buffer_handle = NULL; + } + leave_crit (); +} + static void w32_get_mouse_wheel_vertical_delta (void) { @@ -704,10 +755,19 @@ w32_update_end (struct frame *f) static void w32_frame_up_to_date (struct frame *f) { - if (FRAME_W32_P (f)) - FRAME_MOUSE_UPDATE (f); + FRAME_MOUSE_UPDATE (f); + + if (!buffer_flipping_blocked_p () + && FRAME_OUTPUT_DATA (f)->paint_buffer_dirty) + w32_show_back_buffer (f); } +static void +w32_buffer_flipping_unblocked_hook (struct frame *f) +{ + if (FRAME_OUTPUT_DATA (f)->paint_buffer_dirty) + w32_show_back_buffer (f); +} /* Draw truncation mark bitmaps, continuation mark bitmaps, overlay arrow bitmaps, or clear the fringes if no bitmaps are required @@ -2872,8 +2932,7 @@ w32_scroll_run (struct window *w, struct run *run) { struct frame *f = XFRAME (w->frame); int x, y, width, height, from_y, to_y, bottom_y; - HWND hwnd = FRAME_W32_WINDOW (f); - HRGN expect_dirty; + HDC hdc; /* Get frame-relative bounding box of the text display area of W, without mode lines. Include in this box the left and right @@ -2892,7 +2951,6 @@ w32_scroll_run (struct window *w, struct run *run) height = bottom_y - from_y; else height = run->height; - expect_dirty = CreateRectRgn (x, y + height, x + width, bottom_y); } else { @@ -2902,44 +2960,15 @@ w32_scroll_run (struct window *w, struct run *run) height = bottom_y - to_y; else height = run->height; - expect_dirty = CreateRectRgn (x, y, x + width, to_y); } block_input (); - /* Cursor off. Will be switched on again in gui_update_window_end. */ gui_clear_cursor (w); - - { - RECT from; - RECT to; - HRGN dirty = CreateRectRgn (0, 0, 0, 0); - HRGN combined = CreateRectRgn (0, 0, 0, 0); - - from.left = to.left = x; - from.right = to.right = x + width; - from.top = from_y; - from.bottom = from_y + height; - to.top = y; - to.bottom = bottom_y; - - ScrollWindowEx (hwnd, 0, to_y - from_y, &from, &to, dirty, - NULL, SW_INVALIDATE); - - /* Combine this with what we expect to be dirty. This covers the - case where not all of the region we expect is actually dirty. */ - CombineRgn (combined, dirty, expect_dirty, RGN_OR); - - /* If the dirty region is not what we expected, redraw the entire frame. */ - if (!EqualRgn (combined, expect_dirty)) - SET_FRAME_GARBAGED (f); - - DeleteObject (dirty); - DeleteObject (combined); - } - + hdc = get_frame_dc (f); + BitBlt (hdc, x, to_y, width, height, hdc, x, from_y, SRCCOPY); + release_frame_dc (f, hdc); unblock_input (); - DeleteObject (expect_dirty); } @@ -4809,6 +4838,9 @@ w32_scroll_bar_clear (struct frame *f) { Lisp_Object bar; + if (FRAME_OUTPUT_DATA (f)->paint_buffer) + return; + /* We can have scroll bars even if this is 0, if we just turned off scroll bar mode. But in that case we should not clear them. */ @@ -4928,6 +4960,8 @@ w32_read_socket (struct terminal *terminal, /* w32_name_of_message (msg.msg.message), */ /* msg.msg.time)); */ + f = NULL; + EVENT_INIT (inev); inev.kind = NO_EVENT; inev.arg = Qnil; @@ -4969,24 +5003,33 @@ w32_read_socket (struct terminal *terminal, } else { - /* Erase background again for safety. But don't do - that if the frame's 'garbaged' flag is set, since - in that case expose_frame will do nothing, and if - the various redisplay flags happen to be unset, - we are left with a blank frame. */ - if (!FRAME_GARBAGED_P (f) || FRAME_PARENT_FRAME (f)) + enter_crit (); + if (!FRAME_OUTPUT_DATA (f)->paint_buffer) { - HDC hdc = get_frame_dc (f); - - w32_clear_rect (f, hdc, &msg.rect); - release_frame_dc (f, hdc); + /* Erase background again for safety. But don't do + that if the frame's 'garbaged' flag is set, since + in that case expose_frame will do nothing, and if + the various redisplay flags happen to be unset, + we are left with a blank frame. */ + + if (!FRAME_GARBAGED_P (f) || FRAME_PARENT_FRAME (f)) + { + HDC hdc = get_frame_dc (f); + + w32_clear_rect (f, hdc, &msg.rect); + release_frame_dc (f, hdc); + } + + expose_frame (f, + msg.rect.left, + msg.rect.top, + msg.rect.right - msg.rect.left, + msg.rect.bottom - msg.rect.top); + w32_clear_under_internal_border (f); } - expose_frame (f, - msg.rect.left, - msg.rect.top, - msg.rect.right - msg.rect.left, - msg.rect.bottom - msg.rect.top); - w32_clear_under_internal_border (f); + else + w32_show_back_buffer (f); + leave_crit (); } } break; @@ -5659,6 +5702,8 @@ w32_read_socket (struct terminal *terminal, if (width != FRAME_PIXEL_WIDTH (f) || height != FRAME_PIXEL_HEIGHT (f)) { + w32_release_paint_buffer (f); + change_frame_size (f, width, height, false, true, false); SET_FRAME_GARBAGED (f); @@ -5840,6 +5885,17 @@ w32_read_socket (struct terminal *terminal, } count++; } + + /* Event processing might have drawn to F outside redisplay. If + that is the case, flush any changes that have been made to + the front buffer. */ + + if (f && FRAME_OUTPUT_DATA (f)->paint_buffer_dirty + /* WM_WINDOWPOSCHANGED makes the buffer dirty, but there's + no reason to flush it here, and that also causes + flicker. */ + && !f->garbaged && msg.msg.message != WM_WINDOWPOSCHANGED) + w32_show_back_buffer (f); } /* If the focus was just given to an autoraising frame, @@ -7054,6 +7110,9 @@ w32_free_frame_resources (struct frame *f) face. */ free_frame_faces (f); + /* Now release the back buffer if any exists. */ + w32_release_paint_buffer (f); + if (FRAME_W32_WINDOW (f)) my_destroy_window (f, FRAME_W32_WINDOW (f)); @@ -7350,6 +7409,7 @@ w32_create_terminal (struct w32_display_info *dpyinfo) terminal->update_end_hook = w32_update_end; terminal->read_socket_hook = w32_read_socket; terminal->frame_up_to_date_hook = w32_frame_up_to_date; + terminal->buffer_flipping_unblocked_hook = w32_buffer_flipping_unblocked_hook; terminal->defined_color_hook = w32_defined_color; terminal->query_frame_background_color = w32_query_frame_background_color; terminal->query_colors = w32_query_colors; @@ -7505,6 +7565,7 @@ w32_delete_display (struct w32_display_info *dpyinfo) if (dpyinfo->palette) DeleteObject (dpyinfo->palette); } + w32_reset_fringes (); } diff --git a/src/w32term.h b/src/w32term.h index 6c48323651..2dcc43fc59 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -412,6 +412,27 @@ #define PIX_TYPE COLORREF geometry when 'fullscreen' is reset to nil. */ WINDOWPLACEMENT normal_placement; int prev_fsmode; + + /* The back buffer if there is an ongoing double-buffered drawing + operation. */ + HBITMAP paint_buffer; + + /* The handle of the back buffer and a DC that ought to be released + alongside the back buffer. */ + HDC paint_dc, paint_buffer_handle; + + /* The object previously selected into `paint_dc'. */ + HGDIOBJ paint_dc_object; + + /* The width and height of `paint_buffer'. */ + int paint_buffer_width, paint_buffer_height; + + /* Whether or not some painting was done to this window that has not + yet been drawn. */ + unsigned paint_buffer_dirty : 1; + + /* Whether or not this frame should be double buffered. */ + unsigned want_paint_buffer : 1; }; extern struct w32_output w32term_display; @@ -876,6 +897,7 @@ #define GUI_SDATA(x) ((guichar_t*) SDATA (x)) extern Lisp_Object w32_popup_dialog (struct frame *, Lisp_Object, Lisp_Object); extern void w32_arrow_cursor (void); +extern void w32_release_paint_buffer (struct frame *); extern void syms_of_w32term (void); extern void syms_of_w32menu (void); diff --git a/src/w32xfns.c b/src/w32xfns.c index d5974b906e..139985c5bd 100644 --- a/src/w32xfns.c +++ b/src/w32xfns.c @@ -136,13 +136,13 @@ select_palette (struct frame *f, HDC hdc) f->output_data.w32->old_palette = NULL; if (RealizePalette (hdc) != GDI_ERROR) - { - Lisp_Object frame, framelist; - FOR_EACH_FRAME (framelist, frame) { - SET_FRAME_GARBAGED (XFRAME (frame)); + Lisp_Object frame, framelist; + FOR_EACH_FRAME (framelist, frame) + { + SET_FRAME_GARBAGED (XFRAME (frame)); + } } - } } void @@ -157,19 +157,68 @@ deselect_palette (struct frame *f, HDC hdc) HDC get_frame_dc (struct frame *f) { - HDC hdc; + HDC hdc, paint_dc; + HBITMAP back_buffer; + HGDIOBJ obj; + struct w32_output *output; if (f->output_method != output_w32) emacs_abort (); enter_crit (); + output = FRAME_OUTPUT_DATA (f); + + if (output->paint_dc) + { + if (output->paint_buffer_width != FRAME_PIXEL_WIDTH (f) + || output->paint_buffer_height != FRAME_PIXEL_HEIGHT (f)) + w32_release_paint_buffer (f); + else + { + output->paint_buffer_dirty = 1; + return output->paint_dc; + } + } - hdc = GetDC (f->output_data.w32->window_desc); + hdc = GetDC (output->window_desc); /* If this gets called during startup before the frame is valid, there is a chance of corrupting random data or crashing. */ if (hdc) - select_palette (f, hdc); + { + select_palette (f, hdc); + + if (FRAME_OUTPUT_DATA (f)->want_paint_buffer) + { + back_buffer + = CreateCompatibleBitmap (hdc, FRAME_PIXEL_WIDTH (f), + FRAME_PIXEL_HEIGHT (f)); + + if (back_buffer) + { + paint_dc = CreateCompatibleDC (hdc); + + if (!paint_dc) + DeleteObject (back_buffer); + else + { + obj = SelectObject (paint_dc, back_buffer); + + output->paint_dc_object = obj; + output->paint_dc = paint_dc; + output->paint_buffer_handle = hdc; + output->paint_buffer = back_buffer; + output->paint_buffer_width = FRAME_PIXEL_WIDTH (f); + output->paint_buffer_height = FRAME_PIXEL_HEIGHT (f); + output->paint_buffer_dirty = 1; + + SET_FRAME_GARBAGED (f); + + return paint_dc; + } + } + } + } return hdc; } @@ -179,8 +228,15 @@ release_frame_dc (struct frame *f, HDC hdc) { int ret; - deselect_palette (f, hdc); - ret = ReleaseDC (f->output_data.w32->window_desc, hdc); + /* Avoid releasing the double-buffered DC here, since it'll be + released upon the next buffer flip instead. */ + if (hdc != FRAME_OUTPUT_DATA (f)->paint_dc) + { + deselect_palette (f, hdc); + ret = ReleaseDC (f->output_data.w32->window_desc, hdc); + } + else + ret = 0; leave_crit (); -- 2.34.1