]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/gtkwin.c
Introduce a config option for building on OS X GTK.
[PuTTY.git] / unix / gtkwin.c
index 726a7d2cddee438e857bd6d687766f68bf9c0dee..69535c36308da3f1a0324ebcf1868d0cd2f3897d 100644 (file)
 #if !GTK_CHECK_VERSION(3,0,0)
 #include <gdk/gdkkeysyms.h>
 #endif
-#ifndef NOT_X_WINDOWS
-#include <gdk/gdkx.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/Xatom.h>
-#endif
 
 #if GTK_CHECK_VERSION(2,0,0)
 #include <gtk/gtkimmodule.h>
 #include "gtkcompat.h"
 #include "gtkfont.h"
 
+#ifndef NOT_X_WINDOWS
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#endif
+
 #define CAT2(x,y) x ## y
 #define CAT(x,y) CAT2(x,y)
 #define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
@@ -93,7 +94,9 @@ struct gui_data {
 #endif
     GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor;
     GdkColor cols[NALLCOLOURS];
+#if !GTK_CHECK_VERSION(3,0,0)
     GdkColormap *colmap;
+#endif
     wchar_t *pastein_data;
     int direct_to_font;
     int pastein_data_len;
@@ -521,6 +524,66 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
     return TRUE;
 }
 
+#ifdef DRAW_TEXT_CAIRO
+static void cairo_setup_dctx(struct draw_ctx *dctx)
+{
+    cairo_get_matrix(dctx->uctx.u.cairo.cr,
+                     &dctx->uctx.u.cairo.origmatrix);
+    cairo_set_line_width(dctx->uctx.u.cairo.cr, 1.0);
+    cairo_set_line_cap(dctx->uctx.u.cairo.cr, CAIRO_LINE_CAP_SQUARE);
+    cairo_set_line_join(dctx->uctx.u.cairo.cr, CAIRO_LINE_JOIN_MITER);
+    /* This antialiasing setting appears to be ignored for Pango
+     * font rendering but honoured for stroking and filling paths;
+     * I don't quite understand the logic of that, but I won't
+     * complain since it's exactly what I happen to want */
+    cairo_set_antialias(dctx->uctx.u.cairo.cr, CAIRO_ANTIALIAS_NONE);
+}
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+    struct gui_data *inst = (struct gui_data *)data;
+
+    if (inst->term) {
+        struct draw_ctx adctx, *dctx = &adctx;
+        GdkRectangle dirtyrect;
+
+        dctx->inst = inst;
+        dctx->uctx.type = DRAWTYPE_CAIRO;
+        dctx->uctx.u.cairo.widget = widget;
+        dctx->uctx.u.cairo.cr = cr;
+        cairo_setup_dctx(dctx);
+
+        gdk_cairo_get_clip_rectangle(cr, &dirtyrect);
+
+        /*
+         * As in window.c, we clear the 'immediately' flag in the
+         * term_paint() call if the terminal has an update pending, in
+         * case we're constrained within this event to only draw on
+         * the exposed rectangle of the window. (Because if the whole
+         * of a character cell needs a redraw due to a terminal
+         * contents change, the last thing we want is to give it a
+         * _partial_ redraw here due to system-imposed clipping, and
+         * then have the next main terminal update believe it's been
+         * redrawn in full.)
+         *
+         * I don't actually know if GTK draw events will constrain us
+         * in this way, but it's best to be careful...
+         */
+        term_paint(inst->term, dctx,
+                   (dirtyrect.x - inst->window_border) / inst->font_width,
+                   (dirtyrect.y - inst->window_border) / inst->font_height,
+                   (dirtyrect.x + dirtyrect.width -
+                    inst->window_border) / inst->font_width,
+                   (dirtyrect.y + dirtyrect.height -
+                    inst->window_border) / inst->font_height,
+                   !inst->term->window_update_pending);
+    }
+
+    return TRUE;
+}
+#else
 gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
@@ -542,20 +605,6 @@ gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)
 #else
     if (inst->term) {
         Context ctx = get_ctx(inst);
-        /*
-         * As in window.c, we clear the 'immediately' flag in the
-         * term_paint() call if the terminal has an update pending, in
-         * case we're constrained within this event to only draw on
-         * the exposed rectangle of the window. (Because if the whole
-         * of a character cell needs a redraw due to a terminal
-         * contents change, the last thing we want is to give it a
-         * _partial_ redraw here due to system-imposed clipping, and
-         * then have the next main terminal update believe it's been
-         * redrawn in full.)
-         *
-         * I don't actually know if GTK expose events will constrain
-         * us in this way, but it's best to be careful...
-         */
         term_paint(inst->term, ctx,
                    (event->area.x - inst->window_border) / inst->font_width,
                    (event->area.y - inst->window_border) / inst->font_height,
@@ -570,6 +619,7 @@ gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data)
 
     return TRUE;
 }
+#endif
 
 #define KEY_PRESSED(k) \
     (inst->keystate[(k) / 32] & (1 << ((k) % 32)))
@@ -590,6 +640,134 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
     special = use_ucsoutput = FALSE;
     output_charset = CS_ISO8859_1;
 
+#ifdef KEY_EVENT_DIAGNOSTICS
+    /*
+     * Condition this mess in if you want to debug keyboard events as
+     * they come in to this function (e.g. because some particular
+     * port of GDK is producing an unexpected arrangement of
+     * modifiers).
+     */
+#define TRY(val, prefix, string)                                \
+    if ((val) == prefix ## string) printf("%s", #string); else
+#define GIVE_UP(val)                            \
+    printf("%d", (int)(val))
+#define TRY_MASK(val, prefix, string, suffix)           \
+        if ((val) & prefix ## string ## suffix) {       \
+            (val) &= ~ prefix ## string ## suffix;      \
+            printf("%s", #string);                      \
+            if (val) printf("|");                       \
+        }
+#define GIVE_UP_MASK(val)                               \
+        do                                              \
+        {                                               \
+            if ((val)) printf("%d", (int)(val));        \
+        } while (0)
+
+    printf("key_event: type=");
+    TRY(event->type, GDK_KEY_, PRESS)
+    TRY(event->type, GDK_KEY_, RELEASE)
+    GIVE_UP(event->type);
+    printf(" keyval=");
+    TRY(event->keyval, GDK_KEY_, Alt_L)
+    TRY(event->keyval, GDK_KEY_, Alt_R)
+    TRY(event->keyval, GDK_KEY_, BackSpace)
+    TRY(event->keyval, GDK_KEY_, Begin)
+    TRY(event->keyval, GDK_KEY_, Break)
+    TRY(event->keyval, GDK_KEY_, Delete)
+    TRY(event->keyval, GDK_KEY_, Down)
+    TRY(event->keyval, GDK_KEY_, End)
+    TRY(event->keyval, GDK_KEY_, Escape)
+    TRY(event->keyval, GDK_KEY_, F10)
+    TRY(event->keyval, GDK_KEY_, F11)
+    TRY(event->keyval, GDK_KEY_, F12)
+    TRY(event->keyval, GDK_KEY_, F13)
+    TRY(event->keyval, GDK_KEY_, F14)
+    TRY(event->keyval, GDK_KEY_, F15)
+    TRY(event->keyval, GDK_KEY_, F16)
+    TRY(event->keyval, GDK_KEY_, F17)
+    TRY(event->keyval, GDK_KEY_, F18)
+    TRY(event->keyval, GDK_KEY_, F19)
+    TRY(event->keyval, GDK_KEY_, F1)
+    TRY(event->keyval, GDK_KEY_, F20)
+    TRY(event->keyval, GDK_KEY_, F2)
+    TRY(event->keyval, GDK_KEY_, F3)
+    TRY(event->keyval, GDK_KEY_, F4)
+    TRY(event->keyval, GDK_KEY_, F5)
+    TRY(event->keyval, GDK_KEY_, F6)
+    TRY(event->keyval, GDK_KEY_, F7)
+    TRY(event->keyval, GDK_KEY_, F8)
+    TRY(event->keyval, GDK_KEY_, F9)
+    TRY(event->keyval, GDK_KEY_, Home)
+    TRY(event->keyval, GDK_KEY_, Insert)
+    TRY(event->keyval, GDK_KEY_, ISO_Left_Tab)
+    TRY(event->keyval, GDK_KEY_, KP_0)
+    TRY(event->keyval, GDK_KEY_, KP_1)
+    TRY(event->keyval, GDK_KEY_, KP_2)
+    TRY(event->keyval, GDK_KEY_, KP_3)
+    TRY(event->keyval, GDK_KEY_, KP_4)
+    TRY(event->keyval, GDK_KEY_, KP_5)
+    TRY(event->keyval, GDK_KEY_, KP_6)
+    TRY(event->keyval, GDK_KEY_, KP_7)
+    TRY(event->keyval, GDK_KEY_, KP_8)
+    TRY(event->keyval, GDK_KEY_, KP_9)
+    TRY(event->keyval, GDK_KEY_, KP_Add)
+    TRY(event->keyval, GDK_KEY_, KP_Begin)
+    TRY(event->keyval, GDK_KEY_, KP_Decimal)
+    TRY(event->keyval, GDK_KEY_, KP_Delete)
+    TRY(event->keyval, GDK_KEY_, KP_Divide)
+    TRY(event->keyval, GDK_KEY_, KP_Down)
+    TRY(event->keyval, GDK_KEY_, KP_End)
+    TRY(event->keyval, GDK_KEY_, KP_Enter)
+    TRY(event->keyval, GDK_KEY_, KP_Home)
+    TRY(event->keyval, GDK_KEY_, KP_Insert)
+    TRY(event->keyval, GDK_KEY_, KP_Left)
+    TRY(event->keyval, GDK_KEY_, KP_Multiply)
+    TRY(event->keyval, GDK_KEY_, KP_Page_Down)
+    TRY(event->keyval, GDK_KEY_, KP_Page_Up)
+    TRY(event->keyval, GDK_KEY_, KP_Right)
+    TRY(event->keyval, GDK_KEY_, KP_Subtract)
+    TRY(event->keyval, GDK_KEY_, KP_Up)
+    TRY(event->keyval, GDK_KEY_, Left)
+    TRY(event->keyval, GDK_KEY_, Meta_L)
+    TRY(event->keyval, GDK_KEY_, Meta_R)
+    TRY(event->keyval, GDK_KEY_, Num_Lock)
+    TRY(event->keyval, GDK_KEY_, Page_Down)
+    TRY(event->keyval, GDK_KEY_, Page_Up)
+    TRY(event->keyval, GDK_KEY_, Return)
+    TRY(event->keyval, GDK_KEY_, Right)
+    TRY(event->keyval, GDK_KEY_, Tab)
+    TRY(event->keyval, GDK_KEY_, Up)
+    TRY(event->keyval, GDK_KEY_, Shift_L)
+    TRY(event->keyval, GDK_KEY_, Shift_R)
+    TRY(event->keyval, GDK_KEY_, Control_L)
+    TRY(event->keyval, GDK_KEY_, Control_R)
+    TRY(event->keyval, GDK_KEY_, Caps_Lock)
+    TRY(event->keyval, GDK_KEY_, Shift_Lock)
+    TRY(event->keyval, GDK_KEY_, Super_L)
+    TRY(event->keyval, GDK_KEY_, Super_R)
+    TRY(event->keyval, GDK_KEY_, Hyper_L)
+    TRY(event->keyval, GDK_KEY_, Hyper_R)
+    GIVE_UP(event->keyval);
+    printf(" state=");
+    {
+        int val = event->state;
+        TRY_MASK(val, GDK_, SHIFT, _MASK)
+        TRY_MASK(val, GDK_, LOCK, _MASK)
+        TRY_MASK(val, GDK_, CONTROL, _MASK)
+        TRY_MASK(val, GDK_, MOD1, _MASK)
+        TRY_MASK(val, GDK_, MOD2, _MASK)
+        TRY_MASK(val, GDK_, MOD3, _MASK)
+        TRY_MASK(val, GDK_, MOD4, _MASK)
+        TRY_MASK(val, GDK_, MOD5, _MASK)
+        TRY_MASK(val, GDK_, SUPER, _MASK)
+        TRY_MASK(val, GDK_, HYPER, _MASK)
+        TRY_MASK(val, GDK_, META, _MASK)
+        GIVE_UP_MASK(val);
+    }
+    printf(" hardware_keycode=%d is_modifier=%s\n",
+           (int)event->hardware_keycode, event->is_modifier ? "TRUE" : "FALSE");
+#endif /* KEY_EVENT_DIAGNOSTICS */
+
     /*
      * If Alt is being released after typing an Alt+numberpad
      * sequence, we should generate the code that was typed.
@@ -865,6 +1043,33 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
            end = 2;
        }
 
+        /* Some GTK backends (e.g. Quartz) do not change event->string
+         * in response to the Control modifier. So we do it ourselves
+         * here, if it's not already happened.
+         *
+         * The translations below are in line with X11 policy as far
+         * as I know. */
+        if ((event->state & GDK_CONTROL_MASK) && end == 2) {
+            if (output[1] >= '3' && output[1] <= '7') {
+                /* ^3,...,^7 map to 0x1B,...,0x1F */
+                output[1] += '\x1B' - '3';
+            } else if (output[1] == '2') {
+                /* ^2 is ^@, i.e. \0 */
+                output[1] = '\0';
+            } else if (output[1] == '8') {
+                /* ^8 is DEL */
+                output[1] = '\x7F';
+            } else if (output[1] == '/') {
+                /* ^/ is the same as ^_ */
+                output[1] = '\x1F';
+            } else if (output[1] >= 0x40 && output[1] < 0x7F) {
+                /* Everything anywhere near the alphabetics just gets
+                 * masked. */
+                output[1] &= 0x1F;
+            }
+            /* Anything else, e.g. '0', is unchanged. */
+        }
+
        /* Control-Break sends a Break special to the backend */
        if (event->keyval == GDK_KEY_Break &&
            (event->state & GDK_CONTROL_MASK)) {
@@ -1632,13 +1837,28 @@ void timer_change_notify(unsigned long next)
     timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
 }
 
-void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
+#if GTK_CHECK_VERSION(2,0,0)
+gboolean fd_input_func(GIOChannel *source, GIOCondition condition,
+                       gpointer data)
 {
+    int sourcefd = g_io_channel_unix_get_fd(source);
     /*
      * We must process exceptional notifications before ordinary
      * readability ones, or we may go straight past the urgent
      * marker.
      */
+    if (condition & G_IO_PRI)
+        select_result(sourcefd, 4);
+    if (condition & G_IO_IN)
+        select_result(sourcefd, 1);
+    if (condition & G_IO_OUT)
+        select_result(sourcefd, 2);
+
+    return TRUE;
+}
+#else
+void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
+{
     if (condition & GDK_INPUT_EXCEPTION)
         select_result(sourcefd, 4);
     if (condition & GDK_INPUT_READ)
@@ -1646,6 +1866,7 @@ void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
     if (condition & GDK_INPUT_WRITE)
         select_result(sourcefd, 2);
 }
+#endif
 
 void destroy(GtkWidget *widget, gpointer data)
 {
@@ -1682,6 +1903,9 @@ void set_raw_mouse_mode(void *frontend, int activate)
 void request_resize(void *frontend, int w, int h)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
+
+#if !GTK_CHECK_VERSION(3,0,0)
+
     int large_x, large_y;
     int offset_x, offset_y;
     int area_x, area_y;
@@ -1754,31 +1978,69 @@ void request_resize(void *frontend, int w, int h)
     gdk_window_resize(gtk_widget_get_window(inst->window),
                      area_x + offset_x, area_y + offset_y);
 #endif
+
+#else /* GTK_CHECK_VERSION(3,0,0) */
+
+    /*
+     * In GTK3, we can do this by using gtk_window_resize_to_geometry,
+     * which uses the fact that we've already set up the main window's
+     * WM hints to reflect the terminal drawing area's resize
+     * increment (i.e. character cell) and the fixed amount of stuff
+     * round the edges.
+     */
+    gtk_window_resize_to_geometry(GTK_WINDOW(inst->window), w, h);
+
+#endif
+
 }
 
 static void real_palette_set(struct gui_data *inst, int n, int r, int g, int b)
 {
-    gboolean success[1];
-
     inst->cols[n].red = r * 0x0101;
     inst->cols[n].green = g * 0x0101;
     inst->cols[n].blue = b * 0x0101;
 
-    gdk_colormap_free_colors(inst->colmap, inst->cols + n, 1);
-    gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1,
-                             FALSE, TRUE, success);
-    if (!success[0])
-       g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", appname,
-               n, r, g, b);
+#if !GTK_CHECK_VERSION(3,0,0)
+    {
+        gboolean success[1];
+        gdk_colormap_free_colors(inst->colmap, inst->cols + n, 1);
+        gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1,
+                                  FALSE, TRUE, success);
+        if (!success[0])
+            g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n",
+                    appname, n, r, g, b);
+    }
+#endif
+}
+
+void set_gdk_window_background(GdkWindow *win, const GdkColor *col)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+    /* gdk_window_set_background is deprecated; work around its
+     * absence. */
+    GdkRGBA rgba;
+    rgba.red = col->red / 65535.0;
+    rgba.green = col->green / 65535.0;
+    rgba.blue = col->blue / 65535.0;
+    rgba.alpha = 1.0;
+    gdk_window_set_background_rgba(win, &rgba);
+#else
+    {
+        /* For GTK1, which doesn't have a 'const' on
+         * gdk_window_set_background's second parameter type. */
+        GdkColor col_mutable = *col;
+        gdk_window_set_background(win, &col_mutable);
+    }
+#endif
 }
 
 void set_window_background(struct gui_data *inst)
 {
     if (inst->area && gtk_widget_get_window(inst->area))
-       gdk_window_set_background(gtk_widget_get_window(inst->area),
+       set_gdk_window_background(gtk_widget_get_window(inst->area),
                                   &inst->cols[258]);
     if (inst->window && gtk_widget_get_window(inst->window))
-       gdk_window_set_background(gtk_widget_get_window(inst->window),
+       set_gdk_window_background(gtk_widget_get_window(inst->window),
                                   &inst->cols[258]);
 }
 
@@ -1808,16 +2070,17 @@ void palette_reset(void *frontend)
        0, 8, 1, 9, 2, 10, 3, 11,
        4, 12, 5, 13, 6, 14, 7, 15
     };
-    gboolean success[NALLCOLOURS];
     int i;
 
     assert(lenof(ww) == NCFGCOLOURS);
 
+#if !GTK_CHECK_VERSION(3,0,0)
     if (!inst->colmap) {
        inst->colmap = gdk_colormap_get_system();
     } else {
        gdk_colormap_free_colors(inst->colmap, inst->cols, NALLCOLOURS);
     }
+#endif
 
     for (i = 0; i < NCFGCOLOURS; i++) {
        inst->cols[ww[i]].red =
@@ -1842,16 +2105,21 @@ void palette_reset(void *frontend)
        }
     }
 
-    gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS,
-                             FALSE, TRUE, success);
-    for (i = 0; i < NALLCOLOURS; i++) {
-       if (!success[i])
-           g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n",
-                    appname, i,
-                   conf_get_int_int(inst->conf, CONF_colours, i*3+0),
-                   conf_get_int_int(inst->conf, CONF_colours, i*3+1),
-                   conf_get_int_int(inst->conf, CONF_colours, i*3+2));
+#if !GTK_CHECK_VERSION(3,0,0)
+    {
+        gboolean success[NALLCOLOURS];
+        gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS,
+                                  FALSE, TRUE, success);
+        for (i = 0; i < NALLCOLOURS; i++) {
+            if (!success[i])
+                g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n",
+                        appname, i,
+                        conf_get_int_int(inst->conf, CONF_colours, i*3+0),
+                        conf_get_int_int(inst->conf, CONF_colours, i*3+1),
+                        conf_get_int_int(inst->conf, CONF_colours, i*3+2));
+        }
     }
+#endif
 
     /* Since Default Background may have changed, ensure that space
      * between text area and window border is refreshed. */
@@ -2346,16 +2614,7 @@ Context get_ctx(void *frontend)
     if (dctx->uctx.type == DRAWTYPE_CAIRO) {
         dctx->uctx.u.cairo.widget = GTK_WIDGET(inst->area);
         dctx->uctx.u.cairo.cr = gdk_cairo_create(target);
-        cairo_get_matrix(dctx->uctx.u.cairo.cr,
-                         &dctx->uctx.u.cairo.origmatrix);
-        cairo_set_line_width(dctx->uctx.u.cairo.cr, 1.0);
-        cairo_set_line_cap(dctx->uctx.u.cairo.cr, CAIRO_LINE_CAP_SQUARE);
-        cairo_set_line_join(dctx->uctx.u.cairo.cr, CAIRO_LINE_JOIN_MITER);
-        /* This antialiasing setting appears to be ignored for Pango
-         * font rendering but honoured for stroking and filling paths;
-         * I don't quite understand the logic of that, but I won't
-         * complain since it's exactly what I happen to want */
-        cairo_set_antialias(dctx->uctx.u.cairo.cr, CAIRO_ANTIALIAS_NONE);
+        cairo_setup_dctx(dctx);
     }
 #endif
     return dctx;
@@ -2933,7 +3192,7 @@ void cmdline_error(const char *p, ...)
     exit(1);
 }
 
-char *get_x_display(void *frontend)
+const char *get_x_display(void *frontend)
 {
     return gdk_get_display();
 }
@@ -2942,7 +3201,7 @@ char *get_x_display(void *frontend)
 long get_windowid(void *frontend)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
-    return (long)GDK_WINDOW_XWINDOW(gtk_widget_get_window(inst->area));
+    return (long)GDK_WINDOW_XID(gtk_widget_get_window(inst->area));
 }
 #endif
 
@@ -3218,17 +3477,46 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
     return err;
 }
 
-int uxsel_input_add(int fd, int rwx) {
+struct uxsel_id {
+#if GTK_CHECK_VERSION(2,0,0)
+    GIOChannel *chan;
+    guint watch_id;
+#else
+    int id;
+#endif
+};
+
+uxsel_id *uxsel_input_add(int fd, int rwx) {
+    uxsel_id *id = snew(uxsel_id);
+
+#if GTK_CHECK_VERSION(2,0,0)
+    int flags = 0;
+    if (rwx & 1) flags |= G_IO_IN;
+    if (rwx & 2) flags |= G_IO_OUT;
+    if (rwx & 4) flags |= G_IO_PRI;
+    id->chan = g_io_channel_unix_new(fd);
+    g_io_channel_set_encoding(id->chan, NULL, NULL);
+    id->watch_id = g_io_add_watch(id->chan, flags, fd_input_func, NULL);
+#else
     int flags = 0;
     if (rwx & 1) flags |= GDK_INPUT_READ;
     if (rwx & 2) flags |= GDK_INPUT_WRITE;
     if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
     assert(flags);
-    return gdk_input_add(fd, flags, fd_input_func, NULL);
+    id->id = gdk_input_add(fd, flags, fd_input_func, NULL);
+#endif
+
+    return id;
 }
 
-void uxsel_input_remove(int id) {
-    gdk_input_remove(id);
+void uxsel_input_remove(uxsel_id *id) {
+#if GTK_CHECK_VERSION(2,0,0)
+    g_source_remove(id->watch_id);
+    g_io_channel_unref(id->chan);
+#else
+    gdk_input_remove(id->id);
+#endif
+    sfree(id);
 }
 
 char *setup_fonts_ucs(struct gui_data *inst)
@@ -4127,8 +4415,13 @@ int pt_main(int argc, char **argv)
                      G_CALLBACK(focus_event), inst);
     g_signal_connect(G_OBJECT(inst->area), "configure_event",
                      G_CALLBACK(configure_area), inst);
+#if GTK_CHECK_VERSION(3,0,0)
+    g_signal_connect(G_OBJECT(inst->area), "draw",
+                     G_CALLBACK(draw_area), inst);
+#else
     g_signal_connect(G_OBJECT(inst->area), "expose_event",
                      G_CALLBACK(expose_area), inst);
+#endif
     g_signal_connect(G_OBJECT(inst->area), "button_press_event",
                      G_CALLBACK(button_event), inst);
     g_signal_connect(G_OBJECT(inst->area), "button_release_event",