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. */