Offscreen rendering in GTK

 

GTK provides a simple OffscreenWindow API to render content offscreen and then paint a widget with that content on demand. This is important if you want to avoid flickering or don't want to show user the content till it's fully ready.

The "draw" event handler of gtk_drawing_area_new widget widget draws webview widget on the cairo surface passed to the handler using gtk_widget_draw (GTK_WIDGET (webView), cr) . Keyboard, mouse and scroll events are copied and forwarded to the webview widget. We can listen to WebKitWebView's "load-changed" event for blocklist managment.


./browser "http://www.google.com"



browser.c

#include <gtk/gtkx.h>
#include <webkit2/webkit2.h>

static gboolean
refresh_widget (GtkWidget *widget, GdkEvent *event,
                        gpointer xv_widget)
{
  if (GTK_IS_WIDGET (xv_widget))
    gtk_widget_queue_draw (GTK_WIDGET (xv_widget));

  return FALSE;
}

static gboolean
xwidget_osr_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data)
{
  GtkWidget *webView = g_object_get_data (G_OBJECT (widget), "web");
  gtk_widget_draw (GTK_WIDGET (webView), cr);
  return FALSE;
}

static gboolean
xwidget_osr_event_forward (GtkWidget *widget, GdkEvent *event,
                           gpointer data)
{
  /* Copy events that arrive at the outer widget to the offscreen widget.  */
  GdkEvent *eventcopy = gdk_event_copy (event);
  eventcopy->any.window = gtk_widget_get_window (GTK_WIDGET (data));

  /* TODO: This might leak events.  They should be deallocated later,
     perhaps in xwgir_event_cb.  */
  gtk_main_do_event (eventcopy);

  /* Don't propagate this event further.  */
  return TRUE;
}

int main(int argc, char* argv[])
{
  // Initialize GTK+
  gtk_init(&argc, &argv);

  // Create an 800x600 visible window
  GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  GtkWidget *vbox = gtk_box_new(FALSE, 0);
  GtkWidget *draw = gtk_drawing_area_new ();
 
  gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
  gtk_box_pack_start(GTK_BOX(vbox), draw, TRUE,  TRUE, 0);
  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox));

  g_signal_connect(window, "delete-event", gtk_main_quit, NULL);
  gtk_widget_set_can_focus(draw, TRUE);
  gtk_widget_grab_focus(draw);
  gtk_widget_add_events (draw, GDK_ALL_EVENTS_MASK);
  gtk_widget_show_all(window);

  // Create an 800x600 offscreen window that will contain the browser instance
  GtkWidget *window_osr = gtk_offscreen_window_new();

  // Create a browser instance
  WebKitWebView *webView = WEBKIT_WEB_VIEW(webkit_web_view_new());

  gtk_window_set_default_size(GTK_WINDOW(window_osr), 800, 600);
  gtk_container_add(GTK_CONTAINER(window_osr), GTK_WIDGET(webView));

  // Draw signals
  g_signal_connect(window_osr, "draw", G_CALLBACK(refresh_widget), draw);
  g_signal_connect(draw, "draw", G_CALLBACK(xwidget_osr_draw_cb), NULL);

  // Interaction signals
  g_signal_connect(draw, "button-press-event", G_CALLBACK(xwidget_osr_event_forward), webView);
  g_signal_connect(draw, "button-release-event", G_CALLBACK(xwidget_osr_event_forward), webView);
  g_signal_connect(draw, "key-press-event", G_CALLBACK(xwidget_osr_event_forward), webView);
  g_signal_connect(draw, "key-release-event", G_CALLBACK(xwidget_osr_event_forward), webView);
  g_signal_connect(draw, "scroll-event", G_CALLBACK(xwidget_osr_event_forward), webView);
      
  // Make webView available to draw widget
  g_object_set_data (G_OBJECT (draw), "web", webView);

  // Make sure the main window and all its contents are visible
  gtk_widget_show_all(window_osr);

  // Load a web page into the browser instance
  webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webView), argv[1]);

  // Run the main GTK+ event loop
  gtk_main();

  return 0;
}

 
 
Compile the file using following command to generate executable browser.
 
gcc browser.c $(pkg-config --libs --cflags gtk+-3.0 webkit2gtk-4.0) -o browser


Comments

Popular posts from this blog

GNU Emacs as a Comic Book Reader

Tinylisp and Multi-threaded Emacs

Data Visualization with GNU Emacs