summaryrefslogtreecommitdiffstats
path: root/src/gui_gtk_x11.c
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2004-06-13 20:20:40 +0000
committerBram Moolenaar <Bram@vim.org>2004-06-13 20:20:40 +0000
commit071d4279d6ab81b7187b48f3a0fc61e587b6db6c (patch)
tree221cbe3c40e043163c06f61c52a7ba2eb41e12ce /src/gui_gtk_x11.c
parentb4210b3bc14e2918f153a7307530fbe6eba659e1 (diff)
updated for version 7.0001v7.0001
Diffstat (limited to 'src/gui_gtk_x11.c')
-rw-r--r--src/gui_gtk_x11.c6589
1 files changed, 6589 insertions, 0 deletions
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
new file mode 100644
index 0000000000..e36c85827f
--- /dev/null
+++ b/src/gui_gtk_x11.c
@@ -0,0 +1,6589 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * Porting to GTK+ was done by:
+ *
+ * (C) 1998,1999,2000 by Marcin Dalecki <dalecki@evision.ag>
+ *
+ * With GREAT support and continuous encouragements by Andy Kahn and of
+ * course Bram Moolenaar!
+ *
+ * Support for GTK+ 2 was added by:
+ *
+ * (C) 2002,2003 Jason Hildebrand <jason@peaceworks.ca>
+ * Daniel Elstner <daniel.elstner@gmx.net>
+ */
+
+#include "vim.h"
+#ifdef FEAT_GUI_GNOME
+/* Gnome redefines _() and N_(). Grrr... */
+# ifdef _
+# undef _
+# endif
+# ifdef N_
+# undef N_
+# endif
+# ifdef textdomain
+# undef textdomain
+# endif
+# ifdef bindtextdomain
+# undef bindtextdomain
+# endif
+# if defined(FEAT_GETTEXT) && !defined(ENABLE_NLS)
+# define ENABLE_NLS /* so the texts in the dialog boxes are translated */
+# endif
+# include <gnome.h>
+# include "version.h"
+#endif
+
+#if !defined(FEAT_GUI_GTK) && defined(PROTO)
+/* When generating prototypes we don't want syntax errors. */
+# define GdkAtom int
+# define GdkEventExpose int
+# define GdkEventFocus int
+# define GdkEventVisibility int
+# define GdkEventProperty int
+# define GtkContainer int
+# define GtkTargetEntry int
+# define GtkType int
+# define GtkWidget int
+# define gint int
+# define gpointer int
+# define guint int
+# define GdkEventKey int
+# define GdkEventSelection int
+# define GtkSelectionData int
+# define GdkEventMotion int
+# define GdkEventButton int
+# define GdkDragContext int
+# define GdkEventConfigure int
+# define GdkEventClient int
+#else
+# include <gdk/gdkkeysyms.h>
+# include <gdk/gdk.h>
+# ifdef WIN3264
+# include <gdk/gdkwin32.h>
+# else
+# include <gdk/gdkx.h>
+# endif
+
+# include <gtk/gtk.h>
+# include "gui_gtk_f.h"
+#endif
+
+#ifdef HAVE_X11_SUNKEYSYM_H
+# include <X11/Sunkeysym.h>
+#endif
+
+/*
+ * Easy-to-use macro for multihead support.
+ */
+#ifdef HAVE_GTK_MULTIHEAD
+# define GET_X_ATOM(atom) gdk_x11_atom_to_xatom_for_display( \
+ gtk_widget_get_display(gui.mainwin), atom)
+#else
+# define GET_X_ATOM(atom) ((Atom)(atom))
+#endif
+
+/* Selection type distinguishers */
+enum
+{
+ TARGET_TYPE_NONE,
+ TARGET_UTF8_STRING,
+ TARGET_STRING,
+ TARGET_COMPOUND_TEXT,
+ TARGET_TEXT,
+ TARGET_TEXT_URI_LIST,
+ TARGET_TEXT_PLAIN,
+ TARGET_VIM,
+ TARGET_VIMENC
+};
+
+/*
+ * Table of selection targets supported by Vim.
+ * Note: Order matters, preferred types should come first.
+ */
+static const GtkTargetEntry selection_targets[] =
+{
+ {VIMENC_ATOM_NAME, 0, TARGET_VIMENC},
+ {VIM_ATOM_NAME, 0, TARGET_VIM},
+#ifdef FEAT_MBYTE
+ {"UTF8_STRING", 0, TARGET_UTF8_STRING},
+#endif
+ {"COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT},
+ {"TEXT", 0, TARGET_TEXT},
+ {"STRING", 0, TARGET_STRING}
+};
+#define N_SELECTION_TARGETS (sizeof(selection_targets) / sizeof(selection_targets[0]))
+
+#ifdef FEAT_DND
+/*
+ * Table of DnD targets supported by Vim.
+ * Note: Order matters, preferred types should come first.
+ */
+static const GtkTargetEntry dnd_targets[] =
+{
+ {"text/uri-list", 0, TARGET_TEXT_URI_LIST},
+# ifdef FEAT_MBYTE
+ {"UTF8_STRING", 0, TARGET_UTF8_STRING},
+# endif
+ {"STRING", 0, TARGET_STRING},
+ {"text/plain", 0, TARGET_TEXT_PLAIN}
+};
+# define N_DND_TARGETS (sizeof(dnd_targets) / sizeof(dnd_targets[0]))
+#endif
+
+
+#ifdef HAVE_GTK2
+/*
+ * "Monospace" is a standard font alias that should be present
+ * on all proper Pango/fontconfig installations.
+ */
+# define DEFAULT_FONT "Monospace 10"
+
+#else /* !HAVE_GTK2 */
+/*
+ * This is the single only fixed width font in X11, which seems to be present
+ * on all servers and available in all the variants we need.
+ */
+# define DEFAULT_FONT "-adobe-courier-medium-r-normal-*-14-*-*-*-m-*-*-*"
+
+#endif /* !HAVE_GTK2 */
+
+#if !(defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION))
+/*
+ * Atoms used to communicate save-yourself from the X11 session manager. There
+ * is no need to move them into the GUI struct, since they should be constant.
+ */
+static GdkAtom wm_protocols_atom = GDK_NONE;
+static GdkAtom save_yourself_atom = GDK_NONE;
+#endif
+
+/*
+ * Atoms used to control/reference X11 selections.
+ */
+#ifdef FEAT_MBYTE
+static GdkAtom utf8_string_atom = GDK_NONE;
+#endif
+#ifndef HAVE_GTK2
+static GdkAtom compound_text_atom = GDK_NONE;
+static GdkAtom text_atom = GDK_NONE;
+#endif
+static GdkAtom vim_atom = GDK_NONE; /* Vim's own special selection format */
+#ifdef FEAT_MBYTE
+static GdkAtom vimenc_atom = GDK_NONE; /* Vim's extended selection format */
+#endif
+
+/*
+ * Keycodes recognized by vim.
+ * NOTE: when changing this, the table in gui_x11.c probably needs the same
+ * change!
+ */
+static struct special_key
+{
+ guint key_sym;
+ char_u code0;
+ char_u code1;
+}
+const special_keys[] =
+{
+ {GDK_Up, 'k', 'u'},
+ {GDK_Down, 'k', 'd'},
+ {GDK_Left, 'k', 'l'},
+ {GDK_Right, 'k', 'r'},
+ {GDK_F1, 'k', '1'},
+ {GDK_F2, 'k', '2'},
+ {GDK_F3, 'k', '3'},
+ {GDK_F4, 'k', '4'},
+ {GDK_F5, 'k', '5'},
+ {GDK_F6, 'k', '6'},
+ {GDK_F7, 'k', '7'},
+ {GDK_F8, 'k', '8'},
+ {GDK_F9, 'k', '9'},
+ {GDK_F10, 'k', ';'},
+ {GDK_F11, 'F', '1'},
+ {GDK_F12, 'F', '2'},
+ {GDK_F13, 'F', '3'},
+ {GDK_F14, 'F', '4'},
+ {GDK_F15, 'F', '5'},
+ {GDK_F16, 'F', '6'},
+ {GDK_F17, 'F', '7'},
+ {GDK_F18, 'F', '8'},
+ {GDK_F19, 'F', '9'},
+ {GDK_F20, 'F', 'A'},
+ {GDK_F21, 'F', 'B'},
+ {GDK_Pause, 'F', 'B'}, /* Pause == F21 according to netbeans.txt */
+ {GDK_F22, 'F', 'C'},
+ {GDK_F23, 'F', 'D'},
+ {GDK_F24, 'F', 'E'},
+ {GDK_F25, 'F', 'F'},
+ {GDK_F26, 'F', 'G'},
+ {GDK_F27, 'F', 'H'},
+ {GDK_F28, 'F', 'I'},
+ {GDK_F29, 'F', 'J'},
+ {GDK_F30, 'F', 'K'},
+ {GDK_F31, 'F', 'L'},
+ {GDK_F32, 'F', 'M'},
+ {GDK_F33, 'F', 'N'},
+ {GDK_F34, 'F', 'O'},
+ {GDK_F35, 'F', 'P'},
+#ifdef SunXK_F36
+ {SunXK_F36, 'F', 'Q'},
+ {SunXK_F37, 'F', 'R'},
+#endif
+ {GDK_Help, '%', '1'},
+ {GDK_Undo, '&', '8'},
+ {GDK_BackSpace, 'k', 'b'},
+ {GDK_Insert, 'k', 'I'},
+ {GDK_Delete, 'k', 'D'},
+ {GDK_3270_BackTab, 'k', 'B'},
+ {GDK_Clear, 'k', 'C'},
+ {GDK_Home, 'k', 'h'},
+ {GDK_End, '@', '7'},
+ {GDK_Prior, 'k', 'P'},
+ {GDK_Next, 'k', 'N'},
+ {GDK_Print, '%', '9'},
+ /* Keypad keys: */
+ {GDK_KP_Left, 'k', 'l'},
+ {GDK_KP_Right, 'k', 'r'},
+ {GDK_KP_Up, 'k', 'u'},
+ {GDK_KP_Down, 'k', 'd'},
+ {GDK_KP_Insert, KS_EXTRA, (char_u)KE_KINS},
+ {GDK_KP_Delete, KS_EXTRA, (char_u)KE_KDEL},
+ {GDK_KP_Home, 'K', '1'},
+ {GDK_KP_End, 'K', '4'},
+ {GDK_KP_Prior, 'K', '3'}, /* page up */
+ {GDK_KP_Next, 'K', '5'}, /* page down */
+
+ {GDK_KP_Add, 'K', '6'},
+ {GDK_KP_Subtract, 'K', '7'},
+ {GDK_KP_Divide, 'K', '8'},
+ {GDK_KP_Multiply, 'K', '9'},
+ {GDK_KP_Enter, 'K', 'A'},
+ {GDK_KP_Decimal, 'K', 'B'},
+
+ {GDK_KP_0, 'K', 'C'},
+ {GDK_KP_1, 'K', 'D'},
+ {GDK_KP_2, 'K', 'E'},
+ {GDK_KP_3, 'K', 'F'},
+ {GDK_KP_4, 'K', 'G'},
+ {GDK_KP_5, 'K', 'H'},
+ {GDK_KP_6, 'K', 'I'},
+ {GDK_KP_7, 'K', 'J'},
+ {GDK_KP_8, 'K', 'K'},
+ {GDK_KP_9, 'K', 'L'},
+
+ /* End of list marker: */
+ {0, 0, 0}
+};
+
+/*
+ * Flags for command line options table below.
+ */
+#define ARG_FONT 1
+#define ARG_GEOMETRY 2
+#define ARG_REVERSE 3
+#define ARG_NOREVERSE 4
+#define ARG_BACKGROUND 5
+#define ARG_FOREGROUND 6
+#define ARG_ICONIC 7
+#define ARG_ROLE 8
+#define ARG_NETBEANS 9
+#define ARG_XRM 10 /* ignored */
+#define ARG_MENUFONT 11 /* ignored */
+#define ARG_INDEX_MASK 0x00ff
+#define ARG_HAS_VALUE 0x0100 /* a value is expected after the argument */
+#define ARG_NEEDS_GUI 0x0200 /* need to initialize the GUI for this */
+#define ARG_FOR_GTK 0x0400 /* argument is handled by GTK+ or GNOME */
+#define ARG_COMPAT_LONG 0x0800 /* accept -foo but substitute with --foo */
+#define ARG_KEEP 0x1000 /* don't remove argument from argv[] */
+
+/*
+ * This table holds all the X GUI command line options allowed. This includes
+ * the standard ones so that we can skip them when Vim is started without the
+ * GUI (but the GUI might start up later).
+ *
+ * When changing this, also update doc/gui_x11.txt and the usage message!!!
+ */
+typedef struct
+{
+ const char *name;
+ unsigned int flags;
+}
+cmdline_option_T;
+
+static const cmdline_option_T cmdline_options[] =
+{
+ /* We handle these options ourselves */
+ {"-fn", ARG_FONT|ARG_HAS_VALUE},
+ {"-font", ARG_FONT|ARG_HAS_VALUE},
+ {"-geom", ARG_GEOMETRY|ARG_HAS_VALUE},
+ {"-geometry", ARG_GEOMETRY|ARG_HAS_VALUE},
+ {"-rv", ARG_REVERSE},
+ {"-reverse", ARG_REVERSE},
+ {"+rv", ARG_NOREVERSE},
+ {"+reverse", ARG_NOREVERSE},
+ {"-bg", ARG_BACKGROUND|ARG_HAS_VALUE},
+ {"-background", ARG_BACKGROUND|ARG_HAS_VALUE},
+ {"-fg", ARG_FOREGROUND|ARG_HAS_VALUE},
+ {"-foreground", ARG_FOREGROUND|ARG_HAS_VALUE},
+ {"-iconic", ARG_ICONIC},
+#ifdef HAVE_GTK2
+ {"--role", ARG_ROLE|ARG_HAS_VALUE},
+#endif
+#ifdef FEAT_NETBEANS_INTG
+ {"-nb", ARG_NETBEANS}, /* non-standard value format */
+ {"-xrm", ARG_XRM|ARG_HAS_VALUE}, /* not implemented */
+ {"-mf", ARG_MENUFONT|ARG_HAS_VALUE}, /* not implemented */
+ {"-menufont", ARG_MENUFONT|ARG_HAS_VALUE}, /* not implemented */
+#endif
+#if 0 /* not implemented; these arguments don't make sense for GTK+ */
+ {"-boldfont", ARG_HAS_VALUE},
+ {"-italicfont", ARG_HAS_VALUE},
+ {"-bw", ARG_HAS_VALUE},
+ {"-borderwidth", ARG_HAS_VALUE},
+ {"-sw", ARG_HAS_VALUE},
+ {"-scrollbarwidth", ARG_HAS_VALUE},
+#endif
+ /* Arguments handled by GTK (and GNOME) internally. */
+ {"--g-fatal-warnings", ARG_FOR_GTK},
+ {"--gdk-debug", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--gdk-no-debug", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--gtk-debug", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--gtk-no-debug", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--gtk-module", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--sync", ARG_FOR_GTK},
+ {"--display", ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG},
+ {"--name", ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG},
+ {"--class", ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG},
+#ifdef HAVE_GTK2
+ {"--screen", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--gxid-host", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--gxid-port", ARG_FOR_GTK|ARG_HAS_VALUE},
+#else /* these don't seem to exist anymore */
+ {"--no-xshm", ARG_FOR_GTK},
+ {"--xim-preedit", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--xim-status", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--gxid_host", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--gxid_port", ARG_FOR_GTK|ARG_HAS_VALUE},
+#endif
+#ifdef FEAT_GUI_GNOME
+ {"--load-modules", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--sm-client-id", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--sm-config-prefix", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--sm-disable", ARG_FOR_GTK},
+ {"--oaf-ior-fd", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--oaf-activate-iid", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"--oaf-private", ARG_FOR_GTK},
+ {"--enable-sound", ARG_FOR_GTK},
+ {"--disable-sound", ARG_FOR_GTK},
+ {"--espeaker", ARG_FOR_GTK|ARG_HAS_VALUE},
+ {"-?", ARG_FOR_GTK|ARG_NEEDS_GUI},
+ {"--help", ARG_FOR_GTK|ARG_NEEDS_GUI|ARG_KEEP},
+ {"--usage", ARG_FOR_GTK|ARG_NEEDS_GUI},
+# if 0 /* conflicts with Vim's own --version argument */
+ {"--version", ARG_FOR_GTK|ARG_NEEDS_GUI},
+# endif
+ {"--disable-crash-dialog", ARG_FOR_GTK},
+#endif
+ {NULL, 0}
+};
+
+static int gui_argc = 0;
+static char **gui_argv = NULL;
+
+#ifdef HAVE_GTK2
+static const char *role_argument = NULL;
+#endif
+#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)
+static const char *restart_command = NULL;
+#endif
+static int found_iconic_arg = FALSE;
+
+#ifdef FEAT_GUI_GNOME
+/*
+ * Can't use Gnome if --socketid given
+ */
+static int using_gnome = 0;
+#else
+# define using_gnome 0
+#endif
+
+/*
+ * Parse the GUI related command-line arguments. Any arguments used are
+ * deleted from argv, and *argc is decremented accordingly. This is called
+ * when vim is started, whether or not the GUI has been started.
+ */
+ void
+gui_mch_prepare(int *argc, char **argv)
+{
+ const cmdline_option_T *option;
+ int i = 0;
+ int len = 0;
+
+#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)
+ /*
+ * Determine the command used to invoke Vim, to be passed as restart
+ * command to the session manager. If argv[0] contains any directory
+ * components try building an absolute path, otherwise leave it as is.
+ */
+ restart_command = argv[0];
+
+ if (strchr(argv[0], G_DIR_SEPARATOR) != NULL)
+ {
+ char_u buf[MAXPATHL];
+
+ if (mch_FullName((char_u *)argv[0], buf, (int)sizeof(buf), TRUE) == OK)
+ /* Tiny leak; doesn't matter, and usually we don't even get here */
+ restart_command = (char *)vim_strsave(buf);
+ }
+#endif
+
+ /*
+ * Move all the entries in argv which are relevant to GTK+ and GNOME
+ * into gui_argv. Freed later in gui_mch_init().
+ */
+ gui_argc = 0;
+ gui_argv = (char **)alloc((unsigned)((*argc + 1) * sizeof(char *)));
+
+ g_return_if_fail(gui_argv != NULL);
+
+ gui_argv[gui_argc++] = argv[i++];
+
+ while (i < *argc)
+ {
+ /* Don't waste CPU cycles on non-option arguments. */
+ if (argv[i][0] != '-' && argv[i][0] != '+')
+ {
+ ++i;
+ continue;
+ }
+
+ /* Look for argv[i] in cmdline_options[] table. */
+ for (option = &cmdline_options[0]; option->name != NULL; ++option)
+ {
+ len = strlen(option->name);
+
+ if (strncmp(argv[i], option->name, len) == 0)
+ {
+ if (argv[i][len] == '\0')
+ break;
+ /* allow --foo=bar style */
+ if (argv[i][len] == '=' && (option->flags & ARG_HAS_VALUE))
+ break;
+#ifdef FEAT_NETBEANS_INTG
+ /* darn, -nb has non-standard syntax */
+ if (vim_strchr((char_u *)":=", argv[i][len]) != NULL
+ && (option->flags & ARG_INDEX_MASK) == ARG_NETBEANS)
+ break;
+#endif
+ }
+ else if ((option->flags & ARG_COMPAT_LONG)
+ && strcmp(argv[i], option->name + 1) == 0)
+ {
+ /* Replace the standard X arguments "-name" and "-display"
+ * with their GNU-style long option counterparts. */
+ argv[i] = (char *)option->name;
+ break;
+ }
+ }
+ if (option->name == NULL) /* no match */
+ {
+ ++i;
+ continue;
+ }
+
+ if (option->flags & ARG_FOR_GTK)
+ {
+ /* Move the argument into gui_argv, which
+ * will later be passed to gtk_init_check() */
+ gui_argv[gui_argc++] = argv[i];
+ }
+ else
+ {
+ char *value = NULL;
+
+ /* Extract the option's value if there is one.
+ * Accept both "--foo bar" and "--foo=bar" style. */
+ if (option->flags & ARG_HAS_VALUE)
+ {
+ if (argv[i][len] == '=')
+ value = &argv[i][len + 1];
+ else if (i + 1 < *argc && strcmp(argv[i + 1], "--") != 0)
+ value = argv[i + 1];
+ }
+
+ /* Check for options handled by Vim itself */
+ switch (option->flags & ARG_INDEX_MASK)
+ {
+ case ARG_REVERSE:
+ found_reverse_arg = TRUE;
+ break;
+ case ARG_NOREVERSE:
+ found_reverse_arg = FALSE;
+ break;
+ case ARG_FONT:
+ font_argument = value;
+ break;
+ case ARG_GEOMETRY:
+ if (value != NULL)
+ gui.geom = vim_strsave((char_u *)value);
+ break;
+ case ARG_BACKGROUND:
+ background_argument = value;
+ break;
+ case ARG_FOREGROUND:
+ foreground_argument = value;
+ break;
+ case ARG_ICONIC:
+ found_iconic_arg = TRUE;
+ break;
+#ifdef HAVE_GTK2
+ case ARG_ROLE:
+ role_argument = value; /* used later in gui_mch_open() */
+ break;
+#endif
+#ifdef FEAT_NETBEANS_INTG
+ case ARG_NETBEANS:
+ ++usingNetbeans;
+ gui.dofork = FALSE; /* don't fork() when starting GUI */
+ netbeansArg = argv[i];
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+ /* These arguments make gnome_program_init() print a message and exit.
+ * Must start the GUI for this, otherwise ":gui" will exit later! */
+ if (option->flags & ARG_NEEDS_GUI)
+ gui.starting = TRUE;
+
+ if (option->flags & ARG_KEEP)
+ ++i;
+ else
+ {
+ /* Remove the flag from the argument vector. */
+ if (--*argc > i)
+ {
+ int n_strip = 1;
+
+ /* Move the argument's value as well, if there is one. */
+ if ((option->flags & ARG_HAS_VALUE)
+ && argv[i][len] != '='
+ && strcmp(argv[i + 1], "--") != 0)
+ {
+ ++n_strip;
+ --*argc;
+ if (option->flags & ARG_FOR_GTK)
+ gui_argv[gui_argc++] = argv[i + 1];
+ }
+
+ if (*argc > i)
+ mch_memmove(&argv[i], &argv[i + n_strip],
+ (*argc - i) * sizeof(char *));
+ }
+ argv[*argc] = NULL;
+ }
+ }
+
+ gui_argv[gui_argc] = NULL;
+}
+
+/*
+ * This should be maybe completely removed.
+ * Doesn't seem possible, since check_copy_area() relies on
+ * this information. --danielk
+ */
+/*ARGSUSED*/
+ static gint
+visibility_event(GtkWidget *widget, GdkEventVisibility *event, gpointer data)
+{
+ gui.visibility = event->state;
+ /*
+ * When we do an gdk_window_copy_area(), and the window is partially
+ * obscured, we want to receive an event to tell us whether it worked
+ * or not.
+ */
+ if (gui.text_gc != NULL)
+ gdk_gc_set_exposures(gui.text_gc,
+ gui.visibility != GDK_VISIBILITY_UNOBSCURED);
+ return FALSE;
+}
+
+/*
+ * Redraw the corresponding portions of the screen.
+ */
+/*ARGSUSED*/
+ static gint
+expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ /* Skip this when the GUI isn't set up yet, will redraw later. */
+ if (gui.starting)
+ return FALSE;
+
+ out_flush(); /* make sure all output has been processed */
+ gui_redraw(event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ /* Clear the border areas if needed */
+ if (event->area.x < FILL_X(0))
+ gdk_window_clear_area(gui.drawarea->window, 0, 0, FILL_X(0), 0);
+ if (event->area.y < FILL_Y(0))
+ gdk_window_clear_area(gui.drawarea->window, 0, 0, 0, FILL_Y(0));
+ if (event->area.x > FILL_X(Columns))
+ gdk_window_clear_area(gui.drawarea->window,
+ FILL_X((int)Columns), 0, 0, 0);
+ if (event->area.y > FILL_Y(Rows))
+ gdk_window_clear_area(gui.drawarea->window, 0, FILL_Y((int)Rows), 0, 0);
+
+ return FALSE;
+}
+
+#ifdef FEAT_CLIENTSERVER
+/*
+ * Handle changes to the "Comm" property
+ */
+/*ARGSUSED2*/
+ static gint
+property_event(GtkWidget *widget, GdkEventProperty *event, gpointer data)
+{
+ if (event->type == GDK_PROPERTY_NOTIFY
+ && event->state == (int)GDK_PROPERTY_NEW_VALUE
+ && GDK_WINDOW_XWINDOW(event->window) == commWindow
+ && GET_X_ATOM(event->atom) == commProperty)
+ {
+ XEvent xev;
+
+ /* Translate to XLib */
+ xev.xproperty.type = PropertyNotify;
+ xev.xproperty.atom = commProperty;
+ xev.xproperty.window = commWindow;
+ xev.xproperty.state = PropertyNewValue;
+ serverEventProc(GDK_WINDOW_XDISPLAY(widget->window), &xev);
+
+ if (gtk_main_level() > 0)
+ gtk_main_quit();
+ }
+ return FALSE;
+}
+#endif
+
+
+/****************************************************************************
+ * Focus handlers:
+ */
+
+
+/*
+ * This is a simple state machine:
+ * BLINK_NONE not blinking at all
+ * BLINK_OFF blinking, cursor is not shown
+ * BLINK_ON blinking, cursor is shown
+ */
+
+#define BLINK_NONE 0
+#define BLINK_OFF 1
+#define BLINK_ON 2
+
+static int blink_state = BLINK_NONE;
+static long_u blink_waittime = 700;
+static long_u blink_ontime = 400;
+static long_u blink_offtime = 250;
+static guint blink_timer = 0;
+
+ void
+gui_mch_set_blinking(long waittime, long on, long off)
+{
+ blink_waittime = waittime;
+ blink_ontime = on;
+ blink_offtime = off;
+}
+
+/*
+ * Stop the cursor blinking. Show the cursor if it wasn't shown.
+ */
+ void
+gui_mch_stop_blink(void)
+{
+ if (blink_timer)
+ {
+ gtk_timeout_remove(blink_timer);
+ blink_timer = 0;
+ }
+ if (blink_state == BLINK_OFF)
+ gui_update_cursor(TRUE, FALSE);
+ blink_state = BLINK_NONE;
+}
+
+/*ARGSUSED*/
+ static gint
+blink_cb(gpointer data)
+{
+ if (blink_state == BLINK_ON)
+ {
+ gui_undraw_cursor();
+ blink_state = BLINK_OFF;
+ blink_timer = gtk_timeout_add((guint32)blink_offtime,
+ (GtkFunction) blink_cb, NULL);
+ }
+ else
+ {
+ gui_update_cursor(TRUE, FALSE);
+ blink_state = BLINK_ON;
+ blink_timer = gtk_timeout_add((guint32)blink_ontime,
+ (GtkFunction) blink_cb, NULL);
+ }
+
+ return FALSE; /* don't happen again */
+}
+
+/*
+ * Start the cursor blinking. If it was already blinking, this restarts the
+ * waiting time and shows the cursor.
+ */
+ void
+gui_mch_start_blink(void)
+{
+ if (blink_timer)
+ gtk_timeout_remove(blink_timer);
+ /* Only switch blinking on if none of the times is zero */
+ if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
+ {
+ blink_timer = gtk_timeout_add((guint32)blink_waittime,
+ (GtkFunction) blink_cb, NULL);
+ blink_state = BLINK_ON;
+ gui_update_cursor(TRUE, FALSE);
+ }
+}
+
+/*ARGSUSED*/
+ static gint
+enter_notify_event(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
+{
+ if (blink_state == BLINK_NONE)
+ gui_mch_start_blink();
+
+ /* make sure keyboard input goes there */
+ if (gtk_socket_id == 0 || !GTK_WIDGET_HAS_FOCUS(gui.drawarea))
+ gtk_widget_grab_focus(gui.drawarea);
+
+ return FALSE;
+}
+
+/*ARGSUSED*/
+ static gint
+leave_notify_event(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
+{
+ if (blink_state != BLINK_NONE)
+ gui_mch_stop_blink();
+
+ return FALSE;
+}
+
+/*ARGSUSED*/
+ static gint
+focus_in_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
+{
+ gui_focus_change(TRUE);
+
+ if (blink_state == BLINK_NONE)
+ gui_mch_start_blink();
+
+ /* make sure keyboard input goes there */
+ if (gtk_socket_id == 0)
+ gtk_widget_grab_focus(gui.drawarea);
+
+ return TRUE;
+}
+
+/*ARGSUSED*/
+ static gint
+focus_out_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
+{
+ gui_focus_change(FALSE);
+
+ if (blink_state != BLINK_NONE)
+ gui_mch_stop_blink();
+
+ return TRUE;
+}
+
+
+#ifdef HAVE_GTK2
+/*
+ * Translate a GDK key value to UTF-8 independently of the current locale.
+ * The output is written to string, which must have room for at least 6 bytes
+ * plus the NUL terminator. Returns the length in bytes.
+ *
+ * This function is used in the GTK+ 2 GUI only. The GTK+ 1 code makes use
+ * of GdkEventKey::string instead. But event->string is evil; see here why:
+ * http://developer.gnome.org/doc/API/2.0/gdk/gdk-Event-Structures.html#GdkEventKey
+ */
+ static int
+keyval_to_string(unsigned int keyval, unsigned int state, char_u *string)
+{
+ int len;
+ guint32 uc;
+
+ uc = gdk_keyval_to_unicode(keyval);
+ if (uc != 0)
+ {
+ /* Check for CTRL-foo */
+ if ((state & GDK_CONTROL_MASK) && uc >= 0x20 && uc < 0x80)
+ {
+ /* These mappings look arbitrary at the first glance, but in fact
+ * resemble quite exactly the behaviour of the GTK+ 1.2 GUI on my
+ * machine. The only difference is BS vs. DEL for CTRL-8 (makes
+ * more sense and is consistent with usual terminal behaviour). */
+ if (uc >= '@')
+ string[0] = uc & 0x1F;
+ else if (uc == '2')
+ string[0] = NUL;
+ else if (uc >= '3' && uc <= '7')
+ string[0] = uc ^ 0x28;
+ else if (uc == '8')
+ string[0] = BS;
+ else if (uc == '?')
+ string[0] = DEL;
+ else
+ string[0] = uc;
+ len = 1;
+ }
+ else
+ {
+ /* Translate a normal key to UTF-8. This doesn't work for dead
+ * keys of course, you _have_ to use an input method for that. */
+ len = utf_char2bytes((int)uc, string);
+ }
+ }
+ else
+ {
+ /* Translate keys which are represented by ASCII control codes in Vim.
+ * There are only a few of those; most control keys are translated to
+ * special terminal-like control sequences. */
+ len = 1;
+ switch (keyval)
+ {
+ case GDK_Tab: case GDK_KP_Tab: case GDK_ISO_Left_Tab:
+ string[0] = TAB;
+ break;
+ case GDK_Linefeed:
+ string[0] = NL;
+ break;
+ case GDK_Return: case GDK_ISO_Enter: case GDK_3270_Enter:
+ string[0] = CAR;
+ break;
+ case GDK_Escape:
+ string[0] = ESC;
+ break;
+ default:
+ len = 0;
+ break;
+ }
+ }
+ string[len] = NUL;
+
+ return len;
+}
+#endif /* HAVE_GTK2 */
+
+/*
+ * Main keyboard handler:
+ */
+/*ARGSUSED*/
+ static gint
+key_press_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+#ifdef HAVE_GTK2
+ /* 256 bytes is way over the top, but for safety let's reduce it only
+ * for GTK+ 2 where we know for sure how large the string might get.
+ * (That is, up to 6 bytes + NUL + CSI escapes + safety measure.) */
+ char_u string[32], string2[32];
+#else
+ char_u string[256], string2[256];
+#endif
+ guint key_sym;
+ int len;
+ int i;
+ int modifiers;
+ int key;
+ guint state;
+ char_u *s, *d;
+
+ key_sym = event->keyval;
+ state = event->state;
+#ifndef HAVE_GTK2 /* deprecated */
+ len = event->length;
+ g_assert(len <= sizeof(string));
+#endif
+
+#ifndef HAVE_GTK2
+ /*
+ * It appears as if we always want to consume a key-press (there currently
+ * aren't any 'return FALSE's), so we always do this: when running in a
+ * GtkPlug and not a window, we must prevent emission of the key_press
+ * EVENT from continuing (which is 'beyond' the level of stopping mere
+ * signals by returning FALSE), otherwise things like tab/cursor-keys are
+ * processed by the GtkPlug default handler, which moves input focus away
+ * from us!
+ * Note: This should no longer be necessary with GTK+ 2.
+ */
+ if (gtk_socket_id != 0)
+ gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");
+#endif
+
+#ifdef FEAT_XIM
+ if (xim_queue_key_press_event(event, TRUE))
+ return TRUE;
+#endif
+
+#ifdef FEAT_HANGULIN
+ if (key_sym == GDK_space && (state & GDK_SHIFT_MASK))
+ {
+ hangul_input_state_toggle();
+ return TRUE;
+ }
+#endif
+
+#ifdef SunXK_F36
+ /*
+ * These keys have bogus lookup strings, and trapping them here is
+ * easier than trying to XRebindKeysym() on them with every possible
+ * combination of modifiers.
+ */
+ if (key_sym == SunXK_F36 || key_sym == SunXK_F37)
+ len = 0;
+ else
+#endif
+ {
+#ifdef HAVE_GTK2
+ len = keyval_to_string(key_sym, state, string2);
+
+ /* Careful: convert_input() doesn't handle the NUL character.
+ * No need to convert pure ASCII anyway, thus the len > 1 check. */
+ if (len > 1 && input_conv.vc_type != CONV_NONE)
+ len = convert_input(string2, len, sizeof(string2));
+
+ s = string2;
+#else
+# ifdef FEAT_MBYTE
+ if (input_conv.vc_type != CONV_NONE)
+ {
+ mch_memmove(string2, event->string, len);
+ len = convert_input(string2, len, sizeof(string2));
+ s = string2;
+ }
+ else
+# endif
+ s = (char_u *)event->string;
+#endif
+
+ d = string;
+ for (i = 0; i < len; ++i)
+ {
+ *d++ = s[i];
+ if (d[-1] == CSI && d + 2 < string + sizeof(string))
+ {
+ /* Turn CSI into K_CSI. */
+ *d++ = KS_EXTRA;
+ *d++ = (int)KE_CSI;
+ }
+ }
+ len = d - string;
+ }
+
+ /* Shift-Tab results in Left_Tab, but we want <S-Tab> */
+ if (key_sym == GDK_ISO_Left_Tab)
+ {
+ key_sym = GDK_Tab;
+ state |= GDK_SHIFT_MASK;
+ }
+
+#ifndef HAVE_GTK2 /* for GTK+ 2, we handle this in keyval_to_string() */
+ if ((key_sym == GDK_2 || key_sym == GDK_at) && (state & GDK_CONTROL_MASK))
+ {
+ string[0] = NUL; /* CTRL-2 and CTRL-@ is NUL */
+ len = 1;
+ }
+ else if (len == 0 && (key_sym == GDK_space || key_sym == GDK_Tab))
+ {
+ /* When there are modifiers, these keys get zero length; we need the
+ * original key here to be able to add a modifier below. */
+ string[0] = (key_sym & 0xff);
+ len = 1;
+ }
+#endif
+
+#ifdef FEAT_MENU
+ /* If there is a menu and 'wak' is "yes", or 'wak' is "menu" and the key
+ * is a menu shortcut, we ignore everything with the ALT modifier. */
+ if ((state & GDK_MOD1_MASK)
+ && gui.menu_is_active
+ && (*p_wak == 'y'
+ || (*p_wak == 'm'
+ && len == 1
+ && gui_is_menu_shortcut(string[0]))))
+# ifdef HAVE_GTK2
+ /* For GTK2 we return false to signify that we haven't handled the
+ * keypress, so that gtk will handle the mnemonic or accelerator. */
+ return FALSE;
+# else
+ return TRUE;
+# endif
+#endif
+
+ /* Check for Alt/Meta key (Mod1Mask), but not for a BS, DEL or character
+ * that already has the 8th bit set.
+ * Don't do this for <S-M-Tab>, that should become K_S_TAB with ALT.
+ * Don't do this for double-byte encodings, it turns the char into a lead
+ * byte. */
+ if (len == 1
+ && (state & GDK_MOD1_MASK)
+ && !(key_sym == GDK_BackSpace || key_sym == GDK_Delete)
+ && (string[0] & 0x80) == 0
+ && !(key_sym == GDK_Tab && (state & GDK_SHIFT_MASK))
+#ifdef FEAT_MBYTE
+ && !enc_dbcs
+#endif
+ )
+ {
+ string[0] |= 0x80;
+ state &= ~GDK_MOD1_MASK; /* don't use it again */
+#ifdef FEAT_MBYTE
+ if (enc_utf8) /* convert to utf-8 */
+ {
+ string[1] = string[0] & 0xbf;
+ string[0] = ((unsigned)string[0] >> 6) + 0xc0;
+ if (string[1] == CSI)
+ {
+ string[2] = KS_EXTRA;
+ string[3] = (int)KE_CSI;
+ len = 4;
+ }
+ else
+ len = 2;
+ }
+#endif
+ }
+
+ /* Check for special keys. Also do this when len == 1 (key has an ASCII
+ * value) to detect backspace, delete and keypad keys. */
+ if (len == 0 || len == 1)
+ {
+ for (i = 0; special_keys[i].key_sym != 0; i++)
+ {
+ if (special_keys[i].key_sym == key_sym)
+ {
+ string[0] = CSI;
+ string[1] = special_keys[i].code0;
+ string[2] = special_keys[i].code1;
+ len = -3;
+ break;
+ }
+ }
+ }
+
+ if (len == 0) /* Unrecognized key */
+ return TRUE;
+
+#if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) && !defined(HAVE_GTK2)
+ /* Cancel or type backspace. For GTK2, im_commit_cb() does the same. */
+ preedit_start_col = MAXCOL;
+ xim_changed_while_preediting = TRUE;
+#endif
+
+ /* Special keys (and a few others) may have modifiers. Also when using a
+ * double-byte encoding (can't set the 8th bit). */
+ if (len == -3 || key_sym == GDK_space || key_sym == GDK_Tab
+ || key_sym == GDK_Return || key_sym == GDK_Linefeed
+ || key_sym == GDK_Escape || key_sym == GDK_KP_Tab
+ || key_sym == GDK_ISO_Enter || key_sym == GDK_3270_Enter
+#ifdef FEAT_MBYTE
+ || (enc_dbcs && len == 1 && (state & GDK_MOD1_MASK))
+#endif
+ )
+ {
+ modifiers = 0;
+ if (state & GDK_SHIFT_MASK)
+ modifiers |= MOD_MASK_SHIFT;
+ if (state & GDK_CONTROL_MASK)
+ modifiers |= MOD_MASK_CTRL;
+ if (state & GDK_MOD1_MASK)
+ modifiers |= MOD_MASK_ALT;
+
+ /*
+ * For some keys a shift modifier is translated into another key
+ * code.
+ */
+ if (len == -3)
+ key = TO_SPECIAL(string[1], string[2]);
+ else
+ key = string[0];
+
+ key = simplify_key(key, &modifiers);
+ if (key == CSI)
+ key = K_CSI;
+ if (IS_SPECIAL(key))
+ {
+ string[0] = CSI;
+ string[1] = K_SECOND(key);
+ string[2] = K_THIRD(key);
+ len = 3;
+ }
+ else
+ {
+ string[0] = key;
+ len = 1;
+ }
+
+ if (modifiers != 0)
+ {
+ string2[0] = CSI;
+ string2[1] = KS_MODIFIER;
+ string2[2] = modifiers;
+ add_to_input_buf(string2, 3);
+ }
+ }
+
+ if (len == 1 && ((string[0] == Ctrl_C && ctrl_c_interrupts)
+ || (string[0] == intr_char && intr_char != Ctrl_C)))
+ {
+ trash_input_buf();
+ got_int = TRUE;
+ }
+
+ add_to_input_buf(string, len);
+
+ /* blank out the pointer if necessary */
+ if (p_mh)
+ gui_mch_mousehide(TRUE);
+
+ if (gtk_main_level() > 0)
+ gtk_main_quit();
+
+