* Emacs canvas support [not found] <875zdikdge.fsf.ref@yahoo.com> @ 2020-04-29 6:34 ` Po Lu via Emacs development discussions. 2020-04-29 8:24 ` Eli Zaretskii 2020-04-29 20:48 ` Juri Linkov 0 siblings, 2 replies; 53+ messages in thread From: Po Lu via Emacs development discussions. @ 2020-04-29 6:34 UTC (permalink / raw) To: emacs-devel [-- Attachment #1: Type: text/plain, Size: 301 bytes --] I'd appreciate some feedback on something I came up with during my spare time: Emacs canvas support. For now it only works on X11 + Cairo builds, and I haven't quite figured out how to make redisplay work reliably on canvases, but it already seems to be quite promising. A patch is attached below. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: Canvas patche --] [-- Type: text/x-patch, Size: 61624 bytes --] diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index e53f0e9f60..2a24576620 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -27,6 +27,7 @@ Display * Window Dividers:: Separating windows visually. * Display Property:: Images, margins, text size, etc. * Images:: Displaying images in Emacs buffers. +* Canvases:: Drawing areas inside Emacs buffers. * Xwidgets:: Displaying native widgets in Emacs buffers. * Buttons:: Adding clickable buttons to Emacs buffers. * Abstract Display:: Emacs's Widget for Object Collections. @@ -6509,6 +6510,152 @@ Image Cache debugging. @end defvar +@node Canvases +@cindex drawing canvases +@cindex drawing areas +@cindex canvases +@section Canvases + +This chapter describes canvases, objects that can store drawing operations +which are then displayed inside buffer text. + +@menu +* Creating Canvases:: How canvases can be created +* Operating on Canvases:: How canvases can be used +* Displaying Canvases:: How canvases can be displayed +@end menu + +@node Creating Canvases +@cindex creating Canvases +@pindex make-canvas + + This section describes how canvases can be created. +To create a canvas, call the function @code{make-canvas}. + +@defun make-canvas width height +This function takes 2 arguments @code{width}, and @code{height}, +and creates a canvas @code{width} wide and @code{height} tall. +@end defun + +@defun canvas-from-image image &optional width height +This function creates a canvas from the image descriptor +@code{image}. The created canvas will be @code{width} wide, +and @code{height} tall, if specified. +@end defun + +@node Operating on Canvases +@cindex operating on Canvases +@pindex canvas-rectangle + + This section describes how canvases can be drawn to, +and manipulated. + +@defun canvasp canvas +Return whether @code{canvas} is a canvas or not. +@end defun + +@defun canvas-dimensions canvas +Return the dimensions of @code{canvas} as a pair. +@end defun + +@defun canvas-ellipse canvas x y width height &optional color + hollow opacity +Draw an ellipse centred upon @code{x}, @code{y} onto the canvas +@code{canvas}. The drawn ellipse will be colored @code{color}, +or the current frame's foreground color if @code{color} is not +specified or nil. The opacity of the drawn item will be +@code{opacity}, and the item will be hollow if @code{hollow} is +non-nil. +@end defun + +@defun canvas-rectangle canvas x y width height &optional color + hollow opacity +Draw a rectangle at @code{x}, and @code{y} onto the canvas +@code{canvas}. The rectangle will be colored @code{color}, +or the current frame's foreground color if @code{color} is nil. +The opacity of the drawn item will be @code{opacity}, and the item +will be hollow if @code{hollow} is non-nil. +@end defun + +@defun canvas-fill-pixel canvas x y color opacity +Fill the pixel at @code{x}, @code{y} inside @code{canvas} +to @code{color}, with the opacity @code{opacity}. +@end defun + +@defun canvas-draw-string canvas x y string + &optional color opacity family size +Draw the string @code{string} to @code{x}, @code{y} +inside the canvas @code{canvas}. The font family used +will be @code{family}, the color @code{color}, the opacity +@code{opacity}, and the size @code{size}. + +@code{family} can either be a string, or a list in which +the first element should be the family as a string, the +second element should be whether the font should be italic, +and an optional third argument describing whether or not +the font should be bold. +@end defun + +@defun canvas-draw-image canvas image-spec x y + &optional width height frame opacity +Paint @code{image-spec} into @code{canvas} at @code{x}, +@code{y}. If @code{width} or @code{height} +is set and the image is wider than @code{width} or @code{height} respectively, +the image will be cropped to fit. The alpha channel of @code{image-spec} +will be set to @code{opacity}. +@end defun + +@defun canvas-measure-string canvas string &optional family size +Return a cons pair containing the width and height of @code{string}, +when rendered onto @code{canvas}, with the font @code{family} at +@code{size}. +@end defun + +@defun canvas-rounded-rectangle canvas x y width height radius + &optional color hollow opacity +Draw a rounded rectangle at @code{x}, @code{y} onto @code{canvas}. +The opacity of the rectangle will be @code{opacity}. +The radius of the rectangle will be @code{radius}. +@end defun + +@defun canvas-pixel-at canvas x y +Return the pixel at @code{x}, @code{y} inside @code{canvas}, +as an ARGB list. +@end defun + +@defun canvas-draw-canvas canvas canvas2 x y &optional width height opacity +Draw @code{canvas2} onto @code{canvas} at @code{x}, @code{y}. +@code{canvas2}'s alpha channel will be set to @code{opacity}, +if specified. +@code{canvas2} will not be taller than @code{height} or wider than +@code{width}, if specified. +@end defun + +@defun canvas-width canvas +Return the width of @code{canvas}. +@end defun + +@defun canvas-height canvas +Return the height of @code{canvas}. +@end defun + +@defun canvas-region canvas x y width height +Return a subsection of @code{canvas} at @code{x}, +@code{y}, that is @code{width} wide and @code{height} tall. +@end defun + +@defun canvas-arc canvas x y radius angle1 angle2 &optional color opacity +Draw an arc at @code{x}, @code{y}, with a radius of @code{radius}, +and the angles @code{angle1}, @code{angle2}. +@end defun + +@node Displaying Canvases +@cindex displaying Canvases + + Canvases can be displayed by setting them as the +@code{display} property of a string. + + @node Xwidgets @section Embedded Native Widgets @cindex xwidget diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index bba1b63115..d995ec4606 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -1430,6 +1430,7 @@ Top * Window Dividers:: Separating windows visually. * Display Property:: Enabling special display features. * Images:: Displaying images in Emacs buffers. +* Canvases:: Drawing areas inside Emacs buffers. * Buttons:: Adding clickable buttons to Emacs buffers. * Abstract Display:: Emacs's Widget for Object Collections. * Blinking:: How Emacs shows the matching open parenthesis. diff --git a/lisp/canvas.el b/lisp/canvas.el new file mode 100644 index 0000000000..d60d3efa4f --- /dev/null +++ b/lisp/canvas.el @@ -0,0 +1,49 @@ +;;; canvas.el --- Canvas support for GNU Emacs + +;; Copyright (C) 2020 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; These are several utlity functions for canvas operations that can +;;; be implemented in Lisp code. + +\f +;;; Code: + +(defun canvas-fill-pixel (canvas x y color opacity) + "Set the pixel at X, Y inside CANVAS to COLOR, with the opacity OPACITY." + (canvas-rectangle canvas x y 1 1 color opacity)) + +(defun canvas-from-image (image &optional width height) + "Create a canvas from IMAGE. +The canvas will be no wider than WIDTH (if specified), +and no taller than HEIGHT (if specified)." + (let ((canvas (make-canvas (or width (car (image-size image t))) + (or height (cdr (image-size image t)))))) + (prog1 canvas (canvas-draw-image canvas image 0 0)))) + +(defun canvas-width (canvas) + "Return the width of CANVAS." + (car (canvas-dimensions canvas))) + +(defun canvas-height (canvas) + "Return the height of CANVAS." + (cdr (canvas-dimensions canvas))) + +(provide 'canvas) +;;; canvas.el ends here diff --git a/lisp/loadup.el b/lisp/loadup.el index 97525b2708..1a9b1c7410 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -285,6 +285,8 @@ (load "emacs-lisp/tabulated-list") (load "buff-menu") +(load "canvas") + (if (fboundp 'x-create-frame) (progn (load "fringe") diff --git a/src/Makefile.in b/src/Makefile.in index 552dd2e50a..0d70c68b04 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -407,6 +407,7 @@ .m.o: ## be dumped as pure by dump-emacs. base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ charset.o coding.o category.o ccl.o character.o chartab.o bidi.o \ + common-canvas.o \ $(CM_OBJ) term.o terminal.o xfaces.o $(XOBJ) $(GTK_OBJ) $(DBUS_OBJ) \ emacs.o keyboard.o macros.o keymap.o sysdep.o \ bignum.o buffer.o filelock.o insdel.o marker.o \ diff --git a/src/alloc.c b/src/alloc.c index cc9ba8dbf5..acce0109e1 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -47,6 +47,7 @@ Copyright (C) 1985-1986, 1988, 1993-1995, 1997-2020 Free Software #include "blockinput.h" #include "pdumper.h" #include "termhooks.h" /* For struct terminal. */ +#include "canvas.h" #ifdef HAVE_WINDOW_SYSTEM #include TERM_HEADER #endif /* HAVE_WINDOW_SYSTEM */ @@ -3114,6 +3115,11 @@ cleanup_vector (struct Lisp_Vector *vector) module_finalize_function (function); } #endif + else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_CANVAS)) + { + struct canvas *cnvs = (struct canvas *) vector; + destroy_canvas_contents (cnvs->canvas); + } } /* Reclaim space used by unmarked vectors. */ diff --git a/src/canvas.h b/src/canvas.h new file mode 100644 index 0000000000..c42a15e6fd --- /dev/null +++ b/src/canvas.h @@ -0,0 +1,83 @@ +/* Canvas support for GNU Emacs. + Copyright (C) 2020 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "lisp.h" +#include "frame.h" +#include "window.h" + +#ifdef USE_CAIRO +#include <cairo/cairo.h> +typedef cairo_surface_t *canvas_contents_t; +#else +typedef void *canvas_contents_t; +#endif + +struct canvas +{ + union vectorlike_header header; + Lisp_Object cnvs_objects; + Lisp_Object window; + Lisp_Object object; + + canvas_contents_t canvas; + int width, height; + bool multiple_objects_seen; + bool changed_since_last_redisplay; +} GCALIGNED_STRUCT; + +/* Test for xwidget pseudovector. */ +#define CANVASP(x) PSEUDOVECTORP (x, PVEC_CANVAS) +#define XCANVAS(a) \ + (eassert (CANVASP (a)), XUNTAG (a, Lisp_Vectorlike, struct canvas)) + +#define CHECK_CANVAS(x) CHECK_TYPE (CANVASP (x), Qcanvasp, x) + +#define MARK_CANVAS_CHANGED(x) \ + (((x)->changed_since_last_redisplay = true), \ + (windows_or_buffers_changed = 2), (redisplay ())); \ + do \ + { \ + if ((x)->multiple_objects_seen || !EQ (selected_window, (x)->window)) \ + { \ + Lisp_Object tail, head; \ + FOR_EACH_FRAME (tail, head) \ + { \ + struct frame *f = XFRAME (head); \ + SET_FRAME_GARBAGED (f); \ + } \ + } \ + } \ + while (false) + +extern Lisp_Object +make_canvas (int width, int height); + +extern canvas_contents_t +make_canvas_contents (int width, int height); + +extern void +destroy_canvas_contents (canvas_contents_t contents); + +extern void +syms_of_canvas (void); + +extern void +canvas_end_redisplay (struct window *w, + struct glyph_matrix *matrix); diff --git a/src/common-canvas.c b/src/common-canvas.c new file mode 100644 index 0000000000..501f4930f7 --- /dev/null +++ b/src/common-canvas.c @@ -0,0 +1,856 @@ +/* Canvas support for GNU Emacs. + Copyright (C) 2020 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ + +#include "canvas.h" +#include "coding.h" + +#include <math.h> + +Lisp_Object +make_canvas (int width, int height) +{ +#ifndef USE_CAIRO + error ("Canvases are not supported without cairo.") +#endif + struct canvas *canvas = ALLOCATE_PSEUDOVECTOR + (struct canvas, object, PVEC_CANVAS); + canvas->width = width; + canvas->height = height; + canvas->object = Qnil; + canvas->multiple_objects_seen = false; + canvas->canvas = make_canvas_contents (width, height); + canvas->changed_since_last_redisplay = true; + + Lisp_Object cnvs; + XSETCANVAS (cnvs, canvas); + return cnvs; +} + +void +destroy_canvas_contents (canvas_contents_t contents) +{ +#ifdef USE_CAIRO + cairo_surface_destroy (contents); +#else + error ("Not implemented"); +#endif +} + +canvas_contents_t +make_canvas_contents (int width, int height) +{ +#ifdef USE_CAIRO + cairo_surface_t *crs = + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + return crs; +#else + error ("Not implemented"); +#endif +} + +void +canvas_end_redisplay (struct window *w, + struct glyph_matrix *matrix) +{ + int i; + int area; + + for (i = 0; i < matrix->nrows; ++i) + { + struct glyph_row *row; + row = MATRIX_ROW (matrix, i); + if (row->enabled_p) + for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area) + { + struct glyph *glyph = row->glyphs[area]; + struct glyph *glyph_end = glyph + row->used[area]; + for (; glyph < glyph_end; ++glyph) + if (glyph->type == CANVAS_GLYPH && + glyph->u.canvas->changed_since_last_redisplay) + { + canvas_update_glyph + (w, i, row, area, row->glyphs[area] - + glyph, 1 + row->glyphs[area] - glyph, + glyph); + } + } + } +} + +DEFUN ("make-canvas", Fmake_canvas, Smake_canvas, 2, 2, 0, + doc: /* Create a canvas, WIDTH pixels wide, and HEIGHT pixels tall. */) + (Lisp_Object width, Lisp_Object height) +{ + check_integer_range (height, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + + return make_canvas (XFIXNUM (width), XFIXNUM (height)); +} + +DEFUN ("canvas-ellipse", Fcanvas_ellipse, Scanvas_ellipse, 5, 8, 0, + doc: /* Draw a WIDTH wide and HEIGHT tall ellipse centred at X, Y in CANVAS. +The color of the ellipse will be COLOR (or the foreground color of COLOR is nil). +The ellipse will be hollow if HOLLOW is non-nil. +The opacity of the circle will be OPACITY, which should be a floating-point +number between 1 and 0. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, + Lisp_Object width, Lisp_Object height, Lisp_Object color, + Lisp_Object hollow, Lisp_Object opacity) +{ + if (NILP (opacity)) + opacity = make_float (1.0); + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + check_integer_range (height, 0, INT_MAX); + CHECK_NUMBER (opacity); +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame))->query_colors + (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + cairo_matrix_t save_matrix; + cairo_get_matrix (cr, &save_matrix); + cairo_translate (cr, XFIXNUM (x), XFIXNUM (y)); + cairo_scale (cr, 1, 1); + cairo_new_path (cr); + cairo_arc (cr, 0, 0, + XFIXNUM (width) / 2.0, 0, 2 * M_PI); + cairo_set_matrix (cr, &save_matrix); + + cairo_set_line_width (cr, canvas_stroke_width); + if (!NILP (hollow)) + cairo_stroke (cr); + else + cairo_fill (cr); + + cairo_destroy (cr); + MARK_CANVAS_CHANGED (cv); +#endif + return Qnil; +} + +DEFUN ("canvasp", Fcanvasp, Scanvasp, 1, 1, 0, + doc: /* Return t if CANVAS is a canvas, else nil. */) + (Lisp_Object canvas) +{ + return CANVASP (canvas) ? Qt : Qnil; +} + +DEFUN ("canvas-rectangle", Fcanvas_rectangle, Scanvas_rectangle, 5, 8, 0, + doc: /* Draw a WIDTH wide and HEIGHT tall rectangle at X, Y in CANVAS. +The color of the rectangle will be COLOR (or the foreground color of COLOR is nil). +The rectangle will be hollow if HOLLOW is non-nil. +The opacity of the circle will be OPACITY, which should be a floating-point +number between 1 and 0. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, + Lisp_Object width, Lisp_Object height, Lisp_Object color, + Lisp_Object hollow, Lisp_Object opacity) +{ + if (NILP (opacity)) + opacity = make_float (1.0); + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + check_integer_range (height, 0, INT_MAX); + CHECK_NUMBER (opacity); +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame))->query_colors + (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + if (NILP (hollow)) + { + cairo_rectangle (cr, XFIXNUM (x), XFIXNUM (y), XFIXNUM (width), + XFIXNUM (height)); + cairo_fill (cr); + } + else + { + cairo_rectangle (cr, XFIXNUM (x), XFIXNUM (y), XFIXNUM (width), + XFIXNUM (height)); + cairo_stroke (cr); + } + cairo_destroy (cr); + MARK_CANVAS_CHANGED (cv); + return Qnil; +#else + error ("Not implemented"); +#endif +} + +DEFUN ("canvas-rounded-rectangle", Fcanvas_rounded_rectangle, Scanvas_rounded_rectangle, 6, MANY, 0, + doc: /* Draw a WIDTH wide and HEIGHT tall rectangle at X, Y in CANVAS. +The color of the rectangle will be COLOR (or the foreground color of COLOR is nil). +The rectangle will be hollow if HOLLOW is non-nil. +The opacity of the circle will be OPACITY, which should be a floating-point +number between 1 and 0. +usage: (canvas-rounded-rectangle canvas x y width height radius &optional color hollow opacity) */) + (ptrdiff_t nargs, Lisp_Object *args) +{ + Lisp_Object canvas, x, y, width, height, radius, color, hollow, opacity; + canvas = args[0]; + x = args[1]; + y = args[2]; + width = args[3]; + height = args[4]; + radius = args[5]; + color = Qnil; + hollow = Qnil; + opacity = Qnil; + + if (nargs > 6) + color = args[6]; + if (nargs > 7) + hollow = args[7]; + if (nargs > 8) + opacity = args[8]; + if (nargs > 9) + xsignal2 (Qwrong_number_of_arguments, + Qcanvas_rounded_rectangle, + make_fixnum (nargs)); + if (NILP (opacity)) + opacity = make_float (1.0); + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + check_integer_range (height, 0, INT_MAX); + CHECK_NUMBER (opacity); +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame))->query_colors + (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + { +#define radius XFLOATINT (radius) + double degrees = M_PI / 180.0; +#define width XFIXNUM (width) +#define height XFIXNUM (height) +#define x XFIXNUM (x) +#define y XFIXNUM (y) + cairo_new_sub_path (cr); + cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees); + cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees); + cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees); + cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees); + cairo_close_path (cr); +#undef radius +#undef y +#undef x +#undef height +#undef width + } +#undef radius + cairo_set_line_width (cr, canvas_stroke_width); + if (NILP (hollow)) + cairo_fill (cr); + else + cairo_stroke (cr); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (cv); + return Qnil; +#else + error ("Not implemented"); +#endif +} + +DEFUN ("canvas-draw-string", Fcanvas_draw_string, Scanvas_draw_string, 4, 8, 0, + doc: /* Draw the string STRING onto the canvas CANVAS at X, Y. +The opacity of the drawn text will be OPACITY, and the color of the drawn text will be COLOR. +The font-family used will be FAMILY, which can be a string or a list of +the font-family as a string, whether or not the the text should be italic, +and whether or not the text should be bold. +The size of the text will be SIZE, or the default text size if nil. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, Lisp_Object string, + Lisp_Object color, Lisp_Object opacity, Lisp_Object family, Lisp_Object size) +{ + if (noninteractive) + error ("`canvas-draw-string' cannot be called when running in batch mode."); + if (NILP (opacity)) + opacity = make_float (1.0); + if (NILP (size)) + size = make_fixnum (FRAME_TEXT_HEIGHT (XFRAME (selected_frame))); + if (NILP (family)) + family = build_string ("monospace"); + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + CHECK_NUMBER (opacity); + if (!Flistp (family)) + CHECK_STRING (family); + CHECK_STRING (string); +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame))->query_colors + (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + struct canvas *c = XCANVAS (canvas); + cairo_t *cr = cairo_create (c->canvas); + cairo_font_slant_t slant_flags = CAIRO_FONT_SLANT_NORMAL; + cairo_font_weight_t weight_flags = CAIRO_FONT_WEIGHT_NORMAL; + const char *family_utf8; + if (!NILP (Flistp (family))) + { + if (!NILP (CALLN (Flss, Flength (family), make_fixnum (2)))) + error ("Invalid font spec"); + else + { + int length = XFIXNUM (Flength (family)); + if (length > 3) + error ("Invalid font spec"); + Lisp_Object italic, bold; + CHECK_STRING_CAR (family); + family_utf8 = SSDATA (ENCODE_UTF_8 (XCAR (family))); + if (length == 3) + bold = XCAR (XCDR (XCDR (family))); + else + bold = Qnil; + italic = XCAR (XCDR (family)); + if (!NILP (italic)) + slant_flags = CAIRO_FONT_SLANT_ITALIC; + if (!NILP (bold)) + weight_flags = CAIRO_FONT_WEIGHT_BOLD; + } + } + else + { + family_utf8 = SSDATA (ENCODE_UTF_8 (family)); + } + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + cairo_select_font_face (cr, family_utf8, + slant_flags, + weight_flags); + cairo_set_font_size (cr, XFIXNUM (size)); + cairo_move_to (cr, XFIXNUM (x), XFIXNUM (y) + XFIXNUM (size)); + cairo_show_text (cr, SSDATA (ENCODE_UTF_8 (string))); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (c); + return Qnil; +#else + return Qnil; +#endif +} + +DEFUN ("canvas-measure-string", Fcanvas_measure_string, Scanvas_measure_string, 2, 4, 0, + doc: /* Return a pair containing the width and height of STRING, +if drawn on CANVAS with the family FAMILY at SIZE. */) +(Lisp_Object canvas, Lisp_Object string, Lisp_Object family, Lisp_Object size) +{ + if (noninteractive) + error ("`canvas-draw-string' cannot be called when running in batch mode."); + if (NILP (size)) + size = make_fixnum (FRAME_TEXT_HEIGHT (XFRAME (selected_frame))); + if (NILP (family)) + family = build_string ("cairo:monospace"); + CHECK_CANVAS (canvas); + + if (!Flistp (family)) + CHECK_STRING (family); + CHECK_STRING (string); +#ifdef USE_CAIRO + struct canvas *c = XCANVAS (canvas); + cairo_t *cr = cairo_create (c->canvas); + cairo_font_slant_t slant_flags = CAIRO_FONT_SLANT_NORMAL; + cairo_font_weight_t weight_flags = CAIRO_FONT_WEIGHT_NORMAL; + const char *family_utf8; + if (!NILP (Flistp (family))) + { + if (!NILP (CALLN (Flss, Flength (family), make_fixnum (2)))) + error ("Invalid font spec"); + else + { + int length = XFIXNUM (Flength (family)); + if (length > 3) + error ("Invalid font spec"); + Lisp_Object italic, bold; + CHECK_STRING_CAR (family); + family_utf8 = SSDATA (ENCODE_UTF_8 (XCAR (family))); + if (length == 3) + bold = XCAR (XCDR (XCDR (family))); + else + bold = Qnil; + italic = XCAR (XCDR (family)); + if (!NILP (italic)) + slant_flags = CAIRO_FONT_SLANT_ITALIC; + if (!NILP (bold)) + weight_flags = CAIRO_FONT_WEIGHT_BOLD; + } + } + else + { + family_utf8 = SSDATA (ENCODE_UTF_8 (family)); + } + cairo_select_font_face (cr, family_utf8, + slant_flags, + weight_flags); + cairo_set_font_size (cr, XFIXNUM (size)); + cairo_text_extents_t extents; + cairo_text_extents (cr, SSDATA (ENCODE_UTF_8 (string)), &extents); + cairo_destroy (cr); + return Fcons (make_fixnum (extents.width), + make_fixnum (extents.height)); +#else + return Qnil; +#endif +} + +DEFUN ("canvas-draw-image", Fcanvas_draw_image, Scanvas_draw_image, 4, 8, + 0, doc: /* Paint IMAGE_SPEC onto CANVAS, at X, Y. +If WIDTH or HEIGHT is set, and IMAGE is wider than WIDTH or taller than HEIGHT, +IMAGE_SPEC will be cropped to fit WIDTH and/or HEIGHT respectively. +FRAME should be a live frame. +The opacity of the drawn image will be OPACITY. */) + (Lisp_Object canvas, Lisp_Object image_spec, + Lisp_Object x, Lisp_Object y, + Lisp_Object width, Lisp_Object height, + Lisp_Object frame, Lisp_Object opacity) +{ + if (valid_image_p (image_spec)) + { + if (NILP (frame)) + frame = selected_frame; + struct frame *f = decode_window_system_frame (frame); + ptrdiff_t id = lookup_image (f, image_spec); + struct image *img = IMAGE_FROM_ID (f, id); + if (!img) + return Qnil; + if (img->load_failed_p) + return Qnil; + int iwidth = img->width + 2 * img->hmargin; + int iheight = img->height + 2 * img->vmargin; + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + if (NILP (width)) + width = make_fixnum (iwidth); + check_integer_range (width, 0, INT_MAX); + if (NILP (height)) + height = make_fixnum (iheight); + check_integer_range (height, 0, INT_MAX); + if (NILP (width)) + width = make_fixnum (iwidth); + if (NILP (opacity)) + opacity = make_fixnum (1); + CHECK_NUMBER (opacity); +#ifdef USE_CAIRO + cairo_surface_t *crs + = cairo_image_surface_create_for_data ((unsigned char *) + img->pixmap->data, + (img->pixmap->bits_per_pixel + == 32 + ? CAIRO_FORMAT_RGB24 + : CAIRO_FORMAT_A8), + img->pixmap->width, + img->pixmap->height, + img->pixmap->bytes_per_line); + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_save (cr); + cairo_translate (cr, XFIXNUM (x), XFIXNUM (y)); + cairo_rectangle (cr, 0, 0, + XFIXNUM (width), XFIXNUM (height)); + cairo_clip (cr); + cairo_set_source_surface (cr, crs, 0, 0); + cairo_paint_with_alpha (cr, XFLOATINT (opacity)); + cairo_restore (cr); + cairo_destroy (cr); + cairo_surface_destroy (crs); + MARK_CANVAS_CHANGED (cv); +#endif + } + else + error ("Invalid image specification"); + return Qnil; +} + +DEFUN ("canvas-arc", Fcanvas_arc, Scanvas_arc, 6, 8, 0, + doc: /* Draw an arc on CANVAS starting from XC, YC, +with a radius of RADIUS and 2 angles angle1 and angle2. +Use the color COLOR with the alpha channel set to OPACITY, if specified. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, Lisp_Object radius, + Lisp_Object angle1, Lisp_Object angle2, Lisp_Object color, Lisp_Object opacity) +{ + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + CHECK_NUMBER (angle1); + CHECK_NUMBER (angle2); + CHECK_NUMBER (radius); + + if (NILP (opacity)) + opacity = make_fixnum (1.0); + CHECK_NUMBER (opacity); + +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame)) + ->query_colors (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + cairo_t *cr = cairo_create (XCANVAS (canvas)->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + cairo_arc (cr, XFLOATINT (x), XFLOATINT (y), XFLOATINT (radius), + XFLOATINT (angle1), XFLOATINT (angle2)); + cairo_stroke (cr); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (XCANVAS (canvas)); +#else +#endif + return Qnil; +} + +DEFUN ("canvas-filled-arc", Fcanvas_filled_arc, Scanvas_filled_arc, 6, 8, 0, + doc: /* Draw a filled arc on CANVAS starting from XC, YC, +with a radius of RADIUS and 2 angles angle1 and angle2. +Use the color COLOR with the alpha channel set to OPACITY, if specified. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, Lisp_Object radius, + Lisp_Object angle1, Lisp_Object angle2, Lisp_Object color, Lisp_Object opacity) +{ + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + CHECK_NUMBER (angle1); + CHECK_NUMBER (angle2); + CHECK_NUMBER (radius); + + if (NILP (opacity)) + opacity = make_fixnum (1.0); + CHECK_NUMBER (opacity); + +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame)) + ->query_colors (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + cairo_t *cr = cairo_create (XCANVAS (canvas)->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + cairo_arc (cr, XFLOATINT (x), XFLOATINT (y), XFLOATINT (radius), + XFLOATINT (angle1), XFLOATINT (angle2)); + cairo_fill (cr); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (XCANVAS (canvas)); +#else +#endif + return Qnil; +} + +DEFUN ("canvas-region", Fcanvas_region, Scanvas_region, 5, 5, 0, + doc: /* Return a canvas containing a WIDTH wide and HEIGHT tall +subsection of CANVAS at X, Y */) + (Lisp_Object canvas, Lisp_Object x, + Lisp_Object y, Lisp_Object width, Lisp_Object height) +{ + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + check_integer_range (height, 0, INT_MAX); + +#ifdef USE_CAIRO + int ix = XFIXNUM (x), + iy = XFIXNUM (y), + iw = XFIXNUM (width), + ih = XFIXNUM (height); + cairo_surface_t *s = cairo_surface_create_for_rectangle + (XCANVAS (canvas)->canvas, ix, iy, iw, ih); + Lisp_Object newcvs = make_canvas (iw, ih); + struct canvas *target = XCANVAS (newcvs); + cairo_t *t = cairo_create (target->canvas); + cairo_set_source_surface (t, s, 0, 0); + cairo_paint (t); + cairo_destroy (t); + cairo_surface_destroy (s); + return newcvs; +#else + error ("Not implemented.") +#endif +} + + +DEFUN ("canvas-draw-canvas", Fcanvas_draw_canvas, Scanvas_draw_canvas, 4, 7, + 0, doc: /* Paint CANVAS2 onto CANVAS, at X, Y. +If WIDTH or HEIGHT is set, and IMAGE is wider than WIDTH or taller than HEIGHT, +IMAGE_SPEC will be cropped to fit WIDTH and/or HEIGHT respectively. +The opacity of the drawn image will be OPACITY. */) + (Lisp_Object canvas, Lisp_Object canvas2, + Lisp_Object x, Lisp_Object y, + Lisp_Object width, Lisp_Object height, Lisp_Object opacity) +{ + CHECK_CANVAS (canvas); + CHECK_CANVAS (canvas2); + int iwidth = XCANVAS (canvas2)->width; + int iheight = XCANVAS (canvas2)->height; + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + if (NILP (width)) + width = make_fixnum (iwidth); + check_integer_range (width, 0, INT_MAX); + if (NILP (height)) + height = make_fixnum (iheight); + check_integer_range (height, 0, INT_MAX); + if (NILP (width)) + width = make_fixnum (iwidth); + if (NILP (opacity)) + opacity = make_fixnum (1); + CHECK_NUMBER (opacity); + +#ifdef USE_CAIRO + cairo_surface_t *crs = XCANVAS (canvas2)->canvas; + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_save (cr); + cairo_translate (cr, XFIXNUM (x), XFIXNUM (y)); + cairo_rectangle (cr, 0, 0, XFIXNUM (width), XFIXNUM (height)); + cairo_clip (cr); + cairo_set_source_surface (cr, crs, 0, 0); + cairo_paint_with_alpha (cr, XFLOATINT (opacity)); + cairo_restore (cr); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (cv); +#endif + return Qnil; +} + +DEFUN ("canvas-pixel-at", Fcanvas_pixel_at, Scanvas_pixel_at, 3, 3, 0, + doc: /* Return the color of the pixel at X, Y inside CANVAS as an ARGB list. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y) +{ +#ifndef USE_CAIRO + error ("Not implemented."); +#else + CHECK_CANVAS (canvas); + check_integer_range (x, 0, XCANVAS (canvas)->width); + check_integer_range (y, 0, XCANVAS (canvas)->height); + struct { +#ifdef WORDS_BIGENDIAN + uint8_t a, r, g, b; +#else + uint8_t b, g, r, a; +#endif + } *argb32 = + (void *) cairo_image_surface_get_data (XCANVAS (canvas)->canvas); + typeof (*argb32) res = argb32 [XFIXNUM (y) * XCANVAS (canvas)->width + + XFIXNUM (x)]; + return CALLN (Flist, make_fixnum (res.a), + make_fixnum (res.r), + make_fixnum (res.g), + make_fixnum (res.b)); +#endif +} + +DEFUN ("canvas-dimensions", Fcanvas_dimensions, Scanvas_dimensions, 1, 1, 0, + doc: /* Return a cons pair containing the width and height of CANVAS. */) + (Lisp_Object canvas) +{ + CHECK_CANVAS (canvas); + return Fcons (make_fixnum (XCANVAS (canvas)->width), + make_fixnum (XCANVAS (canvas)->height)); +} + +void +syms_of_canvas (void) +{ + defsubr (&Smake_canvas); + defsubr (&Scanvas_rectangle); + defsubr (&Scanvas_ellipse); + defsubr (&Scanvas_rectangle); + defsubr (&Scanvas_draw_string); + defsubr (&Scanvas_draw_image); + defsubr (&Scanvas_draw_canvas); + defsubr (&Scanvas_measure_string); + defsubr (&Scanvas_dimensions); + defsubr (&Scanvas_region); + defsubr (&Scanvas_pixel_at); + defsubr (&Scanvas_arc); + defsubr (&Scanvasp); + defsubr (&Scanvas_filled_arc); + defsubr (&Scanvas_rounded_rectangle); + DEFSYM (Qcanvas_rounded_rectangle, "canvas-rounded-rectangle"); + DEFSYM (Qcanvasp, "canvasp"); + DEFSYM (Qcolor_values, "color-values"); + DEFVAR_INT ("canvas-stroke-width", canvas_stroke_width, + doc: /* The stroke width to be used in canvases. */); + canvas_stroke_width = 4; +} diff --git a/src/data.c b/src/data.c index bce2e53cfb..aefd6d7e70 100644 --- a/src/data.c +++ b/src/data.c @@ -263,6 +263,8 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, return Qxwidget; case PVEC_XWIDGET_VIEW: return Qxwidget_view; + case PVEC_CANVAS: + return Qcanvas; /* "Impossible" cases. */ case PVEC_MISC_PTR: case PVEC_OTHER: @@ -3859,6 +3861,7 @@ #define PUT_ERROR(sym, tail, msg) \ DEFSYM (Qchar_table, "char-table"); DEFSYM (Qbool_vector, "bool-vector"); DEFSYM (Qhash_table, "hash-table"); + DEFSYM (Qcanvas, "canvas"); DEFSYM (Qthread, "thread"); DEFSYM (Qmutex, "mutex"); DEFSYM (Qcondition_variable, "condition-variable"); diff --git a/src/dispextern.h b/src/dispextern.h index 0b1f3d14ae..d33b87c3df 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -382,7 +382,10 @@ #define SET_GLYPH_FROM_GLYPH_CODE(glyph, gc) \ STRETCH_GLYPH, /* Glyph is an external widget drawn by the GUI toolkit. */ - XWIDGET_GLYPH + XWIDGET_GLYPH, + + /* Glyph is a canvas. */ + CANVAS_GLYPH }; @@ -540,6 +543,9 @@ #define FACE_ID_BITS 20 struct xwidget *xwidget; #endif + /* Canvas reference (type == CANVAS_GLYPH). */ + struct canvas *canvas; + /* Sub-structure for type == STRETCH_GLYPH. */ struct { @@ -1405,6 +1411,9 @@ #define OVERLAPS_ERASED_CURSOR (1 << 2) /* Xwidget. */ struct xwidget *xwidget; + /* Canvas. */ + struct canvas *canvas; + /* Slice */ struct glyph_slice slice; @@ -2158,7 +2167,10 @@ #define MAX_FRINGE_BITMAPS (1<<FRINGE_ID_BITS) IT_CONTINUATION, /* Xwidget. */ - IT_XWIDGET + IT_XWIDGET, + + /* Canvas. */ + IT_CANVAS }; @@ -2223,6 +2235,7 @@ #define MAX_FRINGE_BITMAPS (1<<FRINGE_ID_BITS) GET_FROM_IMAGE, GET_FROM_STRETCH, GET_FROM_XWIDGET, + GET_FROM_CANVAS, NUM_IT_METHODS }; @@ -2447,6 +2460,10 @@ #define OVERLAY_STRING_CHUNK_SIZE 16 struct { Lisp_Object object; } xwidget; + /* method == GET_FROM_CANVAS */ + struct { + Lisp_Object object; + } canvas; } u; /* Current text and display positions. */ @@ -2578,6 +2595,9 @@ #define OVERLAY_STRING_CHUNK_SIZE 16 /* If what == IT_XWIDGET. */ struct xwidget *xwidget; + /* If what == IT_CANVAS. */ + struct canvas *canvas; + /* Values from `slice' property. */ struct it_slice slice; @@ -3714,6 +3734,11 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask) \ #endif /* HAVE_WINDOW_SYSTEM */ +extern void +canvas_update_glyph (struct window *w, int x, struct glyph_row *row, + enum glyph_row_area area, ptrdiff_t start, ptrdiff_t end, + struct glyph *glyph); + INLINE_HEADER_END #endif /* not DISPEXTERN_H_INCLUDED */ diff --git a/src/dispnew.c b/src/dispnew.c index 5b6fa51a56..1ba639cb79 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -43,6 +43,7 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2020 Free Software Foundation, #include "tparam.h" #include "xwidget.h" #include "pdumper.h" +#include "canvas.h" #ifdef HAVE_WINDOW_SYSTEM #include TERM_HEADER @@ -3697,6 +3698,7 @@ update_window (struct window *w, bool force_p) #endif xwidget_end_redisplay (w, w->current_matrix); + canvas_end_redisplay (w, w->current_matrix); clear_glyph_matrix (desired_matrix); return paused_p; @@ -3782,6 +3784,7 @@ gui_update_window_end (struct window *w, bool cursor_on_p, FRAME_RIF (f)->update_window_end_hook (w, cursor_on_p, mouse_face_overwritten_p); + canvas_end_redisplay (w, w->current_matrix); } #endif /* HAVE_WINDOW_SYSTEM */ @@ -4371,6 +4374,11 @@ scrolling_window (struct window *w, int tab_line_p) return 0; #endif + /* We need this to fix canvas movement detection in a reliable way. + FIXME. */ + if (w->have_canvas_p) + return 0; + /* Give up if some rows in the desired matrix are not enabled. */ if (! MATRIX_ROW_ENABLED_P (desired_matrix, i)) return -1; @@ -5565,6 +5573,10 @@ mode_line_string (struct window *w, enum window_part part, y0 -= row->ascent - glyph->ascent; } #endif + if (glyph->type == CANVAS_GLYPH) + { + y0 -= row->ascent - glyph->ascent; + } } else { @@ -5654,6 +5666,10 @@ marginal_area_string (struct window *w, enum window_part part, y0 += glyph->slice.img.y; } #endif + if (glyph->type == CANVAS_GLYPH) + { + y0 -= row->ascent - glyph->ascent; + } } else { diff --git a/src/emacs.c b/src/emacs.c index ea9c4cd79d..c16af2c14c 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -94,6 +94,8 @@ #define MAIN_PROGRAM #include "getpagesize.h" #include "gnutls.h" +#include "canvas.h" + #ifdef PROFILING # include <sys/gmon.h> extern void moncontrol (int mode); @@ -1567,6 +1569,8 @@ main (int argc, char **argv) /* Before init_window_once, because it sets up the Vcoding_system_hash_table. */ syms_of_coding (); /* This should be after syms_of_fileio. */ + + syms_of_canvas (); init_frame_once (); /* Before init_window_once. */ init_window_once (); /* Init the window system. */ #ifdef HAVE_WINDOW_SYSTEM diff --git a/src/lisp.h b/src/lisp.h index b4ac017dcf..f463399dad 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -1103,6 +1103,7 @@ DEFINE_GDB_SYMBOL_END (PSEUDOVECTOR_FLAG) PVEC_MUTEX, PVEC_CONDVAR, PVEC_MODULE_FUNCTION, + PVEC_CANVAS, /* These should be last, for internal_equal and sxhash_obj. */ PVEC_COMPILED, @@ -1349,6 +1350,7 @@ #define XSETSUB_CHAR_TABLE(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_SUB_CHAR_TABLE)) #define XSETTHREAD(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_THREAD)) #define XSETMUTEX(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_MUTEX)) #define XSETCONDVAR(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_CONDVAR)) +#define XSETCANVAS(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_CANVAS)) /* Efficiently convert a pointer to a Lisp object and back. The pointer is represented as a fixnum, so the garbage collector diff --git a/src/pdumper.c b/src/pdumper.c index 63424c5734..33c527c7d8 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -3036,6 +3036,8 @@ dump_vectorlike (struct dump_context *ctx, error_unsupported_dump_object (ctx, lv, "condvar"); case PVEC_MODULE_FUNCTION: error_unsupported_dump_object (ctx, lv, "module function"); + case PVEC_CANVAS: + error_unsupported_dump_object (ctx, lv, "canvas"); default: error_unsupported_dump_object(ctx, lv, "weird pseudovector"); } diff --git a/src/print.c b/src/print.c index bd1769144e..28a620fd20 100644 --- a/src/print.c +++ b/src/print.c @@ -34,6 +34,7 @@ Copyright (C) 1985-1986, 1988, 1993-1995, 1997-2020 Free Software #include "blockinput.h" #include "xwidget.h" #include "dynlib.h" +#include "canvas.h" #include <c-ctype.h> #include <float.h> @@ -1833,6 +1834,16 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag, } break; #endif + case PVEC_CANVAS: + { + print_c_string ("#<canvas ", printcharfun); + const struct canvas *canvas = XCANVAS (obj); + print_object (make_fixnum (canvas->width), printcharfun, false); + printchar ('x', printcharfun); + print_object (make_fixnum (canvas->height), printcharfun, false); + print_c_string (">", printcharfun); + } + break; default: emacs_abort (); diff --git a/src/window.c b/src/window.c index e2dea8b70e..5e0ed1ab94 100644 --- a/src/window.c +++ b/src/window.c @@ -4289,6 +4289,7 @@ make_window (void) w->scroll_bar_width = -1; w->scroll_bar_height = -1; w->column_number_displayed = -1; + w->have_canvas_p = false; /* Reset window_list. */ Vwindow_list = Qnil; /* Return window. */ diff --git a/src/window.h b/src/window.h index 167d1be7ab..67c7417007 100644 --- a/src/window.h +++ b/src/window.h @@ -445,6 +445,9 @@ #define WINDOW_H_INCLUDED window. */ bool_bf suspend_auto_hscroll : 1; + /* True if we think a canvas is being displayed in this window. */ + bool_bf have_canvas_p : 1; + /* Amount by which lines of this window are scrolled in y-direction (smooth scrolling). */ int vscroll; diff --git a/src/xdisp.c b/src/xdisp.c index 140d134572..e63c497100 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -462,6 +462,7 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2020 Free Software Foundation, #include "fontset.h" #include "blockinput.h" #include "xwidget.h" +#include "canvas.h" #ifdef HAVE_WINDOW_SYSTEM #include TERM_HEADER #endif /* HAVE_WINDOW_SYSTEM */ @@ -1055,6 +1056,7 @@ #define THIN_SPACE_WIDTH 1 static bool next_element_from_image (struct it *); static bool next_element_from_stretch (struct it *); static bool next_element_from_xwidget (struct it *); +static bool next_element_from_canvas (struct it *); static void load_overlay_strings (struct it *, ptrdiff_t); static bool get_next_display_element (struct it *); static enum move_it_result @@ -2688,7 +2690,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) if (g < end) { - if (g->type == IMAGE_GLYPH) + if (g->type == IMAGE_GLYPH || g->type == CANVAS_GLYPH) { /* Don't remember when mouse is over image, as image may have hot-spots. */ @@ -5589,7 +5591,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, /* After this point, VALUE is the property after any margin prefix has been stripped. It must be a string, - an image specification, or `(space ...)'. + an image specification, a canvas, or `(space ...)'. LOCATION specifies where to display: `left-margin', `right-margin' or nil. */ @@ -5601,7 +5603,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, #endif /* not HAVE_WINDOW_SYSTEM */ || (CONSP (value) && EQ (XCAR (value), Qspace)) || ((it ? FRAME_WINDOW_P (it->f) : frame_window_p) - && valid_xwidget_spec_p (value))); + && valid_xwidget_spec_p (value))) || CANVASP (value); if (valid_p && display_replaced == 0) { @@ -5686,6 +5688,22 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, *position = start_pos; it->xwidget = lookup_xwidget (value); } + else if (CANVASP (value)) + { + it->what = IT_CANVAS; + it->method = GET_FROM_CANVAS; + it->position = start_pos; + it->object = NILP (object) ? it->w->contents : object; + *position = start_pos; + it->canvas = XCANVAS (value); + if ((!NILP (it->canvas->object) && + !EQ (it->canvas->object, it->object)) || + (!NILP (it->canvas->window) && + XWINDOW (it->canvas->window) != it->w)) + it->canvas->multiple_objects_seen = true; + it->canvas->object = it->object; + XSETWINDOW (it->canvas->window, it->w); + } #ifdef HAVE_WINDOW_SYSTEM else { @@ -6446,6 +6464,9 @@ push_it (struct it *it, struct text_pos *position) case GET_FROM_XWIDGET: p->u.xwidget.object = it->object; break; + case GET_FROM_CANVAS: + p->u.canvas.object = it->object; + break; case GET_FROM_BUFFER: case GET_FROM_DISPLAY_VECTOR: case GET_FROM_STRING: @@ -6550,6 +6571,9 @@ pop_it (struct it *it) case GET_FROM_XWIDGET: it->object = p->u.xwidget.object; break; + case GET_FROM_CANVAS: + it->object = p->u.canvas.object; + break; case GET_FROM_STRETCH: it->object = p->u.stretch.object; break; @@ -7236,6 +7260,7 @@ reseat_to_string (struct it *it, const char *s, Lisp_Object string, next_element_from_image, next_element_from_stretch, next_element_from_xwidget, + next_element_from_canvas, }; #define GET_NEXT_DISPLAY_ELEMENT(it) (*get_next_element[(it)->method]) (it) @@ -8151,6 +8176,7 @@ set_iterator_to_next (struct it *it, bool reseat_p) case GET_FROM_IMAGE: case GET_FROM_STRETCH: case GET_FROM_XWIDGET: + case GET_FROM_CANVAS: /* The position etc with which we have to proceed are on the stack. The position may be at the end of a string, @@ -8619,6 +8645,12 @@ next_element_from_xwidget (struct it *it) return true; } +static bool +next_element_from_canvas (struct it *it) +{ + it->what = IT_CANVAS; + return true; +} /* Fill iterator IT with next display element from a stretch glyph property. IT->object is the value of the text property. Value is @@ -27810,6 +27842,19 @@ fill_xwidget_glyph_string (struct glyph_string *s) s->xwidget = s->first_glyph->u.xwidget; } #endif + +static void +fill_canvas_glyph_string (struct glyph_string *s) +{ + eassert (s->first_glyph->type == CANVAS_GLYPH); + s->w->have_canvas_p = true; + s->face = FACE_FROM_ID (s->f, s->first_glyph->face_id); + s->font = s->face->font; + s->width = s->first_glyph->pixel_width; + s->ybase += s->first_glyph->voffset; + s->canvas = s->first_glyph->u.canvas; + s->canvas->changed_since_last_redisplay = false; +} /* Fill glyph string S from a sequence of stretch glyphs. START is the index of the first glyph to consider, @@ -28227,6 +28272,18 @@ #define BUILD_IMAGE_GLYPH_STRING(START, END, HEAD, TAIL, HL, X, LAST_X) \ while (false) #endif +#define BUILD_CANVAS_GLYPH_STRING(START, END, HEAD, TAIL, HL, X, LAST_X) \ + do \ + { \ + s = alloca (sizeof *s); \ + INIT_GLYPH_STRING (s, NULL, w, row, area, START, HL); \ + append_glyph_string (&(HEAD), &(TAIL), s); \ + ++(START); \ + s->x = (X); \ + fill_canvas_glyph_string (s); \ + } \ + while (false) + /* Add a glyph string for a sequence of character glyphs to the list of strings between HEAD and TAIL. START is the index of the first glyph in row area AREA of glyph row ROW that is part of the new @@ -28378,7 +28435,11 @@ #define BUILD_GLYPH_STRINGS_1(START, END, HEAD, TAIL, HL, X, LAST_X) \ case IMAGE_GLYPH: \ BUILD_IMAGE_GLYPH_STRING (START, END, HEAD, TAIL, \ HL, X, LAST_X); \ - break; + break; \ + case CANVAS_GLYPH: \ + BUILD_CANVAS_GLYPH_STRING (START, END, HEAD, TAIL, \ + HL, X, LAST_X); \ + break; \ #define BUILD_GLYPH_STRINGS_XW(START, END, HEAD, TAIL, HL, X, LAST_X) \ case XWIDGET_GLYPH: \ @@ -29086,6 +29147,116 @@ produce_image_glyph (struct it *it) } } +static void +produce_canvas_glyph (struct it *it) +{ + struct canvas *canvas; + int glyph_ascent, crop; + + eassert (it->what == IT_CANVAS); + + struct face *face = FACE_FROM_ID (it->f, it->face_id); + prepare_face_for_display (it->f, face); + + canvas = it->canvas; + it->ascent = it->phys_ascent = glyph_ascent = canvas->height / 2; + it->descent = it->phys_descent = canvas->height / 2; + it->pixel_width = canvas->width; + + if (it->descent < 0) + it->descent = 0; + + it->nglyphs = 1; + + if (face->box != FACE_NO_BOX) + { + if (face->box_horizontal_line_width > 0) + { + it->ascent += face->box_horizontal_line_width; + it->descent += face->box_horizontal_line_width; + } + + if (face->box_vertical_line_width > 0) + { + if (it->start_of_box_run_p) + it->pixel_width += face->box_vertical_line_width; + it->pixel_width += face->box_vertical_line_width; + } + } + + take_vertical_position_into_account (it); + + /* Automatically crop wide image glyphs at right edge so we can + draw the cursor on same display row. */ + crop = it->pixel_width - (it->last_visible_x - it->current_x); + if (crop > 0 && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4)) + it->pixel_width -= crop; + + if (it->glyph_row) + { + enum glyph_row_area area = it->area; + struct glyph *glyph + = it->glyph_row->glyphs[area] + it->glyph_row->used[area]; + + if (it->glyph_row->reversed_p) + { + struct glyph *g; + + /* Make room for the new glyph. */ + for (g = glyph - 1; g >= it->glyph_row->glyphs[it->area]; g--) + g[1] = *g; + glyph = it->glyph_row->glyphs[it->area]; + } + if (glyph < it->glyph_row->glyphs[area + 1]) + { + glyph->charpos = CHARPOS (it->position); + glyph->object = it->object; + glyph->pixel_width = clip_to_bounds (-1, it->pixel_width, SHRT_MAX); + glyph->ascent = glyph_ascent; + glyph->descent = it->descent; + glyph->voffset = it->voffset; + glyph->type = CANVAS_GLYPH; + glyph->avoid_cursor_p = it->avoid_cursor_p; + glyph->multibyte_p = it->multibyte_p; + glyph->u.canvas = it->canvas; + if (it->glyph_row->reversed_p && area == TEXT_AREA) + { + /* In R2L rows, the left and the right box edges need to be + drawn in reverse direction. */ + glyph->right_box_line_p = it->start_of_box_run_p; + glyph->left_box_line_p = it->end_of_box_run_p; + } + else + { + glyph->left_box_line_p = it->start_of_box_run_p; + glyph->right_box_line_p = it->end_of_box_run_p; + } + glyph->overlaps_vertically_p = 0; + glyph->padding_p = 0; + glyph->glyph_not_available_p = 0; + glyph->face_id = it->face_id; + glyph->font_type = FONT_TYPE_UNKNOWN; + if (it->bidi_p) + { + glyph->resolved_level = it->bidi_it.resolved_level; + eassert ((it->bidi_it.type & 7) == it->bidi_it.type); + glyph->bidi_type = it->bidi_it.type; + } + ++it->glyph_row->used[area]; + } + else + IT_EXPAND_MATRIX_WIDTH (it, area); + } +} + +void +canvas_update_glyph (struct window *w, int x, struct glyph_row *row, + enum glyph_row_area area, ptrdiff_t start, ptrdiff_t end, + struct glyph *glyph) +{ + draw_glyphs (w, x, row, area, start, end, DRAW_NORMAL_TEXT, 0); +} + static void produce_xwidget_glyph (struct it *it) { @@ -30608,6 +30779,8 @@ gui_produce_glyphs (struct it *it) produce_stretch_glyph (it); else if (it->what == IT_XWIDGET) produce_xwidget_glyph (it); + else if (it->what == IT_CANVAS) + produce_canvas_glyph (it); done: /* Accumulate dimensions. Note: can't assume that it->descent > 0 @@ -31008,6 +31181,10 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width, cursor_type = HOLLOW_BOX_CURSOR; } } + if (glyph != NULL && glyph->type == CANVAS_GLYPH) + { + cursor_type = HOLLOW_BOX_CURSOR; + } return cursor_type; } diff --git a/src/xterm.c b/src/xterm.c index 7989cecec7..716ed0ef97 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -75,6 +75,7 @@ Copyright (C) 1989, 1993-2020 Free Software Foundation, Inc. #include "sysselect.h" #include "menu.h" #include "pdumper.h" +#include "canvas.h" #ifdef USE_X_TOOLKIT #include <X11/Shell.h> @@ -2066,6 +2067,30 @@ x_draw_glyphless_glyph_string_foreground (struct glyph_string *s) } } +static void +x_draw_canvas_glyph_string_foreground (struct glyph_string *s) +{ + eassert (s->first_glyph->type == CANVAS_GLYPH); +#ifdef USE_CAIRO + cairo_t *cr = x_begin_cr_clip (s->f, s->gc); + int x = s->x; + int y = s->ybase - s->first_glyph->ascent; + + if (s->face->box != FACE_NO_BOX && + s->first_glyph->left_box_line_p) + x += max (s->face->box_vertical_line_width, 0); + + x_set_glyph_string_clipping (s); + x_clear_area (s->f, x, y, s->width, s->height); + cairo_set_source_surface (cr, s->canvas->canvas, + s->x, s->y); + cairo_paint (cr); + x_end_cr_clip (s->f); +#else + emacs_abort (); +#endif +} + #ifdef USE_X_TOOLKIT #ifdef USE_LUCID @@ -3811,6 +3836,11 @@ x_draw_glyph_string (struct glyph_string *s) x_draw_glyphless_glyph_string_foreground (s); break; + case CANVAS_GLYPH: + x_draw_glyph_string_background (s, true); + x_draw_canvas_glyph_string_foreground (s); + break; + default: emacs_abort (); } ^ permalink raw reply related [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 6:34 ` Emacs canvas support Po Lu via Emacs development discussions. @ 2020-04-29 8:24 ` Eli Zaretskii 2020-04-29 9:57 ` Po Lu 2020-04-29 20:48 ` Juri Linkov 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 8:24 UTC (permalink / raw) To: Po Lu; +Cc: emacs-devel > Date: Wed, 29 Apr 2020 14:34:09 +0800 > From: Po Lu via "Emacs development discussions." <emacs-devel@gnu.org> > > I'd appreciate some feedback on something I came up with during my spare > time: Emacs canvas support. I've read the code and the docs, but I don't think I have a clear idea of what "canvases" are and what would be their intended usage. Perhaps consider starting the documentation with some introductory comments and even a small tutorial. It sounds like you are talking about a way to create images dynamically, but then I don't understand why we need canvas-from-image, for example. Also, is there support for clicking the mouse on a canvas? I don't see it. > For now it only works on X11 + Cairo builds, and I haven't quite figured > out how to make redisplay work reliably on canvases Well, that'd be my main comments. I don't quite understand the parts of the display code you use for this, they seem like a copy/paste from other objects, sometimes with comments that weren't updated and still reference images instead of canvases. If the drawing on the canvas is supposed to be modified by Lisp code, then we'd need a much more elaborate machinery than just one flag to decide when a canvas needs to be redrawn. Thanks. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 8:24 ` Eli Zaretskii @ 2020-04-29 9:57 ` Po Lu 2020-04-29 10:10 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Po Lu @ 2020-04-29 9:57 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1847 bytes --] Eli Zaretskii <eliz@gnu.org> writes: > I've read the code and the docs, but I don't think I have a clear idea > of what "canvases" are and what would be their intended usage. > Perhaps consider starting the documentation with some introductory > comments and even a small tutorial. A canvas is an object that contains memory that can be painted to, and then displayed by setting it as the `display' text property. > It sounds like you are talking about a way to create images > dynamically, but then I don't understand why we need canvas-from-image, > for example. It's not really a way to create images dynamically, but rather paint to a section of the screen. `canvas-from-image' is just a convenience function that produces a paintable canvas from a static image. > Also, is there support for clicking the mouse on a canvas? I don't > see it. You can bind mouse events as usual. > Well, that'd be my main comments. I don't quite understand the parts > of the display code you use for this, they seem like a copy/paste from Correct, glyph production and iterator code code was mostly copied from the xwidgets code, since I was somewhat in a rush. Thanks for the feedback. > other objects, sometimes with comments that weren't updated and still > reference images instead of canvases. I'll fix that promptly. > If the drawing on the canvas is supposed to be modified by Lisp code, > then we'd need a much more elaborate machinery than just one flag to > decide when a canvas needs to be redrawn. Agreed, that is something I've been trying to figure out for some time now. Right now, I just do my best to figure out if the canvas has not touched anything important, and if it has, it just garbages each frame and calls redisplay. Attached is some sample code that should hopefully demonstrate exactly what they are capable of. [-- Attachment #2: canvas test --] [-- Type: text/plain, Size: 2879 bytes --] (setq canvas (make-canvas 1800 1800)) (erase-buffer) (insert (propertize " " 'display canvas)) (canvas-draw-image canvas (create-image "splash.png") 0 0 180 180 (selected-frame) 1.0) (canvas-ellipse canvas (/ 180 2) (/ 180 2) 130 130 "green") (canvas-rectangle canvas 0 0 180 180 "red" t) (canvas-rectangle canvas 20 20 40 100 "white" t 0.5) (canvas-draw-string canvas 0 0 "Hello, Emacs" "red" 1.0 "monospace" 50) (canvas-draw-string canvas 60 60 "Hello, Emacs" "white" 1.0 '("monospace" t t) 50) (let ((image (create-image "splash.png"))) (canvas-draw-image canvas image 700 20)) (setq track-mouse t) (defun mouse-movement-canvas-4-paint () "The function to be called when the mouse is moved." (interactive "") (track-mouse (let ((event)) (while (mouse-movement-p (setq event (read-event))) (let* ((position (event-start event)) (x (car (posn-x-y position))) (y (cdr (posn-x-y position)))) (message (format "%d %d" x y)) (canvas-filled-arc canvas x y 100.0 (* 45.0 (/ pi 180.0)) pi "blue")))))) (defun mouse-movement-canvas-paint () "The function to be called when the mouse is moved." (interactive "") (track-mouse (let ((event)) (while (mouse-movement-p (setq event (read-event))) (let* ((position (event-start event)) (x (car (posn-x-y position))) (y (cdr (posn-x-y position)))) (message (format "%d %d" x y)) (canvas-ellipse canvas x y 10 10 "blue")))))) (defun mouse-movement-3-canvas-paint () "The function to be called when the mouse is moved." (interactive "") (track-mouse (let ((event)) (while (mouse-movement-p (setq event (read-event))) (let* ((position (event-start event)) (x (car (posn-x-y position))) (y (cdr (posn-x-y position)))) (message (format "%d %d" x y)) (canvas-rectangle canvas x y 80 80 "white")))))) (defun mouse-movement-2-canvas-paint () "The function to be called when the mouse is moved." (interactive "") (track-mouse (let ((event)) (while (mouse-movement-p (setq event (read-event))) (let* ((position (event-start event)) (x (car (posn-x-y position))) (y (cdr (posn-x-y position)))) (message (format "%d %d" x y)) (canvas-draw-string canvas x y "Welcome to Emacs" nil nil nil 22)))))) (local-set-key [down-mouse-1] #'mouse-movement-canvas-paint) (local-set-key [down-mouse-3] #'mouse-movement-2-canvas-paint) (local-set-key [down-mouse-2] #'mouse-movement-3-canvas-paint) (local-set-key [C-down-mouse-1] #'mouse-movement-canvas-4-paint) (canvas-measure-string canvas "hi") (canvas-draw-string canvas 0 0 "Hi" "yellow") (setq canvas2 (canvas-from-image (create-image "splash.png"))) (canvas-rectangle canvas2 0 0 40 20 "orange") (canvas-rectangle canvas2 20 0 40 20 "black") (canvas-rounded-rectangle canvas 20 0 40 20 10.0 "red" nil) (canvas-draw-canvas canvas canvas2 500 500) ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 9:57 ` Po Lu @ 2020-04-29 10:10 ` Eli Zaretskii 2020-04-29 10:22 ` Po Lu 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 10:10 UTC (permalink / raw) To: Po Lu; +Cc: emacs-devel > From: Po Lu <luangruo@yahoo.com> > Cc: emacs-devel@gnu.org > Date: Wed, 29 Apr 2020 17:57:03 +0800 > > > I've read the code and the docs, but I don't think I have a clear idea > > of what "canvases" are and what would be their intended usage. > > Perhaps consider starting the documentation with some introductory > > comments and even a small tutorial. > > A canvas is an object that contains memory that can be painted to, and > then displayed by setting it as the `display' text property. That's the technical description of the implementation. It doesn't explain when canvases can be useful and for what purposes. > > It sounds like you are talking about a way to create images > > dynamically, but then I don't understand why we need canvas-from-image, > > for example. > > It's not really a way to create images dynamically, but rather paint to > a section of the screen. But the result of this painting is some graphical object, similar to an image, that will be displayed within a buffer, right? > > Also, is there support for clicking the mouse on a canvas? I don't > > see it. > > You can bind mouse events as usual. Did I miss the code that tells Emacs the click was on a canvas? E.g., if you click on a canvas, what does posn-object return when passed the click event as its argument? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 10:10 ` Eli Zaretskii @ 2020-04-29 10:22 ` Po Lu 2020-04-29 10:27 ` Po Lu via Emacs development discussions. 2020-04-29 10:35 ` Eli Zaretskii 0 siblings, 2 replies; 53+ messages in thread From: Po Lu @ 2020-04-29 10:22 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Eli Zaretskii <eliz@gnu.org> writes: > That's the technical description of the implementation. It doesn't > explain when canvases can be useful and for what purposes. Canvases are useful for when I want to be able to control a portion of a screen dynamically, in a fast way, from Lisp code. For instance, displaying a constantly changing bar chart or graph inside Emacs. > But the result of this painting is some graphical object, similar to > an image, that will be displayed within a buffer, right? Correct. > Did I miss the code that tells Emacs the click was on a canvas? E.g., > if you click on a canvas, what does posn-object return when passed the > click event as its argument? Oops. I missed that. Thanks for bringing it up. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 10:22 ` Po Lu @ 2020-04-29 10:27 ` Po Lu via Emacs development discussions. 2020-04-29 11:47 ` Eli Zaretskii 2020-04-29 10:35 ` Eli Zaretskii 1 sibling, 1 reply; 53+ messages in thread From: Po Lu via Emacs development discussions. @ 2020-04-29 10:27 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 896 bytes --] Po Lu <luangruo@yahoo.com> writes: > Eli Zaretskii <eliz@gnu.org> writes: > > >> That's the technical description of the implementation. It doesn't >> explain when canvases can be useful and for what purposes. > > Canvases are useful for when I want to be able to control a portion of a > screen dynamically, in a fast way, from Lisp code. For instance, > displaying a constantly changing bar chart or graph inside Emacs. > >> But the result of this painting is some graphical object, similar to >> an image, that will be displayed within a buffer, right? > > Correct. > >> Did I miss the code that tells Emacs the click was on a canvas? E.g., >> if you click on a canvas, what does posn-object return when passed the >> click event as its argument? > > Oops. I missed that. Thanks for bringing it up. Here is a version of the patch with several of the problems you mentioned rectified. [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-emacs-canvas-patches.diff --] [-- Type: text/x-patch, Size: 62494 bytes --] diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index e53f0e9f60..2a24576620 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -27,6 +27,7 @@ Display * Window Dividers:: Separating windows visually. * Display Property:: Images, margins, text size, etc. * Images:: Displaying images in Emacs buffers. +* Canvases:: Drawing areas inside Emacs buffers. * Xwidgets:: Displaying native widgets in Emacs buffers. * Buttons:: Adding clickable buttons to Emacs buffers. * Abstract Display:: Emacs's Widget for Object Collections. @@ -6509,6 +6510,152 @@ Image Cache debugging. @end defvar +@node Canvases +@cindex drawing canvases +@cindex drawing areas +@cindex canvases +@section Canvases + +This chapter describes canvases, objects that can store drawing operations +which are then displayed inside buffer text. + +@menu +* Creating Canvases:: How canvases can be created +* Operating on Canvases:: How canvases can be used +* Displaying Canvases:: How canvases can be displayed +@end menu + +@node Creating Canvases +@cindex creating Canvases +@pindex make-canvas + + This section describes how canvases can be created. +To create a canvas, call the function @code{make-canvas}. + +@defun make-canvas width height +This function takes 2 arguments @code{width}, and @code{height}, +and creates a canvas @code{width} wide and @code{height} tall. +@end defun + +@defun canvas-from-image image &optional width height +This function creates a canvas from the image descriptor +@code{image}. The created canvas will be @code{width} wide, +and @code{height} tall, if specified. +@end defun + +@node Operating on Canvases +@cindex operating on Canvases +@pindex canvas-rectangle + + This section describes how canvases can be drawn to, +and manipulated. + +@defun canvasp canvas +Return whether @code{canvas} is a canvas or not. +@end defun + +@defun canvas-dimensions canvas +Return the dimensions of @code{canvas} as a pair. +@end defun + +@defun canvas-ellipse canvas x y width height &optional color + hollow opacity +Draw an ellipse centred upon @code{x}, @code{y} onto the canvas +@code{canvas}. The drawn ellipse will be colored @code{color}, +or the current frame's foreground color if @code{color} is not +specified or nil. The opacity of the drawn item will be +@code{opacity}, and the item will be hollow if @code{hollow} is +non-nil. +@end defun + +@defun canvas-rectangle canvas x y width height &optional color + hollow opacity +Draw a rectangle at @code{x}, and @code{y} onto the canvas +@code{canvas}. The rectangle will be colored @code{color}, +or the current frame's foreground color if @code{color} is nil. +The opacity of the drawn item will be @code{opacity}, and the item +will be hollow if @code{hollow} is non-nil. +@end defun + +@defun canvas-fill-pixel canvas x y color opacity +Fill the pixel at @code{x}, @code{y} inside @code{canvas} +to @code{color}, with the opacity @code{opacity}. +@end defun + +@defun canvas-draw-string canvas x y string + &optional color opacity family size +Draw the string @code{string} to @code{x}, @code{y} +inside the canvas @code{canvas}. The font family used +will be @code{family}, the color @code{color}, the opacity +@code{opacity}, and the size @code{size}. + +@code{family} can either be a string, or a list in which +the first element should be the family as a string, the +second element should be whether the font should be italic, +and an optional third argument describing whether or not +the font should be bold. +@end defun + +@defun canvas-draw-image canvas image-spec x y + &optional width height frame opacity +Paint @code{image-spec} into @code{canvas} at @code{x}, +@code{y}. If @code{width} or @code{height} +is set and the image is wider than @code{width} or @code{height} respectively, +the image will be cropped to fit. The alpha channel of @code{image-spec} +will be set to @code{opacity}. +@end defun + +@defun canvas-measure-string canvas string &optional family size +Return a cons pair containing the width and height of @code{string}, +when rendered onto @code{canvas}, with the font @code{family} at +@code{size}. +@end defun + +@defun canvas-rounded-rectangle canvas x y width height radius + &optional color hollow opacity +Draw a rounded rectangle at @code{x}, @code{y} onto @code{canvas}. +The opacity of the rectangle will be @code{opacity}. +The radius of the rectangle will be @code{radius}. +@end defun + +@defun canvas-pixel-at canvas x y +Return the pixel at @code{x}, @code{y} inside @code{canvas}, +as an ARGB list. +@end defun + +@defun canvas-draw-canvas canvas canvas2 x y &optional width height opacity +Draw @code{canvas2} onto @code{canvas} at @code{x}, @code{y}. +@code{canvas2}'s alpha channel will be set to @code{opacity}, +if specified. +@code{canvas2} will not be taller than @code{height} or wider than +@code{width}, if specified. +@end defun + +@defun canvas-width canvas +Return the width of @code{canvas}. +@end defun + +@defun canvas-height canvas +Return the height of @code{canvas}. +@end defun + +@defun canvas-region canvas x y width height +Return a subsection of @code{canvas} at @code{x}, +@code{y}, that is @code{width} wide and @code{height} tall. +@end defun + +@defun canvas-arc canvas x y radius angle1 angle2 &optional color opacity +Draw an arc at @code{x}, @code{y}, with a radius of @code{radius}, +and the angles @code{angle1}, @code{angle2}. +@end defun + +@node Displaying Canvases +@cindex displaying Canvases + + Canvases can be displayed by setting them as the +@code{display} property of a string. + + @node Xwidgets @section Embedded Native Widgets @cindex xwidget diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index bba1b63115..d995ec4606 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -1430,6 +1430,7 @@ Top * Window Dividers:: Separating windows visually. * Display Property:: Enabling special display features. * Images:: Displaying images in Emacs buffers. +* Canvases:: Drawing areas inside Emacs buffers. * Buttons:: Adding clickable buttons to Emacs buffers. * Abstract Display:: Emacs's Widget for Object Collections. * Blinking:: How Emacs shows the matching open parenthesis. diff --git a/lisp/canvas.el b/lisp/canvas.el new file mode 100644 index 0000000000..d60d3efa4f --- /dev/null +++ b/lisp/canvas.el @@ -0,0 +1,49 @@ +;;; canvas.el --- Canvas support for GNU Emacs + +;; Copyright (C) 2020 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; These are several utlity functions for canvas operations that can +;;; be implemented in Lisp code. + +\f +;;; Code: + +(defun canvas-fill-pixel (canvas x y color opacity) + "Set the pixel at X, Y inside CANVAS to COLOR, with the opacity OPACITY." + (canvas-rectangle canvas x y 1 1 color opacity)) + +(defun canvas-from-image (image &optional width height) + "Create a canvas from IMAGE. +The canvas will be no wider than WIDTH (if specified), +and no taller than HEIGHT (if specified)." + (let ((canvas (make-canvas (or width (car (image-size image t))) + (or height (cdr (image-size image t)))))) + (prog1 canvas (canvas-draw-image canvas image 0 0)))) + +(defun canvas-width (canvas) + "Return the width of CANVAS." + (car (canvas-dimensions canvas))) + +(defun canvas-height (canvas) + "Return the height of CANVAS." + (cdr (canvas-dimensions canvas))) + +(provide 'canvas) +;;; canvas.el ends here diff --git a/lisp/loadup.el b/lisp/loadup.el index 97525b2708..1a9b1c7410 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -285,6 +285,8 @@ (load "emacs-lisp/tabulated-list") (load "buff-menu") +(load "canvas") + (if (fboundp 'x-create-frame) (progn (load "fringe") diff --git a/src/Makefile.in b/src/Makefile.in index 552dd2e50a..0d70c68b04 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -407,6 +407,7 @@ .m.o: ## be dumped as pure by dump-emacs. base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ charset.o coding.o category.o ccl.o character.o chartab.o bidi.o \ + common-canvas.o \ $(CM_OBJ) term.o terminal.o xfaces.o $(XOBJ) $(GTK_OBJ) $(DBUS_OBJ) \ emacs.o keyboard.o macros.o keymap.o sysdep.o \ bignum.o buffer.o filelock.o insdel.o marker.o \ diff --git a/src/alloc.c b/src/alloc.c index cc9ba8dbf5..acce0109e1 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -47,6 +47,7 @@ Copyright (C) 1985-1986, 1988, 1993-1995, 1997-2020 Free Software #include "blockinput.h" #include "pdumper.h" #include "termhooks.h" /* For struct terminal. */ +#include "canvas.h" #ifdef HAVE_WINDOW_SYSTEM #include TERM_HEADER #endif /* HAVE_WINDOW_SYSTEM */ @@ -3114,6 +3115,11 @@ cleanup_vector (struct Lisp_Vector *vector) module_finalize_function (function); } #endif + else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_CANVAS)) + { + struct canvas *cnvs = (struct canvas *) vector; + destroy_canvas_contents (cnvs->canvas); + } } /* Reclaim space used by unmarked vectors. */ diff --git a/src/canvas.h b/src/canvas.h new file mode 100644 index 0000000000..036f2bf499 --- /dev/null +++ b/src/canvas.h @@ -0,0 +1,83 @@ +/* Canvas support for GNU Emacs. + Copyright (C) 2020 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "lisp.h" +#include "frame.h" +#include "window.h" + +#ifdef USE_CAIRO +#include <cairo/cairo.h> +typedef cairo_surface_t *canvas_contents_t; +#else +typedef void *canvas_contents_t; +#endif + +struct canvas +{ + union vectorlike_header header; + Lisp_Object cnvs_objects; + Lisp_Object window; + Lisp_Object object; + + canvas_contents_t canvas; + int width, height; + bool multiple_objects_seen; + bool changed_since_last_redisplay; +} GCALIGNED_STRUCT; + +/* Test for canvas pseudovector. */ +#define CANVASP(x) PSEUDOVECTORP (x, PVEC_CANVAS) +#define XCANVAS(a) \ + (eassert (CANVASP (a)), XUNTAG (a, Lisp_Vectorlike, struct canvas)) + +#define CHECK_CANVAS(x) CHECK_TYPE (CANVASP (x), Qcanvasp, x) + +#define MARK_CANVAS_CHANGED(x) \ + (((x)->changed_since_last_redisplay = true), \ + (windows_or_buffers_changed = 2), (redisplay ())); \ + do \ + { \ + if ((x)->multiple_objects_seen || !EQ (selected_window, (x)->window)) \ + { \ + Lisp_Object tail, head; \ + FOR_EACH_FRAME (tail, head) \ + { \ + struct frame *f = XFRAME (head); \ + SET_FRAME_GARBAGED (f); \ + } \ + } \ + } \ + while (false) + +extern Lisp_Object +make_canvas (int width, int height); + +extern canvas_contents_t +make_canvas_contents (int width, int height); + +extern void +destroy_canvas_contents (canvas_contents_t contents); + +extern void +syms_of_canvas (void); + +extern void +canvas_end_redisplay (struct window *w, + struct glyph_matrix *matrix); diff --git a/src/common-canvas.c b/src/common-canvas.c new file mode 100644 index 0000000000..329c37daad --- /dev/null +++ b/src/common-canvas.c @@ -0,0 +1,868 @@ +/* Canvas support for GNU Emacs. + Copyright (C) 2020 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ + +#include "canvas.h" +#include "coding.h" + +#include <math.h> + +Lisp_Object +make_canvas (int width, int height) +{ +#ifndef USE_CAIRO + error ("Canvases are not supported without cairo.") +#endif + struct canvas *canvas = ALLOCATE_PSEUDOVECTOR + (struct canvas, object, PVEC_CANVAS); + canvas->width = width; + canvas->height = height; + canvas->object = Qnil; + canvas->multiple_objects_seen = false; + canvas->canvas = make_canvas_contents (width, height); + canvas->changed_since_last_redisplay = true; + + Lisp_Object cnvs; + XSETCANVAS (cnvs, canvas); + return cnvs; +} + +void +destroy_canvas_contents (canvas_contents_t contents) +{ +#ifdef USE_CAIRO + cairo_surface_destroy (contents); +#else + error ("Not implemented"); +#endif +} + +canvas_contents_t +make_canvas_contents (int width, int height) +{ +#ifdef USE_CAIRO + cairo_surface_t *crs = + cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); + return crs; +#else + error ("Not implemented"); +#endif +} + +void +canvas_end_redisplay (struct window *w, + struct glyph_matrix *matrix) +{ + int i; + int area; + + struct canvas **unmark_canvases; + ptrdiff_t unmark_size = 0; + unmark_canvases = xmalloc (sizeof *unmark_canvases * unmark_size); + + for (i = 0; i < matrix->nrows; ++i) + { + struct glyph_row *row; + row = MATRIX_ROW (matrix, i); + if (row->enabled_p) + for (area = LEFT_MARGIN_AREA; area < LAST_AREA; ++area) + { + struct glyph *glyph = row->glyphs[area]; + struct glyph *glyph_end = glyph + row->used[area]; + for (; glyph < glyph_end; ++glyph) + if (glyph->type == CANVAS_GLYPH && + glyph->u.canvas->changed_since_last_redisplay) + { + ++unmark_size; + unmark_canvases + = xrealloc (unmark_canvases, + (sizeof *unmark_canvases * unmark_size)); + unmark_canvases[unmark_size - 1] = glyph->u.canvas; + canvas_update_glyph + (w, i, row, area, row->glyphs[area] - + glyph, 1 + row->glyphs[area] - glyph, + glyph); + } + } + } + for (; unmark_size; unmark_size--) + unmark_canvases[unmark_size - 1]->changed_since_last_redisplay = false; + xfree (unmark_canvases); +} + +DEFUN ("make-canvas", Fmake_canvas, Smake_canvas, 2, 2, 0, + doc: /* Create a canvas, WIDTH pixels wide, and HEIGHT pixels tall. */) + (Lisp_Object width, Lisp_Object height) +{ + check_integer_range (height, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + + return make_canvas (XFIXNUM (width), XFIXNUM (height)); +} + +DEFUN ("canvas-ellipse", Fcanvas_ellipse, Scanvas_ellipse, 5, 8, 0, + doc: /* Draw a WIDTH wide and HEIGHT tall ellipse centred at X, Y in CANVAS. +The color of the ellipse will be COLOR (or the foreground color of COLOR is nil). +The ellipse will be hollow if HOLLOW is non-nil. +The opacity of the circle will be OPACITY, which should be a floating-point +number between 1 and 0. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, + Lisp_Object width, Lisp_Object height, Lisp_Object color, + Lisp_Object hollow, Lisp_Object opacity) +{ + if (NILP (opacity)) + opacity = make_float (1.0); + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + check_integer_range (height, 0, INT_MAX); + CHECK_NUMBER (opacity); +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame))->query_colors + (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + cairo_matrix_t save_matrix; + cairo_get_matrix (cr, &save_matrix); + cairo_translate (cr, XFIXNUM (x), XFIXNUM (y)); + cairo_scale (cr, 1, 1); + cairo_new_path (cr); + cairo_arc (cr, 0, 0, + XFIXNUM (width) / 2.0, 0, 2 * M_PI); + cairo_set_matrix (cr, &save_matrix); + + cairo_set_line_width (cr, canvas_stroke_width); + if (!NILP (hollow)) + cairo_stroke (cr); + else + cairo_fill (cr); + + cairo_destroy (cr); + MARK_CANVAS_CHANGED (cv); +#endif + return Qnil; +} + +DEFUN ("canvasp", Fcanvasp, Scanvasp, 1, 1, 0, + doc: /* Return t if CANVAS is a canvas, else nil. */) + (Lisp_Object canvas) +{ + return CANVASP (canvas) ? Qt : Qnil; +} + +DEFUN ("canvas-rectangle", Fcanvas_rectangle, Scanvas_rectangle, 5, 8, 0, + doc: /* Draw a WIDTH wide and HEIGHT tall rectangle at X, Y in CANVAS. +The color of the rectangle will be COLOR (or the foreground color of COLOR is nil). +The rectangle will be hollow if HOLLOW is non-nil. +The opacity of the circle will be OPACITY, which should be a floating-point +number between 1 and 0. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, + Lisp_Object width, Lisp_Object height, Lisp_Object color, + Lisp_Object hollow, Lisp_Object opacity) +{ + if (NILP (opacity)) + opacity = make_float (1.0); + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + check_integer_range (height, 0, INT_MAX); + CHECK_NUMBER (opacity); +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame))->query_colors + (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + if (NILP (hollow)) + { + cairo_rectangle (cr, XFIXNUM (x), XFIXNUM (y), XFIXNUM (width), + XFIXNUM (height)); + cairo_fill (cr); + } + else + { + cairo_rectangle (cr, XFIXNUM (x), XFIXNUM (y), XFIXNUM (width), + XFIXNUM (height)); + cairo_stroke (cr); + } + cairo_destroy (cr); + MARK_CANVAS_CHANGED (cv); + return Qnil; +#else + error ("Not implemented"); +#endif +} + +DEFUN ("canvas-rounded-rectangle", Fcanvas_rounded_rectangle, Scanvas_rounded_rectangle, 6, MANY, 0, + doc: /* Draw a WIDTH wide and HEIGHT tall rectangle at X, Y in CANVAS. +The color of the rectangle will be COLOR (or the foreground color of COLOR is nil). +The rectangle will be hollow if HOLLOW is non-nil. +The opacity of the circle will be OPACITY, which should be a floating-point +number between 1 and 0. +usage: (canvas-rounded-rectangle canvas x y width height radius &optional color hollow opacity) */) + (ptrdiff_t nargs, Lisp_Object *args) +{ + Lisp_Object canvas, x, y, width, height, radius, color, hollow, opacity; + canvas = args[0]; + x = args[1]; + y = args[2]; + width = args[3]; + height = args[4]; + radius = args[5]; + color = Qnil; + hollow = Qnil; + opacity = Qnil; + + if (nargs > 6) + color = args[6]; + if (nargs > 7) + hollow = args[7]; + if (nargs > 8) + opacity = args[8]; + if (nargs > 9) + xsignal2 (Qwrong_number_of_arguments, + Qcanvas_rounded_rectangle, + make_fixnum (nargs)); + if (NILP (opacity)) + opacity = make_float (1.0); + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + check_integer_range (height, 0, INT_MAX); + CHECK_NUMBER (opacity); +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame))->query_colors + (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + { +#define radius XFLOATINT (radius) + double degrees = M_PI / 180.0; +#define width XFIXNUM (width) +#define height XFIXNUM (height) +#define x XFIXNUM (x) +#define y XFIXNUM (y) + cairo_new_sub_path (cr); + cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees); + cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees); + cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees); + cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees); + cairo_close_path (cr); +#undef radius +#undef y +#undef x +#undef height +#undef width + } +#undef radius + cairo_set_line_width (cr, canvas_stroke_width); + if (NILP (hollow)) + cairo_fill (cr); + else + cairo_stroke (cr); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (cv); + return Qnil; +#else + error ("Not implemented"); +#endif +} + +DEFUN ("canvas-draw-string", Fcanvas_draw_string, Scanvas_draw_string, 4, 8, 0, + doc: /* Draw the string STRING onto the canvas CANVAS at X, Y. +The opacity of the drawn text will be OPACITY, and the color of the drawn text will be COLOR. +The font-family used will be FAMILY, which can be a string or a list of +the font-family as a string, whether or not the the text should be italic, +and whether or not the text should be bold. +The size of the text will be SIZE, or the default text size if nil. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, Lisp_Object string, + Lisp_Object color, Lisp_Object opacity, Lisp_Object family, Lisp_Object size) +{ + if (noninteractive) + error ("`canvas-draw-string' cannot be called when running in batch mode."); + if (NILP (opacity)) + opacity = make_float (1.0); + if (NILP (size)) + size = make_fixnum (FRAME_TEXT_HEIGHT (XFRAME (selected_frame))); + if (NILP (family)) + family = build_string ("monospace"); + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + CHECK_NUMBER (opacity); + if (!Flistp (family)) + CHECK_STRING (family); + CHECK_STRING (string); +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame))->query_colors + (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + struct canvas *c = XCANVAS (canvas); + cairo_t *cr = cairo_create (c->canvas); + cairo_font_slant_t slant_flags = CAIRO_FONT_SLANT_NORMAL; + cairo_font_weight_t weight_flags = CAIRO_FONT_WEIGHT_NORMAL; + const char *family_utf8; + if (!NILP (Flistp (family))) + { + if (!NILP (CALLN (Flss, Flength (family), make_fixnum (2)))) + error ("Invalid font spec"); + else + { + int length = XFIXNUM (Flength (family)); + if (length > 3) + error ("Invalid font spec"); + Lisp_Object italic, bold; + CHECK_STRING_CAR (family); + family_utf8 = SSDATA (ENCODE_UTF_8 (XCAR (family))); + if (length == 3) + bold = XCAR (XCDR (XCDR (family))); + else + bold = Qnil; + italic = XCAR (XCDR (family)); + if (!NILP (italic)) + slant_flags = CAIRO_FONT_SLANT_ITALIC; + if (!NILP (bold)) + weight_flags = CAIRO_FONT_WEIGHT_BOLD; + } + } + else + { + family_utf8 = SSDATA (ENCODE_UTF_8 (family)); + } + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + cairo_select_font_face (cr, family_utf8, + slant_flags, + weight_flags); + cairo_set_font_size (cr, XFIXNUM (size)); + cairo_move_to (cr, XFIXNUM (x), XFIXNUM (y) + XFIXNUM (size)); + cairo_show_text (cr, SSDATA (ENCODE_UTF_8 (string))); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (c); + return Qnil; +#else + return Qnil; +#endif +} + +DEFUN ("canvas-measure-string", Fcanvas_measure_string, Scanvas_measure_string, 2, 4, 0, + doc: /* Return a pair containing the width and height of STRING, +if drawn on CANVAS with the family FAMILY at SIZE. */) +(Lisp_Object canvas, Lisp_Object string, Lisp_Object family, Lisp_Object size) +{ + if (noninteractive) + error ("`canvas-draw-string' cannot be called when running in batch mode."); + if (NILP (size)) + size = make_fixnum (FRAME_TEXT_HEIGHT (XFRAME (selected_frame))); + if (NILP (family)) + family = build_string ("cairo:monospace"); + CHECK_CANVAS (canvas); + + if (!Flistp (family)) + CHECK_STRING (family); + CHECK_STRING (string); +#ifdef USE_CAIRO + struct canvas *c = XCANVAS (canvas); + cairo_t *cr = cairo_create (c->canvas); + cairo_font_slant_t slant_flags = CAIRO_FONT_SLANT_NORMAL; + cairo_font_weight_t weight_flags = CAIRO_FONT_WEIGHT_NORMAL; + const char *family_utf8; + if (!NILP (Flistp (family))) + { + if (!NILP (CALLN (Flss, Flength (family), make_fixnum (2)))) + error ("Invalid font spec"); + else + { + int length = XFIXNUM (Flength (family)); + if (length > 3) + error ("Invalid font spec"); + Lisp_Object italic, bold; + CHECK_STRING_CAR (family); + family_utf8 = SSDATA (ENCODE_UTF_8 (XCAR (family))); + if (length == 3) + bold = XCAR (XCDR (XCDR (family))); + else + bold = Qnil; + italic = XCAR (XCDR (family)); + if (!NILP (italic)) + slant_flags = CAIRO_FONT_SLANT_ITALIC; + if (!NILP (bold)) + weight_flags = CAIRO_FONT_WEIGHT_BOLD; + } + } + else + { + family_utf8 = SSDATA (ENCODE_UTF_8 (family)); + } + cairo_select_font_face (cr, family_utf8, + slant_flags, + weight_flags); + cairo_set_font_size (cr, XFIXNUM (size)); + cairo_text_extents_t extents; + cairo_text_extents (cr, SSDATA (ENCODE_UTF_8 (string)), &extents); + cairo_destroy (cr); + return Fcons (make_fixnum (extents.width), + make_fixnum (extents.height)); +#else + return Qnil; +#endif +} + +DEFUN ("canvas-draw-image", Fcanvas_draw_image, Scanvas_draw_image, 4, 8, + 0, doc: /* Paint IMAGE_SPEC onto CANVAS, at X, Y. +If WIDTH or HEIGHT is set, and IMAGE is wider than WIDTH or taller than HEIGHT, +IMAGE_SPEC will be cropped to fit WIDTH and/or HEIGHT respectively. +FRAME should be a live frame. +The opacity of the drawn image will be OPACITY. */) + (Lisp_Object canvas, Lisp_Object image_spec, + Lisp_Object x, Lisp_Object y, + Lisp_Object width, Lisp_Object height, + Lisp_Object frame, Lisp_Object opacity) +{ + if (valid_image_p (image_spec)) + { + if (NILP (frame)) + frame = selected_frame; + struct frame *f = decode_window_system_frame (frame); + ptrdiff_t id = lookup_image (f, image_spec); + struct image *img = IMAGE_FROM_ID (f, id); + if (!img) + return Qnil; + if (img->load_failed_p) + return Qnil; + int iwidth = img->width + 2 * img->hmargin; + int iheight = img->height + 2 * img->vmargin; + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + if (NILP (width)) + width = make_fixnum (iwidth); + check_integer_range (width, 0, INT_MAX); + if (NILP (height)) + height = make_fixnum (iheight); + check_integer_range (height, 0, INT_MAX); + if (NILP (width)) + width = make_fixnum (iwidth); + if (NILP (opacity)) + opacity = make_fixnum (1); + CHECK_NUMBER (opacity); +#ifdef USE_CAIRO + cairo_surface_t *crs + = cairo_image_surface_create_for_data ((unsigned char *) + img->pixmap->data, + (img->pixmap->bits_per_pixel + == 32 + ? CAIRO_FORMAT_RGB24 + : CAIRO_FORMAT_A8), + img->pixmap->width, + img->pixmap->height, + img->pixmap->bytes_per_line); + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_save (cr); + cairo_translate (cr, XFIXNUM (x), XFIXNUM (y)); + cairo_rectangle (cr, 0, 0, + XFIXNUM (width), XFIXNUM (height)); + cairo_clip (cr); + cairo_set_source_surface (cr, crs, 0, 0); + cairo_paint_with_alpha (cr, XFLOATINT (opacity)); + cairo_restore (cr); + cairo_destroy (cr); + cairo_surface_destroy (crs); + MARK_CANVAS_CHANGED (cv); +#endif + } + else + error ("Invalid image specification"); + return Qnil; +} + +DEFUN ("canvas-arc", Fcanvas_arc, Scanvas_arc, 6, 8, 0, + doc: /* Draw an arc on CANVAS starting from XC, YC, +with a radius of RADIUS and 2 angles angle1 and angle2. +Use the color COLOR with the alpha channel set to OPACITY, if specified. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, Lisp_Object radius, + Lisp_Object angle1, Lisp_Object angle2, Lisp_Object color, Lisp_Object opacity) +{ + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + CHECK_NUMBER (angle1); + CHECK_NUMBER (angle2); + CHECK_NUMBER (radius); + + if (NILP (opacity)) + opacity = make_fixnum (1.0); + CHECK_NUMBER (opacity); + +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame)) + ->query_colors (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + cairo_t *cr = cairo_create (XCANVAS (canvas)->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + cairo_arc (cr, XFLOATINT (x), XFLOATINT (y), XFLOATINT (radius), + XFLOATINT (angle1), XFLOATINT (angle2)); + cairo_stroke (cr); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (XCANVAS (canvas)); +#else +#endif + return Qnil; +} + +DEFUN ("canvas-filled-arc", Fcanvas_filled_arc, Scanvas_filled_arc, 6, 8, 0, + doc: /* Draw a filled arc on CANVAS starting from XC, YC, +with a radius of RADIUS and 2 angles angle1 and angle2. +Use the color COLOR with the alpha channel set to OPACITY, if specified. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y, Lisp_Object radius, + Lisp_Object angle1, Lisp_Object angle2, Lisp_Object color, Lisp_Object opacity) +{ + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + CHECK_NUMBER (angle1); + CHECK_NUMBER (angle2); + CHECK_NUMBER (radius); + + if (NILP (opacity)) + opacity = make_fixnum (1.0); + CHECK_NUMBER (opacity); + +#ifdef USE_CAIRO + if (NILP (color)) + color = Qunspecified; + Lisp_Object color_values = call1 (Qcolor_values, color); + if (!NILP (color_values)) + CHECK_LIST (color_values); + double r, g, b; + if (NILP (color_values)) + { + Emacs_Color col; + col.pixel = FRAME_FOREGROUND_PIXEL (XFRAME (selected_frame)); + FRAME_TERMINAL (XFRAME (selected_frame)) + ->query_colors (XFRAME (selected_frame), &col, 1); + r = col.red / 65535.0; + g = col.green / 65535.0; + b = col.blue / 65535.0; + } + else + { + Lisp_Object lr = Fnth (make_fixnum (0), color_values), + lg = Fnth (make_fixnum (1), color_values), + lb = Fnth (make_fixnum (2), color_values); + + check_integer_range (lr, 0, 65535); + check_integer_range (lg, 0, 65535); + check_integer_range (lb, 0, 65535); + + r = XFIXNUM (lr) / 65535.0; + g = XFIXNUM (lg) / 65535.0; + b = XFIXNUM (lb) / 65535.0; + } + cairo_t *cr = cairo_create (XCANVAS (canvas)->canvas); + cairo_set_source_rgba (cr, r, g, b, XFLOATINT (opacity)); + cairo_arc (cr, XFLOATINT (x), XFLOATINT (y), XFLOATINT (radius), + XFLOATINT (angle1), XFLOATINT (angle2)); + cairo_fill (cr); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (XCANVAS (canvas)); +#else +#endif + return Qnil; +} + +DEFUN ("canvas-region", Fcanvas_region, Scanvas_region, 5, 5, 0, + doc: /* Return a canvas containing a WIDTH wide and HEIGHT tall +subsection of CANVAS at X, Y */) + (Lisp_Object canvas, Lisp_Object x, + Lisp_Object y, Lisp_Object width, Lisp_Object height) +{ + CHECK_CANVAS (canvas); + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + check_integer_range (width, 0, INT_MAX); + check_integer_range (height, 0, INT_MAX); + +#ifdef USE_CAIRO + int ix = XFIXNUM (x), + iy = XFIXNUM (y), + iw = XFIXNUM (width), + ih = XFIXNUM (height); + cairo_surface_t *s = cairo_surface_create_for_rectangle + (XCANVAS (canvas)->canvas, ix, iy, iw, ih); + Lisp_Object newcvs = make_canvas (iw, ih); + struct canvas *target = XCANVAS (newcvs); + cairo_t *t = cairo_create (target->canvas); + cairo_set_source_surface (t, s, 0, 0); + cairo_paint (t); + cairo_destroy (t); + cairo_surface_destroy (s); + return newcvs; +#else + error ("Not implemented.") +#endif +} + + +DEFUN ("canvas-draw-canvas", Fcanvas_draw_canvas, Scanvas_draw_canvas, 4, 7, + 0, doc: /* Paint CANVAS2 onto CANVAS, at X, Y. +If WIDTH or HEIGHT is set, and IMAGE is wider than WIDTH or taller than HEIGHT, +IMAGE_SPEC will be cropped to fit WIDTH and/or HEIGHT respectively. +The opacity of the drawn image will be OPACITY. */) + (Lisp_Object canvas, Lisp_Object canvas2, + Lisp_Object x, Lisp_Object y, + Lisp_Object width, Lisp_Object height, Lisp_Object opacity) +{ + CHECK_CANVAS (canvas); + CHECK_CANVAS (canvas2); + int iwidth = XCANVAS (canvas2)->width; + int iheight = XCANVAS (canvas2)->height; + check_integer_range (x, 0, INT_MAX); + check_integer_range (y, 0, INT_MAX); + if (NILP (width)) + width = make_fixnum (iwidth); + check_integer_range (width, 0, INT_MAX); + if (NILP (height)) + height = make_fixnum (iheight); + check_integer_range (height, 0, INT_MAX); + if (NILP (width)) + width = make_fixnum (iwidth); + if (NILP (opacity)) + opacity = make_fixnum (1); + CHECK_NUMBER (opacity); + +#ifdef USE_CAIRO + cairo_surface_t *crs = XCANVAS (canvas2)->canvas; + struct canvas *cv = XCANVAS (canvas); + cairo_t *cr = cairo_create (cv->canvas); + cairo_save (cr); + cairo_translate (cr, XFIXNUM (x), XFIXNUM (y)); + cairo_rectangle (cr, 0, 0, XFIXNUM (width), XFIXNUM (height)); + cairo_clip (cr); + cairo_set_source_surface (cr, crs, 0, 0); + cairo_paint_with_alpha (cr, XFLOATINT (opacity)); + cairo_restore (cr); + cairo_destroy (cr); + MARK_CANVAS_CHANGED (cv); +#endif + return Qnil; +} + +DEFUN ("canvas-pixel-at", Fcanvas_pixel_at, Scanvas_pixel_at, 3, 3, 0, + doc: /* Return the color of the pixel at X, Y inside CANVAS as an ARGB list. */) + (Lisp_Object canvas, Lisp_Object x, Lisp_Object y) +{ +#ifndef USE_CAIRO + error ("Not implemented."); +#else + CHECK_CANVAS (canvas); + check_integer_range (x, 0, XCANVAS (canvas)->width); + check_integer_range (y, 0, XCANVAS (canvas)->height); + struct { +#ifdef WORDS_BIGENDIAN + uint8_t a, r, g, b; +#else + uint8_t b, g, r, a; +#endif + } *argb32 = + (void *) cairo_image_surface_get_data (XCANVAS (canvas)->canvas); + typeof (*argb32) res = argb32 [XFIXNUM (y) * XCANVAS (canvas)->width + + XFIXNUM (x)]; + return CALLN (Flist, make_fixnum (res.a), + make_fixnum (res.r), + make_fixnum (res.g), + make_fixnum (res.b)); +#endif +} + +DEFUN ("canvas-dimensions", Fcanvas_dimensions, Scanvas_dimensions, 1, 1, 0, + doc: /* Return a cons pair containing the width and height of CANVAS. */) + (Lisp_Object canvas) +{ + CHECK_CANVAS (canvas); + return Fcons (make_fixnum (XCANVAS (canvas)->width), + make_fixnum (XCANVAS (canvas)->height)); +} + +void +syms_of_canvas (void) +{ + defsubr (&Smake_canvas); + defsubr (&Scanvas_rectangle); + defsubr (&Scanvas_ellipse); + defsubr (&Scanvas_rectangle); + defsubr (&Scanvas_draw_string); + defsubr (&Scanvas_draw_image); + defsubr (&Scanvas_draw_canvas); + defsubr (&Scanvas_measure_string); + defsubr (&Scanvas_dimensions); + defsubr (&Scanvas_region); + defsubr (&Scanvas_pixel_at); + defsubr (&Scanvas_arc); + defsubr (&Scanvasp); + defsubr (&Scanvas_filled_arc); + defsubr (&Scanvas_rounded_rectangle); + DEFSYM (Qcanvas_rounded_rectangle, "canvas-rounded-rectangle"); + DEFSYM (Qcanvasp, "canvasp"); + DEFSYM (Qcolor_values, "color-values"); + DEFVAR_INT ("canvas-stroke-width", canvas_stroke_width, + doc: /* The stroke width to be used in canvases. */); + canvas_stroke_width = 4; +} diff --git a/src/data.c b/src/data.c index bce2e53cfb..aefd6d7e70 100644 --- a/src/data.c +++ b/src/data.c @@ -263,6 +263,8 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, return Qxwidget; case PVEC_XWIDGET_VIEW: return Qxwidget_view; + case PVEC_CANVAS: + return Qcanvas; /* "Impossible" cases. */ case PVEC_MISC_PTR: case PVEC_OTHER: @@ -3859,6 +3861,7 @@ #define PUT_ERROR(sym, tail, msg) \ DEFSYM (Qchar_table, "char-table"); DEFSYM (Qbool_vector, "bool-vector"); DEFSYM (Qhash_table, "hash-table"); + DEFSYM (Qcanvas, "canvas"); DEFSYM (Qthread, "thread"); DEFSYM (Qmutex, "mutex"); DEFSYM (Qcondition_variable, "condition-variable"); diff --git a/src/dispextern.h b/src/dispextern.h index 0b1f3d14ae..d33b87c3df 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -382,7 +382,10 @@ #define SET_GLYPH_FROM_GLYPH_CODE(glyph, gc) \ STRETCH_GLYPH, /* Glyph is an external widget drawn by the GUI toolkit. */ - XWIDGET_GLYPH + XWIDGET_GLYPH, + + /* Glyph is a canvas. */ + CANVAS_GLYPH }; @@ -540,6 +543,9 @@ #define FACE_ID_BITS 20 struct xwidget *xwidget; #endif + /* Canvas reference (type == CANVAS_GLYPH). */ + struct canvas *canvas; + /* Sub-structure for type == STRETCH_GLYPH. */ struct { @@ -1405,6 +1411,9 @@ #define OVERLAPS_ERASED_CURSOR (1 << 2) /* Xwidget. */ struct xwidget *xwidget; + /* Canvas. */ + struct canvas *canvas; + /* Slice */ struct glyph_slice slice; @@ -2158,7 +2167,10 @@ #define MAX_FRINGE_BITMAPS (1<<FRINGE_ID_BITS) IT_CONTINUATION, /* Xwidget. */ - IT_XWIDGET + IT_XWIDGET, + + /* Canvas. */ + IT_CANVAS }; @@ -2223,6 +2235,7 @@ #define MAX_FRINGE_BITMAPS (1<<FRINGE_ID_BITS) GET_FROM_IMAGE, GET_FROM_STRETCH, GET_FROM_XWIDGET, + GET_FROM_CANVAS, NUM_IT_METHODS }; @@ -2447,6 +2460,10 @@ #define OVERLAY_STRING_CHUNK_SIZE 16 struct { Lisp_Object object; } xwidget; + /* method == GET_FROM_CANVAS */ + struct { + Lisp_Object object; + } canvas; } u; /* Current text and display positions. */ @@ -2578,6 +2595,9 @@ #define OVERLAY_STRING_CHUNK_SIZE 16 /* If what == IT_XWIDGET. */ struct xwidget *xwidget; + /* If what == IT_CANVAS. */ + struct canvas *canvas; + /* Values from `slice' property. */ struct it_slice slice; @@ -3714,6 +3734,11 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask) \ #endif /* HAVE_WINDOW_SYSTEM */ +extern void +canvas_update_glyph (struct window *w, int x, struct glyph_row *row, + enum glyph_row_area area, ptrdiff_t start, ptrdiff_t end, + struct glyph *glyph); + INLINE_HEADER_END #endif /* not DISPEXTERN_H_INCLUDED */ diff --git a/src/dispnew.c b/src/dispnew.c index 5b6fa51a56..9e0c1a060e 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -43,6 +43,7 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2020 Free Software Foundation, #include "tparam.h" #include "xwidget.h" #include "pdumper.h" +#include "canvas.h" #ifdef HAVE_WINDOW_SYSTEM #include TERM_HEADER @@ -3697,6 +3698,7 @@ update_window (struct window *w, bool force_p) #endif xwidget_end_redisplay (w, w->current_matrix); + canvas_end_redisplay (w, w->current_matrix); clear_glyph_matrix (desired_matrix); return paused_p; @@ -3782,6 +3784,7 @@ gui_update_window_end (struct window *w, bool cursor_on_p, FRAME_RIF (f)->update_window_end_hook (w, cursor_on_p, mouse_face_overwritten_p); + canvas_end_redisplay (w, w->current_matrix); } #endif /* HAVE_WINDOW_SYSTEM */ @@ -4371,6 +4374,11 @@ scrolling_window (struct window *w, int tab_line_p) return 0; #endif + /* We need this to fix canvas movement detection in a reliable way. + FIXME. */ + if (w->have_canvas_p) + return 0; + /* Give up if some rows in the desired matrix are not enabled. */ if (! MATRIX_ROW_ENABLED_P (desired_matrix, i)) return -1; @@ -5462,6 +5470,10 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p if (img && !NILP (img->spec)) *object = img->spec; } + else if (it.what == IT_CANVAS) + { + XSETCANVAS (*object, it.canvas); + } #endif /* IT's vpos counts from the glyph row that includes the window's @@ -5565,6 +5577,10 @@ mode_line_string (struct window *w, enum window_part part, y0 -= row->ascent - glyph->ascent; } #endif + if (glyph->type == CANVAS_GLYPH) + { + y0 -= row->ascent - glyph->ascent; + } } else { @@ -5654,6 +5670,10 @@ marginal_area_string (struct window *w, enum window_part part, y0 += glyph->slice.img.y; } #endif + if (glyph->type == CANVAS_GLYPH) + { + y0 -= row->ascent - glyph->ascent; + } } else { diff --git a/src/emacs.c b/src/emacs.c index ea9c4cd79d..c16af2c14c 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -94,6 +94,8 @@ #define MAIN_PROGRAM #include "getpagesize.h" #include "gnutls.h" +#include "canvas.h" + #ifdef PROFILING # include <sys/gmon.h> extern void moncontrol (int mode); @@ -1567,6 +1569,8 @@ main (int argc, char **argv) /* Before init_window_once, because it sets up the Vcoding_system_hash_table. */ syms_of_coding (); /* This should be after syms_of_fileio. */ + + syms_of_canvas (); init_frame_once (); /* Before init_window_once. */ init_window_once (); /* Init the window system. */ #ifdef HAVE_WINDOW_SYSTEM diff --git a/src/lisp.h b/src/lisp.h index b4ac017dcf..f463399dad 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -1103,6 +1103,7 @@ DEFINE_GDB_SYMBOL_END (PSEUDOVECTOR_FLAG) PVEC_MUTEX, PVEC_CONDVAR, PVEC_MODULE_FUNCTION, + PVEC_CANVAS, /* These should be last, for internal_equal and sxhash_obj. */ PVEC_COMPILED, @@ -1349,6 +1350,7 @@ #define XSETSUB_CHAR_TABLE(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_SUB_CHAR_TABLE)) #define XSETTHREAD(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_THREAD)) #define XSETMUTEX(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_MUTEX)) #define XSETCONDVAR(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_CONDVAR)) +#define XSETCANVAS(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_CANVAS)) /* Efficiently convert a pointer to a Lisp object and back. The pointer is represented as a fixnum, so the garbage collector diff --git a/src/pdumper.c b/src/pdumper.c index 63424c5734..33c527c7d8 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -3036,6 +3036,8 @@ dump_vectorlike (struct dump_context *ctx, error_unsupported_dump_object (ctx, lv, "condvar"); case PVEC_MODULE_FUNCTION: error_unsupported_dump_object (ctx, lv, "module function"); + case PVEC_CANVAS: + error_unsupported_dump_object (ctx, lv, "canvas"); default: error_unsupported_dump_object(ctx, lv, "weird pseudovector"); } diff --git a/src/print.c b/src/print.c index bd1769144e..28a620fd20 100644 --- a/src/print.c +++ b/src/print.c @@ -34,6 +34,7 @@ Copyright (C) 1985-1986, 1988, 1993-1995, 1997-2020 Free Software #include "blockinput.h" #include "xwidget.h" #include "dynlib.h" +#include "canvas.h" #include <c-ctype.h> #include <float.h> @@ -1833,6 +1834,16 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag, } break; #endif + case PVEC_CANVAS: + { + print_c_string ("#<canvas ", printcharfun); + const struct canvas *canvas = XCANVAS (obj); + print_object (make_fixnum (canvas->width), printcharfun, false); + printchar ('x', printcharfun); + print_object (make_fixnum (canvas->height), printcharfun, false); + print_c_string (">", printcharfun); + } + break; default: emacs_abort (); diff --git a/src/window.c b/src/window.c index e2dea8b70e..5e0ed1ab94 100644 --- a/src/window.c +++ b/src/window.c @@ -4289,6 +4289,7 @@ make_window (void) w->scroll_bar_width = -1; w->scroll_bar_height = -1; w->column_number_displayed = -1; + w->have_canvas_p = false; /* Reset window_list. */ Vwindow_list = Qnil; /* Return window. */ diff --git a/src/window.h b/src/window.h index 167d1be7ab..67c7417007 100644 --- a/src/window.h +++ b/src/window.h @@ -445,6 +445,9 @@ #define WINDOW_H_INCLUDED window. */ bool_bf suspend_auto_hscroll : 1; + /* True if we think a canvas is being displayed in this window. */ + bool_bf have_canvas_p : 1; + /* Amount by which lines of this window are scrolled in y-direction (smooth scrolling). */ int vscroll; diff --git a/src/xdisp.c b/src/xdisp.c index 140d134572..758f07fec5 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -462,6 +462,7 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2020 Free Software Foundation, #include "fontset.h" #include "blockinput.h" #include "xwidget.h" +#include "canvas.h" #ifdef HAVE_WINDOW_SYSTEM #include TERM_HEADER #endif /* HAVE_WINDOW_SYSTEM */ @@ -1055,6 +1056,7 @@ #define THIN_SPACE_WIDTH 1 static bool next_element_from_image (struct it *); static bool next_element_from_stretch (struct it *); static bool next_element_from_xwidget (struct it *); +static bool next_element_from_canvas (struct it *); static void load_overlay_strings (struct it *, ptrdiff_t); static bool get_next_display_element (struct it *); static enum move_it_result @@ -2688,7 +2690,7 @@ remember_mouse_glyph (struct frame *f, int gx, int gy, NativeRectangle *rect) if (g < end) { - if (g->type == IMAGE_GLYPH) + if (g->type == IMAGE_GLYPH || g->type == CANVAS_GLYPH) { /* Don't remember when mouse is over image, as image may have hot-spots. */ @@ -5589,7 +5591,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, /* After this point, VALUE is the property after any margin prefix has been stripped. It must be a string, - an image specification, or `(space ...)'. + an image specification, a canvas, or `(space ...)'. LOCATION specifies where to display: `left-margin', `right-margin' or nil. */ @@ -5601,7 +5603,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, #endif /* not HAVE_WINDOW_SYSTEM */ || (CONSP (value) && EQ (XCAR (value), Qspace)) || ((it ? FRAME_WINDOW_P (it->f) : frame_window_p) - && valid_xwidget_spec_p (value))); + && valid_xwidget_spec_p (value))) || CANVASP (value); if (valid_p && display_replaced == 0) { @@ -5686,6 +5688,22 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object, *position = start_pos; it->xwidget = lookup_xwidget (value); } + else if (CANVASP (value)) + { + it->what = IT_CANVAS; + it->method = GET_FROM_CANVAS; + it->position = start_pos; + it->object = NILP (object) ? it->w->contents : object; + *position = start_pos; + it->canvas = XCANVAS (value); + if ((!NILP (it->canvas->object) && + !EQ (it->canvas->object, it->object)) || + (!NILP (it->canvas->window) && + XWINDOW (it->canvas->window) != it->w)) + it->canvas->multiple_objects_seen = true; + it->canvas->object = it->object; + XSETWINDOW (it->canvas->window, it->w); + } #ifdef HAVE_WINDOW_SYSTEM else { @@ -6446,6 +6464,9 @@ push_it (struct it *it, struct text_pos *position) case GET_FROM_XWIDGET: p->u.xwidget.object = it->object; break; + case GET_FROM_CANVAS: + p->u.canvas.object = it->object; + break; case GET_FROM_BUFFER: case GET_FROM_DISPLAY_VECTOR: case GET_FROM_STRING: @@ -6550,6 +6571,9 @@ pop_it (struct it *it) case GET_FROM_XWIDGET: it->object = p->u.xwidget.object; break; + case GET_FROM_CANVAS: + it->object = p->u.canvas.object; + break; case GET_FROM_STRETCH: it->object = p->u.stretch.object; break; @@ -7236,6 +7260,7 @@ reseat_to_string (struct it *it, const char *s, Lisp_Object string, next_element_from_image, next_element_from_stretch, next_element_from_xwidget, + next_element_from_canvas, }; #define GET_NEXT_DISPLAY_ELEMENT(it) (*get_next_element[(it)->method]) (it) @@ -8151,6 +8176,7 @@ set_iterator_to_next (struct it *it, bool reseat_p) case GET_FROM_IMAGE: case GET_FROM_STRETCH: case GET_FROM_XWIDGET: + case GET_FROM_CANVAS: /* The position etc with which we have to proceed are on the stack. The position may be at the end of a string, @@ -8619,6 +8645,12 @@ next_element_from_xwidget (struct it *it) return true; } +static bool +next_element_from_canvas (struct it *it) +{ + it->what = IT_CANVAS; + return true; +} /* Fill iterator IT with next display element from a stretch glyph property. IT->object is the value of the text property. Value is @@ -27810,6 +27842,19 @@ fill_xwidget_glyph_string (struct glyph_string *s) s->xwidget = s->first_glyph->u.xwidget; } #endif + +static void +fill_canvas_glyph_string (struct glyph_string *s) +{ + eassert (s->first_glyph->type == CANVAS_GLYPH); + s->w->have_canvas_p = true; + s->face = FACE_FROM_ID (s->f, s->first_glyph->face_id); + s->font = s->face->font; + s->width = s->first_glyph->pixel_width; + s->ybase += s->first_glyph->voffset; + s->canvas = s->first_glyph->u.canvas; + s->canvas->changed_since_last_redisplay = false; +} /* Fill glyph string S from a sequence of stretch glyphs. START is the index of the first glyph to consider, @@ -28227,6 +28272,18 @@ #define BUILD_IMAGE_GLYPH_STRING(START, END, HEAD, TAIL, HL, X, LAST_X) \ while (false) #endif +#define BUILD_CANVAS_GLYPH_STRING(START, END, HEAD, TAIL, HL, X, LAST_X) \ + do \ + { \ + s = alloca (sizeof *s); \ + INIT_GLYPH_STRING (s, NULL, w, row, area, START, HL); \ + append_glyph_string (&(HEAD), &(TAIL), s); \ + ++(START); \ + s->x = (X); \ + fill_canvas_glyph_string (s); \ + } \ + while (false) + /* Add a glyph string for a sequence of character glyphs to the list of strings between HEAD and TAIL. START is the index of the first glyph in row area AREA of glyph row ROW that is part of the new @@ -28378,7 +28435,11 @@ #define BUILD_GLYPH_STRINGS_1(START, END, HEAD, TAIL, HL, X, LAST_X) \ case IMAGE_GLYPH: \ BUILD_IMAGE_GLYPH_STRING (START, END, HEAD, TAIL, \ HL, X, LAST_X); \ - break; + break; \ + case CANVAS_GLYPH: \ + BUILD_CANVAS_GLYPH_STRING (START, END, HEAD, TAIL, \ + HL, X, LAST_X); \ + break; \ #define BUILD_GLYPH_STRINGS_XW(START, END, HEAD, TAIL, HL, X, LAST_X) \ case XWIDGET_GLYPH: \ @@ -29086,6 +29147,116 @@ produce_image_glyph (struct it *it) } } +static void +produce_canvas_glyph (struct it *it) +{ + struct canvas *canvas; + int glyph_ascent, crop; + + eassert (it->what == IT_CANVAS); + + struct face *face = FACE_FROM_ID (it->f, it->face_id); + prepare_face_for_display (it->f, face); + + canvas = it->canvas; + it->ascent = it->phys_ascent = glyph_ascent = canvas->height / 2; + it->descent = it->phys_descent = canvas->height / 2; + it->pixel_width = canvas->width; + + if (it->descent < 0) + it->descent = 0; + + it->nglyphs = 1; + + if (face->box != FACE_NO_BOX) + { + if (face->box_horizontal_line_width > 0) + { + it->ascent += face->box_horizontal_line_width; + it->descent += face->box_horizontal_line_width; + } + + if (face->box_vertical_line_width > 0) + { + if (it->start_of_box_run_p) + it->pixel_width += face->box_vertical_line_width; + it->pixel_width += face->box_vertical_line_width; + } + } + + take_vertical_position_into_account (it); + + /* Automatically crop wide canvas glyphs at right edge so we can + draw the cursor on same display row. */ + crop = it->pixel_width - (it->last_visible_x - it->current_x); + if (crop > 0 && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4)) + it->pixel_width -= crop; + + if (it->glyph_row) + { + enum glyph_row_area area = it->area; + struct glyph *glyph + = it->glyph_row->glyphs[area] + it->glyph_row->used[area]; + + if (it->glyph_row->reversed_p) + { + struct glyph *g; + + /* Make room for the new glyph. */ + for (g = glyph - 1; g >= it->glyph_row->glyphs[it->area]; g--) + g[1] = *g; + glyph = it->glyph_row->glyphs[it->area]; + } + if (glyph < it->glyph_row->glyphs[area + 1]) + { + glyph->charpos = CHARPOS (it->position); + glyph->object = it->object; + glyph->pixel_width = clip_to_bounds (-1, it->pixel_width, SHRT_MAX); + glyph->ascent = glyph_ascent; + glyph->descent = it->descent; + glyph->voffset = it->voffset; + glyph->type = CANVAS_GLYPH; + glyph->avoid_cursor_p = it->avoid_cursor_p; + glyph->multibyte_p = it->multibyte_p; + glyph->u.canvas = it->canvas; + if (it->glyph_row->reversed_p && area == TEXT_AREA) + { + /* In R2L rows, the left and the right box edges need to be + drawn in reverse direction. */ + glyph->right_box_line_p = it->start_of_box_run_p; + glyph->left_box_line_p = it->end_of_box_run_p; + } + else + { + glyph->left_box_line_p = it->start_of_box_run_p; + glyph->right_box_line_p = it->end_of_box_run_p; + } + glyph->overlaps_vertically_p = 0; + glyph->padding_p = 0; + glyph->glyph_not_available_p = 0; + glyph->face_id = it->face_id; + glyph->font_type = FONT_TYPE_UNKNOWN; + if (it->bidi_p) + { + glyph->resolved_level = it->bidi_it.resolved_level; + eassert ((it->bidi_it.type & 7) == it->bidi_it.type); + glyph->bidi_type = it->bidi_it.type; + } + ++it->glyph_row->used[area]; + } + else + IT_EXPAND_MATRIX_WIDTH (it, area); + } +} + +void +canvas_update_glyph (struct window *w, int x, struct glyph_row *row, + enum glyph_row_area area, ptrdiff_t start, ptrdiff_t end, + struct glyph *glyph) +{ + draw_glyphs (w, x, row, area, start, end, DRAW_NORMAL_TEXT, 0); +} + static void produce_xwidget_glyph (struct it *it) { @@ -30608,6 +30779,8 @@ gui_produce_glyphs (struct it *it) produce_stretch_glyph (it); else if (it->what == IT_XWIDGET) produce_xwidget_glyph (it); + else if (it->what == IT_CANVAS) + produce_canvas_glyph (it); done: /* Accumulate dimensions. Note: can't assume that it->descent > 0 @@ -31008,6 +31181,10 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width, cursor_type = HOLLOW_BOX_CURSOR; } } + if (glyph != NULL && glyph->type == CANVAS_GLYPH) + { + cursor_type = HOLLOW_BOX_CURSOR; + } return cursor_type; } diff --git a/src/xterm.c b/src/xterm.c index 7989cecec7..716ed0ef97 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -75,6 +75,7 @@ Copyright (C) 1989, 1993-2020 Free Software Foundation, Inc. #include "sysselect.h" #include "menu.h" #include "pdumper.h" +#include "canvas.h" #ifdef USE_X_TOOLKIT #include <X11/Shell.h> @@ -2066,6 +2067,30 @@ x_draw_glyphless_glyph_string_foreground (struct glyph_string *s) } } +static void +x_draw_canvas_glyph_string_foreground (struct glyph_string *s) +{ + eassert (s->first_glyph->type == CANVAS_GLYPH); +#ifdef USE_CAIRO + cairo_t *cr = x_begin_cr_clip (s->f, s->gc); + int x = s->x; + int y = s->ybase - s->first_glyph->ascent; + + if (s->face->box != FACE_NO_BOX && + s->first_glyph->left_box_line_p) + x += max (s->face->box_vertical_line_width, 0); + + x_set_glyph_string_clipping (s); + x_clear_area (s->f, x, y, s->width, s->height); + cairo_set_source_surface (cr, s->canvas->canvas, + s->x, s->y); + cairo_paint (cr); + x_end_cr_clip (s->f); +#else + emacs_abort (); +#endif +} + #ifdef USE_X_TOOLKIT #ifdef USE_LUCID @@ -3811,6 +3836,11 @@ x_draw_glyph_string (struct glyph_string *s) x_draw_glyphless_glyph_string_foreground (s); break; + case CANVAS_GLYPH: + x_draw_glyph_string_background (s, true); + x_draw_canvas_glyph_string_foreground (s); + break; + default: emacs_abort (); } ^ permalink raw reply related [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 10:27 ` Po Lu via Emacs development discussions. @ 2020-04-29 11:47 ` Eli Zaretskii 0 siblings, 0 replies; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 11:47 UTC (permalink / raw) To: Po Lu; +Cc: emacs-devel > From: Po Lu <luangruo@yahoo.com> > Cc: emacs-devel@gnu.org > Date: Wed, 29 Apr 2020 18:27:49 +0800 > > >> That's the technical description of the implementation. It doesn't > >> explain when canvases can be useful and for what purposes. > > > > Canvases are useful for when I want to be able to control a portion of a > > screen dynamically, in a fast way, from Lisp code. For instance, > > displaying a constantly changing bar chart or graph inside Emacs. > > > >> But the result of this painting is some graphical object, similar to > >> an image, that will be displayed within a buffer, right? > > > > Correct. > > > >> Did I miss the code that tells Emacs the click was on a canvas? E.g., > >> if you click on a canvas, what does posn-object return when passed the > >> click event as its argument? > > > > Oops. I missed that. Thanks for bringing it up. > > Here is a version of the patch with several of the problems you > mentioned rectified. Thanks. > +@node Canvases > +@cindex drawing canvases > +@cindex drawing areas > +@cindex canvases > +@section Canvases > + > +This chapter describes canvases, objects that can store drawing operations > +which are then displayed inside buffer text. Please consider telling something here about when this is useful. Also, I think there should be a short subsection in "Editing Types" with the description of the canvas type, like we have for other editing types. > @@ -4371,6 +4374,11 @@ scrolling_window (struct window *w, int tab_line_p) > return 0; > #endif > > + /* We need this to fix canvas movement detection in a reliable way. > + FIXME. */ > + if (w->have_canvas_p) > + return 0; This is sub-optimal. Please don't follow the xwidget example of disabling redisplay optimizations because it might be hairy to support them. It would make any window with canvases redisplay much slower in many cases. > @@ -5462,6 +5470,10 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p > if (img && !NILP (img->spec)) > *object = img->spec; > } > + else if (it.what == IT_CANVAS) > + { > + XSETCANVAS (*object, it.canvas); > + } > #endif This is just the object. That's okay, but don't you want to support clicks on various parts of the canvas, like we do with images? That would require more info to be returned. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 10:22 ` Po Lu 2020-04-29 10:27 ` Po Lu via Emacs development discussions. @ 2020-04-29 10:35 ` Eli Zaretskii 2020-04-29 10:41 ` Po Lu 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 10:35 UTC (permalink / raw) To: Po Lu; +Cc: emacs-devel > From: Po Lu <luangruo@yahoo.com> > Cc: emacs-devel@gnu.org > Date: Wed, 29 Apr 2020 18:22:13 +0800 > > Eli Zaretskii <eliz@gnu.org> writes: > > > That's the technical description of the implementation. It doesn't > > explain when canvases can be useful and for what purposes. > > Canvases are useful for when I want to be able to control a portion of a > screen dynamically, in a fast way, from Lisp code. For instance, > displaying a constantly changing bar chart or graph inside Emacs. So this _is_ a way of putting images on the Emacs display, but in a way that creates the image dynamically from Lisp, instead of reading it from a file or producing the binary data for that image. Right? Incidentally, how does this compare with functions in svg.el? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 10:35 ` Eli Zaretskii @ 2020-04-29 10:41 ` Po Lu 2020-04-29 11:51 ` Eli Zaretskii 2020-04-29 16:14 ` David Engster 0 siblings, 2 replies; 53+ messages in thread From: Po Lu @ 2020-04-29 10:41 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Eli Zaretskii <eliz@gnu.org> writes: > So this _is_ a way of putting images on the Emacs display, but in a > way that creates the image dynamically from Lisp, instead of reading > it from a file or producing the binary data for that image. Right? Yes. > Incidentally, how does this compare with functions in svg.el? While right now svg.el seems to have more features, it isn't particularly well suited to displaying things quickly. In fact, my own bad experiences with using svg.el to draw rapidly changing information inspired me to begin working on this. In the future, I hope that canvases also become more flexible than svg.el. A nice goal would be to have everything cairo has. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 10:41 ` Po Lu @ 2020-04-29 11:51 ` Eli Zaretskii 2020-04-29 12:12 ` Po Lu 2020-04-29 16:14 ` David Engster 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 11:51 UTC (permalink / raw) To: Po Lu; +Cc: emacs-devel > From: Po Lu <luangruo@yahoo.com> > Cc: emacs-devel@gnu.org > Date: Wed, 29 Apr 2020 18:41:08 +0800 > > > Incidentally, how does this compare with functions in svg.el? > > While right now svg.el seems to have more features, it isn't > particularly well suited to displaying things quickly. > > In fact, my own bad experiences with using svg.el to draw rapidly > changing information inspired me to begin working on this. What exactly is slow there, and why? > In the future, I hope that canvases also become more flexible than > svg.el. A nice goal would be to have everything cairo has. If canvasses are meant to be a replacement for svg.el, we need to keep both equivalent, functionality-wise, for the benefit of platforms that don't use Cairo, and also to ease the transition period for those platforms which do. So the relation between svg.el and canvasses should be rethought, I think. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 11:51 ` Eli Zaretskii @ 2020-04-29 12:12 ` Po Lu 0 siblings, 0 replies; 53+ messages in thread From: Po Lu @ 2020-04-29 12:12 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel Eli Zaretskii <eliz@gnu.org> writes: > If canvasses are meant to be a replacement for svg.el, we need to keep > both equivalent, functionality-wise, for the benefit of platforms that > don't use Cairo, and also to ease the transition period for those > platforms which do. > > So the relation between svg.el and canvasses should be rethought, I > think. Yeah you indeed have a point there. I'll think over this tonight. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 10:41 ` Po Lu 2020-04-29 11:51 ` Eli Zaretskii @ 2020-04-29 16:14 ` David Engster 2020-04-29 16:54 ` Eli Zaretskii 2020-04-29 17:08 ` Eli Zaretskii 1 sibling, 2 replies; 53+ messages in thread From: David Engster @ 2020-04-29 16:14 UTC (permalink / raw) To: Po Lu; +Cc: Eli Zaretskii, emacs-devel > While right now svg.el seems to have more features, it isn't > particularly well suited to displaying things quickly. > > In fact, my own bad experiences with using svg.el to draw rapidly > changing information inspired me to begin working on this. > > In the future, I hope that canvases also become more flexible than > svg.el. A nice goal would be to have everything cairo has. A while ago I played around with using Cairo primitives to draw in Emacs. While it would be nice to have something like a 'canvas object' to draw on, Eli is correct that you can achieve similar things already with SVG - it may be slow, but that could be fixable. IMHO, What would be much more exciting, is if you could draw directly over normal text. This would enable a whole new set of possibilities. For instance, I always liked visual diff tools like 'Meld' or 'Code Compare', but I don't see how you could do this with Emacs. Or things like proper indentation guides, or widgets inside text for code folding. Yes, those can be done in Emacs today, but it is very difficult to get right and often not really satisfactory (weird interactions between packages, too slow, etc.). I know the tricky part is not the drawing, but redisplay. I would have no idea how to handle this. -David ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 16:14 ` David Engster @ 2020-04-29 16:54 ` Eli Zaretskii 2020-04-29 17:16 ` tomas ` (2 more replies) 2020-04-29 17:08 ` Eli Zaretskii 1 sibling, 3 replies; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 16:54 UTC (permalink / raw) To: David Engster; +Cc: luangruo, emacs-devel > From: David Engster <deng@randomsample.de> > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > Date: Wed, 29 Apr 2020 18:14:55 +0200 > > IMHO, What would be much more exciting, is if you could draw directly > over normal text. This would enable a whole new set of > possibilities. For instance, I always liked visual diff tools like > 'Meld' or 'Code Compare', but I don't see how you could do this with > Emacs. Or things like proper indentation guides, or widgets inside text > for code folding. Yes, those can be done in Emacs today, but it is very > difficult to get right and often not really satisfactory (weird > interactions between packages, too slow, etc.). AFAIU, Meld seems to draw the graphics _between_ windows, and only highlights the source lines with background colors. I think we can do that if we use an extra window in-between the two being compared, and put images in that extra window to show the graphical description of the changes between the two versions. Drawing over normal text, if we don't want to redesign the entire display engine, needs some new kind of "display element" ( a sibling to "character", "image", "stretch", etc.), one that doesn't necessarily have any effect on the metrics of the screen lines it is drawn upon. I'm not sure I have a clear idea about what features such a drawing will need to support, but it could be possible to add such an element with not too much effort. Would someone want to come up with a reasonable list of requirements for such a feature? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 16:54 ` Eli Zaretskii @ 2020-04-29 17:16 ` tomas 2020-04-29 17:27 ` Eli Zaretskii 2020-04-29 19:23 ` David Engster 2020-04-30 6:52 ` Corwin Brust 2 siblings, 1 reply; 53+ messages in thread From: tomas @ 2020-04-29 17:16 UTC (permalink / raw) To: emacs-devel [-- Attachment #1: Type: text/plain, Size: 914 bytes --] On Wed, Apr 29, 2020 at 07:54:02PM +0300, Eli Zaretskii wrote: [...] > Drawing over normal text, if we don't want to redesign the entire > display engine, needs some new kind of "display element" ( a sibling > to "character", "image", "stretch", etc.), one that doesn't > necessarily have any effect on the metrics of the screen lines it is > drawn upon. I'm not sure I have a clear idea about what features such > a drawing will need to support, but it could be possible to add such > an element with not too much effort. Would someone want to come up > with a reasonable list of requirements for such a feature? That sounds... exciting. Basically, Emacs would have to have a "display list" of graphical elements to draw, each one perhaps having a bounding box (to discard those from redisplay which aren't currently visible) and perhaps a "layer" (more to the background or foreground). Hmmm. Cheers -- t [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 198 bytes --] ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 17:16 ` tomas @ 2020-04-29 17:27 ` Eli Zaretskii 2020-04-29 17:38 ` Eli Zaretskii 2020-04-29 18:51 ` tomas 0 siblings, 2 replies; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 17:27 UTC (permalink / raw) To: tomas; +Cc: emacs-devel > Date: Wed, 29 Apr 2020 19:16:19 +0200 > From: <tomas@tuxteam.de> > > > Drawing over normal text, if we don't want to redesign the entire > > display engine, needs some new kind of "display element" ( a sibling > > to "character", "image", "stretch", etc.), one that doesn't > > necessarily have any effect on the metrics of the screen lines it is > > drawn upon. I'm not sure I have a clear idea about what features such > > a drawing will need to support, but it could be possible to add such > > an element with not too much effort. Would someone want to come up > > with a reasonable list of requirements for such a feature? > > That sounds... exciting. Basically, Emacs would have to have a "display > list" of graphical elements to draw, each one perhaps having a bounding > box (to discard those from redisplay which aren't currently visible) > and perhaps a "layer" (more to the background or foreground). That's not how Emacs controls what's on display. It basically represents each window as a 2D array of glyphs, each one of which has a certain graphical representation. The representation itself is of no concern to the display engine (well, almost); the only thing it cares about is the metrics of each glyph, because that's what it needs to do layout calculations. We need to try to fit into this framework, if possible. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 17:27 ` Eli Zaretskii @ 2020-04-29 17:38 ` Eli Zaretskii 2020-04-30 13:11 ` Arthur Miller 2020-04-29 18:51 ` tomas 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 17:38 UTC (permalink / raw) To: tomas; +Cc: emacs-devel > Date: Wed, 29 Apr 2020 20:27:53 +0300 > From: Eli Zaretskii <eliz@gnu.org> > Cc: emacs-devel@gnu.org > > That's not how Emacs controls what's on display. It basically > represents each window as a 2D array of glyphs, each one of which has > a certain graphical representation. The representation itself is of > no concern to the display engine (well, almost); the only thing it > cares about is the metrics of each glyph, because that's what it needs > to do layout calculations. Oh, and one more important aspect: everything that winds up on display must come from some buffer or some Lisp string. It could be a special text property or somesuch, but it must be found by walking some buffer or some known-in-advance Lisp string, because that's what top-level display functions do: they iterate over these objects, and produce the glyphs based on what they find there. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 17:38 ` Eli Zaretskii @ 2020-04-30 13:11 ` Arthur Miller 2020-04-30 14:18 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2020-04-30 13:11 UTC (permalink / raw) To: Eli Zaretskii; +Cc: tomas, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> Date: Wed, 29 Apr 2020 20:27:53 +0300 >> From: Eli Zaretskii <eliz@gnu.org> >> Cc: emacs-devel@gnu.org >> >> That's not how Emacs controls what's on display. It basically >> represents each window as a 2D array of glyphs, each one of which has >> a certain graphical representation. The representation itself is of >> no concern to the display engine (well, almost); the only thing it >> cares about is the metrics of each glyph, because that's what it needs >> to do layout calculations. > > Oh, and one more important aspect: everything that winds up on display > must come from some buffer or some Lisp string. It could be a special > text property or somesuch, but it must be found by walking some buffer > or some known-in-advance Lisp string, because that's what top-level > display functions do: they iterate over these objects, and produce the > glyphs based on what they find there. I have a question here about display: when lisp engine decides to draw the display, how difficult would it be to add a callback for pre- and post render? Prerender callback could be any user lisp function called that do anything it wants, say draw something on the screen as it owned window itself. Than render engine would it's ordinary rendering drawing just as it does now, pretending that there was nothing already drawn, so it would mean no change at all to current c code for rendering. Similarly one call a post-render callback and draw over the already rendered text so user could draw whatever on top of already draw onverlas and what not. It is just how z-buffer works and it would mean z order from pre-render, normal render (as it is now) and post render as implicit z-depths. Finally one could expose ordinary X11, GDI, Cairo drawing funcions so thet draw to an image (or why not directly to the gui window?) that could be displayed say in prerender hook, which would get drawn as background below the buffer text, and woulnd't collide with overlays and other stuff. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 13:11 ` Arthur Miller @ 2020-04-30 14:18 ` Eli Zaretskii 2020-04-30 14:58 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 14:18 UTC (permalink / raw) To: Arthur Miller; +Cc: tomas, emacs-devel > From: Arthur Miller <arthur.miller@live.com> > Date: Thu, 30 Apr 2020 15:11:10 +0200 > Cc: tomas@tuxteam.de, emacs-devel@gnu.org > > I have a question here about display: when lisp engine decides to draw > the display, how difficult would it be to add a callback for pre- and > post render? I don't think I understand the question. It is trivial to add a call to a Lisp function anywhere in Emacs, the question is what that function will be able to do to be useful. If the called Lisp can do anything it wants, then how can it be useful without knowing a whole lot about the window layout, and what was just now, or will be in a moment, drawn on it, and in which parts? > Prerender callback could be any user lisp function called > that do anything it wants, say draw something on the screen as it owned > window itself. Than render engine would it's ordinary rendering drawing > just as it does now, pretending that there was nothing already drawn, so > it would mean no change at all to current c code for rendering. So how do we prevent the display engine from blissfully drawing over what the pre-render draws? > Similarly one call a post-render callback and draw over the already > rendered text so user could draw whatever on top of already draw > onverlas and what not. How will the post-render know where it can and where it cannot draw? Or even where to draw, for that matter (assuming you want to do something more useful than just put the same pixels in the same pixel coordinates)? For example, if the window scrolls, wouldn't you want the graphics drawn by these pre/post-renderers to move on display as well, at least sometimes? If you do, how can you do that without knowing some details about the scroll? > It is just how z-buffer works and it would mean z order from pre-render, > normal render (as it is now) and post render as implicit z-depths. AFIU, z-buffer doesn't care to obscure what's displayed below. is this what you have in mind: obscuring the "normal" display with some graphics? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 14:18 ` Eli Zaretskii @ 2020-04-30 14:58 ` Arthur Miller 2020-04-30 17:30 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Arthur Miller @ 2020-04-30 14:58 UTC (permalink / raw) To: Eli Zaretskii; +Cc: tomas, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Arthur Miller <arthur.miller@live.com> >> Date: Thu, 30 Apr 2020 15:11:10 +0200 >> Cc: tomas@tuxteam.de, emacs-devel@gnu.org >> >> I have a question here about display: when lisp engine decides to draw >> the display, how difficult would it be to add a callback for pre- and >> post render? > > I don't think I understand the question. It is trivial to add a call I am probably not so clear in my writing, but what I mean is, can it be impelemnted as a hook/callback, to enter same redisplay machinery several times. As you described in other mails how redisplay is figuring out what draw, everything has to come from buffers that are essentially glyph matrices etc. Whatever entry in that machinery is now, can't we just have a list of hooks that each call into that machinery? Normally there would be just one such so Emacs work as it is now, but user could add one before or after, or several do do some extra drawing. > to a Lisp function anywhere in Emacs, the question is what thats > function will be able to do to be useful. Essentially it would be like layered rendering. Imagine Gimp layers. Since each hook is drawing it's own stuff it would be like a layer. For example, if Emacs exposed some graphics stuff like say: draw-this, fill-that, and if drawing layered, like in post-render, that wouldn't collide with overlays and other stuff, one could draw over images etc. Similarly a pre-render could put some graphics and images that appear as a background below normal buffer text. > If the called Lisp can do > anything it wants, then how can it be useful without knowing a whole > lot about the window layout, and what was just now, or will be in a > moment, drawn on it, and in which parts? Each hook woul have it's own associated buffer, which could be same as current buffer or different one (say an temporary with an image in it). Redisplay engine can then just do whatever it does now to figure out that buffer so no changes to redisplay engine. Yes, it would draw in same pixel coordinates of the window it is drawing on. The only hard part would be to figure out how to trigger redisplay engine when some of buffers with associated hooks changes (I think, but I am not knowledgable enough about implementation details here). >> Prerender callback could be any user lisp function called >> that do anything it wants, say draw something on the screen as it owned >> window itself. Than render engine would it's ordinary rendering drawing >> just as it does now, pretending that there was nothing already drawn, so >> it would mean no change at all to current c code for rendering. > > So how do we prevent the display engine from blissfully drawing over > what the pre-render draws? You don't! It is exactly my point to redraw over stuff. Render should re-draw over what pre-render has drawn etc. That way, if a pre-render hook, draws say a buffer with an image in it, then ordinary buffer can draw it's text over it and image would appear as a background in final view. >> Similarly one call a post-render callback and draw over the already >> rendered text so user could draw whatever on top of already draw >> onverlas and what not. > > How will the post-render know where it can and where it cannot draw? > Or even where to draw, for that matter (assuming you want to do > something more useful than just put the same pixels in the same pixel > coordinates)? Actually sometimes, you do want to just put pixels in same pixel coordinates :-). But anyway, one could draw some lines below/above text in buffers, rectangles, and so on. Info about where and what to draw comes well from user code. User's elisp can figure out where and what to draw by examining the buffer and window with usual elisp we have now? > For example, if the window scrolls, wouldn't you want > the graphics drawn by these pre/post-renderers to move on display as > well, at least sometimes? If you do, how can you do that without > knowing some details about the scroll? Of course. Wouldn't came out automatically, by redisplay engine as it already is? >> It is just how z-buffer works and it would mean z order from pre-render, >> normal render (as it is now) and post render as implicit z-depths. > > AFIU, z-buffer doesn't care to obscure what's displayed below. is > this what you have in mind: obscuring the "normal" display with some > graphics? No I didn't, I had in mind to redraw stuff, so user can draw below or above whatever Emacs normally draws. Yeah sure if user chooses to draw two different text buffers on top of each other it would be a mess, but I don't think that is an issue because, normally, probably nobody want's to draw to text files on top of each other. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 14:58 ` Arthur Miller @ 2020-04-30 17:30 ` Eli Zaretskii 2020-05-01 14:32 ` Arthur Miller 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 17:30 UTC (permalink / raw) To: Arthur Miller; +Cc: tomas, emacs-devel > From: Arthur Miller <arthur.miller@live.com> > Cc: tomas@tuxteam.de, emacs-devel@gnu.org > Date: Thu, 30 Apr 2020 16:58:10 +0200 > > > For example, if the window scrolls, wouldn't you want > > the graphics drawn by these pre/post-renderers to move on display as > > well, at least sometimes? If you do, how can you do that without > > knowing some details about the scroll? > Of course. Wouldn't came out automatically, by redisplay engine as it > already is? No, of course not. > No I didn't, I had in mind to redraw stuff, so user can draw below or > above whatever Emacs normally draws. > > Yeah sure if user chooses to draw two different text buffers on top of > each other it would be a mess, but I don't think that is an issue > because, normally, probably nobody want's to draw to text files on top > of each other. I think what you describe doesn't fit with how the redisplay works, but I'm probably missing something, since Stefan also thinks this could work. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 17:30 ` Eli Zaretskii @ 2020-05-01 14:32 ` Arthur Miller 0 siblings, 0 replies; 53+ messages in thread From: Arthur Miller @ 2020-05-01 14:32 UTC (permalink / raw) To: Eli Zaretskii; +Cc: tomas, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: Arthur Miller <arthur.miller@live.com> >> Cc: tomas@tuxteam.de, emacs-devel@gnu.org >> Date: Thu, 30 Apr 2020 16:58:10 +0200 >> >> > For example, if the window scrolls, wouldn't you want >> > the graphics drawn by these pre/post-renderers to move on display as >> > well, at least sometimes? If you do, how can you do that without >> > knowing some details about the scroll? >> Of course. Wouldn't came out automatically, by redisplay engine as it >> already is? > > No, of course not. > >> No I didn't, I had in mind to redraw stuff, so user can draw below or >> above whatever Emacs normally draws. >> >> Yeah sure if user chooses to draw two different text buffers on top of >> each other it would be a mess, but I don't think that is an issue >> because, normally, probably nobody want's to draw to text files on top >> of each other. > > I think what you describe doesn't fit with how the redisplay works, > but I'm probably missing something, since Stefan also thinks this > could work. To be honest I am not sure myself if it could or not work, that is why I posed at as a question in my first mail. I am not so familiar with Emacs internal but I do find it intersting to look at C code from time to time :-). My idea is to make it as least intrusive on current pipeline as possible. I see current drawing stuff as it draws to one (implicit) layer: the current window (I mean OS window). The idea is to wrap it in some way (as a callback) and call in it "per layer" each time, where each layer would draw it's own bufer. This way no new objects are needed, no new concepts, just to redraw buffers in same window once per callback. It does not have to be a callback. Instead it could be a list of buffers to draw just in ordinary list; a qeue to draw from first to last. Same code it does now, just re-enter it again per each buffer, and draw them on top of each other. I am not sure where and how to fit it in. What this be per-buffer, or per-window, or even-per frame? I was looking at Window and Buffer structures, but I don't know myself, to be honest. Then if we could get some graphics functions that draw to say XPixmap or Cairo surface (which should be trivial to expose), we could draw graphics to an image, and then display image in one layer and draw text into another layer. It would give a "Canvas" like functionality and layered rendering. How it would be usefull is up to users, but if we look at what people do in Emacs, all this UML with Ditaa, vertical lines with characters etc, obviosly there is some need for graphics in Emacs. I am sure some powerline users would be happy to draw some cool graphics to their modeline. While drawing to image and layering stuff below/above text buffer is not THE most efficient way, it could still be more efficient then drawing to SVG (xml) and rendering svg and display it as image anway or doing canvas with "put pixel" stuff. Also, not a perfect, but as rest of Emacs implementation, probably *good enough* for many uses? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 17:27 ` Eli Zaretskii 2020-04-29 17:38 ` Eli Zaretskii @ 2020-04-29 18:51 ` tomas 2020-04-29 19:03 ` Eli Zaretskii 1 sibling, 1 reply; 53+ messages in thread From: tomas @ 2020-04-29 18:51 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 2044 bytes --] On Wed, Apr 29, 2020 at 08:27:53PM +0300, Eli Zaretskii wrote: > > Date: Wed, 29 Apr 2020 19:16:19 +0200 > > From: <tomas@tuxteam.de> > > > > > Drawing over normal text, if we don't want to redesign the entire > > > display engine, needs some new kind of "display element" ( a sibling > > > to "character", "image", "stretch", etc.), one that doesn't > > > necessarily have any effect on the metrics of the screen lines it is > > > drawn upon. I'm not sure I have a clear idea about what features such > > > a drawing will need to support, but it could be possible to add such > > > an element with not too much effort. Would someone want to come up > > > with a reasonable list of requirements for such a feature? > > > > That sounds... exciting. Basically, Emacs would have to have a "display > > list" of graphical elements to draw, each one perhaps having a bounding > > box (to discard those from redisplay which aren't currently visible) > > and perhaps a "layer" (more to the background or foreground). > > That's not how Emacs controls what's on display. It basically > represents each window as a 2D array of glyphs, each one of which has > a certain graphical representation. ...a display list of sorts. > The representation itself is of > no concern to the display engine (well, almost); the only thing it > cares about is the metrics of each glyph, because that's what it needs > to do layout calculations. A graphical overlay wouldn't have "glyph metrics", the graphical objects would (I think) have "absolute" [1] positions (possibly precalculated by something else). > We need to try to fit into this framework, if possible. I think the "interesting" problem is to know (quickly) which graphical objects intersect the (visible) window -- something graphics programs do as their main job. Cheers [1] Well, absolute is relative ;-) perhaps anchored at some text in the buffer, perhaps anchored to the window... I don't know. -- tomás [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 198 bytes --] ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 18:51 ` tomas @ 2020-04-29 19:03 ` Eli Zaretskii 2020-04-29 19:08 ` tomas 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 19:03 UTC (permalink / raw) To: tomas; +Cc: emacs-devel > Date: Wed, 29 Apr 2020 20:51:28 +0200 > From: tomas@tuxteam.de > Cc: emacs-devel@gnu.org > > > That's not how Emacs controls what's on display. It basically > > represents each window as a 2D array of glyphs, each one of which has > > a certain graphical representation. > > ...a display list of sorts. It's a far cry from any "list" in my book... > > The representation itself is of > > no concern to the display engine (well, almost); the only thing it > > cares about is the metrics of each glyph, because that's what it needs > > to do layout calculations. > > A graphical overlay wouldn't have "glyph metrics", the graphical > objects would (I think) have "absolute" [1] positions (possibly > precalculated by something else). The position is basically determined by the place in a buffer or a string where they were found. > I think the "interesting" problem is to know (quickly) which > graphical objects intersect the (visible) window -- something > graphics programs do as their main job. That's easy, we have infrastructure for that already -- it is used in expose_frame and its subroutines. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 19:03 ` Eli Zaretskii @ 2020-04-29 19:08 ` tomas 2020-04-29 19:25 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: tomas @ 2020-04-29 19:08 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1066 bytes --] On Wed, Apr 29, 2020 at 10:03:00PM +0300, Eli Zaretskii wrote: > > Date: Wed, 29 Apr 2020 20:51:28 +0200 > > From: tomas@tuxteam.de > > Cc: emacs-devel@gnu.org > > > > > That's not how Emacs controls what's on display. It basically > > > represents each window as a 2D array of glyphs, each one of which has > > > a certain graphical representation. > > > > ...a display list of sorts. > > It's a far cry from any "list" in my book... Don't take the "list" too literally. I was possibly misusing (by extension) some now-obsolete graphics jargon. > > I think the "interesting" problem is to know (quickly) which > > graphical objects intersect the (visible) window -- something > > graphics programs do as their main job. > > That's easy, we have infrastructure for that already -- it is used in > expose_frame and its subroutines. This sounds... encouraging :-) (TBH I was coming from the other side: index the graphics objects in a way that one doesn't need to look at most of those which are currently hidden). Cheers -- t [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 198 bytes --] ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 19:08 ` tomas @ 2020-04-29 19:25 ` Eli Zaretskii 2020-04-29 19:59 ` tomas 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 19:25 UTC (permalink / raw) To: tomas; +Cc: emacs-devel > Date: Wed, 29 Apr 2020 21:08:54 +0200 > From: tomas@tuxteam.de > Cc: emacs-devel@gnu.org > > > > I think the "interesting" problem is to know (quickly) which > > > graphical objects intersect the (visible) window -- something > > > graphics programs do as their main job. > > > > That's easy, we have infrastructure for that already -- it is used in > > expose_frame and its subroutines. > > This sounds... encouraging :-) > > (TBH I was coming from the other side: index the graphics objects > in a way that one doesn't need to look at most of those which > are currently hidden). The current display engine works by screen lines, so if we want to keep it, we must use the existing framework. When a portion of a window is exposed, we redraw all the glyphs in the exposed area(s), and we find the glyphs that need to be redrawn by comparing their coordinates with those of the exposed rectangle(s). ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 19:25 ` Eli Zaretskii @ 2020-04-29 19:59 ` tomas 2020-04-30 1:19 ` Stefan Monnier 2020-04-30 13:33 ` Eli Zaretskii 0 siblings, 2 replies; 53+ messages in thread From: tomas @ 2020-04-29 19:59 UTC (permalink / raw) To: Eli Zaretskii; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 913 bytes --] On Wed, Apr 29, 2020 at 10:25:04PM +0300, Eli Zaretskii wrote: [...] > The current display engine works by screen lines, so if we want to > keep it, we must use the existing framework. Now imagine there's a (graphical) line going from (text) display line 3 to 7. You are not implying that we have to segment that into four chunks, one per text line? > When a portion of a > window is exposed, we redraw all the glyphs in the exposed area(s), > and we find the glyphs that need to be redrawn by comparing their > coordinates with those of the exposed rectangle(s). ... but rather that we intersect the exposed area(s) with each [1] of the graphical objects and redraw that (be it before or after the text)? Cheers [1] conceptually; in reality we'll need some index structure to avoid looking at most of the graphical objects. Quad- trees, whatever. -- t [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 198 bytes --] ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 19:59 ` tomas @ 2020-04-30 1:19 ` Stefan Monnier 2020-04-30 6:55 ` tomas ` (3 more replies) 2020-04-30 13:33 ` Eli Zaretskii 1 sibling, 4 replies; 53+ messages in thread From: Stefan Monnier @ 2020-04-30 1:19 UTC (permalink / raw) To: tomas; +Cc: Eli Zaretskii, emacs-devel > Now imagine there's a (graphical) line going from (text) display > line 3 to 7. You are not implying that we have to segment that > into four chunks, one per text line? I think it would make more sense to treat the overlaid canvas as a completely separate pixmap: when we get an request to redraw a particular area of the screen, we'd ask the current redisplay code to redraw the corresponding text content and then we'd ask the canvas code to draw on top of it. So for rendering of the canvas code we don't need to know which part of the canvas cover which characters, we just render the glyph matrix into a pixmap, render the canvas into another pixmap and then combine them onto the screen. Stefan ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 1:19 ` Stefan Monnier @ 2020-04-30 6:55 ` tomas 2020-04-30 12:03 ` Stefan Monnier 2020-04-30 8:04 ` Po Lu ` (2 subsequent siblings) 3 siblings, 1 reply; 53+ messages in thread From: tomas @ 2020-04-30 6:55 UTC (permalink / raw) To: Stefan Monnier; +Cc: Eli Zaretskii, emacs-devel [-- Attachment #1: Type: text/plain, Size: 939 bytes --] On Wed, Apr 29, 2020 at 09:19:51PM -0400, Stefan Monnier wrote: > > Now imagine there's a (graphical) line going from (text) display > > line 3 to 7. You are not implying that we have to segment that > > into four chunks, one per text line? > > I think it would make more sense to treat the overlaid canvas as > a completely separate pixmap: when we get an request to redraw > a particular area of the screen, we'd ask the current redisplay code to > redraw the corresponding text content and then we'd ask the canvas code > to draw on top of it. So for rendering of the canvas code we don't need > to know which part of the canvas cover which characters, we just render > the glyph matrix into a pixmap, render the canvas into another pixmap > and then combine them onto the screen. This is sounding more and more like a compositor :-) Nit: perhaps we'd like to have pics below as well as above text. Cheers -- t [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 198 bytes --] ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 6:55 ` tomas @ 2020-04-30 12:03 ` Stefan Monnier 2020-04-30 12:50 ` tomas 0 siblings, 1 reply; 53+ messages in thread From: Stefan Monnier @ 2020-04-30 12:03 UTC (permalink / raw) To: tomas; +Cc: Eli Zaretskii, emacs-devel > This is sounding more and more like a compositor :-) Yes, the combination of the glyph matrix and the canvas is done by "a compositor", indeed. But it's a kind of "trivial" version of a compositor, and it's a very small part of the work (the work taking place mostly on the canvas side where we'll need to draw at positions that depend on the glyph matrix). > Nit: perhaps we'd like to have pics below as well as above text. Not sure if the benefit is worth adding complexity for that. But maybe it's easy and natural to make the layering between glyph matrix and canvas into an arbitrary stack of layers. E.g. I could imagine a design where windows can display a stack of buffers layered one on top of the other (where typically only one of them contains text and the rest only contains canvas objects). But "synchronizing" the canvas in one buffer with the text in the other buffer would likely be more difficult than if we add a canvas "overlay" directly to the buffers. Stefan ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 12:03 ` Stefan Monnier @ 2020-04-30 12:50 ` tomas 0 siblings, 0 replies; 53+ messages in thread From: tomas @ 2020-04-30 12:50 UTC (permalink / raw) To: Stefan Monnier; +Cc: Eli Zaretskii, emacs-devel [-- Attachment #1: Type: text/plain, Size: 1669 bytes --] On Thu, Apr 30, 2020 at 08:03:35AM -0400, Stefan Monnier wrote: > > This is sounding more and more like a compositor :-) > > Yes, the combination of the glyph matrix and the canvas is done by > "a compositor", indeed. But it's a kind of "trivial" version of > a compositor, and it's a very small part of the work (the work taking > place mostly on the canvas side where we'll need to draw at positions > that depend on the glyph matrix). Yes, one would probably want to anchor a graphics object to some position in the (text) buffer. > > Nit: perhaps we'd like to have pics below as well as above text. > > Not sure if the benefit is worth adding complexity for that. > But maybe it's easy and natural to make the layering between glyph > matrix and canvas into an arbitrary stack of layers. AFAIK Cairo supports painting on layers; so each graphics object could carry a layer (encoded e.g. as an int) and drawing operations could proceed sorted by that. > E.g. I could imagine a design where windows can display a stack of > buffers layered one on top of the other (where typically only one of > them contains text and the rest only contains canvas objects). > But "synchronizing" the canvas in one buffer with the text in the other > buffer would likely be more difficult than if we add a canvas "overlay" > directly to the buffers. I envision the thing rather as a collection of graphics objects which are rendered to the same surface as the text. The Cairo backend makes that easy, but I don't see why a plain X backend would prevent that either (I have no idea about how things are on Windows or Mac). Cheers -- tomás [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 198 bytes --] ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 1:19 ` Stefan Monnier 2020-04-30 6:55 ` tomas @ 2020-04-30 8:04 ` Po Lu 2020-04-30 12:08 ` Stefan Monnier 2020-04-30 13:55 ` Eli Zaretskii 2020-04-30 13:46 ` Eli Zaretskii 2020-04-30 14:27 ` Drew Adams 3 siblings, 2 replies; 53+ messages in thread From: Po Lu @ 2020-04-30 8:04 UTC (permalink / raw) To: Stefan Monnier; +Cc: tomas, Eli Zaretskii, emacs-devel Stefan Monnier <monnier@iro.umontreal.ca> writes: > I think it would make more sense to treat the overlaid canvas as > a completely separate pixmap: when we get an request to redraw > a particular area of the screen, we'd ask the current redisplay code to > redraw the corresponding text content and then we'd ask the canvas code > to draw on top of it. So for rendering of the canvas code we don't need > to know which part of the canvas cover which characters, we just render > the glyph matrix into a pixmap, render the canvas into another pixmap > and then combine them onto the screen. That's what I'm trying to do right now, but so far keeping track of exactly where each canvas is displayed on the screen seems to be difficult. I'd appreciate some suggestions, thanks. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 8:04 ` Po Lu @ 2020-04-30 12:08 ` Stefan Monnier 2020-04-30 13:55 ` Eli Zaretskii 1 sibling, 0 replies; 53+ messages in thread From: Stefan Monnier @ 2020-04-30 12:08 UTC (permalink / raw) To: Po Lu; +Cc: Eli Zaretskii, tomas, emacs-devel >> I think it would make more sense to treat the overlaid canvas as >> a completely separate pixmap: when we get an request to redraw >> a particular area of the screen, we'd ask the current redisplay code to >> redraw the corresponding text content and then we'd ask the canvas code >> to draw on top of it. So for rendering of the canvas code we don't need >> to know which part of the canvas cover which characters, we just render >> the glyph matrix into a pixmap, render the canvas into another pixmap >> and then combine them onto the screen. > > That's what I'm trying to do right now, but so far keeping track of > exactly where each canvas is displayed on the screen seems to be > difficult. I was thinking of having just one canvas per (Emacs) window. > I'd appreciate some suggestions, thanks. I can help with type system and compiler design. But I have no practical experience with graphical APIs, sadly. Stefan ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 8:04 ` Po Lu 2020-04-30 12:08 ` Stefan Monnier @ 2020-04-30 13:55 ` Eli Zaretskii 2020-05-01 23:27 ` Po Lu 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 13:55 UTC (permalink / raw) To: Po Lu; +Cc: tomas, monnier, emacs-devel > From: Po Lu <luangruo@yahoo.com> > Cc: tomas@tuxteam.de, Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > Date: Thu, 30 Apr 2020 16:04:38 +0800 > > Stefan Monnier <monnier@iro.umontreal.ca> writes: > > That's what I'm trying to do right now, but so far keeping track of > exactly where each canvas is displayed on the screen seems to be > difficult. I'd appreciate some suggestions, thanks. I think the information needs to be recorded in the glyph structure that represents a canvas. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 13:55 ` Eli Zaretskii @ 2020-05-01 23:27 ` Po Lu 2020-05-02 6:51 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Po Lu @ 2020-05-01 23:27 UTC (permalink / raw) To: Eli Zaretskii; +Cc: tomas, monnier, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: > I think the information needs to be recorded in the glyph structure > that represents a canvas. I have a canvas field inside `struct glyph' that points to the canvas object. What I'm trying to do right now is track the position of the canvases on-screen, so I can draw them separately from redisplay. I'd appreciate some suggestions, thanks. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-05-01 23:27 ` Po Lu @ 2020-05-02 6:51 ` Eli Zaretskii 0 siblings, 0 replies; 53+ messages in thread From: Eli Zaretskii @ 2020-05-02 6:51 UTC (permalink / raw) To: Po Lu; +Cc: tomas, monnier, emacs-devel > From: Po Lu <luangruo@yahoo.com> > Cc: tomas@tuxteam.de, monnier@iro.umontreal.ca, emacs-devel@gnu.org > Date: Sat, 02 May 2020 07:27:07 +0800 > > Eli Zaretskii <eliz@gnu.org> writes: > > > I think the information needs to be recorded in the glyph structure > > that represents a canvas. > > I have a canvas field inside `struct glyph' that points to the canvas > object. It is not a good idea to have pointers in the glyph structure. (Yes, xwidget uses that, but don't take example from that, please.) A glyph should be self-describing from the metrics POV. If you need additional information (beyond the basic metrics), use the example of images and faces: we cache the objects and record their cache ID in the glyph. > What I'm trying to do right now is track the position of the > canvases on-screen, so I can draw them separately from redisplay. My suggestion is not to "draw separately from redisplay". I don't see how this could work, unless you also modify extensively how the second phase of redisplay, the one entered via update_frame and update_window, works. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 1:19 ` Stefan Monnier 2020-04-30 6:55 ` tomas 2020-04-30 8:04 ` Po Lu @ 2020-04-30 13:46 ` Eli Zaretskii 2020-04-30 14:37 ` Stefan Monnier 2020-04-30 14:27 ` Drew Adams 3 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 13:46 UTC (permalink / raw) To: Stefan Monnier; +Cc: tomas, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > Date: Wed, 29 Apr 2020 21:19:51 -0400 > > I think it would make more sense to treat the overlaid canvas as > a completely separate pixmap: when we get an request to redraw > a particular area of the screen, we'd ask the current redisplay code to > redraw the corresponding text content and then we'd ask the canvas code > to draw on top of it. Unfortunately, most redisplay cycles don't start from a request to redraw a particular area of the screen. This only happens when we get expose events from the WM, which is not the "normal" trigger of redisplay. Usually, redisplay is entered when Emacs is idle, and it's one of the display engine's main jobs to figure out what parts of which windows need to be redrawn, by just considering the changes in buffer text, their properties/overlays, and window geometry and starting point. In doing so, the display engine need to know, up front, which parts of the display reside where on the screen, whether any of them needs to be redrawn because the underlying Lisp data was modified by some command since the last redisplay. So AFAIU it won't work to redraw the "canvas" only when the text beneath it changes; the display engine needs to be able to redraw the canvas itself even if the text didn't change, and it needs to know which portions of the window are affected by the canvas in order to make decisions regarding how to redraw the window optimally. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 13:46 ` Eli Zaretskii @ 2020-04-30 14:37 ` Stefan Monnier 2020-04-30 17:27 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Stefan Monnier @ 2020-04-30 14:37 UTC (permalink / raw) To: Eli Zaretskii; +Cc: tomas, emacs-devel >> I think it would make more sense to treat the overlaid canvas as >> a completely separate pixmap: when we get an request to redraw >> a particular area of the screen, we'd ask the current redisplay code to >> redraw the corresponding text content and then we'd ask the canvas code >> to draw on top of it. > > Unfortunately, most redisplay cycles don't start from a request to > redraw a particular area of the screen. I don't think it makes a big difference. Upon redisplay, the glyph-matrix layer will compute which parts of the window needs updates in the that layer (exactly like it currently does), and the canvas will do its own computation of which part of it needs updating (presumably this will be handled by the external canvas library such as Cairo's rather than by Emacs's own code) and then the compositing layer will figure out how to combine those two changes. IIUC libraries like Cairo already provide functionality to do compositing of layers, so that would take care of that part as well. Stefan ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 14:37 ` Stefan Monnier @ 2020-04-30 17:27 ` Eli Zaretskii 2020-04-30 18:22 ` Stefan Monnier 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 17:27 UTC (permalink / raw) To: Stefan Monnier; +Cc: tomas, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: tomas@tuxteam.de, emacs-devel@gnu.org > Date: Thu, 30 Apr 2020 10:37:03 -0400 > > > Unfortunately, most redisplay cycles don't start from a request to > > redraw a particular area of the screen. > > I don't think it makes a big difference. Upon redisplay, the > glyph-matrix layer will compute which parts of the window needs updates > in the that layer (exactly like it currently does), and the canvas will > do its own computation of which part of it needs updating (presumably > this will be handled by the external canvas library such as Cairo's > rather than by Emacs's own code) and then the compositing layer will > figure out how to combine those two changes. I don't think I understand how this would fit into the current redisplay framework. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 17:27 ` Eli Zaretskii @ 2020-04-30 18:22 ` Stefan Monnier 2020-04-30 18:42 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Stefan Monnier @ 2020-04-30 18:22 UTC (permalink / raw) To: Eli Zaretskii; +Cc: tomas, emacs-devel > I don't think I understand how this would fit into the current > redisplay framework. I think it should basically not require touching xdisp.c. It should work at the level of `update_window` only. Stefan ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 18:22 ` Stefan Monnier @ 2020-04-30 18:42 ` Eli Zaretskii 0 siblings, 0 replies; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 18:42 UTC (permalink / raw) To: Stefan Monnier; +Cc: tomas, emacs-devel > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: tomas@tuxteam.de, emacs-devel@gnu.org > Date: Thu, 30 Apr 2020 14:22:02 -0400 > > > I don't think I understand how this would fit into the current > > redisplay framework. > > I think it should basically not require touching xdisp.c. > It should work at the level of `update_window` only. I _am_ talking about update_frame and update_window. They have their own ideas about which parts of the screen need to be updated and which don't, and how they should be updated (whether by scrolling or by deletion and insertion or by overwriting). Those ideas are based on the assumption that they know what is currently on the glass. That knowledge comes from the glyph matrices. If the canvasses are drawn independently of the glyph matrices, then update_window will make wrong decisions, and the result will be a royal mess on the screen. Maybe I'm missing something, but you didn't actually explain on any level of detail how do you propose to "compose" the "normal" display with the canvasses. How about if you try to describe your ideas in two simple use cases: (1) when a canvas that was on display, overlaying some text, is deleted (and nothing else is changed); and (2) when update_window decides to scroll some part of a window which has a canvas partially overlaying the text in the part to be scrolled? ^ permalink raw reply [flat|nested] 53+ messages in thread
* RE: Emacs canvas support 2020-04-30 1:19 ` Stefan Monnier ` (2 preceding siblings ...) 2020-04-30 13:46 ` Eli Zaretskii @ 2020-04-30 14:27 ` Drew Adams 3 siblings, 0 replies; 53+ messages in thread From: Drew Adams @ 2020-04-30 14:27 UTC (permalink / raw) To: Stefan Monnier, tomas; +Cc: Eli Zaretskii, emacs-devel > I think it would make more sense to treat the overlaid canvas as > a completely separate pixmap: when we get an request to redraw > a particular area of the screen, we'd ask the current redisplay code to > redraw the corresponding text content and then we'd ask the canvas code > to draw on top of it. So for rendering of the canvas code we don't > need to know which part of the canvas cover which characters, we just > render the glyph matrix into a pixmap, render the canvas into another > pixmap and then combine them onto the screen. Exactly what I was thinking/wondering, as someone totally naive in this area. If we're after a graphic canvas (editing with pixels), why involve glyphs/chars at all, for that space? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 19:59 ` tomas 2020-04-30 1:19 ` Stefan Monnier @ 2020-04-30 13:33 ` Eli Zaretskii 2020-04-30 13:52 ` Arthur Miller 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 13:33 UTC (permalink / raw) To: tomas; +Cc: emacs-devel > Date: Wed, 29 Apr 2020 21:59:30 +0200 > From: tomas@tuxteam.de > Cc: emacs-devel@gnu.org > > > The current display engine works by screen lines, so if we want to > > keep it, we must use the existing framework. > > Now imagine there's a (graphical) line going from (text) display > line 3 to 7. You are not implying that we have to segment that > into four chunks, one per text line? That's one way, but it's ugly and complicated. > > > When a portion of a > > window is exposed, we redraw all the glyphs in the exposed area(s), > > and we find the glyphs that need to be redrawn by comparing their > > coordinates with those of the exposed rectangle(s). > > ... but rather that we intersect the exposed area(s) with each [1] > of the graphical objects and redraw that (be it before or after the > text)? Yes, but we examine what you call "objects" by screen lines. > [1] conceptually; in reality we'll need some index structure > to avoid looking at most of the graphical objects. Quad- > trees, whatever. We don't use them now. A typical window is not large enough to make that a worthy complication. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 13:33 ` Eli Zaretskii @ 2020-04-30 13:52 ` Arthur Miller 0 siblings, 0 replies; 53+ messages in thread From: Arthur Miller @ 2020-04-30 13:52 UTC (permalink / raw) To: Eli Zaretskii; +Cc: tomas, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> Date: Wed, 29 Apr 2020 21:59:30 +0200 >> From: tomas@tuxteam.de >> Cc: emacs-devel@gnu.org >> >> > The current display engine works by screen lines, so if we want to >> > keep it, we must use the existing framework. >> >> Now imagine there's a (graphical) line going from (text) display >> line 3 to 7. You are not implying that we have to segment that >> into four chunks, one per text line? > > That's one way, but it's ugly and complicated. > >> >> > When a portion of a >> > window is exposed, we redraw all the glyphs in the exposed area(s), >> > and we find the glyphs that need to be redrawn by comparing their >> > coordinates with those of the exposed rectangle(s). >> >> ... but rather that we intersect the exposed area(s) with each [1] >> of the graphical objects and redraw that (be it before or after the >> text)? > > Yes, but we examine what you call "objects" by screen lines. > >> [1] conceptually; in reality we'll need some index structure >> to avoid looking at most of the graphical objects. Quad- >> trees, whatever. > > We don't use them now. A typical window is not large enough to make > that a worthy complication. If you used exactly same functionality as now, but would call it twice, or N number of times, as a chain of render hooks, and then let every callback in chain draw it's own buffer, wouldn't that solve the issue with relatively small effort in changes? If user had pre-render hook, and say created an image and put it in first char in temp buffer and then add that buffer as pre-render hook, then the render engine just need to be modified to act when any of buffers in render chain changes, instead of just current buffer. But when it comes to rendering functionality, it would stay same, just call whatever you already have now for the buffer drawing, on each render hook and it's associated buffer and let them draw over each other; like in z-buffer. Is it too difficult to implement? It would draw over some portions of screen twice or more times, but who cares. I don't think it is a big issue in an application like Emacs, it is not like Emacs is drawing millions of polygons at 120 FPS. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 16:54 ` Eli Zaretskii 2020-04-29 17:16 ` tomas @ 2020-04-29 19:23 ` David Engster 2020-04-30 13:29 ` Eli Zaretskii 2020-04-30 6:52 ` Corwin Brust 2 siblings, 1 reply; 53+ messages in thread From: David Engster @ 2020-04-29 19:23 UTC (permalink / raw) To: Eli Zaretskii; +Cc: luangruo, emacs-devel >> From: David Engster <deng@randomsample.de> >> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org >> Date: Wed, 29 Apr 2020 18:14:55 +0200 > >> >> IMHO, What would be much more exciting, is if you could draw directly >> over normal text. This would enable a whole new set of >> possibilities. For instance, I always liked visual diff tools like >> 'Meld' or 'Code Compare', but I don't see how you could do this with >> Emacs. Or things like proper indentation guides, or widgets inside text >> for code folding. Yes, those can be done in Emacs today, but it is very >> difficult to get right and often not really satisfactory (weird >> interactions between packages, too slow, etc.). > > AFAIU, Meld seems to draw the graphics _between_ windows, and only > highlights the source lines with background colors. I think we can do > that if we use an extra window in-between the two being compared, and > put images in that extra window to show the graphical description of > the changes between the two versions. I actually tried exactly that, many years ago. I dimly remember that handling the coordinates was pretty complicated and it was slow. Like, orders of magnitude too slow, even with the simplest and fastest image types. There was no way you would ever be able to scroll text and update the diff visualization alongside it. It was just a flickery mess. It would of course be possible to visualize the diff in an idle timer and not during scrolling, but that is exactly what I mean above with "not really satisfactory", so I gave up. > Drawing over normal text, if we don't want to redesign the entire > display engine, needs some new kind of "display element" ( a sibling > to "character", "image", "stretch", etc.), one that doesn't > necessarily have any effect on the metrics of the screen lines it is > drawn upon. I'm not sure I have a clear idea about what features such > a drawing will need to support, but it could be possible to add such > an element with not too much effort. Would someone want to come up > with a reasonable list of requirements for such a feature? When I played around with it, I thought a first iteration could be to simply expose the basic Cairo API to ELisp, which is essentially drawing lines/arcs/rectangles and filling. I thought that maybe it would be possible that one could handle this like a graphical transparent overlay, but I'm afraid I'm way out of my depth here. I have no idea how one would handle scrolling, for instance. -David ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 19:23 ` David Engster @ 2020-04-30 13:29 ` Eli Zaretskii 0 siblings, 0 replies; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 13:29 UTC (permalink / raw) To: David Engster; +Cc: luangruo, emacs-devel > From: David Engster <deng@randomsample.de> > Cc: luangruo@yahoo.com, emacs-devel@gnu.org > Date: Wed, 29 Apr 2020 21:23:30 +0200 > > > AFAIU, Meld seems to draw the graphics _between_ windows, and only > > highlights the source lines with background colors. I think we can do > > that if we use an extra window in-between the two being compared, and > > put images in that extra window to show the graphical description of > > the changes between the two versions. > > I actually tried exactly that, many years ago. I dimly remember that > handling the coordinates was pretty complicated and it was slow. Like, > orders of magnitude too slow, even with the simplest and fastest image > types. There was no way you would ever be able to scroll text and update > the diff visualization alongside it. It was just a flickery mess. It > would of course be possible to visualize the diff in an idle timer and > not during scrolling, but that is exactly what I mean above with "not > really satisfactory", so I gave up. Hmm... yes, I see your point. We'd need to find a way of somehow synchronizing the windows, which is not very easy (witness follow-mode). > > Drawing over normal text, if we don't want to redesign the entire > > display engine, needs some new kind of "display element" ( a sibling > > to "character", "image", "stretch", etc.), one that doesn't > > necessarily have any effect on the metrics of the screen lines it is > > drawn upon. I'm not sure I have a clear idea about what features such > > a drawing will need to support, but it could be possible to add such > > an element with not too much effort. Would someone want to come up > > with a reasonable list of requirements for such a feature? > > When I played around with it, I thought a first iteration could be to > simply expose the basic Cairo API to ELisp, which is essentially drawing > lines/arcs/rectangles and filling. I thought that maybe it would be > possible that one could handle this like a graphical transparent > overlay, but I'm afraid I'm way out of my depth here. I have no idea how > one would handle scrolling, for instance. Right, the main problem is not how to draw, but how to tell the display engine where's the drawing on the screen, and when to redraw it. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 16:54 ` Eli Zaretskii 2020-04-29 17:16 ` tomas 2020-04-29 19:23 ` David Engster @ 2020-04-30 6:52 ` Corwin Brust 2 siblings, 0 replies; 53+ messages in thread From: Corwin Brust @ 2020-04-30 6:52 UTC (permalink / raw) To: Eli Zaretskii; +Cc: luangruo, David Engster, Emacs developers Hi everyone! This is conversation intersects very directly with a project I organize that is creating an RPG game engine and authoring/publishing enhancements based on org-mode and svg.el. Our project is very new and the things that work only work if one is careful and patient. We're targeting whatever will likely be supported in Emacs 28 or so, meaning we would be open to help testing new features that come out of this and similar conversations, if any, and given we're up the tasks. (Our application for savannah hosting is in progress; I've no interest to discuss the project's current non-free hosting solution.) On Wed, Apr 29, 2020 at 11:55 AM Eli Zaretskii <eliz@gnu.org> wrote: > > > From: David Engster <deng@randomsample.de> > > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > > Date: Wed, 29 Apr 2020 18:14:55 +0200 > > > > IMHO, What would be much more exciting, is if you could draw directly > > over normal text. This would enable a whole new set of > > possibilities. > [..] not too much effort. Would someone want to come up > with a reasonable list of requirements for such a feature? To the extent it may help with developing the list you suggested, I wanted to share this SVG image from our project: https://raw.githubusercontent.com/dungeon-mode/game/master/Docs/battleboard/Battleboard.svg This file mocks an important player interface that shows the vital statistics for each of the eight player-characters that compose the "party". The four inner boxes inside each plus-shape create a space for one such statistic, for example, Body Hits, Armor Hit Points, etc. which are displayed integers. These, like the actual text show in the mock, should ideally be text. It seems to me that this should work well with such overlay methods as are discussed down-thread. I hope the graphic provides a useful context for on "in the wild" aspirational use for composed SVG+text in user-space. I'm happy to be pulled into other threads/conversations related to enhancing display options for hybrid text and SVG. PS, For the "mapping" feature (the party controls their movement through a maze), which is the only truly implicitly graphical component we are creating, we have resorted.to drawing text overlays with the "text" SVG element; however, for the battle-board, we had been planning to use a window for each character (so eight windows) and slices within to achieve the crisp lines and while allowing some room for some theming. -- Corwin 612-217-1742 612-298-0615 (fax) 612-695-4276 (mobile) corwin.brust (skype) corwin@bru.st ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 16:14 ` David Engster 2020-04-29 16:54 ` Eli Zaretskii @ 2020-04-29 17:08 ` Eli Zaretskii 2020-04-29 20:14 ` David Engster 1 sibling, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-29 17:08 UTC (permalink / raw) To: David Engster; +Cc: luangruo, emacs-devel > From: David Engster <deng@randomsample.de> > Date: Wed, 29 Apr 2020 18:14:55 +0200 > Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org > > [...] things like proper indentation guides, or widgets inside text > for code folding. Would it make sense to put these on the fringes? If yes, this could be done without any changes on the C level, I think. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 17:08 ` Eli Zaretskii @ 2020-04-29 20:14 ` David Engster 2020-04-30 13:35 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: David Engster @ 2020-04-29 20:14 UTC (permalink / raw) To: Eli Zaretskii; +Cc: luangruo, emacs-devel >> From: David Engster <deng@randomsample.de> >> Date: Wed, 29 Apr 2020 18:14:55 +0200 >> Cc: Eli Zaretskii <eliz@gnu.org>, emacs-devel@gnu.org >> >> [...] things like proper indentation guides, or widgets inside text >> for code folding. > > Would it make sense to put these on the fringes? If yes, this could > be done without any changes on the C level, I think. I just found that this exists already with the 'hideshowvis' package and it works reasonably well. So I agree this is not a good use case. You can also do indentation guides in current Emacs, but the packages I tried did not work great. They usually collide with packages like company-mode which are more important to me. This is often the case: if you have several packages that use overlays and/or text properties for fancy stuff, you can expect them to collide in some way. Let's take another very simple example: I often wished it would be possible to tell Emacs "Put an image at pixel position (x,y)" without having to anchor it to some point position. For instance, I have a block of text and would like to have an image next to it. Last I checked, the only way to do this is to create a "sliced image" which automatically gets cut up into rows and columns, but this is a very brittle workaround (it doesn't work with an increased line-spacing, for instance). -David ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 20:14 ` David Engster @ 2020-04-30 13:35 ` Eli Zaretskii 0 siblings, 0 replies; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 13:35 UTC (permalink / raw) To: David Engster; +Cc: luangruo, emacs-devel > From: David Engster <david@engster.org> > Cc: luangruo@yahoo.com, emacs-devel@gnu.org > Date: Wed, 29 Apr 2020 22:14:18 +0200 > > Let's take another very simple example: I often wished it would be > possible to tell Emacs "Put an image at pixel position (x,y)" without > having to anchor it to some point position. For instance, I have a block > of text and would like to have an image next to it. Last I checked, the > only way to do this is to create a "sliced image" which automatically > gets cut up into rows and columns, but this is a very brittle workaround > (it doesn't work with an increased line-spacing, for instance). Under the current design of the display engine, I believe anything displayed inside a window must come from some buffer text position or from a string. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 6:34 ` Emacs canvas support Po Lu via Emacs development discussions. 2020-04-29 8:24 ` Eli Zaretskii @ 2020-04-29 20:48 ` Juri Linkov 2020-04-30 2:36 ` Eli Zaretskii 1 sibling, 1 reply; 53+ messages in thread From: Juri Linkov @ 2020-04-29 20:48 UTC (permalink / raw) To: Po Lu via Emacs development discussions.; +Cc: Po Lu > I'd appreciate some feedback on something I came up with during my spare > time: Emacs canvas support. Great, thanks! I'm going to try to use canvas-rounded-rectangle for rounded corners of tabs. Sometime ago I tried to draw them with Cairo, but the implementation was too ah-hoc. Whereas your solution is more generally usable. Would you put your code on a branch? ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-29 20:48 ` Juri Linkov @ 2020-04-30 2:36 ` Eli Zaretskii 2020-04-30 20:20 ` Juri Linkov 0 siblings, 1 reply; 53+ messages in thread From: Eli Zaretskii @ 2020-04-30 2:36 UTC (permalink / raw) To: Juri Linkov; +Cc: luangruo, emacs-devel > From: Juri Linkov <juri@linkov.net> > Date: Wed, 29 Apr 2020 23:48:31 +0300 > Cc: Po Lu <luangruo@yahoo.com> > > > I'd appreciate some feedback on something I came up with during my spare > > time: Emacs canvas support. > > Great, thanks! I'm going to try to use canvas-rounded-rectangle > for rounded corners of tabs. Sometime ago I tried to draw them > with Cairo, but the implementation was too ah-hoc. Whereas your > solution is more generally usable. Did you try using svg.el? > Would you put your code on a branch? We need to finish the legal paperwork first. ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 2:36 ` Eli Zaretskii @ 2020-04-30 20:20 ` Juri Linkov 2020-05-01 6:01 ` Eli Zaretskii 0 siblings, 1 reply; 53+ messages in thread From: Juri Linkov @ 2020-04-30 20:20 UTC (permalink / raw) To: Eli Zaretskii; +Cc: luangruo, emacs-devel [-- Attachment #1: Type: text/plain, Size: 955 bytes --] >> > I'd appreciate some feedback on something I came up with during my spare >> > time: Emacs canvas support. >> >> Great, thanks! I'm going to try to use canvas-rounded-rectangle >> for rounded corners of tabs. Sometime ago I tried to draw them >> with Cairo, but the implementation was too ah-hoc. Whereas your >> solution is more generally usable. > > Did you try using svg.el? I tried, but svg is not great, it has many unfixed problems with scaling and backgrounds. OTOH, I hope with canvas it would be easy to draw such shapes around the tab name like in web browsers shown below, and also to implement overlapping tabs. At least canvas in web browsers allow doing such custom drawings. As for the implementation, there is already Cairo code used to draw 3D box shapes around strings with box faces in xterm.c. Canvas could be implemented to generalize these hard-coded shapes and expose their definitions to Lisp as configurable options. [-- Attachment #2: browser_tab.png --] [-- Type: image/png, Size: 1920 bytes --] ^ permalink raw reply [flat|nested] 53+ messages in thread
* Re: Emacs canvas support 2020-04-30 20:20 ` Juri Linkov @ 2020-05-01 6:01 ` Eli Zaretskii 0 siblings, 0 replies; 53+ messages in thread From: Eli Zaretskii @ 2020-05-01 6:01 UTC (permalink / raw) To: Juri Linkov; +Cc: luangruo, emacs-devel > From: Juri Linkov <juri@linkov.net> > Cc: emacs-devel@gnu.org, luangruo@yahoo.com > Date: Thu, 30 Apr 2020 23:20:18 +0300 > > > Did you try using svg.el? > > I tried, but svg is not great, it has many unfixed problems with scaling > and backgrounds. Cannot these bugs be fixed? > OTOH, I hope with canvas it would be easy to draw such shapes around > the tab name like in web browsers shown below, and also to implement > overlapping tabs. At least canvas in web browsers allow doing such > custom drawings. The problem with canvas is that we have yet to figure out how to teach the display engine handle them correctly and efficiently. svg.el doesn't have that problem. ^ permalink raw reply [flat|nested] 53+ messages in thread
end of thread, other threads:[~2020-05-02 6:51 UTC | newest] Thread overview: 53+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <875zdikdge.fsf.ref@yahoo.com> 2020-04-29 6:34 ` Emacs canvas support Po Lu via Emacs development discussions. 2020-04-29 8:24 ` Eli Zaretskii 2020-04-29 9:57 ` Po Lu 2020-04-29 10:10 ` Eli Zaretskii 2020-04-29 10:22 ` Po Lu 2020-04-29 10:27 ` Po Lu via Emacs development discussions. 2020-04-29 11:47 ` Eli Zaretskii 2020-04-29 10:35 ` Eli Zaretskii 2020-04-29 10:41 ` Po Lu 2020-04-29 11:51 ` Eli Zaretskii 2020-04-29 12:12 ` Po Lu 2020-04-29 16:14 ` David Engster 2020-04-29 16:54 ` Eli Zaretskii 2020-04-29 17:16 ` tomas 2020-04-29 17:27 ` Eli Zaretskii 2020-04-29 17:38 ` Eli Zaretskii 2020-04-30 13:11 ` Arthur Miller 2020-04-30 14:18 ` Eli Zaretskii 2020-04-30 14:58 ` Arthur Miller 2020-04-30 17:30 ` Eli Zaretskii 2020-05-01 14:32 ` Arthur Miller 2020-04-29 18:51 ` tomas 2020-04-29 19:03 ` Eli Zaretskii 2020-04-29 19:08 ` tomas 2020-04-29 19:25 ` Eli Zaretskii 2020-04-29 19:59 ` tomas 2020-04-30 1:19 ` Stefan Monnier 2020-04-30 6:55 ` tomas 2020-04-30 12:03 ` Stefan Monnier 2020-04-30 12:50 ` tomas 2020-04-30 8:04 ` Po Lu 2020-04-30 12:08 ` Stefan Monnier 2020-04-30 13:55 ` Eli Zaretskii 2020-05-01 23:27 ` Po Lu 2020-05-02 6:51 ` Eli Zaretskii 2020-04-30 13:46 ` Eli Zaretskii 2020-04-30 14:37 ` Stefan Monnier 2020-04-30 17:27 ` Eli Zaretskii 2020-04-30 18:22 ` Stefan Monnier 2020-04-30 18:42 ` Eli Zaretskii 2020-04-30 14:27 ` Drew Adams 2020-04-30 13:33 ` Eli Zaretskii 2020-04-30 13:52 ` Arthur Miller 2020-04-29 19:23 ` David Engster 2020-04-30 13:29 ` Eli Zaretskii 2020-04-30 6:52 ` Corwin Brust 2020-04-29 17:08 ` Eli Zaretskii 2020-04-29 20:14 ` David Engster 2020-04-30 13:35 ` Eli Zaretskii 2020-04-29 20:48 ` Juri Linkov 2020-04-30 2:36 ` Eli Zaretskii 2020-04-30 20:20 ` Juri Linkov 2020-05-01 6:01 ` Eli Zaretskii
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.