#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)}
#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;
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;
#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,
return TRUE;
}
+#endif
#define KEY_PRESSED(k) \
(inst->keystate[(k) / 32] & (1 << ((k) % 32)))
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.
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)) {
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)
if (condition & GDK_INPUT_WRITE)
select_result(sourcefd, 2);
}
+#endif
void destroy(GtkWidget *widget, gpointer data)
{
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;
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]);
}
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 =
}
}
- 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. */
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;
exit(1);
}
-char *get_x_display(void *frontend)
+const char *get_x_display(void *frontend)
{
return gdk_get_display();
}
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
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)
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",