Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/ui/ui-drv/gtk
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/ui-drv/gtk')
-rw-r--r--src/ui/ui-drv/gtk/Makefile.in39
-rw-r--r--src/ui/ui-drv/gtk/ui_gtk.c764
2 files changed, 803 insertions, 0 deletions
diff --git a/src/ui/ui-drv/gtk/Makefile.in b/src/ui/ui-drv/gtk/Makefile.in
new file mode 100644
index 0000000..e5ca90f
--- /dev/null
+++ b/src/ui/ui-drv/gtk/Makefile.in
@@ -0,0 +1,39 @@
+CC = @CC@
+CFLAGS = @CFLAGS@
+LIBS = @LIBS@ -lm
+LFLAGS = @LDFLAGS@
+#AR = @AR@
+RANLIB = @RANLIB@
+
+SRCS = ui_gtk.c
+
+OBJS = $(SRCS:.c=.o)
+
+TLIB = @LIBPATH@/libuigtk.a
+
+
+all: $(TLIB)
+
+$(TLIB):$(OBJS)
+ rm -f $@
+ $(AR) rc $@ $^
+ $(RANLIB) $@
+
+clean:
+ rm -f $(TLIB)
+ rm -f *.[oas]
+ rm -f *~
+ rm -f core
+
+distclean:clean
+ rm Makefile
+
+#dep:
+# rm -f .depend
+# make .depend
+#
+#.depend:
+# echo '# Program dependencies' >.depend
+# gcc -I svgalib $(DEFINES) -MM $(patsubst %.o,%.c,$(OBJS)) >>.depend
+#
+#include .depend
diff --git a/src/ui/ui-drv/gtk/ui_gtk.c b/src/ui/ui-drv/gtk/ui_gtk.c
new file mode 100644
index 0000000..294b9e0
--- /dev/null
+++ b/src/ui/ui-drv/gtk/ui_gtk.c
@@ -0,0 +1,764 @@
+#include "aconfig.h"
+#ifdef GTK_DRIVER
+/*includes */
+#include <cairo.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <filter.h>
+#include <grlib.h>
+#include <ui.h>
+
+int width = 640;
+int height = 480;
+
+int mouse_x = 0;
+int mouse_y = 0;
+int mouse_buttons = 0;
+int keys = 0;
+
+GtkWidget *drawing_area;
+GtkWidget *menu_bar;
+GtkWidget *window;
+GHashTable *menuitem_table = NULL;
+
+int current_surface;
+cairo_surface_t *surface[2];
+
+static void window_destroyed(GtkWidget * widget, gpointer data) {
+ ui_quit();
+}
+
+static gboolean mouse_moved(GtkWidget * widget, GdkEventMotion * event,
+ gpointer data) {
+ mouse_x = event->x;
+ mouse_y = event->y;
+ return TRUE;
+}
+
+static gboolean key_pressed(GtkWidget * widget, GdkEventKey * event,
+ gpointer data) {
+ guint32 key;
+
+ switch (event->keyval) {
+ case GDK_Left:
+ keys |= 1;
+ ui_key(UIKEY_LEFT);
+ break;
+ case GDK_Right:
+ keys |= 2;
+ ui_key(UIKEY_RIGHT);
+ break;
+ case GDK_Up:
+ keys |= 4;
+ ui_key(UIKEY_UP);
+ break;
+ case GDK_Down:
+ keys |= 8;
+ ui_key(UIKEY_DOWN);
+ break;
+ case GDK_Page_Up:
+ keys |= 4;
+ ui_key(UIKEY_PGUP);
+ break;
+ case GDK_Page_Down:
+ keys |= 8;
+ ui_key(UIKEY_PGDOWN);
+ break;
+ case GDK_BackSpace:
+ ui_key(UIKEY_BACKSPACE);
+ break;
+ case GDK_Escape:
+ ui_key(UIKEY_ESC);
+ break;
+ case GDK_Home:
+ ui_key(UIKEY_HOME);
+ break;
+ case GDK_End:
+ ui_key(UIKEY_END);
+ break;
+ case GDK_Tab:
+ ui_key(UIKEY_TAB);
+ break;
+ default:
+ key = gdk_keyval_to_unicode(event->keyval);
+ ui_key(key);
+ }
+ return TRUE;
+}
+
+static gboolean key_released(GtkWidget * widget, GdkEventKey * event,
+ gpointer data) {
+ switch (event->keyval) {
+ case GDK_Left:
+ keys &= ~1;
+ break;
+ case GDK_Right:
+ keys &= ~2;
+ break;
+ case GDK_Up:
+ keys &= ~4;
+ break;
+ case GDK_Down:
+ keys &= ~8;
+ break;
+ case GDK_Page_Up:
+ keys &= ~4;
+ break;
+ case GDK_Page_Down:
+ keys &= ~8;
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean button_pressed(GtkWidget * widget, GdkEventButton * event,
+ gpointer data) {
+ switch (event->button) {
+ case 1:
+ mouse_buttons |= BUTTON1;
+ break;
+ case 2:
+ mouse_buttons |= BUTTON2;
+ break;
+ case 3:
+ mouse_buttons |= BUTTON3;
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean button_released(GtkWidget * widget, GdkEventButton * event,
+ gpointer data) {
+ switch (event->button) {
+ case 1:
+ mouse_buttons &= ~BUTTON1;
+ break;
+ case 2:
+ mouse_buttons &= ~BUTTON2;
+ break;
+ case 3:
+ mouse_buttons &= ~BUTTON3;
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean drawing_area_resized(GtkWidget * widget,
+ GdkEventConfigure * event) {
+ if (surface[0] && surface[1]) {
+ width = event->width;
+ height = event->height;
+ ui_resize();
+ }
+
+ return FALSE;
+}
+
+static gboolean drawing_area_exposed(GtkWidget * widget,
+ GdkEventExpose * event, gpointer data) {
+ cairo_t *cr;
+
+ cr = gdk_cairo_create(widget->window);
+
+ cairo_set_source_surface(cr, surface[current_surface], 0, 0);
+ cairo_paint(cr);
+
+ cairo_destroy(cr);
+
+ return FALSE;
+}
+
+void menuitem_activated(GtkMenuItem * item, gpointer data) {
+ /*
+ * gtk emits activate signal when radio buttons are deactivated as well.
+ * we want to ignore these signals.
+ */
+
+ if (!GTK_IS_RADIO_MENU_ITEM(item) || gtk_check_menu_item_get_active(
+ GTK_CHECK_MENU_ITEM(item)))
+ ui_menuactivate((CONST menuitem *) data, NULL);
+}
+
+static void build_menu(struct uih_context *uih, CONST char *name,
+ GtkWidget * parent) {
+ CONST menuitem *item;
+ gchar *menulabel;
+ GtkWidget *menuitem;
+ GtkWidget *submenu;
+ GSList *group = NULL;
+
+ int i;
+
+ for (i = 0; (item = menu_item(name, i)) != NULL; i++) {
+ if (item->type == MENU_SEPARATOR)
+ menuitem = gtk_separator_menu_item_new();
+ else {
+ if (item->type == MENU_CUSTOMDIALOG || item->type == MENU_DIALOG)
+ menulabel = g_strconcat(item->name, "...", NULL);
+ else
+ menulabel = g_strdup(item->name);
+
+ if (item->flags & MENUFLAG_CHECKBOX)
+ menuitem = gtk_check_menu_item_new_with_label(menulabel);
+ else if (item->flags & MENUFLAG_RADIO) {
+ menuitem = gtk_radio_menu_item_new_with_label(group, menulabel);
+ group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM
+ (menuitem));
+ } else
+ menuitem = gtk_menu_item_new_with_label(menulabel);
+
+ if (menu_enabled(item, uih))
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
+ (menuitem), TRUE);
+
+ g_free(menulabel);
+
+ g_hash_table_insert(menuitem_table, (gpointer) item->shortname,
+ menuitem);
+ }
+
+ gtk_menu_shell_append(GTK_MENU_SHELL(parent), menuitem);
+ gtk_widget_show(menuitem);
+
+ if (item->type == MENU_SUBMENU) {
+ submenu = gtk_menu_new();
+ build_menu(uih, item->shortname, submenu);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+ } else {
+ g_signal_connect(G_OBJECT(menuitem), "activate",
+ G_CALLBACK(menuitem_activated),
+ (gpointer) item);
+ }
+ }
+}
+
+static void dorootmenu(struct uih_context *uih, CONST char *name) {
+ if (menuitem_table)
+ g_hash_table_destroy(menuitem_table);
+
+ gtk_container_foreach(GTK_CONTAINER(menu_bar),
+ (GtkCallback) gtk_widget_destroy, NULL);
+
+ menuitem_table = g_hash_table_new(g_str_hash, g_str_equal);
+ build_menu(uih, name, menu_bar);
+}
+
+static void dialog(struct uih_context *uih, CONST char *name) {
+ CONST menuitem *item = menu_findcommand(name);
+ if (!item)
+ return; CONST
+ menudialog *dialog = menu_getdialog(uih, item);
+ if (!dialog)
+ return;
+
+ int nitems;
+ for (nitems = 0; dialog[nitems].question; nitems++)
+ ;
+
+ if (nitems == 1 && (dialog[0].type == DIALOG_IFILE || dialog[0].type
+ == DIALOG_OFILE)) {
+
+ GtkFileChooserAction action;
+ gchar *title;
+ switch (dialog[0].type) {
+ case DIALOG_IFILE:
+ action = GTK_FILE_CHOOSER_ACTION_OPEN;
+ title = "Open File";
+ break;
+ case DIALOG_OFILE:
+ action = GTK_FILE_CHOOSER_ACTION_SAVE;
+ title = "Save as...";
+ break;
+ }
+
+ GtkWidget *chooser = gtk_file_chooser_dialog_new(title, GTK_WINDOW
+ (window), action, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
+
+ GtkFileFilter *filter = gtk_file_filter_new();
+ // TODO: Set default extension for save dialog
+ gtk_file_filter_add_pattern(filter, g_strrstr(dialog[0].defstr, "*"));
+ gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(chooser), filter);
+
+ gint result = gtk_dialog_run(GTK_DIALOG(chooser));
+
+ if (result == GTK_RESPONSE_OK) {
+ gchar *filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(chooser));
+ dialogparam *param = malloc(sizeof(dialogparam));
+ param->dstring = strdup(filename);
+ ui_menuactivate(item, param);
+ }
+ gtk_widget_destroy(chooser);
+
+ } else {
+ GtkWidget *window, *table;
+ GHashTable *widget_hash = g_hash_table_new(g_str_hash, g_str_equal);
+
+ window = gtk_dialog_new_with_buttons("XaoS", NULL, GTK_DIALOG_MODAL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK, GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL, GTK_STOCK_HELP, GTK_RESPONSE_HELP, NULL);
+
+ gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_OK);
+
+ table = gtk_table_new(nitems, 5, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(table), 5);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 5);
+
+ int i;
+ for (i = 0; i < nitems; i++) {
+ GtkWidget *label = gtk_label_new(dialog[i].question);
+ GtkWidget *align = gtk_alignment_new(0, 0.5, 0, 0);
+ gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(label));
+ gtk_table_attach_defaults(GTK_TABLE(table), align, 0, 1, i, i + 1);
+
+ switch (dialog[i].type) {
+ case DIALOG_COORD: {
+ GtkWidget *widget;
+ gchar *value;
+ widget = gtk_entry_new();
+ value = g_strdup_printf("%f", dialog[i].deffloat);
+ gtk_entry_set_text(GTK_ENTRY(widget), value);
+ g_free(value);
+ gtk_table_attach_defaults(GTK_TABLE(table), widget, 1, 2, i, i
+ + 1);
+
+ g_hash_table_insert(widget_hash, dialog[i].question, widget);
+
+ widget = gtk_label_new("+");
+ gtk_table_attach_defaults(GTK_TABLE(table), widget, 2, 3, i, i
+ + 1);
+
+ widget = gtk_entry_new();
+ value = g_strdup_printf("%f", dialog[i].deffloat2);
+ gtk_entry_set_text(GTK_ENTRY(widget), value);
+ gtk_table_attach_defaults(GTK_TABLE(table), widget, 3, 4, i, i
+ + 1);
+ g_free(value);
+
+ gchar *key = g_strconcat(dialog[i].question, "2", NULL);
+ g_hash_table_insert(widget_hash, key, widget);
+ //g_free(key);
+
+ widget = gtk_label_new("i");
+ gtk_table_attach_defaults(GTK_TABLE(table), widget, 4, 5, i, i
+ + 1);
+
+ break;
+ }
+ case DIALOG_IFILE:
+ case DIALOG_OFILE: {
+ GtkFileChooserAction action;
+ gchar *title;
+ switch (dialog[i].type) {
+ case DIALOG_IFILE:
+ action = GTK_FILE_CHOOSER_ACTION_OPEN;
+ title = "Open File";
+ break;
+ case DIALOG_OFILE:
+ action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
+ title = "Save as...";
+ break;
+ }
+
+ // TODO: Set default chooser value
+ GtkWidget *chooser = gtk_file_chooser_button_new(title, action);
+ gtk_table_attach_defaults(GTK_TABLE(table), chooser, 1, 4, i, i
+ + 1);
+
+ g_hash_table_insert(widget_hash, dialog[i].question, chooser);
+ break;
+ }
+ case DIALOG_CHOICE: {
+ GtkWidget *combo = gtk_combo_box_new_text();
+ CONST
+ char **str = (CONST char **) dialog[i].defstr;
+ int y;
+ for (y = 0; str[y] != NULL; y++)
+ gtk_combo_box_append_text(GTK_COMBO_BOX(combo), str[y]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo), dialog[i].defint);
+ gtk_table_attach_defaults(GTK_TABLE(table), combo, 1, 4, i, i
+ + 1);
+
+ g_hash_table_insert(widget_hash, dialog[i].question, combo);
+ break;
+
+ }
+ default: {
+ GtkWidget *entry = gtk_entry_new();
+ gchar *value;
+ switch (dialog[i].type) {
+ case DIALOG_INT:
+ value = g_strdup_printf("%d", dialog[i].defint);
+ break;
+ case DIALOG_FLOAT:
+ value = g_strdup_printf("%f", dialog[i].deffloat);
+ break;
+ case DIALOG_STRING:
+ case DIALOG_KEYSTRING:
+ value = g_strdup(dialog[i].defstr);
+ break;
+ }
+ gtk_entry_set_text(GTK_ENTRY(entry), value);
+ g_free(value);
+ gtk_table_attach_defaults(GTK_TABLE(table), entry, 1, 4, i, i
+ + 1);
+
+ g_hash_table_insert(widget_hash, dialog[i].question, entry);
+ break;
+ }
+ }
+
+ }
+
+ gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(window)->vbox), table);
+ gtk_widget_show_all(window);
+
+ gint result = gtk_dialog_run(GTK_DIALOG(window));
+
+ if (result == GTK_RESPONSE_OK) {
+ dialogparam *params = malloc(sizeof(dialogparam) * nitems);
+
+ for (i = 0; i < nitems; i++) {
+ GtkWidget *widget = g_hash_table_lookup(widget_hash,
+ dialog[i]. question);
+ switch (dialog[i].type) {
+ case DIALOG_IFILE:
+ case DIALOG_OFILE: {
+ gchar *filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(widget));
+ if (filename == NULL)
+ filename = "";
+ params[i].dstring = strdup(filename);
+ break;
+ }
+ case DIALOG_STRING:
+ case DIALOG_KEYSTRING:
+ params[i].dstring = strdup(gtk_entry_get_text(
+ GTK_ENTRY(widget)));
+ break;
+ case DIALOG_INT:
+ params[i].dint
+ = atoi(gtk_entry_get_text(GTK_ENTRY(widget)));
+ break;
+ case DIALOG_FLOAT:
+ // TODO: Should use long double conversion instead of atof
+ params[i].number = atof(gtk_entry_get_text(
+ GTK_ENTRY(widget)));
+ break;
+ case DIALOG_COORD:
+ params[i].dcoord[0] = atof(gtk_entry_get_text(
+ GTK_ENTRY(widget)));
+ gchar *key = g_strconcat(dialog[i].question, "2", NULL);
+ widget = g_hash_table_lookup(widget_hash, key);
+ g_free(key);
+ params[i].dcoord[1] = atof(gtk_entry_get_text(
+ GTK_ENTRY(widget)));
+ break;
+ case DIALOG_CHOICE:
+ params[i].dint = gtk_combo_box_get_active(
+ GTK_COMBO_BOX(widget));
+ break;
+ }
+ }
+
+ g_hash_table_destroy(widget_hash);
+ gtk_widget_destroy(window);
+ ui_menuactivate(item, params);
+
+ } else {
+ g_hash_table_destroy(widget_hash);
+ gtk_widget_destroy(window);
+ }
+ }
+
+}
+
+static void enabledisable(struct uih_context *uih, CONST char *name) {
+ if (menuitem_table) {
+ CONST struct menuitem *item = menu_findcommand(name);
+ GtkCheckMenuItem *menuitem = g_hash_table_lookup(menuitem_table, name);
+ g_signal_handlers_block_matched(menuitem, G_SIGNAL_MATCH_FUNC, 0, 0, 0,
+ menuitem_activated, 0);
+ gtk_check_menu_item_set_active(menuitem, menu_enabled(item, uih));
+ g_signal_handlers_unblock_matched(menuitem, G_SIGNAL_MATCH_FUNC, 0, 0,
+ 0, menuitem_activated, 0);
+ }
+}
+
+static void print(int x, int y, CONST char *text) {
+}
+
+static void display() {
+ gtk_widget_queue_draw(drawing_area);
+}
+
+static void flip_buffers() {
+ current_surface ^= 1;
+}
+
+void free_buffers(char *b1, char *b2) {
+ cairo_surface_destroy(surface[0]);
+ cairo_surface_destroy(surface[1]);
+ surface[0] = NULL;
+ surface[1] = NULL;
+}
+
+int alloc_buffers(char **b1, char **b2) {
+ surface[0] = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
+ surface[1] = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
+
+ *b1 = (char *) cairo_image_surface_get_data(surface[0]);
+ *b2 = (char *) cairo_image_surface_get_data(surface[1]);
+
+ current_surface = 0;
+
+ return cairo_image_surface_get_stride(surface[0]);
+}
+
+static void getsize(int *w, int *h) {
+ *w = width;
+ *h = height;
+}
+
+static void processevents(int wait, int *mx, int *my, int *mb, int *k) {
+ while (gtk_events_pending())
+ gtk_main_iteration_do(wait ? TRUE : FALSE);
+
+ *mx = mouse_x;
+ *my = mouse_y;
+ *mb = mouse_buttons;
+ *k = keys;
+}
+
+static int init() {
+ GtkWidget *vbox;
+
+ int argc = 0;
+ char **argv = NULL;
+
+ gtk_init(&argc, &argv);
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+ gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
+ gtk_window_set_title(GTK_WINDOW(window), "XaoS");
+
+ g_signal_connect(G_OBJECT(window), "destroy",
+ G_CALLBACK(window_destroyed), NULL);
+
+ vbox = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+ gtk_widget_show(vbox);
+
+ menu_bar = gtk_menu_bar_new();
+ gtk_box_pack_start(GTK_BOX(vbox), menu_bar, FALSE, FALSE, 0);
+ gtk_widget_show(menu_bar);
+
+ drawing_area = gtk_drawing_area_new();
+ gtk_box_pack_end(GTK_BOX(vbox), drawing_area, TRUE, TRUE, 0);
+ gtk_widget_show(drawing_area);
+
+ GTK_WIDGET_SET_FLAGS(drawing_area, GTK_CAN_FOCUS);
+ gtk_widget_grab_focus(drawing_area);
+
+ gtk_widget_add_events(drawing_area, GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+ | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
+
+ g_signal_connect(G_OBJECT(drawing_area), "motion-notify-event",
+ G_CALLBACK(mouse_moved), NULL);
+
+ g_signal_connect(G_OBJECT(drawing_area), "button-press-event",
+ G_CALLBACK(button_pressed), NULL);
+
+ g_signal_connect(G_OBJECT(drawing_area), "button-release-event",
+ G_CALLBACK(button_released), NULL);
+
+ g_signal_connect(G_OBJECT(drawing_area), "key-press-event",
+ G_CALLBACK(key_pressed), NULL);
+
+ g_signal_connect(G_OBJECT(drawing_area), "key-release-event",
+ G_CALLBACK(key_released), NULL);
+
+ g_signal_connect(G_OBJECT(drawing_area), "expose-event",
+ G_CALLBACK(drawing_area_exposed), NULL);
+
+ g_signal_connect(G_OBJECT(drawing_area), "configure-event",
+ G_CALLBACK(drawing_area_resized), NULL);
+
+ gtk_widget_show_all(window);
+
+ return ( /*1 for sucess 0 for fail */1);
+}
+
+static void uninit() {
+ /*
+ * Disconnect window destroy event to prevent XaoS from
+ * quitting when switching drivers
+ */
+ g_signal_handlers_disconnect_matched(window, G_SIGNAL_MATCH_FUNC, 0, 0, 0,
+ window_destroyed, 0);
+
+ gtk_widget_destroy(window);
+}
+
+static void getmouse(int *x, int *y, int *b) {
+ *x = mouse_x;
+ *y = mouse_y;
+ *b = mouse_buttons;
+}
+
+static void mousetype(int type) {
+}
+
+static int skip(CONST char *text)
+{
+ int i = 0;
+ while (*text && *text != '\n')
+ i++, text++;
+ return (i);
+}
+
+#ifdef PLATFORM_TEXT_RENDERING
+
+int
+xprint(struct image *image, CONST struct xfont *current, int x, int y,
+ CONST char *text, int fgcolor, int bgcolor, int mode)
+{
+ cairo_t *cr = cairo_create(surface[image->currimage]);
+
+ cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size (cr, 18.0);
+
+ char line[BUFSIZ];
+ int pos = strcspn(text, "\n");
+ strncpy(line, text, pos);
+ line[pos] = '\0';
+
+ cairo_text_extents_t extents;
+ cairo_text_extents (cr, line, &extents);
+
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_move_to (cr, x + 1, y + extents.height + 1);
+ cairo_show_text (cr, line);
+
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_move_to (cr, x, y + extents.height);
+ cairo_show_text (cr, line);
+
+ cairo_destroy(cr);
+
+ return strlen(line);
+}
+
+int xtextwidth(CONST struct xfont *font, CONST char *text)
+{
+ cairo_t *cr = cairo_create(surface[current_surface]);
+
+ cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size (cr, 18.0);
+
+ char line[BUFSIZ];
+ int pos = strcspn(text, "\n");
+ strncpy(line, text, pos);
+ line[pos] = '\0';
+
+ cairo_text_extents_t extents;
+ cairo_text_extents (cr, line, &extents);
+
+ cairo_destroy(cr);
+
+ return ceil(extents.width) + 1;
+}
+
+int xtextheight(CONST struct xfont *font) {
+ cairo_t *cr = cairo_create(surface[current_surface]);
+
+ cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size (cr, 18.0);
+
+ cairo_text_extents_t extents;
+ cairo_text_extents (cr, "Wg'", &extents);
+
+ cairo_destroy(cr);
+
+ return ceil(extents.height) + 5;
+}
+
+int xtextcharw(CONST struct xfont *font, CONST char c)
+{
+ cairo_t *cr = cairo_create(surface[current_surface]);
+
+ cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_font_size (cr, 18.0);
+
+ char text[2];
+ sprintf(text, "%c", c);
+
+ cairo_text_extents_t extents;
+ cairo_text_extents (cr, text, &extents);
+
+ cairo_destroy(cr);
+
+ return ceil(extents.width) + 1;
+}
+
+#endif
+
+static struct params params[] = { { "", P_HELP, NULL, "GTK+ driver options:" },
+ { NULL, 0, NULL, NULL } };
+
+struct gui_driver gtk_gui_driver = {
+/* dorootmenu */dorootmenu,
+/* enabledisable */enabledisable,
+/* popup */NULL,
+/* dialog */dialog,
+/* help */NULL };
+
+struct ui_driver gtk_driver = {
+/* name */"GTK+ Driver",
+/* init */init,
+/* getsize */getsize,
+/* processevents */processevents,
+/* getmouse */getmouse,
+/* uninit */uninit,
+/* set_color */NULL,
+/* set_range */NULL,
+/* print */print,
+/* display */display,
+/* alloc_buffers */alloc_buffers,
+/* free_buffers */free_buffers,
+/* filp_buffers */flip_buffers,
+/* mousetype */mousetype,
+/* flush */NULL,
+/* textwidth */12,
+/* textheight */12,
+/* params */params,
+
+/* flags */RESOLUTION | PIXELSIZE | NOFLUSHDISPLAY | FULLSCREEN
+ | PALETTE_ROTATION | ROTATE_INSIDE_CALCULATION,
+
+/* width */0.01,
+/* height */0.01,
+/* maxwidth */0,
+/* maxheight */0,
+/* imagetype */UI_TRUECOLOR,
+/* palettestart */0,
+/* paletteend */0,
+/* maxentries */0,
+/* rmask */0x00ff0000,
+/* gmask */0x0000ff00,
+/* bmask */0x000000ff,
+/* gui_driver */&gtk_gui_driver };
+
+#endif