[vte] widget: Add API to set the operator used to draw the terminal background



commit 91d30f8f678afedd5f8eedbb95b64ec252abb7bd
Author: Christian Persch <chpe src gnome org>
Date:   Tue Oct 24 20:33:17 2017 +0200

    widget: Add API to set the operator used to draw the terminal background
    
    This is useful only to support a background image.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=767575

 configure.ac                   |    2 +
 doc/reference/vte-sections.txt |    1 +
 src/app/app.cc                 |  179 +++++++++++++++++++++++++++++++++++++---
 src/vte.cc                     |    8 ++
 src/vte/vteterminal.h          |    5 +
 src/vtedraw.cc                 |    3 +-
 src/vtedraw.hh                 |    1 +
 src/vtegtk.cc                  |   26 ++++++
 src/vteinternal.hh             |    2 +
 9 files changed, 213 insertions(+), 14 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 3139c96..ad541ae 100644
--- a/configure.ac
+++ b/configure.ac
@@ -317,7 +317,9 @@ PKG_CHECK_MODULES([GTK],[gtk+-$GTK_API_VERSION >= $GTK_REQUIRED])
 PKG_CHECK_MODULES([APP],[
   glib-2.0 >= $GLIB_REQUIRED
   gobject-2.0
+  cairo-gobject
   pango >= $PANGO_REQUIRED
+  gdk-pixbuf-2.0
   gtk+-$GTK_API_VERSION >= $GTK_REQUIRED])
 
 ################################################################################
diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt
index 3561bdb..0b6cd47 100644
--- a/doc/reference/vte-sections.txt
+++ b/doc/reference/vte-sections.txt
@@ -98,6 +98,7 @@ vte_terminal_pty_new_sync
 vte_terminal_watch_child
 
 <SUBSECTION>
+vte_terminal_set_background_operator
 
 <SUBSECTION Standard>
 VTE_TYPE_CURSOR_BLINK_MODE
diff --git a/src/app/app.cc b/src/app/app.cc
index 4446592..ad4e5bf 100644
--- a/src/app/app.cc
+++ b/src/app/app.cc
@@ -26,10 +26,14 @@
 #include <glib.h>
 #include <glib/gprintf.h>
 #include <glib/gi18n.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gtk/gtk.h>
+#include <cairo/cairo-gobject.h>
 #include <vte/vte.h>
 #include "vtepcre2.h"
 
+#include <algorithm>
+
 /* options */
 
 class Options {
@@ -53,10 +57,15 @@ public:
         gboolean use_gregex{false};
         gboolean version{false};
         gboolean whole_window_transparent{false};
+        bool bg_color_set{false};
+        bool fg_color_set{false};
         bool cursor_bg_color_set{false};
         bool cursor_fg_color_set{false};
         bool hl_bg_color_set{false};
         bool hl_fg_color_set{false};
+        bool background_operator_set{false};
+        cairo_extend_t background_extend{CAIRO_EXTEND_NONE};
+        cairo_operator_t background_operator{CAIRO_OPERATOR_SOURCE};
         char* command{nullptr};
         char* encoding{nullptr};
         char* font_string{nullptr};
@@ -67,6 +76,9 @@ public:
         char** dingus{nullptr};
         char** exec_argv{nullptr};
         char** environment{nullptr};
+        GdkPixbuf* background_pixbuf{nullptr};
+        GdkRGBA bg_color{1.0, 1.0, 1.0, 1.0};
+        GdkRGBA fg_color{0.0, 0.0, 0.0, 1.0};
         GdkRGBA cursor_bg_color{};
         GdkRGBA cursor_fg_color{};
         GdkRGBA hl_bg_color{};
@@ -80,6 +92,7 @@ public:
         VteCursorShape cursor_shape{VTE_CURSOR_SHAPE_BLOCK};
 
         ~Options() {
+                g_clear_object(&background_pixbuf);
                 g_free(command);
                 g_free(encoding);
                 g_free(font_string);
@@ -96,7 +109,7 @@ private:
 
         bool parse_enum(GType type,
                         char const* str,
-                        int* value,
+                        int& value,
                         GError** error)
         {
                 GEnumClass* enum_klass = reinterpret_cast<GEnumClass*>(g_type_class_ref(type));
@@ -110,7 +123,7 @@ private:
                         return false;
                 }
 
-                *value = enum_value->value;
+                value = enum_value->value;
                 g_type_class_unref(enum_klass);
                 return true;
         }
@@ -161,6 +174,39 @@ private:
         }
 
         static gboolean
+        parse_background_image(char const* option, char const* value, void* data, GError** error)
+        {
+                Options* that = static_cast<Options*>(data);
+                g_clear_object(&that->background_pixbuf);
+                that->background_pixbuf = gdk_pixbuf_new_from_file(value, error);
+                return that->background_pixbuf != nullptr;
+        }
+
+        static gboolean
+        parse_background_extend(char const* option, char const* value, void* data, GError** error)
+        {
+                Options* that = static_cast<Options*>(data);
+                int v;
+                auto rv = that->parse_enum(CAIRO_GOBJECT_TYPE_EXTEND, value, v, error);
+                if (rv)
+                        that->background_extend = cairo_extend_t(v);
+                return rv;
+        }
+
+        static gboolean
+        parse_background_operator(char const* option, char const* value, void* data, GError** error)
+        {
+                Options* that = static_cast<Options*>(data);
+                int v;
+                auto rv = that->parse_enum(CAIRO_GOBJECT_TYPE_OPERATOR, value, v, error);
+                if (rv) {
+                        that->background_operator = cairo_operator_t(v);
+                        that->background_operator_set = true;
+                }
+                return rv;
+        }
+
+        static gboolean
         parse_cjk_width(char const* option, char const* value, void* data, GError** error)
         {
                 Options* that = static_cast<Options*>(data);
@@ -172,7 +218,7 @@ private:
         {
                 Options* that = static_cast<Options*>(data);
                 int v;
-                auto rv = that->parse_enum(VTE_TYPE_CURSOR_BLINK_MODE, value, &v, error);
+                auto rv = that->parse_enum(VTE_TYPE_CURSOR_BLINK_MODE, value, v, error);
                 if (rv)
                         that->cursor_blink_mode = VteCursorBlinkMode(v);
                 return rv;
@@ -183,13 +229,29 @@ private:
         {
                 Options* that = static_cast<Options*>(data);
                 int v;
-                auto rv = that->parse_enum(VTE_TYPE_CURSOR_SHAPE, value, &v, error);
+                auto rv = that->parse_enum(VTE_TYPE_CURSOR_SHAPE, value, v, error);
                 if (rv)
                         that->cursor_shape = VteCursorShape(v);
                 return rv;
         }
 
         static gboolean
+        parse_bg_color(char const* option, char const* value, void* data, GError** error)
+        {
+                Options* that = static_cast<Options*>(data);
+                bool set;
+                return that->parse_color(value, &that->bg_color, &set, error);
+        }
+
+        static gboolean
+        parse_fg_color(char const* option, char const* value, void* data, GError** error)
+        {
+                Options* that = static_cast<Options*>(data);
+                bool set;
+                return that->parse_color(value, &that->fg_color, &set, error);
+        }
+
+        static gboolean
         parse_cursor_bg_color(char const* option, char const* value, void* data, GError** error)
         {
                 Options* that = static_cast<Options*>(data);
@@ -234,20 +296,22 @@ public:
 
         GdkRGBA get_color_bg() const
         {
-                double alpha = whole_window_transparent ? 1.0d : get_alpha();
-
-                if (reverse)
-                        return GdkRGBA{1.0, 1.0, 1.0, alpha};
+                double alpha;
+                if (background_pixbuf != nullptr)
+                        alpha = 0.0;
+                else if (whole_window_transparent)
+                        alpha = 1.0;
                 else
-                        return GdkRGBA{0.0, 0.0, 0.0, alpha};
+                        alpha = get_alpha();
+
+                GdkRGBA color{bg_color};
+                color.alpha = alpha;
+                return color;
         }
 
         GdkRGBA get_color_fg() const
         {
-                if (reverse)
-                        return GdkRGBA{0.0, 0.0, 0.0, 1.0};
-                else
-                        return GdkRGBA{1.0, 1.0, 1.0, 1.0};
+                return fg_color;
         }
 
         bool parse_argv(int argc,
@@ -261,6 +325,14 @@ public:
                           "Allow window operations (resize, move, raise/lower, (de)iconify)", nullptr },
                         { "audible-bell", 'a', 0, G_OPTION_ARG_NONE, &audible_bell,
                           "Use audible terminal bell", nullptr },
+                        { "background-color", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_bg_color,
+                          "Set default background color", nullptr },
+                        { "background-image", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_background_image,
+                          "Set background image from file", "FILE" },
+                        { "background-extend", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_background_extend,
+                          "Set background image extend", "EXTEND" },
+                        { "background-operator", 0, 0, G_OPTION_ARG_CALLBACK, 
(void*)parse_background_operator,
+                          "Set background draw operator", "OPERATOR" },
                         { "cjk-width", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_cjk_width,
                           "Specify the cjk ambiguous width to use for UTF-8 encoding", "NARROW|WIDE" },
                         { "cursor-blink", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_cursor_blink,
@@ -283,6 +355,8 @@ public:
                           "Add extra margin around the terminal widget", "MARGIN" },
                         { "font", 'f', 0, G_OPTION_ARG_STRING, &font_string,
                           "Specify a font to use", nullptr },
+                        { "foreground-color", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_fg_color,
+                          "Set default foreground color", nullptr },
                         { "gregex", 0, 0, G_OPTION_ARG_NONE, &use_gregex,
                           "Use GRegex instead of PCRE2", nullptr },
                         { "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry,
@@ -383,6 +457,15 @@ public:
 
                 g_option_context_free(context);
                 g_free(dummy_string);
+
+                if (reverse)
+                        std::swap(fg_color, bg_color);
+
+                /* Sanity checks */
+                if (background_pixbuf != nullptr &&
+                    (!background_operator_set || background_operator == CAIRO_OPERATOR_SOURCE))
+                        g_printerr("Background image set but operator is SOURCE; image will not appear.\n");
+
                 return rv;
         }
 };
@@ -721,6 +804,8 @@ typedef struct _VteappTerminalClass  VteappTerminalClass;
 
 struct _VteappTerminal {
         VteTerminal parent;
+
+        cairo_pattern_t* background_pattern;
 };
 
 struct _VteappTerminalClass {
@@ -732,13 +817,81 @@ static GType vteapp_terminal_get_type(void);
 G_DEFINE_TYPE(VteappTerminal, vteapp_terminal, VTE_TYPE_TERMINAL)
 
 static void
+vteapp_terminal_realize(GtkWidget* widget)
+{
+        GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->realize(widget);
+
+        VteappTerminal* terminal = VTEAPP_TERMINAL(widget);
+        if (options.background_pixbuf != nullptr) {
+                auto surface = gdk_cairo_surface_create_from_pixbuf(options.background_pixbuf,
+                                                                    0 /* take scale from window */,
+                                                                    gtk_widget_get_window(widget));
+                terminal->background_pattern = cairo_pattern_create_for_surface(surface);
+                cairo_surface_destroy(surface);
+
+                cairo_pattern_set_extend(terminal->background_pattern, options.background_extend);
+        }
+}
+
+static void
+vteapp_terminal_unrealize(GtkWidget* widget)
+{
+        VteappTerminal* terminal = VTEAPP_TERMINAL(widget);
+        if (terminal->background_pattern != nullptr) {
+                cairo_pattern_destroy(terminal->background_pattern);
+                terminal->background_pattern = nullptr;
+        }
+
+        GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->unrealize(widget);
+}
+
+static gboolean
+vteapp_terminal_draw(GtkWidget* widget,
+                     cairo_t* cr)
+{
+        VteappTerminal* terminal = VTEAPP_TERMINAL(widget);
+        if (terminal->background_pattern != nullptr) {
+                cairo_push_group(cr);
+
+                /* Draw background colour */
+                cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+                cairo_rectangle(cr, 0.0, 0.0,
+                                gtk_widget_get_allocated_width(widget),
+                                gtk_widget_get_allocated_height(widget));
+                auto bg = options.get_color_bg();
+                cairo_set_source_rgba(cr, bg.red, bg.green, bg.blue, 1.0);
+                cairo_paint(cr);
+
+                /* Draw background image */
+                cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+                cairo_set_source(cr, terminal->background_pattern);
+                cairo_paint(cr);
+
+                cairo_pop_group_to_source(cr);
+                cairo_paint_with_alpha(cr, options.get_alpha());
+
+        }
+
+        return GTK_WIDGET_CLASS(vteapp_terminal_parent_class)->draw(widget, cr);
+}
+
+static void
 vteapp_terminal_class_init(VteappTerminalClass *klass)
 {
+        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+        widget_class->realize = vteapp_terminal_realize;
+        widget_class->unrealize = vteapp_terminal_unrealize;
+        widget_class->draw = vteapp_terminal_draw;
 }
 
 static void
 vteapp_terminal_init(VteappTerminal *terminal)
 {
+        terminal->background_pattern = nullptr;
+
+        if (options.background_operator_set)
+                vte_terminal_set_background_operator(VTE_TERMINAL(terminal),
+                                                     options.background_operator);
 }
 
 static GtkWidget *
diff --git a/src/vte.cc b/src/vte.cc
index 0371d70..6625f11 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -9723,6 +9723,7 @@ VteTerminalPrivate::paint_im_preedit_string()
                                row_to_pixel(m_screen->cursor.row),
                                width * columns,
                                height,
+                                m_background_operator,
                                 get_color(VTE_DEFAULT_BG), m_background_alpha);
                 fore = m_color_defaults.attr.fore;
                 back = m_color_defaults.attr.back;
@@ -9779,6 +9780,7 @@ VteTerminalPrivate::widget_draw(cairo_t *cr)
 
        _vte_draw_clear (m_draw, 0, 0,
                         allocated_width, allocated_height,
+                         m_background_operator,
                          get_color(VTE_DEFAULT_BG), m_background_alpha);
 
         /* Clip vertically, for the sake of smooth scrolling. We want the top and bottom paddings to be 
unused.
@@ -11428,3 +11430,9 @@ VteTerminalPrivate::set_word_char_exceptions(char const* exceptions)
 
         return true;
 }
+
+void
+VteTerminalPrivate::set_background_operator(cairo_operator_t op)
+{
+        m_background_operator = op;
+}
diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h
index 1d4a81f..2cb3024 100644
--- a/src/vte/vteterminal.h
+++ b/src/vte/vteterminal.h
@@ -458,6 +458,11 @@ void vte_terminal_set_input_enabled (VteTerminal *terminal,
 _VTE_PUBLIC
 gboolean vte_terminal_get_input_enabled (VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
 
+/* rarely useful functions */
+_VTE_PUBLIC
+void vte_terminal_set_background_operator(VteTerminal* terminal,
+                                          cairo_operator_t op) _VTE_GNUC_NONNULL(1);
+
 /* Writing contents out */
 _VTE_PUBLIC
 gboolean vte_terminal_write_contents_sync (VteTerminal *terminal,
diff --git a/src/vtedraw.cc b/src/vtedraw.cc
index bf53a7d..c713d44 100644
--- a/src/vtedraw.cc
+++ b/src/vtedraw.cc
@@ -820,6 +820,7 @@ _vte_draw_set_source_color_alpha (struct _vte_draw *draw,
 
 void
 _vte_draw_clear (struct _vte_draw *draw, gint x, gint y, gint width, gint height,
+                 cairo_operator_t op,
                  vte::color::rgb const* color, double alpha)
 {
        _vte_debug_print (VTE_DEBUG_DRAW, "draw_clear (%d, %d, %d, %d)\n",
@@ -827,7 +828,7 @@ _vte_draw_clear (struct _vte_draw *draw, gint x, gint y, gint width, gint height
 
         g_assert(draw->cr);
        cairo_rectangle (draw->cr, x, y, width, height);
-       cairo_set_operator (draw->cr, CAIRO_OPERATOR_SOURCE);
+       cairo_set_operator (draw->cr, op);
        _vte_draw_set_source_color_alpha(draw, color, alpha);
        cairo_fill (draw->cr);
 }
diff --git a/src/vtedraw.hh b/src/vtedraw.hh
index cc17a05..c45cb53 100644
--- a/src/vtedraw.hh
+++ b/src/vtedraw.hh
@@ -58,6 +58,7 @@ void _vte_draw_set_cairo(struct _vte_draw *draw,
 
 void _vte_draw_clear(struct _vte_draw *draw,
                     gint x, gint y, gint width, gint height,
+                     cairo_operator_t op,
                      vte::color::rgb const* color, double alpha);
 
 void _vte_draw_set_text_font(struct _vte_draw *draw,
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index 3dbbc73..3f6ba35 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -4004,3 +4004,29 @@ vte_terminal_write_contents_sync (VteTerminal *terminal,
 
         return IMPL(terminal)->write_contents_sync(stream, flags, cancellable, error);
 }
+
+/**
+ * vte_terminal_set_background_operator:
+ * @terminal: a #VteTerminal
+ * @operator: a #cairo_operator_t
+ *
+ * Sets the operator to use to paint the background with the background colour.
+ * The default is %CAIRO_OPERATOR_SOURCE.
+ *
+ * This function is rarely useful. One use for it is to add a background
+ * image to the terminal, in which case %CAIRO_OPERATOR_OVER is most likely
+ * the correct operator to use.
+ *
+ * If the operator is changed while the widget is already shown,
+ * you need to queue a redraw to get it applied.
+ *
+ * Since: 0.52
+ */
+void
+vte_terminal_set_background_operator(VteTerminal* terminal,
+                                     cairo_operator_t op)
+{
+        g_return_if_fail(VTE_IS_TERMINAL(terminal));
+
+        IMPL(terminal)->set_background_operator(op);
+}
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 0cd686b..a275f2c 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -469,6 +469,7 @@ public:
         * and data, which should be dropped when unrealizing and (re)created
         * when realizing. */
         struct _vte_draw *m_draw;
+        cairo_operator_t m_background_operator{CAIRO_OPERATOR_SOURCE};
 
         VtePaletteColor m_palette[VTE_PALETTE_SIZE];
 
@@ -1111,6 +1112,7 @@ public:
         bool set_scroll_on_keystroke(bool scroll);
         bool set_scroll_on_output(bool scroll);
         bool set_word_char_exceptions(char const* exceptions);
+        void set_background_operator(cairo_operator_t op);
 
         bool write_contents_sync (GOutputStream *stream,
                                   VteWriteFlags flags,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]