From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: =?UTF-8?Q?Cl=c3=a9ment_Pit-Claudel?= Newsgroups: gmane.emacs.devel Subject: Re: x-export-frames for non-Cairo builds Date: Fri, 26 Jan 2018 18:13:28 -0500 Message-ID: <13e61094-02dd-d7ba-c48e-25f1187f0e9b@gmail.com> References: <83372tm491.fsf@gnu.org> <83607ola9h.fsf@gnu.org> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------EB67524E0D3FABEC1C2CA7D2" X-Trace: blaine.gmane.org 1517008351 16500 195.159.176.226 (26 Jan 2018 23:12:31 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Fri, 26 Jan 2018 23:12:31 +0000 (UTC) User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.5.0 Cc: emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sat Jan 27 00:12:27 2018 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1efDAQ-0003OO-JA for ged-emacs-devel@m.gmane.org; Sat, 27 Jan 2018 00:12:18 +0100 Original-Received: from localhost ([::1]:33448 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1efDCP-0005l9-1D for ged-emacs-devel@m.gmane.org; Fri, 26 Jan 2018 18:14:21 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:36580) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1efDBh-0005kT-JI for emacs-devel@gnu.org; Fri, 26 Jan 2018 18:13:39 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1efDBc-0001Yd-HA for emacs-devel@gnu.org; Fri, 26 Jan 2018 18:13:37 -0500 Original-Received: from mail-it0-x233.google.com ([2607:f8b0:4001:c0b::233]:36001) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1efDBc-0001YR-83; Fri, 26 Jan 2018 18:13:32 -0500 Original-Received: by mail-it0-x233.google.com with SMTP id n206so1042847itg.1; Fri, 26 Jan 2018 15:13:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language; bh=0D6D31jH0WQbayzezZ9+vkS41LMVYhJuZvU0DAq9T30=; b=p1cVv+Ld84fJpxQ8UJGRdSHVP5+Yn6c4RAP6DUKNMKdQWqy17gtkqNSUKvOn0yZW4A KH3mga1lzuHQFjC/qtQIfUW/CWBMMBmUqk0ZmyC6f7UqMNMcuxatB7jVApweDlna2aaj 3cvN+hrBquzHoNo4k6hDNvjf7xXgdvDJW1FyQZ6uD9kAn9QzlzIhO4GnmvUKU98wOvWr cKApTCjggiNscQ6/bnJ86xmJJ/0kuF7fILuCM+JPSxo9mLj091dr3WnKIlj2yjGaw8Bi IkqPx2u7ybYDUJr+E30IO0oe0HFbaqo58Twcb6pDooGNl9hI8U2eARC/RgEGY8jbvp+l HFug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language; bh=0D6D31jH0WQbayzezZ9+vkS41LMVYhJuZvU0DAq9T30=; b=EOqRFsfltwDAJh2T7VAkvXYmPYK9YK7zj8uQ3Di9RwFY2oSeXQyiqb8l5x8xvsBF04 VQOIwWw4tpkdatJSlZAKKv8XjgNtCgfZ4zg8GDCh/Uo6XUPcQAPd6luL1/I0uVcMWyFH SoEJJlqoY3+E8M7WVsxvKlHUx461jIIkZW7AX/l40kCmmBZMSMKxgmcKVX/CPKfId4E6 OKhP8XF+4RDJjlb74w/F6aZHftVnqoAKaW9MSXq85nZzb+xi7SBLXe21GoAc4dHmEtzq DRsjH7P03fwqGaxxxpgNPH/FovS8kBg6lIe0DpKu0bRP13GKXxxZ9OrmGuE+nbYH6t40 kbZQ== X-Gm-Message-State: AKwxytfzv9LXLH4UymvCoA/CKx7auS1ey01czlFufi42PtOKmHzgG2pH 748ZqJWLwlGmYyRglz2y9eFxXRMo X-Google-Smtp-Source: AH8x227R1qni8ILaRHBJxRg8dppEwnaF0GYqPd81bbvQg1xEC0eW8c71jupyF/TjUt6hLpQFrhyA0Q== X-Received: by 10.36.6.139 with SMTP id 133mr17594211itv.92.1517008411457; Fri, 26 Jan 2018 15:13:31 -0800 (PST) Original-Received: from [18.26.2.123] (26-2-123.dynamic.csail.mit.edu. [18.26.2.123]) by smtp.gmail.com with ESMTPSA id 97sm1089028ioh.31.2018.01.26.15.13.30 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 26 Jan 2018 15:13:30 -0800 (PST) In-Reply-To: <83607ola9h.fsf@gnu.org> Content-Language: en-GB X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2607:f8b0:4001:c0b::233 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 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.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:222262 Archived-At: This is a multi-part message in MIME format. --------------EB67524E0D3FABEC1C2CA7D2 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit On 2018-01-26 13:56, Eli Zaretskii wrote: >> Cc: emacs-devel@gnu.org >> From: Clément Pit-Claudel >> Date: Fri, 26 Jan 2018 11:08:17 -0500 >> >> But do you think it's better to save the image, rather than what x-export-frame currently does? > > Yes, I do. Understood, thanks. Here's a first draft of the patch. I have a few questions: * Is there a way to wrap 'attributes: noreturn' in a preprocessor directive? I used an auxilliary C function instead because of that. * Is it OK to reuse `frame' after calling `decode_window_system_frame (frame)'? * What would be the proper way to save the output of Fx_export_frame (there's a FIXME at that point in the code)? I could either use a_write on the actual string data, or add further branches to x_cr_export_frame to write to disk directly. * x_cr_export_frame does a number of things that I don't understand too well: specbind (Qredisplay_dont_pause, Qt); redisplay_preserve_echo_area (31); block_input (); record_unwind_protect (x_cr_destroy, make_save_ptr (cr)); x_clear_area (f, 0, 0, width, height); expose_frame (f, 0, 0, width, height); Do I need any of these in x_gtk3_export_frame? * The docs of gdk_pixbuf_get_from_window say that "If the window you’re obtaining data from is partially obscured by other windows, then the contents of the pixbuf areas corresponding to the obscured regions are undefined". Is there a way I can check for that? Once the API is stabilized, I'll write a proper commit message and documentation. I made it possible for the function to return a string instead of writing to disk to save time (I'm hoping to make Emacs screencasts). One issue is that the cast to an Emacs string is still quite slow (quick benchmarks: I did 200 screenshots with xwd, with my new function saving a png and then a bmp into a string, and with my new function saving a png and then a bmp to disk: 1.82s, 5.1s, 1.84s, 5.2s, 0.7s[!]). Is there a trick I could use to make image capture faster? Could I store the GdkPixbuf directly into an Emacs image and save it later? Thanks a lot! On 2018-01-26 09:45, Stefan Monnier wrote: > And which code to use should be based on dispatching on the frame type > (so it can work even if we have several different kinds of frame types: I'm not sure I understood this right. Does the patch below do what you had in mind? Cheers, Clément. --------------EB67524E0D3FABEC1C2CA7D2 Content-Type: text/x-patch; name="export-frame.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="export-frame.patch" diff --git a/src/frame.c b/src/frame.c index 9b56080..f0c0d34 100644 --- a/src/frame.c +++ b/src/frame.c @@ -3508,6 +3508,67 @@ bottom edge of FRAME's display. */) return Qt; } + +#ifdef HAVE_WINDOW_SYSTEM +static +#if ! (defined HAVE_GTK3 || defined USE_CAIRO) +_Noreturn +#endif +Lisp_Object +export_frame (Lisp_Object frame, Lisp_Object type, Lisp_Object fname) +{ + struct frame *f = decode_window_system_frame (frame); + + if (!FRAME_VISIBLE_P (f)) + error ("Frames to be exported must be visible"); + + if (FRAME_OBSCURED_P (f)) + error ("Frames to be exported must not be obscured"); + + if (!NILP(fname)) + CHECK_STRING(fname); + + // Stefan, is this what you had in mind? + switch (f->output_method) + { + case output_x_window: +#ifdef USE_CAIRO + // FIXME: save output when FNAME is non-nil + /* Question: Is it OK to reuse FRAME here? */ + return Fx_export_frames(frame, type); +#elif HAVE_GTK3 + return x_gtk3_export_frame(f, type, fname); +#endif + /* Fall through */ + case output_initial: + case output_termcap: + case output_w32: + case output_msdos_raw: + case output_ns: + default: + error ("Unsupported toolkit"); + } +} + +DEFUN ("export-frame", Fexport_frame, + Sexport_frame, 0, 3, 0, + doc: /* Capture a screenshot of FRAME in TYPE format. +Available formats depend on the graphic toolkit in use. +Currently, this function only works with Cairo and GTK3. + +FRAME must be a live, visible, and unobscured frame. It defaults to +the selected one. TYPE is a symbol: on Cairo, valid formats may +include `pdf', `png', `postscript', and `svg', depending on +compilation options; on GTK3, valid formats are `png' and `bmp'. + +If FNAME is non-nil, save the resulting capture under FNAME; if +FNAME is nil, return the captured data as a unibyte string. */) + (Lisp_Object frame, Lisp_Object type, Lisp_Object fname) +{ + return export_frame(frame, type, fname); +} +#endif // HAVE_WINDOW_SYSTEM + /*********************************************************************** Frame Parameters @@ -6141,6 +6202,7 @@ iconify the top level frame instead. */); defsubr (&Sframe_pointer_visible_p); #ifdef HAVE_WINDOW_SYSTEM + defsubr (&Sexport_frame); defsubr (&Sx_get_resource); defsubr (&Sx_parse_geometry); #endif diff --git a/src/xfns.c b/src/xfns.c index 43c55cc..dc4ef8d 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -7419,6 +7419,48 @@ present and mapped to the usual X keysyms. */) Printing ***********************************************************************/ +#ifdef HAVE_GTK3 +Lisp_Object +x_gtk3_export_frame (struct frame* frame, Lisp_Object type, Lisp_Object fname) +{ + int width = FRAME_PIXEL_WIDTH (frame); + int height = FRAME_PIXEL_HEIGHT (frame); + + const char* stype; + if (NILP (type) || EQ (type, Qpng)) + stype = "png"; + else if (EQ (type, Qbmp)) + stype = "bmp"; + else + error("Unsupported image type"); + + GdkWindow *w = gtk_widget_get_window (FRAME_GTK_WIDGET (frame)); + if (!w) + error("Could not retrieve a GTK window for this frame"); + + // gdk_pixbuf_xlib_get_from_drawable(NULL, w, NULL, NULL, 0, 0, 0, 0, width, height); + GdkPixbuf *pb = gdk_pixbuf_get_from_window(w, 0, 0, width, height); + if (!pb) + error("Could not capture a screenshot of this frame"); + + if (NILP(fname)) + { + char* buf; + long unsigned int buf_size; + if (!gdk_pixbuf_save_to_buffer (pb, &buf, &buf_size, stype, NULL, NULL)) + error ("Could not convert the frame data"); + return make_unibyte_string (buf, buf_size); + } + else + { + Lisp_Object encoded_fname = ENCODE_FILE (fname); + if (!gdk_pixbuf_save (pb, SSDATA (encoded_fname), stype, NULL, NULL)) + error ("Could not save the frame data"); + return Qnil; + } +} +#endif // HAVE_GTK3 + #ifdef USE_CAIRO DEFUN ("x-export-frames", Fx_export_frames, Sx_export_frames, 0, 2, 0, doc: /* Return image data of FRAMES in TYPE format. @@ -7443,7 +7485,7 @@ compile-time configuration of cairo. */) XSETFRAME (frame, f); if (!FRAME_VISIBLE_P (f)) - error ("Frames to be exported must be visible."); + error ("Frames to be exported must be visible"); tmp = Fcons (frame, tmp); } frames = Fnreverse (tmp); @@ -7457,7 +7499,7 @@ compile-time configuration of cairo. */) if (EQ (type, Qpng)) { if (!NILP (XCDR (frames))) - error ("PNG export cannot handle multiple frames."); + error ("PNG export cannot handle multiple frames"); surface_type = CAIRO_SURFACE_TYPE_IMAGE; } else @@ -7472,7 +7514,7 @@ compile-time configuration of cairo. */) { /* For now, we stick to SVG 1.1. */ if (!NILP (XCDR (frames))) - error ("SVG export cannot handle multiple frames."); + error ("SVG export cannot handle multiple frames"); surface_type = CAIRO_SURFACE_TYPE_SVG; } else @@ -7545,7 +7587,7 @@ visible. */) XSETFRAME (frame, f); if (!FRAME_VISIBLE_P (f)) - error ("Frames to be printed must be visible."); + error ("Frames to be printed must be visible"); tmp = Fcons (frame, tmp); } frames = Fnreverse (tmp); @@ -7633,6 +7675,7 @@ syms_of_xfns (void) DEFSYM (Qmono, "mono"); DEFSYM (Qassq_delete_all, "assq-delete-all"); + DEFSYM (Qbmp, "bmp"); #ifdef USE_CAIRO DEFSYM (Qpdf, "pdf"); diff --git a/src/xterm.h b/src/xterm.h index 1849a5c..267ade6 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1082,6 +1082,9 @@ extern void x_real_pos_and_offsets (struct frame *f, int *xptr, int *yptr, int *outer_border); +extern Lisp_Object x_gtk3_export_frame (struct frame* frame, + Lisp_Object fname, + Lisp_Object type); /* From xrdb.c. */ --------------EB67524E0D3FABEC1C2CA7D2--