summaryrefslogtreecommitdiff
path: root/frontends
diff options
context:
space:
mode:
authorVincent Sanders <vince@kyllikki.org>2016-07-31 00:13:41 +0100
committerVincent Sanders <vince@kyllikki.org>2016-07-31 13:25:20 +0100
commitca5b165c85e1e8cc7df286b6a0a9a061de99bcaa (patch)
tree871768a492d7456a56600b71b7cb799028102af3 /frontends
parentea84f67035ea9899086223c540a79ffec328caef (diff)
downloadnetsurf-ca5b165c85e1e8cc7df286b6a0a9a061de99bcaa.tar.gz
netsurf-ca5b165c85e1e8cc7df286b6a0a9a061de99bcaa.tar.bz2
add nsgtk support for core window API
Diffstat (limited to 'frontends')
-rw-r--r--frontends/gtk/Makefile2
-rw-r--r--frontends/gtk/corewindow.c638
-rw-r--r--frontends/gtk/corewindow.h104
3 files changed, 743 insertions, 1 deletions
diff --git a/frontends/gtk/Makefile b/frontends/gtk/Makefile
index aae1d4465..f8f7d8c69 100644
--- a/frontends/gtk/Makefile
+++ b/frontends/gtk/Makefile
@@ -163,7 +163,7 @@ S_FRONTEND := gui.c schedule.c layout_pango.c bitmap.c plotters.c \
selection.c history.c window.c fetch.c download.c menu.c \
print.c search.c tabs.c toolbar.c gettext.c \
compat.c cookies.c hotlist.c viewdata.c viewsource.c \
- preferences.c about.c ssl_cert.c resources.c
+ preferences.c about.c ssl_cert.c resources.c corewindow.c
# This is the final source build list
# Note this is deliberately *not* expanded here as common and image
diff --git a/frontends/gtk/corewindow.c b/frontends/gtk/corewindow.c
new file mode 100644
index 000000000..11dec832e
--- /dev/null
+++ b/frontends/gtk/corewindow.c
@@ -0,0 +1,638 @@
+/*
+ * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * GTK generic core window interface.
+ *
+ * Provides interface for core renderers to the gtk toolkit drawable area.
+ * \todo should the interface really be called coredrawable?
+ *
+
+ * This module is an object that must be encapsulated. Client users
+ * should embed a struct nsgtk_corewindow at the beginning of their
+ * context for this display surface, fill in relevant data and then
+ * call nsgtk_corewindow_init()
+ *
+ * The nsgtk core window structure requires the drawing area and
+ * scrollable widgets are present and the callback for draw, key and
+ * mouse operations.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+#include <gtk/gtk.h>
+
+#include "utils/log.h"
+#include "utils/utils.h"
+#include "utils/messages.h"
+#include "utils/utf8.h"
+#include "netsurf/keypress.h"
+#include "netsurf/mouse.h"
+#include "desktop/plot_style.h"
+
+#include "gtk/compat.h"
+#include "gtk/gui.h" /* just for gtk_gui_gdkkey_to_nskey */
+#include "gtk/plotters.h"
+#include "gtk/corewindow.h"
+
+/**
+ * Convert GDK mouse event to netsurf mouse state
+ */
+static browser_mouse_state nsgtk_cw_gdkbutton_to_nsstate(GdkEventButton *event)
+{
+ browser_mouse_state ms;
+
+ if (event->type == GDK_2BUTTON_PRESS) {
+ ms = BROWSER_MOUSE_DOUBLE_CLICK;
+ } else {
+ ms = BROWSER_MOUSE_HOVER;
+ }
+
+ /* button state */
+ switch (event->button) {
+ case 1:
+ ms |= BROWSER_MOUSE_PRESS_1;
+ break;
+
+ case 2:
+ ms |= BROWSER_MOUSE_PRESS_2;
+ break;
+ }
+
+ /* Handle the modifiers too */
+ if (event->state & GDK_SHIFT_MASK) {
+ ms |= BROWSER_MOUSE_MOD_1;
+ }
+
+ if (event->state & GDK_CONTROL_MASK) {
+ ms |= BROWSER_MOUSE_MOD_2;
+ }
+
+ if (event->state & GDK_MOD1_MASK) {
+ ms |= BROWSER_MOUSE_MOD_3;
+ }
+
+ return ms;
+}
+
+/**
+ * gtk event on mouse button press.
+ *
+ * \param widget The gtk widget the event occoured for.
+ * \param event The event that occoured.
+ * \param g The context pointer passed when teh event was registered.
+ */
+static gboolean
+nsgtk_cw_button_press_event(GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer g)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)g;
+ struct nsgtk_corewindow_mouse *mouse = &nsgtk_cw->mouse_state;
+
+ gtk_im_context_reset(nsgtk_cw->input_method);
+ gtk_widget_grab_focus(GTK_WIDGET(nsgtk_cw->drawing_area));
+
+ /* record event information for potentially starting a drag. */
+ mouse->pressed_x = mouse->last_x = event->x;
+ mouse->pressed_y = mouse->last_y = event->y;
+ mouse->pressed = true;
+
+ mouse->state = nsgtk_cw_gdkbutton_to_nsstate(event);
+
+ nsgtk_cw->mouse(nsgtk_cw, mouse->state, event->x, event->y);
+
+ return TRUE;
+}
+
+static gboolean
+nsgtk_cw_button_release_event(GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer g)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)g;
+ struct nsgtk_corewindow_mouse *mouse = &nsgtk_cw->mouse_state;
+
+ /* only button 1 clicks are considered double clicks. If the
+ * mouse state is PRESS then we are waiting for a release to
+ * emit a click event, otherwise just reset the state to nothing.
+ */
+ if (mouse->state & BROWSER_MOUSE_DOUBLE_CLICK) {
+ if (mouse->state & BROWSER_MOUSE_PRESS_1) {
+ mouse->state ^= BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_CLICK_1;
+ } else if (mouse->state & BROWSER_MOUSE_PRESS_2) {
+ mouse->state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_CLICK_2 |
+ BROWSER_MOUSE_DOUBLE_CLICK);
+ }
+ } else if (mouse->state & BROWSER_MOUSE_PRESS_1) {
+ mouse->state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_CLICK_1);
+ } else if (mouse->state & BROWSER_MOUSE_PRESS_2) {
+ mouse->state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_CLICK_2);
+ } else if (mouse->state & BROWSER_MOUSE_HOLDING_1) {
+ mouse->state ^= (BROWSER_MOUSE_HOLDING_1 |
+ BROWSER_MOUSE_DRAG_ON);
+ } else if (mouse->state & BROWSER_MOUSE_HOLDING_2) {
+ mouse->state ^= (BROWSER_MOUSE_HOLDING_2 |
+ BROWSER_MOUSE_DRAG_ON);
+ }
+
+ /* Handle modifiers being removed */
+ if ((mouse->state & BROWSER_MOUSE_MOD_1) &&
+ !(event->state & GDK_SHIFT_MASK)) {
+ mouse->state ^= BROWSER_MOUSE_MOD_1;
+ }
+ if ((mouse->state & BROWSER_MOUSE_MOD_2) &&
+ !(event->state & GDK_CONTROL_MASK)) {
+ mouse->state ^= BROWSER_MOUSE_MOD_2;
+ }
+ if ((mouse->state & BROWSER_MOUSE_MOD_3) &&
+ !(event->state & GDK_MOD1_MASK)) {
+ mouse->state ^= BROWSER_MOUSE_MOD_3;
+ }
+
+ /* end drag with modifiers */
+ if (mouse->state & (BROWSER_MOUSE_MOD_1 |
+ BROWSER_MOUSE_MOD_2 |
+ BROWSER_MOUSE_MOD_3)) {
+ mouse->state = BROWSER_MOUSE_HOVER;
+ }
+
+ nsgtk_cw->mouse(nsgtk_cw, mouse->state, event->x, event->y);
+
+ mouse->pressed = false;
+
+ return TRUE;
+}
+
+static gboolean
+nsgtk_cw_motion_notify_event(GtkWidget *widget,
+ GdkEventMotion *event, gpointer g)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)g;
+ struct nsgtk_corewindow_mouse *mouse = &nsgtk_cw->mouse_state;
+
+ if (mouse->pressed == false) {
+ return TRUE;
+ }
+
+ if ((fabs(event->x - mouse->last_x) < 5.0) &&
+ (fabs(event->y - mouse->last_y) < 5.0)) {
+ /* Mouse hasn't moved far enough from press coordinate
+ * for this to be considered a drag.
+ */
+ return FALSE;
+ }
+
+ /* This is a drag, ensure it's always treated as such, even if
+ * we drag back over the press location.
+ */
+ mouse->last_x = INT_MIN;
+ mouse->last_y = INT_MIN;
+
+
+ if (mouse->state & BROWSER_MOUSE_PRESS_1) {
+ /* Start button 1 drag */
+ nsgtk_cw->mouse(nsgtk_cw,
+ BROWSER_MOUSE_DRAG_1,
+ mouse->pressed_x,
+ mouse->pressed_y);
+
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ mouse->state ^= (BROWSER_MOUSE_PRESS_1 |
+ BROWSER_MOUSE_HOLDING_1);
+ mouse->state |= BROWSER_MOUSE_DRAG_ON;
+
+ } else if (mouse->state & BROWSER_MOUSE_PRESS_2) {
+ /* Start button 2s drag */
+ nsgtk_cw->mouse(nsgtk_cw,
+ BROWSER_MOUSE_DRAG_2,
+ mouse->pressed_x,
+ mouse->pressed_y);
+
+ /* Replace PRESS with HOLDING and declare drag in progress */
+ mouse->state ^= (BROWSER_MOUSE_PRESS_2 |
+ BROWSER_MOUSE_HOLDING_2);
+ mouse->state |= BROWSER_MOUSE_DRAG_ON;
+
+ } else {
+ /* continue drag */
+
+ /* Handle modifiers being removed */
+ if ((mouse->state & BROWSER_MOUSE_MOD_1) &&
+ !(event->state & GDK_SHIFT_MASK)) {
+ mouse->state ^= BROWSER_MOUSE_MOD_1;
+ }
+ if ((mouse->state & BROWSER_MOUSE_MOD_2) &&
+ !(event->state & GDK_CONTROL_MASK)) {
+ mouse->state ^= BROWSER_MOUSE_MOD_2;
+ }
+ if ((mouse->state & BROWSER_MOUSE_MOD_3) &&
+ !(event->state & GDK_MOD1_MASK)) {
+ mouse->state ^= BROWSER_MOUSE_MOD_3;
+ }
+
+ if (mouse->state &
+ (BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_HOLDING_2)) {
+ nsgtk_cw->mouse(nsgtk_cw,
+ mouse->state,
+ event->x, event->y);
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ * Deal with keypress events not handled but input method or callback
+ *
+ * \param nsgtk_cw nsgtk core window key event happened in.
+ * \param nskey The netsurf keycode of the event.
+ * \return NSERROR_OK on sucess otherwise an error code.
+ */
+static nserror nsgtk_cw_key(struct nsgtk_corewindow *nsgtk_cw, uint32_t nskey)
+{
+ double value;
+ GtkAdjustment *vscroll;
+ GtkAdjustment *hscroll;
+ GtkAdjustment *scroll = NULL;
+ gdouble hpage, vpage;
+
+ vscroll = gtk_scrolled_window_get_vadjustment(nsgtk_cw->scrolled);
+ hscroll = gtk_scrolled_window_get_hadjustment(nsgtk_cw->scrolled);
+ g_object_get(vscroll, "page-size", &vpage, NULL);
+ g_object_get(hscroll, "page-size", &hpage, NULL);
+
+ switch(nskey) {
+ case NS_KEY_TEXT_START:
+ scroll = vscroll;
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case NS_KEY_TEXT_END:
+ scroll = vscroll;
+ value = nsgtk_adjustment_get_upper(scroll) - vpage;
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case NS_KEY_LEFT:
+ scroll = hscroll;
+ value = gtk_adjustment_get_value(scroll) -
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case NS_KEY_RIGHT:
+ scroll = hscroll;
+ value = gtk_adjustment_get_value(scroll) +
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value > nsgtk_adjustment_get_upper(scroll) - hpage)
+ value = nsgtk_adjustment_get_upper(scroll) - hpage;
+ break;
+ case NS_KEY_UP:
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) -
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+ break;
+
+ case NS_KEY_DOWN:
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) +
+ nsgtk_adjustment_get_step_increment(scroll);
+ if (value > nsgtk_adjustment_get_upper(scroll) - vpage)
+ value = nsgtk_adjustment_get_upper(scroll) - vpage;
+ break;
+
+ case NS_KEY_PAGE_UP:
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) -
+ nsgtk_adjustment_get_page_increment(scroll);
+
+ if (value < nsgtk_adjustment_get_lower(scroll))
+ value = nsgtk_adjustment_get_lower(scroll);
+
+ break;
+
+ case NS_KEY_PAGE_DOWN:
+ scroll = vscroll;
+ value = gtk_adjustment_get_value(scroll) +
+ nsgtk_adjustment_get_page_increment(scroll);
+
+ if (value > nsgtk_adjustment_get_upper(scroll) - vpage)
+ value = nsgtk_adjustment_get_upper(scroll) - vpage;
+ break;
+
+ }
+
+ if (scroll != NULL) {
+ gtk_adjustment_set_value(scroll, value);
+ }
+
+ return NSERROR_OK;
+}
+
+/**
+ * gtk event on key press.
+ *
+ * \param widget The gtk widget the event occoured for.
+ * \param event The event that occoured.
+ * \param g The context pointer passed when teh event was registered.
+ */
+static gboolean
+nsgtk_cw_keypress_event(GtkWidget *widget, GdkEventKey *event, gpointer g)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)g;
+ nserror res;
+ uint32_t nskey;
+
+ /* check to see if gtk input method swallowed the keypress */
+ if (gtk_im_context_filter_keypress(nsgtk_cw->input_method, event)) {
+ return TRUE;
+ }
+
+ /* convert gtk event to nskey */
+ nskey = gtk_gui_gdkkey_to_nskey(event);
+
+ /* attempt to handle keypress in caller */
+ res = nsgtk_cw->key(nsgtk_cw, nskey);
+ if (res == NSERROR_OK) {
+ return TRUE;
+ } else if (res != NSERROR_NOT_IMPLEMENTED) {
+ LOG("%s", messages_get_errorcode(res));
+ return FALSE;
+ }
+
+ /* deal with unprocessed keypress */
+ res = nsgtk_cw_key(nsgtk_cw, nskey);
+ if (res != NSERROR_OK) {
+ LOG("%s", messages_get_errorcode(res));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+static gboolean
+nsgtk_cw_keyrelease_event(GtkWidget *widget, GdkEventKey *event, gpointer g)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)g;
+
+ return gtk_im_context_filter_keypress(nsgtk_cw->input_method, event);
+}
+
+
+static void
+nsgtk_cw_input_method_commit(GtkIMContext *ctx, const gchar *str, gpointer g)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)g;
+ size_t len;
+ size_t offset = 0;
+ uint32_t nskey;
+
+ len = strlen(str);
+
+ while (offset < len) {
+ nskey = utf8_to_ucs4(str + offset, len - offset);
+
+ nsgtk_cw->key(nsgtk_cw, nskey);
+
+ offset = utf8_next(str, len, offset);
+ }
+}
+
+
+#if GTK_CHECK_VERSION(3,0,0)
+
+/* signal handler for core window redraw */
+static gboolean
+nsgtk_cw_draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)data;
+ double x1;
+ double y1;
+ double x2;
+ double y2;
+ struct rect clip;
+
+ current_widget = widget;
+ current_cr = cr;
+
+ cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+
+ if (tv->tree_flags == TREE_SSLCERT) {
+ ssl_current_session = tv->ssl_data;
+ }
+
+ clip.x0 = x1;
+ clip.y0 = y1;
+ clip.x1 = x2;
+ clip.y1 = y2;
+
+ nsgtk_cw->draw(nsgtk_cw, &clip);
+
+ current_widget = NULL;
+
+ return FALSE;
+}
+
+#else
+
+/* signal handler for core window redraw */
+static gboolean
+nsgtk_cw_draw_event(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer g)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)g;
+ struct rect clip;
+
+ clip.x0 = event->area.x;
+ clip.y0 = event->area.y;
+ clip.x1 = event->area.x + event->area.width;
+ clip.y1 = event->area.y + event->area.height;
+
+ current_widget = widget;
+ current_cr = gdk_cairo_create(nsgtk_widget_get_window(widget));
+
+ nsgtk_cw->draw(nsgtk_cw, &clip);
+
+ current_widget = NULL;
+ cairo_destroy(current_cr);
+
+ return FALSE;
+}
+
+#endif
+
+/**
+ * callback from core to request a redraw
+ */
+static void
+nsgtk_cw_redraw_request(struct core_window *cw, const struct rect *r)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)cw;
+
+ gtk_widget_queue_draw_area(GTK_WIDGET(nsgtk_cw->drawing_area),
+ r->x0, r->y0,
+ r->x1 - r->x0, r->y1 - r->y0);
+}
+
+
+static void
+nsgtk_cw_update_size(struct core_window *cw, int width, int height)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)cw;
+
+ gtk_widget_set_size_request(GTK_WIDGET(nsgtk_cw->drawing_area),
+ width, height);
+}
+
+
+static void
+nsgtk_cw_scroll_visible(struct core_window *cw, const struct rect *r)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)cw;
+ int y = 0, height = 0, y0, y1;
+ gdouble page;
+ GtkAdjustment *vadj;
+
+ vadj = gtk_scrolled_window_get_vadjustment(nsgtk_cw->scrolled);
+
+ assert(vadj);
+
+ g_object_get(vadj, "page-size", &page, NULL);
+
+ y0 = (int)(gtk_adjustment_get_value(vadj));
+ y1 = y0 + page;
+
+ if ((y >= y0) && (y + height <= y1))
+ return;
+ if (y + height > y1)
+ y0 = y0 + (y + height - y1);
+ if (y < y0)
+ y0 = y;
+ gtk_adjustment_set_value(vadj, y0);
+}
+
+
+static void
+nsgtk_cw_get_window_dimensions(struct core_window *cw, int *width, int *height)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)cw;
+ GtkAdjustment *vadj;
+ GtkAdjustment *hadj;
+ gdouble page;
+
+ if (width != NULL) {
+ hadj = gtk_scrolled_window_get_hadjustment(nsgtk_cw->scrolled);
+ g_object_get(hadj, "page-size", &page, NULL);
+ *width = page;
+ }
+
+ if (height != NULL) {
+ vadj = gtk_scrolled_window_get_vadjustment(nsgtk_cw->scrolled);
+ g_object_get(vadj, "page-size", &page, NULL);
+ *height = page;
+ }}
+
+
+static void
+nsgtk_cw_drag_status(struct core_window *cw, core_window_drag_status ds)
+{
+ struct nsgtk_corewindow *nsgtk_cw = (struct nsgtk_corewindow *)cw;
+ nsgtk_cw->drag_staus = ds;
+}
+
+
+struct core_window_callback_table nsgtk_cw_cb_table = {
+ .redraw_request = nsgtk_cw_redraw_request,
+ .update_size = nsgtk_cw_update_size,
+ .scroll_visible = nsgtk_cw_scroll_visible,
+ .get_window_dimensions = nsgtk_cw_get_window_dimensions,
+ .drag_status = nsgtk_cw_drag_status
+};
+
+/* exported function documented gtk/corewindow.h */
+nserror nsgtk_corewindow_init(struct nsgtk_corewindow *nsgtk_cw)
+{
+ nsgtk_cw->cb_table = &nsgtk_cw_cb_table;
+
+ /* input method setup */
+ nsgtk_cw->input_method = gtk_im_multicontext_new();
+ gtk_im_context_set_client_window(nsgtk_cw->input_method,
+ gtk_widget_get_parent_window(GTK_WIDGET(nsgtk_cw->drawing_area)));
+ gtk_im_context_set_use_preedit(nsgtk_cw->input_method, FALSE);
+
+ g_signal_connect(G_OBJECT(nsgtk_cw->input_method),
+ "commit",
+ G_CALLBACK(nsgtk_cw_input_method_commit),
+ nsgtk_cw);
+
+ nsgtk_connect_draw_event(GTK_WIDGET(nsgtk_cw->drawing_area),
+ G_CALLBACK(nsgtk_cw_draw_event),
+ nsgtk_cw);
+
+ g_signal_connect(G_OBJECT(nsgtk_cw->drawing_area),
+ "button-press-event",
+ G_CALLBACK(nsgtk_cw_button_press_event),
+ nsgtk_cw);
+
+ g_signal_connect(G_OBJECT(nsgtk_cw->drawing_area),
+ "button-release-event",
+ G_CALLBACK(nsgtk_cw_button_release_event),
+ nsgtk_cw);
+
+ g_signal_connect(G_OBJECT(nsgtk_cw->drawing_area),
+ "motion-notify-event",
+ G_CALLBACK(nsgtk_cw_motion_notify_event),
+ nsgtk_cw);
+
+ g_signal_connect(G_OBJECT(nsgtk_cw->drawing_area),
+ "key-press-event",
+ G_CALLBACK(nsgtk_cw_keypress_event),
+ nsgtk_cw);
+
+ g_signal_connect(G_OBJECT(nsgtk_cw->drawing_area),
+ "key-release-event",
+ G_CALLBACK(nsgtk_cw_keyrelease_event),
+ nsgtk_cw);
+
+ nsgtk_widget_override_background_color(
+ GTK_WIDGET(nsgtk_cw->drawing_area),
+ GTK_STATE_NORMAL,
+ 0, 0xffff, 0xffff, 0xffff);
+
+ return NSERROR_OK;
+}
+
+nserror nsgtk_corewindow_fini(struct nsgtk_corewindow *nsgtk_cw)
+{
+ g_object_unref(nsgtk_cw->input_method);
+
+ return NSERROR_OK;
+}
diff --git a/frontends/gtk/corewindow.h b/frontends/gtk/corewindow.h
new file mode 100644
index 000000000..d6f3011ba
--- /dev/null
+++ b/frontends/gtk/corewindow.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2016 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTK_COREWINDOW_H
+#define GTK_COREWINDOW_H
+
+#include "desktop/core_window.h"
+
+/**
+ * nsgtk core window mouse state
+ */
+struct nsgtk_corewindow_mouse {
+ browser_mouse_state state; /**< last event status */
+ bool pressed;
+ int pressed_x;
+ int pressed_y;
+ int last_x;
+ int last_y;
+};
+
+/**
+ * nsgtk core window state
+ */
+struct nsgtk_corewindow {
+ /* public variables */
+ /** GTK drawable widget */
+ GtkDrawingArea *drawing_area;
+ /** scrollable area drawing area is within */
+ GtkScrolledWindow *scrolled;
+
+ /* private variables */
+ /** Input method */
+ GtkIMContext *input_method;
+ /** table of callbacks for core window operations */
+ struct core_window_callback_table *cb_table;
+ /** mouse state */
+ struct nsgtk_corewindow_mouse mouse_state;
+ /** drag status set by core */
+ core_window_drag_status drag_staus;
+
+ /**
+ * callback to draw on drawable area of nsgtk core window
+ *
+ * \param nsgtk_cw The nsgtk core window structure.
+ * \param r The rectangle of the window that needs updating.
+ * \return NSERROR_OK on success otherwise apropriate error code
+ */
+ nserror (*draw)(struct nsgtk_corewindow *nsgtk_cw, struct rect *r);
+
+ /**
+ * callback for keypress on nsgtk core window
+ *
+ * \param nsgtk_cw The nsgtk core window structure.
+ * \param nskey The netsurf key code.
+ * \return NSERROR_OK if key processed,
+ * NSERROR_NOT_IMPLEMENTED if key not processed
+ * otherwise apropriate error code
+ */
+ nserror (*key)(struct nsgtk_corewindow *nsgtk_cw, uint32_t nskey);
+
+ /**
+ * callback for mouse event on nsgtk core window
+ *
+ * \param nsgtk_cw The nsgtk core window structure.
+ * \param mouse_state mouse state
+ * \param x location of event
+ * \param y location of event
+ * \return NSERROR_OK on sucess otherwise apropriate error code.
+ */
+ nserror (*mouse)(struct nsgtk_corewindow *nsgtk_cw, browser_mouse_state mouse_state, int x, int y);
+};
+
+/**
+ * initialise elements of gtk core window.
+ *
+ * \param nsgtk_cw A gtk core window structure to initialise
+ * \return NSERROR_OK on successful initialisation otherwise error code.
+ */
+nserror nsgtk_corewindow_init(struct nsgtk_corewindow *nsgtk_cw);
+
+/**
+ * finalise elements of gtk core window.
+ *
+ * \param nsgtk_cw A gtk core window structure to initialise
+ * \return NSERROR_OK on successful finalisation otherwise error code.
+ */
+nserror nsgtk_corewindow_fini(struct nsgtk_corewindow *nsgtk_cw);
+
+#endif