0

I want to only display a cairo drawing inside a drawingArea, but for some reason there is always a colored rectangle shaped background around the drawing. How can I get rid of this? I especially don't want to set it only as transparent (since I might not have support for transparency), but to only show the drawing itself.

My example is in Rust using gtk-rs, but any hints in other languages are great, too!

extern crate cairo;
extern crate gio;
extern crate gtk;

use gtk::prelude::*;

fn main() {
    gtk::init();
    show_drawing();
    gtk::main();
}

fn show_drawing() {
    let window = gtk::Window::new(gtk::WindowType::Popup);
    window.set_default_size(500i32, 500i32);

    // Make window a Notification
    window.set_type_hint(gdk::WindowTypeHint::Notification);
    window.set_position(gtk::WindowPosition::Center);

    // Add drawing
    let drawing_area = Box::new(gtk::DrawingArea::new)();
    drawing_area.connect_draw(move |_, ctx| draw(ctx));
    window.add(&drawing_area);

    window.show_all();
}

fn draw(ctx: &cairo::Context) -> gtk::Inhibit {
    ctx.scale(500f64, 500f64);

    ctx.set_source_rgba(1.0, 0.2, 0.2, 1.0);
    ctx.arc(0.5, 0.5, 0.5, 0., 3.1414 * 2.);
    ctx.fill();

    Inhibit(false)
}

I want this to only show the circle, and see and keep the browser clickable around the circle. How could I achieve this?

enter image description here

Natjo
  • 2,005
  • 29
  • 75

1 Answers1

2

you may try this:

#include <gtk/gtk.h>

GtkWidget *window;
GtkWidget *drawing_area;

gboolean
on_draw (GtkWidget *widget,
         cairo_t *cr,
         gpointer user_data)
{
  guint width, height;
  GdkRGBA color;
  GtkStyleContext *context;

  context = gtk_widget_get_style_context (widget);

  width = gtk_widget_get_allocated_width (widget);
  height = gtk_widget_get_allocated_height (widget);

  gtk_render_background (context, cr, 0, 0, width, height);

  cairo_arc (cr,
             width / 2.0, height / 2.0,
             MIN (width, height) / 2.0,
             0, 2 * G_PI);

  gtk_style_context_get_color (context,
                               gtk_style_context_get_state (context),
                               &color);
  gdk_cairo_set_source_rgba (cr, &color);

  cairo_fill (cr);

  return FALSE;
}

gboolean on_window_button_press (GtkWidget *window,
                                 GdkEvent *event,
                                 gpointer user)
{
  GdkEventButton *e = (GdkEventButton*) event;

  if (e->type == GDK_BUTTON_PRESS && e->button == 1) {
    gtk_window_begin_move_drag (GTK_WINDOW(window),
                                e->button,
                                e->x_root,
                                e->y_root,
                                e->time);
    return TRUE;
  }
  return FALSE;
}

int
main (int argc, char **argv) {
  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  drawing_area = gtk_drawing_area_new ();

  gtk_widget_set_size_request (drawing_area, 300, 300);
  gtk_container_add (GTK_CONTAINER(window), drawing_area);

  g_signal_connect (drawing_area, "draw", G_CALLBACK (on_draw), NULL);
  g_signal_connect (window, "destroy", gtk_main_quit, NULL);

  /*we want no window decorations*/
  gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
  /*enable use of the alpha channel on window*/
  GdkScreen *screen = gtk_widget_get_screen (window);
  GdkVisual *rgba_visual = gdk_screen_get_rgba_visual (screen);
  gtk_widget_set_visual (window, rgba_visual);
  /*now set a transparent background*/
  GtkCssProvider *css = gtk_css_provider_new ();
  gtk_css_provider_load_from_data (css, "window { background-color: transparent; }", -1, NULL);
  gtk_style_context_add_provider (gtk_widget_get_style_context (window),
                                  GTK_STYLE_PROVIDER (css),
                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  /*or, alternatively, you can set app-paintable to skip drawing the background*/
  /*gtk_widget_set_app_paintable (window, TRUE);*/
  /*drag window around by left click*/
  gtk_widget_add_events (window, GDK_BUTTON_PRESS_MASK);
  g_signal_connect (window,
                    "button-press-event",
                    G_CALLBACK (on_window_button_press),
                    NULL);

  gtk_widget_show_all (window);
  gtk_main();

  return 0;
}

Note: on X11 this will only work with a compositing Window Manager. An alternative is to set the window shape using gdk_window_shape_combine_region (or one of the similar functions). In contrast it uses the XShape extension which is widely available.

Hope it helps! :)

lb90
  • 828
  • 4
  • 8
  • While your example looks right with a compositing manager, it still occludes the windows below. You can see them, but not select them. `gdk_window_shape_combine_region` works, but the provided region can only be a set of rectangle shapes. Is it a feasible approach to use enough rectangles to represent the circle? It wouldn't appear as a good solution to me. – Natjo Nov 03 '19 at 10:29
  • 1
    Yes, a method based on a pixmap rather than rectangles would be very useful! But I don't think that is supported right now. For the above example, on Windows the input on transparent areas automatically passes through and goes to windows beneath, that's done automatically by the OS. Instead on X11 (probably also Wayland?) that is not enforced and you should call gdk_window_set_opaque_region: https://developer.gnome.org/gdk3/stable/gdk3-Windows.html#gdk-window-set-opaque-region – lb90 Nov 05 '19 at 08:00
  • I do need a solution that does not require a composite manager unfortunately. Thank you for the link, that might be usefull for a workaround at least. gtk2 supported custom shapes by using a GdkBitmap [see gtk_widget_shape_combine_mask](https://developer.gnome.org/gtk2/stable/GtkWidget.html#gtk-widget-shape-combine-mask). GdkBitmap was dropped in gtk3, but I couldn't figure out if it was replaced by another functionality. – Natjo Nov 05 '19 at 11:34
  • Interesting, didn't know that such a function existed in gtk2! Well, here's the commit that removed it: https://gitlab.gnome.org/GNOME/gtk/commit/a7fec8cf. The commit message is very useful: it explains that you can easely get the same functionality of the removed gtk_widget_shape_combine_mask function with the help of a small utility: gdk_cairo_region_create_from_surface (https://developer.gnome.org/gdk3/stable/gdk3-Cairo-Interaction.html#gdk-cairo-region-create-from-surface) – lb90 Nov 05 '19 at 13:34
  • Also, In gtk2 gdk_window_shape_combine_mask is implemented by converting the mask (GdkBitmap) to a region (GdkRegion) and then calling shape_combine_region (https://gitlab.gnome.org/GNOME/gtk/blob/2.24.32/gdk/gdkwindow.c#L8641). GdkRegion is practically the same as cairo_region_t, they even originated from the same code, as you can see from the sources: https://gitlab.gnome.org/GNOME/gtk/blob/2.24.32/gdk/gdkregion-generic.c and https://gitlab.freedesktop.org/cairo/cairo/blob/1.16/src/cairo-region.c – lb90 Nov 05 '19 at 13:44
  • So I really think that you can stick with gtk3 and the utility gdk_cairo_region_create_from_surface. It should be ok from a performance standpoint, afterall that's the way gtk2 has worked for years. – lb90 Nov 05 '19 at 13:47
  • EDIT: when I say that GdkRegion and cairo_region_t originated from the same sources, I mean this: https://gitlab.gnome.org/GNOME/gtk/blob/2.24.32/gdk/gdkregion-generic.c and https://gitlab.freedesktop.org/pixman/pixman/blob/0.34/pixman/pixman-region.c. In fact cairo_region_t is a simple wrapper around pixman_region_t. – lb90 Nov 05 '19 at 14:00