From 13b92b39ae212081a51c67e8c3a639f13291a55c Mon Sep 17 00:00:00 2001 From: Cecilio Pardo Date: Tue, 5 Nov 2024 23:41:30 +0100 Subject: [PATCH] Support for background images on frames. * src/dispnew.c (scrolling_window): * src/frame.c (make_frame): (Fframe_set_background_image): (Fframe_set_background_policy): (syms_of_frame): * src/frame.h (GCALIGNED_STRUCT): * src/w32font.c (w32font_draw): * src/w32term.c (w32_clear_glyph_string_rect): (w32_draw_stretch_glyph_string): (w32_need_to_draw_simple_background): (w32_clear_frame_area_complex): (w32_clear_frame_area): * src/w32term.h: * src/xterm.c (x_clear_frame_area): --- configure.ac | 2 +- src/complexbkg.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++ src/complexbkg.h | 31 ++++++ src/dispnew.c | 3 + src/frame.c | 75 +++++++++++++ src/frame.h | 10 ++ src/ftcrfont.c | 8 ++ src/w32font.c | 8 ++ src/w32term.c | 20 +++- src/xterm.c | 13 ++- src/xterm.h | 3 + 11 files changed, 439 insertions(+), 7 deletions(-) create mode 100644 src/complexbkg.c create mode 100644 src/complexbkg.h diff --git a/configure.ac b/configure.ac index 1c7545ef984..edb237f2051 100644 --- a/configure.ac +++ b/configure.ac @@ -7630,7 +7630,7 @@ AC_DEFUN AC_DEFINE([HAVE_WINDOW_SYSTEM], [1], [Define if you have a window system.]) AC_DEFINE([POLL_FOR_INPUT], [1], [Define if you poll periodically to detect C-g.]) - WINDOW_SYSTEM_OBJ="fontset.o fringe.o image.o" + WINDOW_SYSTEM_OBJ="fontset.o fringe.o image.o complexbkg.o" if test "$window_system" = "x11" || test "$REALLY_ANDROID" = "yes"; then AC_DEFINE([HAVE_TEXT_CONVERSION], [1], diff --git a/src/complexbkg.c b/src/complexbkg.c new file mode 100644 index 00000000000..c3c5679df8b --- /dev/null +++ b/src/complexbkg.c @@ -0,0 +1,273 @@ +#include "config.h" + +#if defined(HAVE_NTGUI) +# include +#endif + +#if defined(USE_CAIRO) +# include "xterm.h" +#endif + +#include "lisp.h" +#include "frame.h" +#include "buffer.h" +#include "complexbkg.h" + +/* +Each GUI backend must provide its own version of the functions +get_bkg_color, begin_bkg_image, draw_bkg_image, end_bkg_image and +draw_vertical_line. The functions are called from complexbkg_clear_frame_area + +- get_bkg_color: Although the background color is passed directly to + complexbkg_clear_frame_area, a backend may compute it from other + information. +- begin_bkg_image: Called once, before any attempt to draw any image. +- draw_bkg_image: Called any number of times, always between calls to + begin_bkg_image and end_bkg_image. +- end_bkg_image: Called once, after all calls to draw_bkg_image. + */ + +#if defined(HAVE_NTGUI) + +static void +get_bkg_color (struct complexbkg_platform_info *data, unsigned long *bcolor) +{ + /* Nothing to see here */ +} + +static void +begin_bkg_image (struct complexbkg_platform_info *data, struct image *img) +{ + data->compat_hdc = CreateCompatibleDC (data->hdc); + data->orig_obj = SelectObject (data->compat_hdc, img->pixmap); +} + +static void +draw_bkg_image (struct complexbkg_platform_info *data, int dest_x, int dest_y, + int width, int height, int src_x, int src_y) +{ + BitBlt (data->hdc, dest_x, dest_y, width, height, + data->compat_hdc, src_x, src_y, SRCCOPY); +} + +static void +end_bkg_image (struct complexbkg_platform_info *data) +{ + SelectObject (data->compat_hdc, data->orig_obj); + DeleteDC (data->compat_hdc); +} + +static void +draw_vertical_line (struct complexbkg_platform_info *data, + int x, int y, int height) +{ + HANDLE pen_old = SelectObject (data->hdc, + CreatePen (PS_SOLID, 1, 0x0000FF)); + MoveToEx (data->hdc, x, y, NULL); + LineTo (data->hdc, x, y + height); + DeleteObject( SelectObject (data->hdc, pen_old) ); +} + +#elif defined(USE_CAIRO) + +/* The cairo backend uses the GC for the clipping and the background color. + If may be NULL. */ + +static void +get_bkg_color (struct complexbkg_platform_info *data, unsigned long *bcolor) +{ + if (data->gc != NULL) + { + XGCValues xgcv; + XGetGCValues (FRAME_X_DISPLAY (data->frame), + data->gc, GCBackground, &xgcv); + *bcolor = xgcv.background; + } +} + +static void +begin_bkg_image (struct complexbkg_platform_info *data, struct image *img) +{ + data->img = img; +} + +static void +draw_bkg_image (struct complexbkg_platform_info *data, int dest_x, int dest_y, + int width, int height, int src_x, int src_y) +{ + x_cr_draw_image( data->frame, + data->gc == NULL ? FRAME_OUTPUT_DATA(data->frame)->normal_gc : data->gc, + data->img->cr_data, + src_x, src_y, width, height, + dest_x, dest_y, false ); +} + +static void +end_bkg_image (struct complexbkg_platform_info *data) +{ +} + +static void +draw_vertical_line (struct complexbkg_platform_info *data, + int x, int y, int height) +{ + cairo_t *cr; + cr = x_begin_cr_clip (data->frame, NULL); + cairo_set_source_rgb (cr, 1.0, 0.0, 0.0); + cairo_move_to (cr, x, y); + cairo_line_to (cr, x, y + height); + cairo_stroke (cr); + x_end_cr_clip(data->frame); +} + +#else /* GUI type not supported. */ +#warning "complexbkg disabled: GUI type not supported." +#define DISABLE_COMPLEXBKG 1 +#endif + +#ifndef DISABLE_COMPLEXBKG +static void +get_all_live_windows (struct window *w, struct window **dest) +{ + if (WINDOW_LEAF_P (w)) + { + int i = 0; + for (; dest[i]; i++) + ; + dest[i] = w; + dest[i+1] = NULL; + } + + if (WINDOW_INTERNAL_P (w)) + for (Lisp_Object ch = w->contents; + ch != Qnil; + ch = XWINDOW (ch)->next ) + get_all_live_windows (XWINDOW (ch), dest); +} + +void +complexbkg_clear_frame_area (struct frame *f, unsigned long bcolor, + int x, int y, int width, int height, + struct complexbkg_platform_info *data) +{ + if (f->background_policy == Qnil) + return; + + get_bkg_color (data, &bcolor); + + if (f->background_image >= 0 && bcolor == FRAME_BACKGROUND_PIXEL (f)) + { + struct image *img = IMAGE_FROM_ID (f, f->background_image); + prepare_image_for_display (f, img); + + if (!img->load_failed_p) + { + begin_bkg_image (data, img); + + if (f->background_tiled) + { + int x1 = x + width, y1 = y + height; + /* Range of tiles that need painting. */ + int tx0 = x / img->width; + int tx1 = (x + width - 1) / img->width; + int ty0 = y / img->height; + int ty1 = (y + height - 1) / img->height; + + for (int tx = tx0; tx <= tx1; tx++) + for (int ty = ty0; ty <= ty1; ty++) + { + int X = tx * img->width, Y = ty * img->width; + int X1 = X + img->width, Y1 = Y + img->height; + + /* Clip the tile against the output rectangle. */ + int + c_left = (X < x) ? (x - X) : 0, + c_right = (X1 > x1) ? (X1 - x1) : 0, + c_top = (Y < y) ? (y - Y) : 0, + c_bottom = (Y1 > y1) ? (Y1 - y1) : 0; + + draw_bkg_image (data, + X + c_left, Y + c_top, + img->width - c_right - c_left, + img->height - c_bottom - c_top, + c_left, c_top ); + } + } + else + { + int src_x = x, src_y = y; + int fw = FRAME_PIXEL_WIDTH (f), fh = FRAME_PIXEL_HEIGHT (f); + + switch (f->background_gravity) + { + case 1: /* north */ + src_x -= (fw - img->width) / 2; + break; + case 2: /* south */ + src_x -= (fw - img->width) / 2; + src_y -= fh - img->height; + break; + case 3: /* east */ + src_x -= fw - img->width; + src_y -= (fh - img->height) / 2; + break; + case 4: /* west */ + src_y -= (fh - img->height) / 2; + break; + case 5: /* ne */ + src_x -= fw - img->width; + break; + case 6: /* nw */ + break; + case 7: /* se */ + src_x -= fw - img->width; + src_y -= fh - img->height; + break; + case 8: /* sw */ + src_y -= fh - img->height; + break; + case 9: /* center */ + src_x -= (fw - img->width) / 2; + src_y -= (fh - img->height) / 2; + break; + } + + draw_bkg_image (data, x, y, width, height, src_x, src_y); + } + end_bkg_image (data); + } + } + + /* Window specific complications. */ + struct window *windows[f->number_of_windows + 1]; + windows[0] = NULL; + get_all_live_windows (XWINDOW (f->root_window), windows); + + Emacs_Rectangle zone = { x, y, width, height }; + for (int i = 0; windows[i]; i++) + { + int boxx, boxy, boxwidth, boxheight; + window_box(windows[i], TEXT_AREA, &boxx, &boxy, &boxwidth, &boxheight); + Emacs_Rectangle wbox, intersect; + wbox.x = boxx; + wbox.y = boxy; + wbox.width = boxwidth; + wbox.height = boxheight; + if (!gui_intersect_rectangles( &zone, &wbox, &intersect )) + continue; + /* This window has to update the 'intersect' rectangle. */ + + Lisp_Object fill_column = BVAR (XBUFFER (WINDOW_BUFFER (windows[i])), fill_column); + if (FIXNUMP (fill_column)) + { + /* TODO: Take the buffer's face with */ + int vertical_line_x = XFIXNUM (fill_column) * FRAME_COLUMN_WIDTH(f); + + if (wbox.x + vertical_line_x >= intersect.x + && wbox.x + vertical_line_x <= intersect.x + intersect.width) + draw_vertical_line (data, wbox.x + vertical_line_x, + intersect.y, intersect.height); + } + } +} +#endif /* DISABLE_COMPLEXBKG */ diff --git a/src/complexbkg.h b/src/complexbkg.h new file mode 100644 index 00000000000..e45ebb2edff --- /dev/null +++ b/src/complexbkg.h @@ -0,0 +1,31 @@ +#if defined(HAVE_NTGUI) + +struct complexbkg_platform_info { + HDC hdc; + /* Internal from here */ + HDC compat_hdc; + HGDIOBJ orig_obj; +}; + +#elif defined(USE_CAIRO) + +struct complexbkg_platform_info { + struct frame *frame; + GC gc; + /* Internal from here */ + struct image *img; +}; + +#else + +struct complexbkg_platform_info { + int dummy; +}; + +#endif + +void complexbkg_clear_frame_area (struct frame *f, + unsigned long bcolor, + int x, int y, + int width, int height, + struct complexbkg_platform_info *data); diff --git a/src/dispnew.c b/src/dispnew.c index 1a243079e46..ca564cd0c46 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -4549,6 +4549,9 @@ scrolling_window (struct window *w, int tab_line_p) struct row_entry *entry; struct redisplay_interface *rif = FRAME_RIF (XFRAME (WINDOW_FRAME (w))); + if (XFRAME (WINDOW_FRAME (w))->background_policy != Qnil) + return 0; + /* Skip over rows equal at the start. */ for (i = tab_line_p; i < current_matrix->nrows - 1; ++i) { diff --git a/src/frame.c b/src/frame.c index 7f4bf274ad9..d2ff749f49d 100644 --- a/src/frame.c +++ b/src/frame.c @@ -1009,6 +1009,11 @@ make_frame (bool mini_p) f->conversion.actions = NULL; #endif +#ifdef HAVE_WINDOW_SYSTEM + f->background_policy = Qnil; + f->background_image = -1; +#endif /* HAVE_WINDOW_SYSTEM */ + root_window = make_window (); rw = XWINDOW (root_window); if (mini_p) @@ -1696,6 +1701,64 @@ DEFUN ("frame-list", Fframe_list, Sframe_list, #endif /* HAVE_WINDOW_SYSTEM */ } +DEFUN("frame-set-background-image", Fframe_set_background_image, + Sframe_set_background_image, 2, 4, 0, + doc: /* Set the background image of FRAME to IMAGE. If present, +GRAVITY must one of symbols: n, s, e, w, ne, nw, se, sw, center. If +TILED it t, repeat the image to fill the whole frame, excetp if GRAVITY +is center. */ ) + (Lisp_Object frame, Lisp_Object image_spec, Lisp_Object gravity, Lisp_Object tiled) +{ + struct frame *f = decode_live_frame (frame); + if (NILP (image_spec)) + f->background_image = -1; + else + { + ptrdiff_t image_id = lookup_image (f, image_spec, DEFAULT_FACE_ID); + f->background_image = image_id; + } + + f->background_gravity = 6; /* north west */ + if (gravity == Qn) + f->background_gravity = 1; + else if (gravity == Qs) + f->background_gravity = 2; + else if (gravity == Qe) + f->background_gravity = 3; + else if (gravity == Qw) + f->background_gravity = 4; + else if (gravity == Qne) + f->background_gravity = 5; + else if (gravity == Qnw) + f->background_gravity = 6; + else if (gravity == Qse) + f->background_gravity = 7; + else if (gravity == Qsw) + f->background_gravity = 8; + else if (gravity == Qcenter) + f->background_gravity = 9; + + f->background_tiled = true; + if (tiled == Qnil) + f->background_tiled = false; + + SET_FRAME_GARBAGED (f); + return image_spec; +} + +DEFUN("frame-set-background-policy", Fframe_set_background_policy, + Sframe_set_background_policy, 2, 2, 0, + doc: /* Set the background policy for FRAME. */ ) + (Lisp_Object frame, Lisp_Object bp) +{ + struct frame *f = decode_live_frame (frame); + + f->background_policy = bp; + SET_FRAME_GARBAGED (f); + + return bp; +} + DEFUN ("frame-parent", Fframe_parent, Sframe_parent, 0, 1, 0, doc: /* Return the parent frame of FRAME. @@ -6817,6 +6880,8 @@ focus (where a frame immediately loses focus when it's left by the mouse frame_internal_parameters = list3 (Qname, Qparent_id, Qwindow_id); #endif + defsubr (&Sframe_set_background_image); + defsubr (&Sframe_set_background_policy); defsubr (&Sframep); defsubr (&Sframe_live_p); defsubr (&Swindow_system); @@ -6901,5 +6966,15 @@ focus (where a frame immediately loses focus when it's left by the mouse #if !defined HAVE_EXT_TOOL_BAR || defined USE_GTK Fprovide (Qmove_toolbar, Qnil); #endif /* !HAVE_EXT_TOOL_BAR || USE_GTK */ + + DEFSYM (Qn, "n"); + DEFSYM (Qs, "s"); + DEFSYM (Qe, "e"); + DEFSYM (Qw, "w"); + DEFSYM (Qne, "ne"); + DEFSYM (Qnw, "nw"); + DEFSYM (Qse, "se"); + DEFSYM (Qsw, "sw"); + DEFSYM (Qcenter, "center"); #endif /* HAVE_WINDOW_SYSTEM */ } diff --git a/src/frame.h b/src/frame.h index 1d920d1a6bc..7c2d82c1be3 100644 --- a/src/frame.h +++ b/src/frame.h @@ -258,6 +258,9 @@ #define EMACS_FRAME_H /* Desired and current contents displayed in that window. */ Lisp_Object desired_tab_bar_string; Lisp_Object current_tab_bar_string; + + /* Configuration for background image. */ + Lisp_Object background_policy; #endif #if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) @@ -741,6 +744,13 @@ #define EMACS_FRAME_H /* Text conversion state used by certain input methods. */ struct text_conversion_state conversion; #endif + +#ifdef HAVE_WINDOW_SYSTEM + ptrdiff_t background_image; + int background_gravity; + bool background_tiled; +#endif /* HAVE_WINDOW_SYSTEM */ + } GCALIGNED_STRUCT; /* Most code should use these functions to set Lisp fields in struct frame. */ diff --git a/src/ftcrfont.c b/src/ftcrfont.c index 3700154e44a..dd019275f43 100644 --- a/src/ftcrfont.c +++ b/src/ftcrfont.c @@ -40,6 +40,8 @@ #ifdef HAVE_PGTK #include "xsettings.h" #endif +#include "complexbkg.h" + #ifdef USE_BE_CAIRO #define RED_FROM_ULONG(color) (((color) >> 16) & 0xff) @@ -601,6 +603,12 @@ ftcrfont_draw (struct glyph_string *s, cairo_rectangle (cr, x, y - FONT_BASE (s->font), s->width, FONT_HEIGHT (s->font)); cairo_fill (cr); + + struct complexbkg_platform_info data = { s->f, s->gc };; + complexbkg_clear_frame_area (s->f, 0, + x, y - FONT_BASE (s->font), + s->width, FONT_HEIGHT (s->font), + &data ); } glyphs = alloca (sizeof (cairo_glyph_t) * len); diff --git a/src/w32font.c b/src/w32font.c index 48968a28fbd..30676e23ccc 100644 --- a/src/w32font.c +++ b/src/w32font.c @@ -24,6 +24,7 @@ #include #include "lisp.h" +#include "complexbkg.h" #include "w32term.h" #include "frame.h" #include "coding.h" /* for ENCODE_SYSTEM, DECODE_SYSTEM */ @@ -696,6 +697,7 @@ w32font_draw (struct glyph_string *s, int from, int to, - s->first_glyph->slice.glyphless.upper_yoff; descent = 0; } + brush = CreateSolidBrush (s->gc->background); rect.left = x; rect.top = y - ascent; @@ -703,6 +705,12 @@ w32font_draw (struct glyph_string *s, int from, int to, rect.bottom = y + descent; FillRect (s->hdc, &rect, brush); DeleteObject (brush); + + struct complexbkg_platform_info data = { s->hdc }; + + complexbkg_clear_frame_area (s->f, s->gc->background, + x, y - ascent, s->width, + ascent + descent, &data); } if (s->padding_p) diff --git a/src/w32term.c b/src/w32term.c index e18f39dd2a8..6663fa1d5e8 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -22,6 +22,7 @@ Copyright (C) 1989, 1993-2024 Free Software Foundation, Inc. #include #include "lisp.h" #include "blockinput.h" +#include "complexbkg.h" #include "w32term.h" #include "w32common.h" /* for OS version info */ #include @@ -1285,8 +1286,11 @@ w32_clear_glyph_string_rect (struct glyph_string *s, - s->gc->clip_rectangle.top); } #endif - w32_fill_area (s->f, s->hdc, s->gc->background, real_x, real_y, - real_w, real_h); + w32_fill_area (s->f, s->hdc, s->gc->background, real_x, real_y, + real_w, real_h); + struct complexbkg_platform_info data = { s->hdc }; + complexbkg_clear_frame_area (s->f, s->gc->background, + real_x, real_y, real_w, real_h, &data); } /* Fill background with bitmap pattern from S at specified position @@ -2604,7 +2608,10 @@ w32_draw_stretch_glyph_string (struct glyph_string *s) } else { - w32_fill_area (s->f, s->hdc, gc->background, x, y, w, h); + w32_fill_area (s->f, s->hdc, gc->background, x, y, w, h); + struct complexbkg_platform_info data = { s->hdc }; + complexbkg_clear_frame_area (s->f, gc->background, + x, y, w, h, &data); } } } @@ -6626,7 +6633,6 @@ w32_draw_bar_cursor (struct window *w, struct glyph_row *row, } } - /* RIF: Define cursor CURSOR on frame F. */ static void @@ -6635,7 +6641,6 @@ w32_define_frame_cursor (struct frame *f, Emacs_Cursor cursor) w32_define_cursor (FRAME_W32_WINDOW (f), cursor); } - /* RIF: Clear area on frame F. */ static void @@ -6644,7 +6649,12 @@ w32_clear_frame_area (struct frame *f, int x, int y, int width, int height) HDC hdc; hdc = get_frame_dc (f); + w32_clear_area (f, hdc, x, y, width, height); + struct complexbkg_platform_info data = { hdc }; + complexbkg_clear_frame_area (f, FRAME_BACKGROUND_PIXEL(f), + x, y, width, height, &data); + release_frame_dc (f, hdc); } diff --git a/src/xterm.c b/src/xterm.c index 0c20d38b0f7..653dcfb2e8c 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -719,6 +719,8 @@ Copyright (C) 1989, 1993-2024 Free Software Foundation, Inc. #include "sysselect.h" #include "menu.h" #include "pdumper.h" +#include "complexbkg.h" + #ifdef USE_X_TOOLKIT #include @@ -6288,7 +6290,7 @@ x_cr_destroy_fringe_bitmap (int which) fringe_bmp[which] = 0; } -static void +void x_cr_draw_image (struct frame *f, GC gc, cairo_pattern_t *image, int src_x, int src_y, int width, int height, int dest_x, int dest_y, bool overlay_p) @@ -8530,6 +8532,8 @@ x_compute_glyph_string_overhangs (struct glyph_string *s) x_clear_glyph_string_rect (struct glyph_string *s, int x, int y, int w, int h) { x_clear_rectangle (s->f, s->gc, x, y, w, h, s->hl != DRAW_CURSOR); + struct complexbkg_platform_info data = { s->f, s->gc }; + complexbkg_clear_frame_area (s->f, 0, x, y, w, h, &data); } #ifndef USE_CAIRO @@ -10713,6 +10717,10 @@ x_draw_stretch_glyph_string (struct glyph_string *s) XSetForeground (display, gc, xgcv.background); x_fill_rectangle (s->f, gc, x, y, w, h, true); XSetForeground (display, gc, xgcv.foreground); + + struct complexbkg_platform_info data = { s->f, gc }; + complexbkg_clear_frame_area (s->f, 0, + x, y, w, h, &data); } x_reset_clip_rectangles (s->f, gc); @@ -26039,6 +26047,9 @@ x_define_frame_cursor (struct frame *f, Emacs_Cursor cursor) x_clear_frame_area (struct frame *f, int x, int y, int width, int height) { x_clear_area (f, x, y, width, height); + struct complexbkg_platform_info data = { f, NULL }; + complexbkg_clear_frame_area (f, FRAME_BACKGROUND_PIXEL(f), + x, y, width, height, &data); } diff --git a/src/xterm.h b/src/xterm.h index 8d5c9917749..ca7c745b369 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -1816,6 +1816,9 @@ #define SELECTION_EVENT_TIME(eventp) \ extern void x_set_cr_source_with_gc_background (struct frame *, GC, bool); extern void x_cr_draw_frame (cairo_t *, struct frame *); extern Lisp_Object x_cr_export_frames (Lisp_Object, cairo_surface_type_t); +extern void x_cr_draw_image (struct frame *f, GC gc, cairo_pattern_t *image, + int src_x, int src_y, int width, int height, + int dest_x, int dest_y, bool overlay_p); #endif #ifdef HAVE_XFIXES -- 2.35.1.windows.2