Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

glue-gui-gtk-monitor.c

/*
 * $Id: glue-gui-gtk-monitor.c,v 1.26 2009-02-11 12:02:02 potyra Exp $
 *
 * Copyright (C) 2003-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include "config.h"

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>

#include <gtk/gtk.h>
#include <glib.h>

#include "rd_types.h"
#include "umutil.h"
#include "glue-gui-gtk.h"
#include "glue-main.h"
#include "glue-png.h"
#include "../node-pc/gui-gtk.h" /* FIXME */

#include "glue-gui-gtk-monitor.h"


static struct gui_gtk_monitor_screen_size sizes_4_3[] = {
      {  320,  240 },
      {  640,  480 },
      {  800,  600 },
      { 1024,  768 }, 
      { 1152,  864 },
      { 1600, 1200 },
      { 2048, 1536 },
      {    0,    0 }
};

static struct gui_gtk_monitor_screen_size sizes_16_10[] = {
      {  320,  200 },
      {  640,  400 },
      {  960,  600 },
      { 1280,  800 },
      { 1680, 1050 },
      { 1920, 1200 },
      {    0,    0 }
};

static struct gui_gtk_monitor_screen_size sizes_5_4[] = {
      {  320,  256 },
      {  640,  512 },
      { 1280, 1024 },
      {    0,    0 }
};

typedef struct {
      GtkHBoxClass parent_class;
} GuiGtkMonitorClass;

static void
gui_gtk_monitor_class_init(GuiGtkMonitorClass *class)
{
}

static void
rec_monitor_set(GuiGtkMonitor *monitor)
{
      unsigned char id = RD_MON_SET_ID;
      struct rd_mon_set_data data;

      if (monitor->fp) {
            data.x = monitor->true_width;
            data.y = monitor->true_height;

            fwrite((void*) &id, sizeof(id), 1, monitor->fp);
            fwrite((void*) &data, sizeof(data), 1, monitor->fp);
      }
}

static void
rec_screenshot(GuiGtkMonitor *monitor, int nr)
{
      char path[PATH_MAX];
      int ret;

      if (nr < 0) {
            /* don't overwrite screenshot, search for free entry */
            static int auto_nr = 0;

            nr = auto_nr; /* So we can recognize if we're looping because
                         there are no more free filenames */
            do {
                  auto_nr = (auto_nr + 1) % 1000;
                  ret = snprintf(path, sizeof(path), 
                        "%s/screenshot-%03d.png", basedir, auto_nr);
                  assert(ret < sizeof(path));

                  if (access(path, F_OK) != 0) {
                        break;
                  }
            } while (nr != auto_nr);
            nr = auto_nr;
      }

      ret = snprintf(path, sizeof(path), 
                  "%s/screenshot-%03d.png", basedir, nr);
      assert(ret < sizeof(path));

      png_write(
            (uint32_t *) monitor->screen_data,
            monitor->monitor_width, monitor->monitor_height,
            0, 0, monitor->true_width, monitor->true_height,
            path);
}

static void
gui_gtk_monitor_screenshot_button_event(GtkWidget *w, gpointer _monitor)
{
      GuiGtkMonitor *monitor = (GuiGtkMonitor *) _monitor;

      rec_screenshot(monitor, -1);
}

static void
gui_gtk_monitor_record_button_event(GtkWidget *w, gpointer _monitor)
{
      GuiGtkMonitor *monitor = (GuiGtkMonitor *) _monitor;

      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
            static const uint16_t id = RD_MAGIC_ID;
            static const unsigned char all_id = RD_MON_ALL_ID;
            static int auto_nr = 0;
            struct rd_mon_init_data init_data;
            char path[PATH_MAX];
            unsigned int nr;
            int ret;

            assert(! monitor->fp);

            /* Don't overwrite records, search for free entry. */

            nr = auto_nr; /* So we can recognize if we're looping because
                         there are no more free filenames */
            do {
                  auto_nr = (auto_nr + 1) % 1000;
                  ret = snprintf(path, sizeof(path), 
                              "%s/record-%03d", basedir, auto_nr);
                  assert(ret < sizeof(path));
                  if (access(path, F_OK) != 0) {
                        break;
                  }
            } while (nr != auto_nr);
            nr = auto_nr;

            ret = snprintf(path, sizeof(path), 
                        "%s/record-%03d", basedir, nr);
            assert(ret < sizeof(path));

            monitor->fp = fopen(path, "w");
            assert(monitor->fp);

            fwrite(&id, sizeof(id), 1, monitor->fp);

            rec_monitor_set(monitor);

            init_data.t = time_virt();
            init_data.time_hz = TIME_HZ;
            init_data.max_width = monitor->monitor_width;
            init_data.max_height = monitor->monitor_height;
            fwrite((const void *) &all_id, sizeof(all_id), 1, monitor->fp);
            fwrite((void *) &init_data, sizeof(init_data), 1, monitor->fp);
            fwrite((const void *) monitor->screen_data,
                        monitor->monitor_width * monitor->monitor_height * 4,
                        1, monitor->fp);

      } else {
            assert(monitor->fp);

            assert(! ferror(monitor->fp));
            fclose(monitor->fp);
            monitor->fp = NULL;
      }
}

static gboolean
gui_gtk_monitor_expose_event
(
      GtkWidget *w,
      GdkEventExpose *event,
      gpointer _monitor
)
{
      GuiGtkMonitor *monitor = (GuiGtkMonitor *) _monitor;

      unsigned int xt;
      unsigned int x_start;
      unsigned int yt;
      unsigned int x_end;
      unsigned int y_end;

      x_end = event->area.x + event->area.width;
      y_end = event->area.y + event->area.height;

      if (monitor->zoom_steps[monitor->zoom_step].width < x_end) {
            x_end = monitor->zoom_steps[monitor->zoom_step].width;
      }

      if (monitor->zoom_steps[monitor->zoom_step].height < y_end) {
            y_end = monitor->zoom_steps[monitor->zoom_step].height;
      }

      x_end = (x_end - 1) / monitor->dest_tile_width;
      y_end = (y_end - 1) / monitor->dest_tile_height;

      x_start = event->area.x / monitor->dest_tile_width;

      for (yt = event->area.y / monitor->dest_tile_height; yt <= y_end; yt++) {
            for (xt = x_start; xt <= x_end; xt++) {
                  monitor->dirty[yt * monitor->tiles_row_stride + xt] = 1;
            }
      }

      return TRUE;
}

static unsigned int
calc_tile_count(unsigned int s, unsigned int d, unsigned int min)
{
      unsigned int dd;
      unsigned int div;
      unsigned int r;

      /* first: div = ggT(s,d) */

      dd  = s;
      div = d;

      while ((r = dd % div) != 0) {
            dd = div;
            div = r;
      }

      /* next: divide by 2 until it gets less than min
       *       or set to min if it is already smaller */

      if (div < min) {
            div = min;
      } else {
            while (!(div % 2) && (min < div)) {
                  div = div / 2;
            }
      }

      return div;
}

static void
gui_gtk_monitor_recalc_scaling(GuiGtkMonitor *monitor)
{
      unsigned int tiles_x;
      unsigned int tiles_y;
      unsigned int x, y;

      tiles_x = calc_tile_count(monitor->true_width,
                        monitor->zoom_steps[monitor->zoom_step].width,
                        monitor->min_tiles_x);
      tiles_y = calc_tile_count(monitor->true_height,
                        monitor->zoom_steps[monitor->zoom_step].height,
                        monitor->min_tiles_y);

      monitor->dest_tile_width =
            monitor->zoom_steps[monitor->zoom_step].width
            / tiles_x;

      monitor->dest_tile_height =
            monitor->zoom_steps[monitor->zoom_step].height
            / tiles_y;

      monitor->src_tile_width = monitor->true_width / tiles_x;
      if (monitor->true_width % tiles_x) {
            monitor->src_tile_width++;
      }

      monitor->src_tile_height = monitor->true_height / tiles_y;
      if (monitor->true_height % tiles_y) {
            monitor->src_tile_height++;
      }

      monitor->screen_width = monitor->src_tile_width * tiles_x;
      monitor->screen_height = monitor->src_tile_height * tiles_y;

      for (y = 0; y < tiles_y; y++) {
            for (x = 0; x < tiles_x; x++) {
                  monitor->dirty[y * monitor->tiles_row_stride + x] = 1;
            }
      }
}

static void
gui_gtk_monitor_check_button_sensitivity(GuiGtkMonitor *monitor)
{
      unsigned int next_width
            = monitor->zoom_steps[monitor->zoom_step+1].width;

      if (next_width == 0 || (monitor->monitor_width < next_width)) {
            gtk_widget_set_sensitive(monitor->zoom_in_button, false);
      } else {
            gtk_widget_set_sensitive(monitor->zoom_in_button, true);
      }

      if (monitor->zoom_step == 0) {
            gtk_widget_set_sensitive(monitor->zoom_out_button, false);
      } else {
            gtk_widget_set_sensitive(monitor->zoom_out_button, true);
      }
}

static void
gui_gtk_monitor_zoom_in_button_event(GtkWidget *w, gpointer _monitor)
{
      GuiGtkMonitor *monitor = (GuiGtkMonitor *) _monitor;

      monitor->zoom_step++;

      gui_gtk_monitor_recalc_scaling(monitor);

      gtk_widget_set_size_request(monitor->screen,
                  monitor->zoom_steps[monitor->zoom_step].width,
                  monitor->zoom_steps[monitor->zoom_step].height);
      /* Set minimum size 1/1 to allow user to resize window. */
      gtk_widget_set_size_request(monitor->scrollwindow, 1, 1);

      gui_gtk_monitor_check_button_sensitivity(monitor);
      gui_gtk_flush();
}

static void
gui_gtk_monitor_zoom_out_button_event(GtkWidget *w, gpointer _monitor)
{
      GuiGtkMonitor *monitor = (GuiGtkMonitor *) _monitor;

      monitor->zoom_step--;

      gui_gtk_monitor_recalc_scaling(monitor);

      gtk_widget_set_size_request(monitor->screen,
                  monitor->zoom_steps[monitor->zoom_step].width,
                  monitor->zoom_steps[monitor->zoom_step].height);
      /* Set minimum size 1/1 to allow user to resize window. */
      gtk_widget_set_size_request(monitor->scrollwindow, 1, 1);

      gui_gtk_monitor_check_button_sensitivity(monitor);
      gui_gtk_flush();
}

static void
gui_gtk_monitor_init(GuiGtkMonitor *monitor)
{
      GtkWidget *vbox;
      const char *path;
      GtkWidget *icon;

      /*
       * Button Area
       */
      vbox = gtk_vbox_new(FALSE, 1);

      /* "Screenshot" Button */
      path = buildpath(PNGDIR, "camera.png");
      icon = gtk_image_new_from_file(path);
      monitor->screenshot_button = gtk_button_new_with_label("Screenshot");
      GTK_WIDGET_UNSET_FLAGS(monitor->screenshot_button, GTK_CAN_FOCUS);
      gtk_button_set_image(GTK_BUTTON(monitor->screenshot_button), icon);
      g_signal_connect(G_OBJECT(monitor->screenshot_button), "clicked",
                  G_CALLBACK(gui_gtk_monitor_screenshot_button_event),
                  monitor);

      gtk_widget_show(monitor->screenshot_button);
      gtk_box_pack_start(GTK_BOX(vbox), monitor->screenshot_button,
                  FALSE, FALSE, 1);

      /* "Record" Button */
      path = buildpath(PNGDIR, "video.png");
      icon = gtk_image_new_from_file(path);
      monitor->record_button = gtk_toggle_button_new_with_label("Record");
      GTK_WIDGET_UNSET_FLAGS(monitor->record_button, GTK_CAN_FOCUS);
      gtk_button_set_image(GTK_BUTTON(monitor->record_button), icon);
      g_signal_connect(G_OBJECT(monitor->record_button), "toggled",
                  G_CALLBACK(gui_gtk_monitor_record_button_event),
                  monitor);

      gtk_widget_show(monitor->record_button);
      gtk_box_pack_start(GTK_BOX(vbox), monitor->record_button,
                  FALSE, FALSE, 1);

      /* "Zoom In" Button */
      monitor->zoom_in_button = gtk_button_new_with_label("Zoom In");
      GTK_WIDGET_UNSET_FLAGS(monitor->zoom_in_button, GTK_CAN_FOCUS);
      gtk_widget_set_sensitive(monitor->zoom_in_button, false);
      icon = gtk_image_new_from_stock("gtk-zoom-in", GTK_ICON_SIZE_BUTTON);
      gtk_button_set_image(GTK_BUTTON(monitor->zoom_in_button), icon);
      g_signal_connect(G_OBJECT(monitor->zoom_in_button), "clicked",
                  G_CALLBACK(gui_gtk_monitor_zoom_in_button_event),
                  monitor);

      gtk_widget_show(monitor->zoom_in_button);
      gtk_box_pack_start(GTK_BOX(vbox), monitor->zoom_in_button,
                  FALSE, FALSE, 1);

      /* "Zoom Out" Button */
      monitor->zoom_out_button = gtk_button_new_with_label("Zoom Out");
      GTK_WIDGET_UNSET_FLAGS(monitor->zoom_out_button, GTK_CAN_FOCUS);
      gtk_widget_set_sensitive(monitor->zoom_out_button, false);
      icon = gtk_image_new_from_stock("gtk-zoom-out", GTK_ICON_SIZE_BUTTON);
      gtk_button_set_image(GTK_BUTTON(monitor->zoom_out_button), icon);
      g_signal_connect(G_OBJECT(monitor->zoom_out_button), "clicked",
                  G_CALLBACK(gui_gtk_monitor_zoom_out_button_event),
                  monitor);

      gtk_widget_show(monitor->zoom_out_button);
      gtk_box_pack_start(GTK_BOX(vbox), monitor->zoom_out_button,
                  FALSE, FALSE, 1);

      gtk_widget_show(vbox);
      gtk_box_pack_start(GTK_BOX(monitor), vbox,
                  FALSE, FALSE, 1);

      /*
       * Screen Area
       */
      monitor->scrollwindow = gtk_scrolled_window_new(NULL, NULL);
      gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(monitor->scrollwindow),
                  GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);

      monitor->eventbox = gtk_event_box_new();
      GTK_WIDGET_SET_FLAGS(monitor->eventbox, GTK_CAN_FOCUS);
      GTK_WIDGET_SET_FLAGS(monitor->eventbox, GTK_HAS_GRAB);
      GTK_WIDGET_SET_FLAGS(monitor->eventbox, GTK_CAN_DEFAULT);
      GTK_WIDGET_SET_FLAGS(monitor->eventbox, GTK_RECEIVES_DEFAULT);
      gtk_widget_add_events(GTK_WIDGET(monitor->eventbox),
                  GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
                  | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
                  | GDK_SCROLL_MASK);
      gtk_widget_set_sensitive(GTK_WIDGET(monitor->eventbox), TRUE);
      g_signal_connect(monitor->eventbox, "key-press-event",
                  G_CALLBACK(key_press_event), NULL);
      g_signal_connect(monitor->eventbox, "key-release-event",
                  G_CALLBACK(key_release_event), NULL);
      g_signal_connect(monitor->eventbox, "motion-notify-event",
                  G_CALLBACK(pointer_motion_event), NULL);
      g_signal_connect(monitor->eventbox, "button-press-event",
                  G_CALLBACK(pointer_press_event), NULL);
      g_signal_connect(monitor->eventbox, "button-release-event",
                  G_CALLBACK(pointer_press_event), NULL);
      g_signal_connect(monitor->eventbox, "scroll-event",
                  G_CALLBACK(pointer_scroll_event), NULL);

      gtk_widget_show(monitor->eventbox);
      gtk_scrolled_window_add_with_viewport(
                  GTK_SCROLLED_WINDOW(monitor->scrollwindow),
                  GTK_WIDGET(monitor->eventbox));

      monitor->screen = gtk_drawing_area_new();
      GTK_WIDGET_SET_FLAGS(monitor->screen, GTK_CAN_FOCUS);
      g_signal_connect(monitor->screen, "expose-event",
                  G_CALLBACK(gui_gtk_monitor_expose_event), monitor);
      gtk_container_add(GTK_CONTAINER(monitor->eventbox), monitor->screen);

      gtk_box_pack_start(GTK_BOX(monitor), monitor->scrollwindow,
                  TRUE, TRUE, 1);
}

GType
gui_gtk_monitor_get_type(void)
{
      static GType ttt_type = 0;

      if (! ttt_type) {
            static const GTypeInfo ttt_info = {
                  sizeof(GuiGtkMonitorClass),
                  NULL, /* base_init */
                  NULL, /* base_finalize */
                  (GClassInitFunc) gui_gtk_monitor_class_init,
                  NULL, /* class_finalize */
                  NULL, /* class_data */
                  sizeof(GuiGtkMonitor),
                  0,    /* n_preallocs */
                  (GInstanceInitFunc) gui_gtk_monitor_init,
            };

            ttt_type = g_type_register_static (GTK_TYPE_HBOX,
                        "Monitor", &ttt_info, 0);
      }

      return ttt_type;
}

#define GUIGTKMONITOR_TYPE    gui_gtk_monitor_get_type()

GtkWidget *
gui_gtk_monitor_new
(
      const char *type,
      unsigned int monitor_width,
      unsigned int monitor_height
)
{
      GuiGtkMonitor *monitor;
      unsigned int i;
      uint8_t *val;
      int step;
      unsigned int disp_width;
      unsigned int disp_height;
      unsigned int tiles_y;

      monitor = GUI_GTK_MONITOR(g_object_new(GUIGTKMONITOR_TYPE, NULL));
      // GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(monitor), GTK_CAN_FOCUS);

      /* find out aspect ratio */
      if (monitor_width * 3 == monitor_height * 4) {
            monitor->min_tiles_x = 32;
            monitor->min_tiles_y = 24;
            monitor->zoom_steps = sizes_4_3;
            monitor->zoom_step = 1;

      } else if (monitor_width * 10 == monitor_height * 16) {
            monitor->min_tiles_x = 40;
            monitor->min_tiles_y = 25;
            monitor->zoom_steps = sizes_16_10;
            monitor->zoom_step = 1;

      } else if (monitor_width * 4 == monitor_height * 5) {
            monitor->min_tiles_x = 40;
            monitor->min_tiles_y = 32;
            monitor->zoom_steps = sizes_5_4;
            monitor->zoom_step = 1;

      } else {
            /* ratio not (yet) supported */
            assert(0);
      }

      /* check, whether given monitor size is
       * one of the allowed zoom steps */  
      for (step = 0; monitor->zoom_steps[step].width != 0; step++) {
            if (monitor->zoom_steps[step].width == monitor_width
                        && monitor->zoom_steps[step].height == monitor_height) {

                  break;
            }
      }
      assert(monitor->zoom_steps[step].width != 0);
      if (step < monitor->zoom_step) {
            monitor->zoom_step = step;
      }

      monitor->monitor_width = monitor_width;
      monitor->monitor_height = monitor_height;

      disp_width = monitor->zoom_steps[monitor->zoom_step].width;
      disp_height = monitor->zoom_steps[monitor->zoom_step].height;

      monitor->true_width = disp_width;
      monitor->true_height = disp_height;

      /* create screen buffer and initialize it to
       * black and completely opaque */
      monitor->screen_data = malloc(monitor_width * monitor_height * 4);
      assert(monitor->screen_data);
      val = monitor->screen_data;
      for (i = 0; i < monitor_width * monitor_height; i++) {
            *val++ = 0;
            *val++ = 0;
            *val++ = 0;
            *val++ = 0xff;
      }

      monitor->tiles_row_stride = monitor_width;
      tiles_y = monitor_height;

      /* create diry tile array */
      monitor->dirty = malloc(monitor->tiles_row_stride * tiles_y * sizeof(int));
      assert(monitor->dirty);

      gui_gtk_monitor_recalc_scaling(monitor);

      gui_gtk_monitor_check_button_sensitivity(monitor);

      gtk_widget_set_size_request(monitor->scrollwindow,
                  disp_width + 25, disp_height + 25);
      gtk_widget_set_size_request(monitor->screen,
                  disp_width, disp_height);

      gtk_widget_show(monitor->screen);
      gtk_widget_show(monitor->scrollwindow);

      return GTK_WIDGET(monitor);
}

void
gui_gtk_monitor_grab_focus(GuiGtkMonitor *monitor)
{
      gtk_widget_grab_focus(monitor->eventbox);
}

void
gui_gtk_monitor_screenshot(GuiGtkMonitor *monitor, int nr)
{
      rec_screenshot(monitor, nr);
}

void
gui_gtk_monitor_pixel_set(
      GuiGtkMonitor *monitor,
      unsigned int x,
      unsigned int y,
      uint8_t r,
      uint8_t g,
      uint8_t b
)
{
      unsigned int index;

      if (monitor->monitor_width <= x
                  || monitor->monitor_height <= y) return;

      index = (y * monitor->monitor_width + x) * 4;

      monitor->screen_data[index + 0] = r;
      monitor->screen_data[index + 1] = g;
      monitor->screen_data[index + 2] = b;

      /* only set dirty if pixel is currently visible;
       * if it is invisible, the tile will be set
       * dirty during next resize anyway */
      if (x < monitor->screen_width
                  && y < monitor->screen_height) {
            monitor->dirty[(y / monitor->src_tile_height) * monitor->tiles_row_stride
                  + (x / monitor->src_tile_width)] = 1;
      }

      if (monitor->fp) {
            static const unsigned char id = RD_MON_UPD_ID;
            struct rd_mon_upd_data data;

            data.t = time_virt();
            data.x = x;
            data.y = y;
            data.r = r;
            data.b = b;
            data.g = g;

            fwrite((const void *) &id, sizeof(id), 1, monitor->fp);
            fwrite((const void *) &data, sizeof(data), 1, monitor->fp);
      }
}

void
gui_gtk_monitor_size_set
(
      GuiGtkMonitor *monitor,
      unsigned int width,
      unsigned int height
)
{
      if (monitor->monitor_width < width 
                  || monitor->monitor_height < height) return;

      monitor->true_width = width;
      monitor->true_height = height;

      gui_gtk_monitor_recalc_scaling(monitor);

      rec_monitor_set(monitor);
}

void
gui_gtk_monitor_sync(GuiGtkMonitor *monitor)
{
      GtkWidget *widget;
      GdkPixbuf *scale_pre;
      GdkPixbuf *scale_post;
      unsigned int x, y;
      unsigned int xt, yt;
      int need_flush = 0;

      widget = monitor->screen;
      if (widget->window) {

            for (yt = 0;
                  yt < monitor->screen_height / monitor->src_tile_height;
                  yt++) {

                  y = yt * monitor->src_tile_height;
                  for (xt = 0;
                        xt < monitor->screen_width / monitor->src_tile_width;
                        xt++) {

                        if (monitor->dirty[yt * monitor->tiles_row_stride + xt]) {

                              x = xt * monitor->src_tile_width;
                              monitor->dirty[yt * monitor->tiles_row_stride + xt] = 0;

                              scale_pre = gdk_pixbuf_new_from_data(
                                          &monitor->screen_data[
                                          y * monitor->monitor_width * 4
                                          + x * 4],
                                          GDK_COLORSPACE_RGB, TRUE, 8,
                                          monitor->src_tile_width,
                                          monitor->src_tile_height,
                                          monitor->monitor_width * 4,
                                          NULL, NULL);

                              scale_post = gdk_pixbuf_scale_simple(scale_pre,
                                          monitor->dest_tile_width,
                                          monitor->dest_tile_height,
                                          GDK_INTERP_BILINEAR);

                              gdk_draw_pixbuf(widget->window, NULL, scale_post,
                                          0, 0,
                                          xt * monitor->dest_tile_width,
                                          yt * monitor->dest_tile_height,
                                          -1, -1, GDK_RGB_DITHER_NONE, 0, 0);

                              need_flush = 1;

                              g_object_unref(G_OBJECT(scale_post));
                              g_object_unref(G_OBJECT(scale_pre));
                        }
                  }
            }

            if (need_flush) {
                  gui_gtk_flush();
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index