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: Thu, 28 Apr 2022 20:45:04 +0800 Message-ID: <87sfpxz8a7.fsf@yahoo.com> References: <877d791thh.fsf.ref@yahoo.com> <877d791thh.fsf@yahoo.com> <83ilqtbl3p.fsf@gnu.org> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="40127"; 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 Thu Apr 28 14:47:56 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 1nk3Yp-000AA4-Oz for ged-emacs-devel@m.gmane-mx.org; Thu, 28 Apr 2022 14:47:55 +0200 Original-Received: from localhost ([::1]:58784 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nk3Yo-00088x-Ef for ged-emacs-devel@m.gmane-mx.org; Thu, 28 Apr 2022 08:47:54 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:34096) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nk3WH-0005Ls-4I for emacs-devel@gnu.org; Thu, 28 Apr 2022 08:45:17 -0400 Original-Received: from sonic315-20.consmr.mail.ne1.yahoo.com ([66.163.190.146]:33918) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1nk3WE-0008OH-0r for emacs-devel@gnu.org; Thu, 28 Apr 2022 08:45:16 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1651149911; bh=KNHWZ/OuGNnlcfEE3thou9IcwLnnpm8J1Xmmc3kW/UA=; h=From:To:Cc:Subject:References:Date:In-Reply-To:From:Subject:Reply-To; b=VjpesyzibJHQLXhfmNTw4h1rdISd/13JOyDppQZtiNeVNCiEA0J2pZmop6Y6BZNiSoZjRJfGxNczfm16CqTADiPEtg8aY9ttqJxco1F+N7gybSZs9JMHcWE5h5i5BxcWgKUSOxIEKGURfjG3Jg6CuuqWffye5BN3s769v9kUho/ZaSOAHAg4l9RiSei46BBphk+G+vnGfpkHe/XZw78G7V/v+RVfRqUqk/ogZF/m39BGLeFyqLINCGVG3W1S3nivxjVRwWOlkG0eojmc8QiFwq4KlpHHIawJVVDHYB2xpft4XROq9FibFSiAS5hy17V6fyJ5ZsILX0iQJGtcMKRTow== X-SONIC-DKIM-SIGN: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1651149911; bh=MhO9f7+HmP1DB3b7EL2DPcVZjXehtStXnTh212hVii+=; h=X-Sonic-MF:From:To:Subject:Date:From:Subject; b=gY9qpLDP+EDcCCDCquBK/vxHq26utZ0eFJa3vieh8U0daNfhsqdFRs/hG4DfXo+qeGucJN1Nv6snsX6xYsvtwMhRfdhkb0SpnahvU0see+w4Lx9IgFl0dAfc4OXWrvTnlACn3Az+fO93VQ6ouMXvDsUQoZns0IhmNXiRzkTnxA6CK5nwGow6O9YTcKlSwi6Vpyjbu9hMGfY2XexPKbdo6utio0O8++8m3B+TzoAkpV1jj/DeKBmVrh2qiXvaZGlhsdmswZ1I5atfExhHX16KHf2oH3cmK8zf9K2YFFBOPkwMJ5tsHLxE/6to8InPgZZTSuaql3REIFr5eE7LC5ZtVw== X-YMail-OSG: rv2RNYsVM1mKPeUxDBZtPA_10SNlcPorX46c5b2cA.Ve5NFqSuIBi46Ci.i6sxf 4BUb5qUpWnxSk2Fkju61Ka7HXHtuZyTtsM6vDRuTuMGVbE3DkkPz4uDb3.oqtgsu3LtB0PNRcH3k udQAstNVH2P0L0OSKMlpu60Qa57wtSa_TJSMDRwwCnHI59HYnbzds97aVNVvJbLFYZlWw.8JiH7Y cvXs6EYUKun1LyXGegyD3H6l9grPrMU64m_wOyZPb4E4GAdLQ1WkjaxUmNv42xf7YXz_jYXrVVU7 _.e6wjH2oiTvpf1iwHNZvNAq10WyjKJkoja2jwjXo_J5Rh9c9hhVuo_mZDQDovD8ClMQ6QKebNzD ZOep2PHz9GzYhYzY30RFE8._l4eQ3yunBAB4AQmaw4z9358FmXyeYG__VyZPvE6aNbGOy4BTV5DA lYid4u8idpH0JwDljjiUPeqOdnUQd.GBLXjNlVNajtBJQ1cyrVcO_JXTh5YNABWcQjBO1mXfmjto Gf6eLgJC6ge1JN02FAHh5IojURxYvIsBWHADcIew9Vc_rU2O3mH_H6kfGHjDQuO_Giv3Gi8v07gu 65jhz4qBHI6i65A4aAdGGwtco0krMxmkk_DL2n0a.Pg0KBs1o.yERmEckJzW8pAXAEXlbNBb5sxQ QE_5l4lJeaJ8KIugw4dwZnN32JLVYyu2sROh1EuXc5GcSsscaphTDDqa869RP3ur8SKODeL8uQiC vL78KJwneBkAAGa5xfZ0OexAyLSlMMYE9JOzESCwKCt7XXvLlA2Ka18W5ym12vES3EomN8YoM6Bv H.71SPx4LHLpDNMn5oe068bNhvwZ0XaOT7brouNCQ. X-Sonic-MF: Original-Received: from sonic.gate.mail.ne1.yahoo.com by sonic315.consmr.mail.ne1.yahoo.com with HTTP; Thu, 28 Apr 2022 12:45:11 +0000 Original-Received: by hermes--canary-production-sg3-795d7b4d54-q68ds (Yahoo Inc. Hermes SMTP Server) with ESMTPA ID 39f8dbbcc10c8791bd9f29753c9acd0c; Thu, 28 Apr 2022 12:45:08 +0000 (UTC) In-Reply-To: <83ilqtbl3p.fsf@gnu.org> (Eli Zaretskii's message of "Thu, 28 Apr 2022 12:42:02 +0300") 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.146; envelope-from=luangruo@yahoo.com; helo=sonic315-20.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:288943 Archived-At: > Seconded. 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. diff --git a/src/w32term.c b/src/w32term.c index 7837032304..245d46a499 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,18 @@ 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) +{ + 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 +2931,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 +2950,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 +2959,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); } @@ -4928,6 +4956,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; @@ -5659,6 +5689,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 +5872,14 @@ 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 + && !f->garbaged) + w32_show_back_buffer (f); } /* If the focus was just given to an autoraising frame, @@ -7054,6 +7094,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 +7393,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 +7549,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..7df270eb5d 100644 --- a/src/w32term.h +++ b/src/w32term.h @@ -412,6 +412,24 @@ #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; }; extern struct w32_output w32term_display; @@ -876,6 +894,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..e08b3519d3 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,65 @@ 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); + + 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 +225,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 ();