diff options
author | Bram Moolenaar <Bram@vim.org> | 2004-06-13 20:20:40 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2004-06-13 20:20:40 +0000 |
commit | 071d4279d6ab81b7187b48f3a0fc61e587b6db6c (patch) | |
tree | 221cbe3c40e043163c06f61c52a7ba2eb41e12ce /src/gui_gtk_x11.c | |
parent | b4210b3bc14e2918f153a7307530fbe6eba659e1 (diff) |
updated for version 7.0001v7.0001
Diffstat (limited to 'src/gui_gtk_x11.c')
-rw-r--r-- | src/gui_gtk_x11.c | 6589 |
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(); + + |