blob: a5af3fa21cbbabdfab6d19f07ba0bf6e3dd76191 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * Porting to GTK+ was done by:
12 *
Bram Moolenaar0a56cb82005-01-04 21:45:14 +000013 * (C) 1998,1999,2000 by Marcin Dalecki <martin@dalecki.de>
Bram Moolenaar071d4272004-06-13 20:20:40 +000014 *
15 * With GREAT support and continuous encouragements by Andy Kahn and of
16 * course Bram Moolenaar!
17 *
18 * Support for GTK+ 2 was added by:
19 *
20 * (C) 2002,2003 Jason Hildebrand <jason@peaceworks.ca>
21 * Daniel Elstner <daniel.elstner@gmx.net>
Bram Moolenaar98921892016-02-23 17:14:37 +010022 *
23 * Support for GTK+ 3 was added by:
24 *
25 * 2016 Kazunobu Kuriyama <kazunobu.kuriyama@gmail.com>
Bram Moolenaar071d4272004-06-13 20:20:40 +000026 */
27
28#include "vim.h"
Bram Moolenaar36e294c2015-12-29 18:55:46 +010029#ifdef USE_GRESOURCE
30#include "auto/gui_gtk_gresources.h"
31#endif
Bram Moolenaarc93e7912008-07-08 10:46:08 +000032
Bram Moolenaar071d4272004-06-13 20:20:40 +000033#ifdef FEAT_GUI_GNOME
Bram Moolenaar30613902019-12-01 22:11:18 +010034// Gnome redefines _() and N_(). Grrr...
Bram Moolenaar071d4272004-06-13 20:20:40 +000035# ifdef _
36# undef _
37# endif
38# ifdef N_
39# undef N_
40# endif
41# ifdef textdomain
42# undef textdomain
43# endif
44# ifdef bindtextdomain
45# undef bindtextdomain
46# endif
Bram Moolenaara2dd9002007-05-14 17:38:30 +000047# ifdef bind_textdomain_codeset
48# undef bind_textdomain_codeset
Bram Moolenaar49325942007-05-10 19:19:59 +000049# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000050# if defined(FEAT_GETTEXT) && !defined(ENABLE_NLS)
Bram Moolenaar30613902019-12-01 22:11:18 +010051# define ENABLE_NLS // so the texts in the dialog boxes are translated
Bram Moolenaar071d4272004-06-13 20:20:40 +000052# endif
53# include <gnome.h>
54# include "version.h"
Bram Moolenaar30613902019-12-01 22:11:18 +010055// missing prototype in bonobo-dock-item.h
Bram Moolenaardb552d602006-03-23 22:59:57 +000056extern void bonobo_dock_item_set_behavior(BonoboDockItem *dock_item, BonoboDockItemBehavior beh);
Bram Moolenaar071d4272004-06-13 20:20:40 +000057#endif
58
59#if !defined(FEAT_GUI_GTK) && defined(PROTO)
Bram Moolenaar30613902019-12-01 22:11:18 +010060// When generating prototypes we don't want syntax errors.
Bram Moolenaar071d4272004-06-13 20:20:40 +000061# define GdkAtom int
62# define GdkEventExpose int
63# define GdkEventFocus int
64# define GdkEventVisibility int
65# define GdkEventProperty int
66# define GtkContainer int
67# define GtkTargetEntry int
68# define GtkType int
69# define GtkWidget int
70# define gint int
71# define gpointer int
72# define guint int
73# define GdkEventKey int
74# define GdkEventSelection int
75# define GtkSelectionData int
76# define GdkEventMotion int
77# define GdkEventButton int
78# define GdkDragContext int
79# define GdkEventConfigure int
80# define GdkEventClient int
81#else
Bram Moolenaar98921892016-02-23 17:14:37 +010082# if GTK_CHECK_VERSION(3,0,0)
83# include <gdk/gdkkeysyms-compat.h>
84# include <gtk/gtkx.h>
85# else
86# include <gdk/gdkkeysyms.h>
87# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000088# include <gdk/gdk.h>
Bram Moolenaar4f974752019-02-17 17:44:42 +010089# ifdef MSWIN
Bram Moolenaar071d4272004-06-13 20:20:40 +000090# include <gdk/gdkwin32.h>
91# else
92# include <gdk/gdkx.h>
93# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000094# include <gtk/gtk.h>
95# include "gui_gtk_f.h"
96#endif
97
98#ifdef HAVE_X11_SUNKEYSYM_H
99# include <X11/Sunkeysym.h>
100#endif
101
102/*
103 * Easy-to-use macro for multihead support.
104 */
Bram Moolenaarb463e8d2017-06-05 15:07:09 +0200105#define GET_X_ATOM(atom) gdk_x11_atom_to_xatom_for_display( \
Bram Moolenaar071d4272004-06-13 20:20:40 +0000106 gtk_widget_get_display(gui.mainwin), atom)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000107
Bram Moolenaar30613902019-12-01 22:11:18 +0100108// Selection type distinguishers
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109enum
110{
111 TARGET_TYPE_NONE,
112 TARGET_UTF8_STRING,
113 TARGET_STRING,
114 TARGET_COMPOUND_TEXT,
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +0000115 TARGET_HTML,
Bram Moolenaar071d4272004-06-13 20:20:40 +0000116 TARGET_TEXT,
117 TARGET_TEXT_URI_LIST,
118 TARGET_TEXT_PLAIN,
119 TARGET_VIM,
120 TARGET_VIMENC
121};
122
123/*
124 * Table of selection targets supported by Vim.
125 * Note: Order matters, preferred types should come first.
126 */
127static const GtkTargetEntry selection_targets[] =
128{
129 {VIMENC_ATOM_NAME, 0, TARGET_VIMENC},
130 {VIM_ATOM_NAME, 0, TARGET_VIM},
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +0000131 {"text/html", 0, TARGET_HTML},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000132 {"UTF8_STRING", 0, TARGET_UTF8_STRING},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000133 {"COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT},
134 {"TEXT", 0, TARGET_TEXT},
135 {"STRING", 0, TARGET_STRING}
136};
137#define N_SELECTION_TARGETS (sizeof(selection_targets) / sizeof(selection_targets[0]))
138
139#ifdef FEAT_DND
140/*
141 * Table of DnD targets supported by Vim.
142 * Note: Order matters, preferred types should come first.
143 */
144static const GtkTargetEntry dnd_targets[] =
145{
146 {"text/uri-list", 0, TARGET_TEXT_URI_LIST},
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +0000147 {"text/html", 0, TARGET_HTML},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000148 {"UTF8_STRING", 0, TARGET_UTF8_STRING},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000149 {"STRING", 0, TARGET_STRING},
150 {"text/plain", 0, TARGET_TEXT_PLAIN}
151};
152# define N_DND_TARGETS (sizeof(dnd_targets) / sizeof(dnd_targets[0]))
153#endif
154
155
Bram Moolenaar071d4272004-06-13 20:20:40 +0000156/*
157 * "Monospace" is a standard font alias that should be present
158 * on all proper Pango/fontconfig installations.
159 */
160# define DEFAULT_FONT "Monospace 10"
161
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +0200162#if defined(FEAT_GUI_GNOME) && defined(FEAT_SESSION)
163# define USE_GNOME_SESSION
164#endif
165
166#if !defined(FEAT_GUI_GNOME)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000167/*
168 * Atoms used to communicate save-yourself from the X11 session manager. There
169 * is no need to move them into the GUI struct, since they should be constant.
170 */
171static GdkAtom wm_protocols_atom = GDK_NONE;
172static GdkAtom save_yourself_atom = GDK_NONE;
173#endif
174
175/*
176 * Atoms used to control/reference X11 selections.
177 */
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +0000178static GdkAtom html_atom = GDK_NONE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000179static GdkAtom utf8_string_atom = GDK_NONE;
Bram Moolenaar30613902019-12-01 22:11:18 +0100180static GdkAtom vim_atom = GDK_NONE; // Vim's own special selection format
181static GdkAtom vimenc_atom = GDK_NONE; // Vim's extended selection format
Bram Moolenaar071d4272004-06-13 20:20:40 +0000182
183/*
184 * Keycodes recognized by vim.
185 * NOTE: when changing this, the table in gui_x11.c probably needs the same
186 * change!
187 */
188static struct special_key
189{
190 guint key_sym;
191 char_u code0;
192 char_u code1;
193}
194const special_keys[] =
195{
196 {GDK_Up, 'k', 'u'},
197 {GDK_Down, 'k', 'd'},
198 {GDK_Left, 'k', 'l'},
199 {GDK_Right, 'k', 'r'},
200 {GDK_F1, 'k', '1'},
201 {GDK_F2, 'k', '2'},
202 {GDK_F3, 'k', '3'},
203 {GDK_F4, 'k', '4'},
204 {GDK_F5, 'k', '5'},
205 {GDK_F6, 'k', '6'},
206 {GDK_F7, 'k', '7'},
207 {GDK_F8, 'k', '8'},
208 {GDK_F9, 'k', '9'},
209 {GDK_F10, 'k', ';'},
210 {GDK_F11, 'F', '1'},
211 {GDK_F12, 'F', '2'},
212 {GDK_F13, 'F', '3'},
213 {GDK_F14, 'F', '4'},
214 {GDK_F15, 'F', '5'},
215 {GDK_F16, 'F', '6'},
216 {GDK_F17, 'F', '7'},
217 {GDK_F18, 'F', '8'},
218 {GDK_F19, 'F', '9'},
219 {GDK_F20, 'F', 'A'},
220 {GDK_F21, 'F', 'B'},
Bram Moolenaar30613902019-12-01 22:11:18 +0100221 {GDK_Pause, 'F', 'B'}, // Pause == F21 according to netbeans.txt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222 {GDK_F22, 'F', 'C'},
223 {GDK_F23, 'F', 'D'},
224 {GDK_F24, 'F', 'E'},
225 {GDK_F25, 'F', 'F'},
226 {GDK_F26, 'F', 'G'},
227 {GDK_F27, 'F', 'H'},
228 {GDK_F28, 'F', 'I'},
229 {GDK_F29, 'F', 'J'},
230 {GDK_F30, 'F', 'K'},
231 {GDK_F31, 'F', 'L'},
232 {GDK_F32, 'F', 'M'},
233 {GDK_F33, 'F', 'N'},
234 {GDK_F34, 'F', 'O'},
235 {GDK_F35, 'F', 'P'},
236#ifdef SunXK_F36
237 {SunXK_F36, 'F', 'Q'},
238 {SunXK_F37, 'F', 'R'},
239#endif
240 {GDK_Help, '%', '1'},
241 {GDK_Undo, '&', '8'},
242 {GDK_BackSpace, 'k', 'b'},
243 {GDK_Insert, 'k', 'I'},
244 {GDK_Delete, 'k', 'D'},
245 {GDK_3270_BackTab, 'k', 'B'},
246 {GDK_Clear, 'k', 'C'},
247 {GDK_Home, 'k', 'h'},
248 {GDK_End, '@', '7'},
249 {GDK_Prior, 'k', 'P'},
250 {GDK_Next, 'k', 'N'},
251 {GDK_Print, '%', '9'},
Bram Moolenaar30613902019-12-01 22:11:18 +0100252 // Keypad keys:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000253 {GDK_KP_Left, 'k', 'l'},
254 {GDK_KP_Right, 'k', 'r'},
255 {GDK_KP_Up, 'k', 'u'},
256 {GDK_KP_Down, 'k', 'd'},
257 {GDK_KP_Insert, KS_EXTRA, (char_u)KE_KINS},
258 {GDK_KP_Delete, KS_EXTRA, (char_u)KE_KDEL},
259 {GDK_KP_Home, 'K', '1'},
260 {GDK_KP_End, 'K', '4'},
Bram Moolenaar30613902019-12-01 22:11:18 +0100261 {GDK_KP_Prior, 'K', '3'}, // page up
262 {GDK_KP_Next, 'K', '5'}, // page down
Bram Moolenaar071d4272004-06-13 20:20:40 +0000263
264 {GDK_KP_Add, 'K', '6'},
265 {GDK_KP_Subtract, 'K', '7'},
266 {GDK_KP_Divide, 'K', '8'},
267 {GDK_KP_Multiply, 'K', '9'},
268 {GDK_KP_Enter, 'K', 'A'},
269 {GDK_KP_Decimal, 'K', 'B'},
270
271 {GDK_KP_0, 'K', 'C'},
272 {GDK_KP_1, 'K', 'D'},
273 {GDK_KP_2, 'K', 'E'},
274 {GDK_KP_3, 'K', 'F'},
275 {GDK_KP_4, 'K', 'G'},
276 {GDK_KP_5, 'K', 'H'},
277 {GDK_KP_6, 'K', 'I'},
278 {GDK_KP_7, 'K', 'J'},
279 {GDK_KP_8, 'K', 'K'},
280 {GDK_KP_9, 'K', 'L'},
281
Bram Moolenaar30613902019-12-01 22:11:18 +0100282 // End of list marker:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283 {0, 0, 0}
284};
285
286/*
287 * Flags for command line options table below.
288 */
289#define ARG_FONT 1
290#define ARG_GEOMETRY 2
291#define ARG_REVERSE 3
292#define ARG_NOREVERSE 4
293#define ARG_BACKGROUND 5
294#define ARG_FOREGROUND 6
295#define ARG_ICONIC 7
296#define ARG_ROLE 8
297#define ARG_NETBEANS 9
Bram Moolenaar30613902019-12-01 22:11:18 +0100298#define ARG_XRM 10 // ignored
299#define ARG_MENUFONT 11 // ignored
Bram Moolenaar071d4272004-06-13 20:20:40 +0000300#define ARG_INDEX_MASK 0x00ff
Bram Moolenaar30613902019-12-01 22:11:18 +0100301#define ARG_HAS_VALUE 0x0100 // a value is expected after the argument
302#define ARG_NEEDS_GUI 0x0200 // need to initialize the GUI for this
303#define ARG_FOR_GTK 0x0400 // argument is handled by GTK+ or GNOME
304#define ARG_COMPAT_LONG 0x0800 // accept -foo but substitute with --foo
305#define ARG_KEEP 0x1000 // don't remove argument from argv[]
Bram Moolenaar071d4272004-06-13 20:20:40 +0000306
307/*
308 * This table holds all the X GUI command line options allowed. This includes
309 * the standard ones so that we can skip them when Vim is started without the
310 * GUI (but the GUI might start up later).
311 *
312 * When changing this, also update doc/gui_x11.txt and the usage message!!!
313 */
314typedef struct
315{
316 const char *name;
317 unsigned int flags;
318}
319cmdline_option_T;
320
321static const cmdline_option_T cmdline_options[] =
322{
Bram Moolenaar30613902019-12-01 22:11:18 +0100323 // We handle these options ourselves
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324 {"-fn", ARG_FONT|ARG_HAS_VALUE},
325 {"-font", ARG_FONT|ARG_HAS_VALUE},
326 {"-geom", ARG_GEOMETRY|ARG_HAS_VALUE},
327 {"-geometry", ARG_GEOMETRY|ARG_HAS_VALUE},
328 {"-rv", ARG_REVERSE},
329 {"-reverse", ARG_REVERSE},
330 {"+rv", ARG_NOREVERSE},
331 {"+reverse", ARG_NOREVERSE},
332 {"-bg", ARG_BACKGROUND|ARG_HAS_VALUE},
333 {"-background", ARG_BACKGROUND|ARG_HAS_VALUE},
334 {"-fg", ARG_FOREGROUND|ARG_HAS_VALUE},
335 {"-foreground", ARG_FOREGROUND|ARG_HAS_VALUE},
336 {"-iconic", ARG_ICONIC},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000337 {"--role", ARG_ROLE|ARG_HAS_VALUE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000338#ifdef FEAT_NETBEANS_INTG
Bram Moolenaar30613902019-12-01 22:11:18 +0100339 {"-nb", ARG_NETBEANS}, // non-standard value format
340 {"-xrm", ARG_XRM|ARG_HAS_VALUE}, // not implemented
341 {"-mf", ARG_MENUFONT|ARG_HAS_VALUE}, // not implemented
342 {"-menufont", ARG_MENUFONT|ARG_HAS_VALUE}, // not implemented
Bram Moolenaar071d4272004-06-13 20:20:40 +0000343#endif
Bram Moolenaar30613902019-12-01 22:11:18 +0100344 // Arguments handled by GTK (and GNOME) internally.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000345 {"--g-fatal-warnings", ARG_FOR_GTK},
346 {"--gdk-debug", ARG_FOR_GTK|ARG_HAS_VALUE},
347 {"--gdk-no-debug", ARG_FOR_GTK|ARG_HAS_VALUE},
348 {"--gtk-debug", ARG_FOR_GTK|ARG_HAS_VALUE},
349 {"--gtk-no-debug", ARG_FOR_GTK|ARG_HAS_VALUE},
350 {"--gtk-module", ARG_FOR_GTK|ARG_HAS_VALUE},
351 {"--sync", ARG_FOR_GTK},
352 {"--display", ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG},
353 {"--name", ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG},
354 {"--class", ARG_FOR_GTK|ARG_HAS_VALUE|ARG_COMPAT_LONG},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355 {"--screen", ARG_FOR_GTK|ARG_HAS_VALUE},
356 {"--gxid-host", ARG_FOR_GTK|ARG_HAS_VALUE},
357 {"--gxid-port", ARG_FOR_GTK|ARG_HAS_VALUE},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358#ifdef FEAT_GUI_GNOME
359 {"--load-modules", ARG_FOR_GTK|ARG_HAS_VALUE},
360 {"--sm-client-id", ARG_FOR_GTK|ARG_HAS_VALUE},
361 {"--sm-config-prefix", ARG_FOR_GTK|ARG_HAS_VALUE},
362 {"--sm-disable", ARG_FOR_GTK},
363 {"--oaf-ior-fd", ARG_FOR_GTK|ARG_HAS_VALUE},
364 {"--oaf-activate-iid", ARG_FOR_GTK|ARG_HAS_VALUE},
365 {"--oaf-private", ARG_FOR_GTK},
366 {"--enable-sound", ARG_FOR_GTK},
367 {"--disable-sound", ARG_FOR_GTK},
368 {"--espeaker", ARG_FOR_GTK|ARG_HAS_VALUE},
369 {"-?", ARG_FOR_GTK|ARG_NEEDS_GUI},
370 {"--help", ARG_FOR_GTK|ARG_NEEDS_GUI|ARG_KEEP},
371 {"--usage", ARG_FOR_GTK|ARG_NEEDS_GUI},
Bram Moolenaar30613902019-12-01 22:11:18 +0100372# if 0 // conflicts with Vim's own --version argument
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373 {"--version", ARG_FOR_GTK|ARG_NEEDS_GUI},
374# endif
375 {"--disable-crash-dialog", ARG_FOR_GTK},
376#endif
377 {NULL, 0}
378};
379
380static int gui_argc = 0;
381static char **gui_argv = NULL;
382
Bram Moolenaar071d4272004-06-13 20:20:40 +0000383static const char *role_argument = NULL;
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +0200384#if defined(USE_GNOME_SESSION)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000385static const char *restart_command = NULL;
Bram Moolenaar9085f802009-06-03 14:20:21 +0000386static char *abs_restart_command = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387#endif
388static int found_iconic_arg = FALSE;
389
390#ifdef FEAT_GUI_GNOME
391/*
392 * Can't use Gnome if --socketid given
393 */
394static int using_gnome = 0;
395#else
396# define using_gnome 0
397#endif
398
399/*
400 * Parse the GUI related command-line arguments. Any arguments used are
401 * deleted from argv, and *argc is decremented accordingly. This is called
402 * when vim is started, whether or not the GUI has been started.
403 */
404 void
405gui_mch_prepare(int *argc, char **argv)
406{
407 const cmdline_option_T *option;
408 int i = 0;
409 int len = 0;
410
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +0200411#if defined(USE_GNOME_SESSION)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412 /*
413 * Determine the command used to invoke Vim, to be passed as restart
414 * command to the session manager. If argv[0] contains any directory
415 * components try building an absolute path, otherwise leave it as is.
416 */
417 restart_command = argv[0];
418
419 if (strchr(argv[0], G_DIR_SEPARATOR) != NULL)
420 {
421 char_u buf[MAXPATHL];
422
423 if (mch_FullName((char_u *)argv[0], buf, (int)sizeof(buf), TRUE) == OK)
Bram Moolenaar9085f802009-06-03 14:20:21 +0000424 {
425 abs_restart_command = (char *)vim_strsave(buf);
426 restart_command = abs_restart_command;
427 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000428 }
429#endif
430
431 /*
432 * Move all the entries in argv which are relevant to GTK+ and GNOME
433 * into gui_argv. Freed later in gui_mch_init().
434 */
435 gui_argc = 0;
Bram Moolenaarc799fe22019-05-28 23:08:19 +0200436 gui_argv = ALLOC_MULT(char *, *argc + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000437
438 g_return_if_fail(gui_argv != NULL);
439
440 gui_argv[gui_argc++] = argv[i++];
441
442 while (i < *argc)
443 {
Bram Moolenaar30613902019-12-01 22:11:18 +0100444 // Don't waste CPU cycles on non-option arguments.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445 if (argv[i][0] != '-' && argv[i][0] != '+')
446 {
447 ++i;
448 continue;
449 }
450
Bram Moolenaar30613902019-12-01 22:11:18 +0100451 // Look for argv[i] in cmdline_options[] table.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000452 for (option = &cmdline_options[0]; option->name != NULL; ++option)
453 {
454 len = strlen(option->name);
455
456 if (strncmp(argv[i], option->name, len) == 0)
457 {
458 if (argv[i][len] == '\0')
459 break;
Bram Moolenaar30613902019-12-01 22:11:18 +0100460 // allow --foo=bar style
Bram Moolenaar071d4272004-06-13 20:20:40 +0000461 if (argv[i][len] == '=' && (option->flags & ARG_HAS_VALUE))
462 break;
463#ifdef FEAT_NETBEANS_INTG
Bram Moolenaar30613902019-12-01 22:11:18 +0100464 // darn, -nb has non-standard syntax
Bram Moolenaar071d4272004-06-13 20:20:40 +0000465 if (vim_strchr((char_u *)":=", argv[i][len]) != NULL
466 && (option->flags & ARG_INDEX_MASK) == ARG_NETBEANS)
467 break;
468#endif
469 }
470 else if ((option->flags & ARG_COMPAT_LONG)
471 && strcmp(argv[i], option->name + 1) == 0)
472 {
Bram Moolenaar30613902019-12-01 22:11:18 +0100473 // Replace the standard X arguments "-name" and "-display"
474 // with their GNU-style long option counterparts.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000475 argv[i] = (char *)option->name;
476 break;
477 }
478 }
Bram Moolenaar30613902019-12-01 22:11:18 +0100479 if (option->name == NULL) // no match
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480 {
481 ++i;
482 continue;
483 }
484
485 if (option->flags & ARG_FOR_GTK)
486 {
Bram Moolenaar30613902019-12-01 22:11:18 +0100487 // Move the argument into gui_argv, which
488 // will later be passed to gtk_init_check()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489 gui_argv[gui_argc++] = argv[i];
490 }
491 else
492 {
493 char *value = NULL;
494
Bram Moolenaar30613902019-12-01 22:11:18 +0100495 // Extract the option's value if there is one.
496 // Accept both "--foo bar" and "--foo=bar" style.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497 if (option->flags & ARG_HAS_VALUE)
498 {
499 if (argv[i][len] == '=')
500 value = &argv[i][len + 1];
501 else if (i + 1 < *argc && strcmp(argv[i + 1], "--") != 0)
502 value = argv[i + 1];
503 }
504
Bram Moolenaar30613902019-12-01 22:11:18 +0100505 // Check for options handled by Vim itself
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506 switch (option->flags & ARG_INDEX_MASK)
507 {
508 case ARG_REVERSE:
509 found_reverse_arg = TRUE;
510 break;
511 case ARG_NOREVERSE:
512 found_reverse_arg = FALSE;
513 break;
514 case ARG_FONT:
515 font_argument = value;
516 break;
517 case ARG_GEOMETRY:
518 if (value != NULL)
519 gui.geom = vim_strsave((char_u *)value);
520 break;
521 case ARG_BACKGROUND:
522 background_argument = value;
523 break;
524 case ARG_FOREGROUND:
525 foreground_argument = value;
526 break;
527 case ARG_ICONIC:
528 found_iconic_arg = TRUE;
529 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000530 case ARG_ROLE:
Bram Moolenaar30613902019-12-01 22:11:18 +0100531 role_argument = value; // used later in gui_mch_open()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000532 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533#ifdef FEAT_NETBEANS_INTG
534 case ARG_NETBEANS:
Bram Moolenaar30613902019-12-01 22:11:18 +0100535 gui.dofork = FALSE; // don't fork() when starting GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +0000536 netbeansArg = argv[i];
537 break;
538#endif
539 default:
540 break;
541 }
542 }
543
Bram Moolenaar30613902019-12-01 22:11:18 +0100544 // These arguments make gnome_program_init() print a message and exit.
545 // Must start the GUI for this, otherwise ":gui" will exit later!
546 // Only when the GUI can start.
Bram Moolenaar717e1962016-08-10 21:28:44 +0200547 if ((option->flags & ARG_NEEDS_GUI)
548 && gui_mch_early_init_check(FALSE) == OK)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000549 gui.starting = TRUE;
550
551 if (option->flags & ARG_KEEP)
552 ++i;
553 else
554 {
Bram Moolenaar30613902019-12-01 22:11:18 +0100555 // Remove the flag from the argument vector.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000556 if (--*argc > i)
557 {
558 int n_strip = 1;
559
Bram Moolenaar30613902019-12-01 22:11:18 +0100560 // Move the argument's value as well, if there is one.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561 if ((option->flags & ARG_HAS_VALUE)
562 && argv[i][len] != '='
563 && strcmp(argv[i + 1], "--") != 0)
564 {
565 ++n_strip;
566 --*argc;
567 if (option->flags & ARG_FOR_GTK)
568 gui_argv[gui_argc++] = argv[i + 1];
569 }
570
571 if (*argc > i)
572 mch_memmove(&argv[i], &argv[i + n_strip],
573 (*argc - i) * sizeof(char *));
574 }
575 argv[*argc] = NULL;
576 }
577 }
578
579 gui_argv[gui_argc] = NULL;
580}
581
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000582#if defined(EXITFREE) || defined(PROTO)
583 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +0100584gui_mch_free_all(void)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000585{
586 vim_free(gui_argv);
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +0200587#if defined(USE_GNOME_SESSION)
Bram Moolenaar9085f802009-06-03 14:20:21 +0000588 vim_free(abs_restart_command);
589#endif
Bram Moolenaarf461c8e2005-06-25 23:04:51 +0000590}
591#endif
592
Bram Moolenaar98921892016-02-23 17:14:37 +0100593#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594/*
595 * This should be maybe completely removed.
596 * Doesn't seem possible, since check_copy_area() relies on
597 * this information. --danielk
598 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000599 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000600visibility_event(GtkWidget *widget UNUSED,
601 GdkEventVisibility *event,
602 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000603{
604 gui.visibility = event->state;
605 /*
606 * When we do an gdk_window_copy_area(), and the window is partially
607 * obscured, we want to receive an event to tell us whether it worked
608 * or not.
609 */
610 if (gui.text_gc != NULL)
611 gdk_gc_set_exposures(gui.text_gc,
612 gui.visibility != GDK_VISIBILITY_UNOBSCURED);
613 return FALSE;
614}
Bram Moolenaar30613902019-12-01 22:11:18 +0100615#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000616
617/*
618 * Redraw the corresponding portions of the screen.
619 */
Bram Moolenaar98921892016-02-23 17:14:37 +0100620#if GTK_CHECK_VERSION(3,0,0)
621static gboolean is_key_pressed = FALSE;
Bram Moolenaar0ecbe332016-03-05 22:40:52 +0100622static gboolean blink_mode = TRUE;
Bram Moolenaar98921892016-02-23 17:14:37 +0100623
624static gboolean gui_gtk_is_blink_on(void);
Bram Moolenaar98921892016-02-23 17:14:37 +0100625
626 static void
627gui_gtk3_redraw(int x, int y, int width, int height)
628{
Bram Moolenaar30613902019-12-01 22:11:18 +0100629 // Range checks are left to gui_redraw_block()
Bram Moolenaar98921892016-02-23 17:14:37 +0100630 gui_redraw_block(Y_2_ROW(y), X_2_COL(x),
631 Y_2_ROW(y + height - 1), X_2_COL(x + width - 1),
632 GUI_MON_NOCLEAR);
633}
634
635 static void
636gui_gtk3_update_cursor(cairo_t *cr)
637{
638 if (gui.row == gui.cursor_row)
639 {
640 gui.by_signal = TRUE;
Bram Moolenaar4fc563b2016-03-12 12:40:58 +0100641 if (State & CMDLINE)
642 gui_update_cursor(TRUE, FALSE);
643 else
644 gui_update_cursor(TRUE, TRUE);
Bram Moolenaar98921892016-02-23 17:14:37 +0100645 gui.by_signal = FALSE;
646 cairo_paint(cr);
647 }
648}
649
650 static gboolean
651gui_gtk3_should_draw_cursor(void)
652{
653 unsigned int cond = 0;
654 cond |= gui_gtk_is_blink_on();
Bram Moolenaarb4ebf9a2016-03-12 16:28:18 +0100655 if (gui.cursor_col >= gui.col)
656 cond |= is_key_pressed;
Bram Moolenaar98921892016-02-23 17:14:37 +0100657 cond |= gui.in_focus == FALSE;
Bram Moolenaar98921892016-02-23 17:14:37 +0100658 return cond;
659}
660
661 static gboolean
Bram Moolenaar8e31fd52016-06-04 22:18:13 +0200662draw_event(GtkWidget *widget UNUSED,
Bram Moolenaar98921892016-02-23 17:14:37 +0100663 cairo_t *cr,
664 gpointer user_data UNUSED)
665{
Bram Moolenaar30613902019-12-01 22:11:18 +0100666 // Skip this when the GUI isn't set up yet, will redraw later.
Bram Moolenaar98921892016-02-23 17:14:37 +0100667 if (gui.starting)
668 return FALSE;
669
Bram Moolenaar30613902019-12-01 22:11:18 +0100670 out_flush(); // make sure all output has been processed
671 // for GTK+ 3, may induce other draw events.
Bram Moolenaar98921892016-02-23 17:14:37 +0100672
673 cairo_set_source_surface(cr, gui.surface, 0, 0);
674
Bram Moolenaar30613902019-12-01 22:11:18 +0100675 // Draw the window without the cursor.
Bram Moolenaar98921892016-02-23 17:14:37 +0100676 gui.by_signal = TRUE;
677 {
678 cairo_rectangle_list_t *list = NULL;
679
Bram Moolenaar98921892016-02-23 17:14:37 +0100680 list = cairo_copy_clip_rectangle_list(cr);
681 if (list->status != CAIRO_STATUS_CLIP_NOT_REPRESENTABLE)
682 {
683 int i;
Bram Moolenaarfe344a92017-02-23 12:20:35 +0100684
Bram Moolenaar30613902019-12-01 22:11:18 +0100685 // First clear all the blocks and then redraw them. Just in case
686 // some blocks overlap.
Bram Moolenaar98921892016-02-23 17:14:37 +0100687 for (i = 0; i < list->num_rectangles; i++)
688 {
689 const cairo_rectangle_t rect = list->rectangles[i];
Bram Moolenaar8e31fd52016-06-04 22:18:13 +0200690
Bram Moolenaarfe344a92017-02-23 12:20:35 +0100691 gui_mch_clear_block(Y_2_ROW((int)rect.y), 0,
692 Y_2_ROW((int)(rect.y + rect.height)) - 1, Columns - 1);
693 }
694
695 for (i = 0; i < list->num_rectangles; i++)
696 {
697 const cairo_rectangle_t rect = list->rectangles[i];
Bram Moolenaar8e31fd52016-06-04 22:18:13 +0200698
Bram Moolenaar0ecbe332016-03-05 22:40:52 +0100699 if (blink_mode)
700 gui_gtk3_redraw(rect.x, rect.y, rect.width, rect.height);
701 else
Bram Moolenaarb4ebf9a2016-03-12 16:28:18 +0100702 {
703 if (get_real_state() & VISUAL)
704 gui_gtk3_redraw(rect.x, rect.y,
705 rect.width, rect.height);
706 else
707 gui_redraw(rect.x, rect.y, rect.width, rect.height);
708 }
Bram Moolenaar98921892016-02-23 17:14:37 +0100709 }
710 }
711 cairo_rectangle_list_destroy(list);
712
Bram Moolenaarb4ebf9a2016-03-12 16:28:18 +0100713 if (get_real_state() & VISUAL)
714 {
715 if (gui.cursor_row == gui.row && gui.cursor_col >= gui.col)
716 gui_update_cursor(TRUE, TRUE);
717 }
718
Bram Moolenaar98921892016-02-23 17:14:37 +0100719 cairo_paint(cr);
720 }
721 gui.by_signal = FALSE;
722
Bram Moolenaar30613902019-12-01 22:11:18 +0100723 // Add the cursor to the window if necessary.
Bram Moolenaarb4ebf9a2016-03-12 16:28:18 +0100724 if (gui_gtk3_should_draw_cursor() && blink_mode)
Bram Moolenaar98921892016-02-23 17:14:37 +0100725 gui_gtk3_update_cursor(cr);
726
727 return FALSE;
728}
Bram Moolenaar30613902019-12-01 22:11:18 +0100729#else // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000731expose_event(GtkWidget *widget UNUSED,
732 GdkEventExpose *event,
733 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000734{
Bram Moolenaar30613902019-12-01 22:11:18 +0100735 // Skip this when the GUI isn't set up yet, will redraw later.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736 if (gui.starting)
737 return FALSE;
738
Bram Moolenaar30613902019-12-01 22:11:18 +0100739 out_flush(); // make sure all output has been processed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000740 gui_redraw(event->area.x, event->area.y,
741 event->area.width, event->area.height);
742
Bram Moolenaar30613902019-12-01 22:11:18 +0100743 // Clear the border areas if needed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000744 if (event->area.x < FILL_X(0))
745 gdk_window_clear_area(gui.drawarea->window, 0, 0, FILL_X(0), 0);
746 if (event->area.y < FILL_Y(0))
747 gdk_window_clear_area(gui.drawarea->window, 0, 0, 0, FILL_Y(0));
748 if (event->area.x > FILL_X(Columns))
749 gdk_window_clear_area(gui.drawarea->window,
750 FILL_X((int)Columns), 0, 0, 0);
751 if (event->area.y > FILL_Y(Rows))
752 gdk_window_clear_area(gui.drawarea->window, 0, FILL_Y((int)Rows), 0, 0);
753
754 return FALSE;
755}
Bram Moolenaar30613902019-12-01 22:11:18 +0100756#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757
758#ifdef FEAT_CLIENTSERVER
759/*
760 * Handle changes to the "Comm" property
761 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000763property_event(GtkWidget *widget,
764 GdkEventProperty *event,
765 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000766{
767 if (event->type == GDK_PROPERTY_NOTIFY
768 && event->state == (int)GDK_PROPERTY_NEW_VALUE
Bram Moolenaar98921892016-02-23 17:14:37 +0100769 && GDK_WINDOW_XID(event->window) == commWindow
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770 && GET_X_ATOM(event->atom) == commProperty)
771 {
772 XEvent xev;
773
Bram Moolenaar30613902019-12-01 22:11:18 +0100774 // Translate to XLib
Bram Moolenaar071d4272004-06-13 20:20:40 +0000775 xev.xproperty.type = PropertyNotify;
776 xev.xproperty.atom = commProperty;
777 xev.xproperty.window = commWindow;
778 xev.xproperty.state = PropertyNewValue;
Bram Moolenaar98921892016-02-23 17:14:37 +0100779 serverEventProc(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(widget)),
780 &xev, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000781 }
782 return FALSE;
783}
Bram Moolenaar30613902019-12-01 22:11:18 +0100784#endif // defined(FEAT_CLIENTSERVER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000785
Bram Moolenaar7ebf4e12018-08-07 20:01:40 +0200786/*
787 * Handle changes to the "Xft/DPI" setting
788 */
789 static void
790gtk_settings_xft_dpi_changed_cb(GtkSettings *gtk_settings UNUSED,
791 GParamSpec *pspec UNUSED,
792 gpointer data UNUSED)
793{
794 // Create a new PangoContext for this screen, and initialize it
795 // with the current font if necessary.
796 if (gui.text_context != NULL)
797 g_object_unref(gui.text_context);
798
799 gui.text_context = gtk_widget_create_pango_context(gui.mainwin);
800 pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR);
801
802 if (gui.norm_font != NULL)
803 {
804 // force default font
805 gui_mch_init_font(*p_guifont == NUL ? NULL : p_guifont, FALSE);
806 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
807 }
808}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000809
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200810typedef gboolean timeout_cb_type;
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200811
812/*
813 * Start a timer that will invoke the specified callback.
814 * Returns the ID of the timer.
815 */
816 static guint
817timeout_add(int time, timeout_cb_type (*callback)(gpointer), int *flagp)
818{
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200819 return g_timeout_add((guint)time, (GSourceFunc)callback, flagp);
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200820}
821
822 static void
823timeout_remove(guint timer)
824{
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200825 g_source_remove(timer);
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200826}
827
828
Bram Moolenaar30613902019-12-01 22:11:18 +0100829/////////////////////////////////////////////////////////////////////////////
830// Focus handlers:
Bram Moolenaar071d4272004-06-13 20:20:40 +0000831
832
833/*
834 * This is a simple state machine:
835 * BLINK_NONE not blinking at all
836 * BLINK_OFF blinking, cursor is not shown
837 * BLINK_ON blinking, cursor is shown
838 */
839
840#define BLINK_NONE 0
841#define BLINK_OFF 1
842#define BLINK_ON 2
843
844static int blink_state = BLINK_NONE;
845static long_u blink_waittime = 700;
846static long_u blink_ontime = 400;
847static long_u blink_offtime = 250;
848static guint blink_timer = 0;
849
Bram Moolenaar98921892016-02-23 17:14:37 +0100850#if GTK_CHECK_VERSION(3,0,0)
851 static gboolean
852gui_gtk_is_blink_on(void)
853{
854 return blink_state == BLINK_ON;
855}
Bram Moolenaar98921892016-02-23 17:14:37 +0100856#endif
857
Bram Moolenaar703a8042016-06-04 16:24:32 +0200858 int
859gui_mch_is_blinking(void)
860{
861 return blink_state != BLINK_NONE;
862}
863
Bram Moolenaar9d5d3c92016-07-07 16:43:02 +0200864 int
865gui_mch_is_blink_off(void)
866{
867 return blink_state == BLINK_OFF;
868}
869
Bram Moolenaar071d4272004-06-13 20:20:40 +0000870 void
871gui_mch_set_blinking(long waittime, long on, long off)
872{
Bram Moolenaar0ecbe332016-03-05 22:40:52 +0100873#if GTK_CHECK_VERSION(3,0,0)
874 if (waittime == 0 || on == 0 || off == 0)
875 {
876 blink_mode = FALSE;
877
878 blink_waittime = 700;
879 blink_ontime = 400;
880 blink_offtime = 250;
881 }
882 else
883 {
884 blink_mode = TRUE;
885
886 blink_waittime = waittime;
887 blink_ontime = on;
888 blink_offtime = off;
889 }
890#else
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891 blink_waittime = waittime;
892 blink_ontime = on;
893 blink_offtime = off;
Bram Moolenaar0ecbe332016-03-05 22:40:52 +0100894#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000895}
896
897/*
898 * Stop the cursor blinking. Show the cursor if it wasn't shown.
899 */
900 void
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +0100901gui_mch_stop_blink(int may_call_gui_update_cursor)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000902{
903 if (blink_timer)
904 {
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200905 timeout_remove(blink_timer);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000906 blink_timer = 0;
907 }
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +0100908 if (blink_state == BLINK_OFF && may_call_gui_update_cursor)
Bram Moolenaarda3a77d2016-07-10 23:16:09 +0200909 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000910 gui_update_cursor(TRUE, FALSE);
Bram Moolenaarda3a77d2016-07-10 23:16:09 +0200911 gui_mch_flush();
912 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913 blink_state = BLINK_NONE;
914}
915
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200916 static timeout_cb_type
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000917blink_cb(gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918{
919 if (blink_state == BLINK_ON)
920 {
921 gui_undraw_cursor();
922 blink_state = BLINK_OFF;
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200923 blink_timer = timeout_add(blink_offtime, blink_cb, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000924 }
925 else
926 {
927 gui_update_cursor(TRUE, FALSE);
928 blink_state = BLINK_ON;
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200929 blink_timer = timeout_add(blink_ontime, blink_cb, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930 }
Bram Moolenaarda3a77d2016-07-10 23:16:09 +0200931 gui_mch_flush();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000932
Bram Moolenaar30613902019-12-01 22:11:18 +0100933 return FALSE; // don't happen again
Bram Moolenaar071d4272004-06-13 20:20:40 +0000934}
935
936/*
937 * Start the cursor blinking. If it was already blinking, this restarts the
938 * waiting time and shows the cursor.
939 */
940 void
941gui_mch_start_blink(void)
942{
943 if (blink_timer)
Bram Moolenaar7bcdb7d2014-04-06 21:08:45 +0200944 {
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200945 timeout_remove(blink_timer);
Bram Moolenaar7bcdb7d2014-04-06 21:08:45 +0200946 blink_timer = 0;
947 }
Bram Moolenaar30613902019-12-01 22:11:18 +0100948 // Only switch blinking on if none of the times is zero
Bram Moolenaar071d4272004-06-13 20:20:40 +0000949 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
950 {
Bram Moolenaar4ab79682017-08-27 14:50:47 +0200951 blink_timer = timeout_add(blink_waittime, blink_cb, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000952 blink_state = BLINK_ON;
953 gui_update_cursor(TRUE, FALSE);
Bram Moolenaarda3a77d2016-07-10 23:16:09 +0200954 gui_mch_flush();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000955 }
956}
957
Bram Moolenaar071d4272004-06-13 20:20:40 +0000958 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000959enter_notify_event(GtkWidget *widget UNUSED,
960 GdkEventCrossing *event UNUSED,
961 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000962{
963 if (blink_state == BLINK_NONE)
964 gui_mch_start_blink();
965
Bram Moolenaar30613902019-12-01 22:11:18 +0100966 // make sure keyboard input goes there
Bram Moolenaar98921892016-02-23 17:14:37 +0100967 if (gtk_socket_id == 0 || !gtk_widget_has_focus(gui.drawarea))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000968 gtk_widget_grab_focus(gui.drawarea);
969
970 return FALSE;
971}
972
Bram Moolenaar071d4272004-06-13 20:20:40 +0000973 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000974leave_notify_event(GtkWidget *widget UNUSED,
975 GdkEventCrossing *event UNUSED,
976 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000977{
978 if (blink_state != BLINK_NONE)
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +0100979 gui_mch_stop_blink(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000980
981 return FALSE;
982}
983
Bram Moolenaar071d4272004-06-13 20:20:40 +0000984 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +0000985focus_in_event(GtkWidget *widget,
986 GdkEventFocus *event UNUSED,
987 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000988{
989 gui_focus_change(TRUE);
990
991 if (blink_state == BLINK_NONE)
992 gui_mch_start_blink();
993
Bram Moolenaar30613902019-12-01 22:11:18 +0100994 // make sure keyboard input goes to the draw area (if this is focus for a
995 // window)
Bram Moolenaarb3656ed2006-03-20 21:59:49 +0000996 if (widget != gui.drawarea)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +0000997 gtk_widget_grab_focus(gui.drawarea);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000998
999 return TRUE;
1000}
1001
Bram Moolenaar071d4272004-06-13 20:20:40 +00001002 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001003focus_out_event(GtkWidget *widget UNUSED,
Bram Moolenaarcc448b32010-07-14 16:52:17 +02001004 GdkEventFocus *event UNUSED,
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001005 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001006{
1007 gui_focus_change(FALSE);
1008
1009 if (blink_state != BLINK_NONE)
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +01001010 gui_mch_stop_blink(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011
1012 return TRUE;
1013}
1014
1015
Bram Moolenaar071d4272004-06-13 20:20:40 +00001016/*
1017 * Translate a GDK key value to UTF-8 independently of the current locale.
1018 * The output is written to string, which must have room for at least 6 bytes
1019 * plus the NUL terminator. Returns the length in bytes.
1020 *
Bram Moolenaarf4ae6b22020-05-30 19:52:46 +02001021 * event->string is evil; see here why:
Bram Moolenaar071d4272004-06-13 20:20:40 +00001022 * http://developer.gnome.org/doc/API/2.0/gdk/gdk-Event-Structures.html#GdkEventKey
1023 */
1024 static int
Bram Moolenaarf4ae6b22020-05-30 19:52:46 +02001025keyval_to_string(unsigned int keyval, char_u *string)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001026{
1027 int len;
1028 guint32 uc;
1029
1030 uc = gdk_keyval_to_unicode(keyval);
1031 if (uc != 0)
1032 {
Bram Moolenaarf4ae6b22020-05-30 19:52:46 +02001033 // Translate a normal key to UTF-8. This doesn't work for dead
1034 // keys of course, you _have_ to use an input method for that.
1035 len = utf_char2bytes((int)uc, string);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001036 }
1037 else
1038 {
Bram Moolenaar30613902019-12-01 22:11:18 +01001039 // Translate keys which are represented by ASCII control codes in Vim.
1040 // There are only a few of those; most control keys are translated to
1041 // special terminal-like control sequences.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001042 len = 1;
1043 switch (keyval)
1044 {
1045 case GDK_Tab: case GDK_KP_Tab: case GDK_ISO_Left_Tab:
1046 string[0] = TAB;
1047 break;
1048 case GDK_Linefeed:
1049 string[0] = NL;
1050 break;
1051 case GDK_Return: case GDK_ISO_Enter: case GDK_3270_Enter:
1052 string[0] = CAR;
1053 break;
1054 case GDK_Escape:
1055 string[0] = ESC;
1056 break;
1057 default:
1058 len = 0;
1059 break;
1060 }
1061 }
1062 string[len] = NUL;
1063
1064 return len;
1065}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001067 static int
1068modifiers_gdk2vim(guint state)
1069{
1070 int modifiers = 0;
1071
1072 if (state & GDK_SHIFT_MASK)
1073 modifiers |= MOD_MASK_SHIFT;
1074 if (state & GDK_CONTROL_MASK)
1075 modifiers |= MOD_MASK_CTRL;
1076 if (state & GDK_MOD1_MASK)
1077 modifiers |= MOD_MASK_ALT;
Bram Moolenaar580061a2010-08-13 13:57:13 +02001078#if GTK_CHECK_VERSION(2,10,0)
Bram Moolenaar30bb4142010-05-17 22:07:15 +02001079 if (state & GDK_SUPER_MASK)
1080 modifiers |= MOD_MASK_META;
1081#endif
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001082 if (state & GDK_MOD4_MASK)
1083 modifiers |= MOD_MASK_META;
1084
1085 return modifiers;
1086}
1087
1088 static int
1089modifiers_gdk2mouse(guint state)
1090{
1091 int modifiers = 0;
1092
1093 if (state & GDK_SHIFT_MASK)
1094 modifiers |= MOUSE_SHIFT;
1095 if (state & GDK_CONTROL_MASK)
1096 modifiers |= MOUSE_CTRL;
1097 if (state & GDK_MOD1_MASK)
1098 modifiers |= MOUSE_ALT;
1099
1100 return modifiers;
1101}
1102
Bram Moolenaar071d4272004-06-13 20:20:40 +00001103/*
1104 * Main keyboard handler:
1105 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001106 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001107key_press_event(GtkWidget *widget UNUSED,
1108 GdkEventKey *event,
1109 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110{
Bram Moolenaar30613902019-12-01 22:11:18 +01001111 // For GTK+ 2 we know for sure how large the string might get.
1112 // (That is, up to 6 bytes + NUL + CSI escapes + safety measure.)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001113 char_u string[32], string2[32];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114 guint key_sym;
1115 int len;
1116 int i;
1117 int modifiers;
1118 int key;
1119 guint state;
1120 char_u *s, *d;
1121
Bram Moolenaar98921892016-02-23 17:14:37 +01001122#if GTK_CHECK_VERSION(3,0,0)
1123 is_key_pressed = TRUE;
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +01001124 gui_mch_stop_blink(TRUE);
Bram Moolenaar98921892016-02-23 17:14:37 +01001125#endif
1126
Bram Moolenaar20892c12011-06-26 04:49:00 +02001127 gui.event_time = event->time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001128 key_sym = event->keyval;
1129 state = event->state;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001130
1131#ifdef FEAT_XIM
1132 if (xim_queue_key_press_event(event, TRUE))
1133 return TRUE;
1134#endif
1135
Bram Moolenaar071d4272004-06-13 20:20:40 +00001136#ifdef SunXK_F36
1137 /*
1138 * These keys have bogus lookup strings, and trapping them here is
1139 * easier than trying to XRebindKeysym() on them with every possible
1140 * combination of modifiers.
1141 */
1142 if (key_sym == SunXK_F36 || key_sym == SunXK_F37)
1143 len = 0;
1144 else
1145#endif
1146 {
Bram Moolenaarf4ae6b22020-05-30 19:52:46 +02001147 len = keyval_to_string(key_sym, string2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001148
Bram Moolenaar30613902019-12-01 22:11:18 +01001149 // Careful: convert_input() doesn't handle the NUL character.
1150 // No need to convert pure ASCII anyway, thus the len > 1 check.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001151 if (len > 1 && input_conv.vc_type != CONV_NONE)
1152 len = convert_input(string2, len, sizeof(string2));
1153
1154 s = string2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001155 d = string;
1156 for (i = 0; i < len; ++i)
1157 {
1158 *d++ = s[i];
1159 if (d[-1] == CSI && d + 2 < string + sizeof(string))
1160 {
Bram Moolenaar30613902019-12-01 22:11:18 +01001161 // Turn CSI into K_CSI.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162 *d++ = KS_EXTRA;
1163 *d++ = (int)KE_CSI;
1164 }
1165 }
1166 len = d - string;
1167 }
1168
Bram Moolenaar30613902019-12-01 22:11:18 +01001169 // Shift-Tab results in Left_Tab, but we want <S-Tab>
Bram Moolenaar071d4272004-06-13 20:20:40 +00001170 if (key_sym == GDK_ISO_Left_Tab)
1171 {
1172 key_sym = GDK_Tab;
1173 state |= GDK_SHIFT_MASK;
1174 }
1175
Bram Moolenaar071d4272004-06-13 20:20:40 +00001176#ifdef FEAT_MENU
Bram Moolenaar30613902019-12-01 22:11:18 +01001177 // If there is a menu and 'wak' is "yes", or 'wak' is "menu" and the key
1178 // is a menu shortcut, we ignore everything with the ALT modifier.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001179 if ((state & GDK_MOD1_MASK)
1180 && gui.menu_is_active
1181 && (*p_wak == 'y'
1182 || (*p_wak == 'm'
1183 && len == 1
1184 && gui_is_menu_shortcut(string[0]))))
Bram Moolenaar30613902019-12-01 22:11:18 +01001185 // For GTK2 we return false to signify that we haven't handled the
1186 // keypress, so that gtk will handle the mnemonic or accelerator.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001187 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001188#endif
1189
Bram Moolenaarfd615a32020-05-16 14:01:51 +02001190 // We used to apply Alt/Meta to the key here (Mod1Mask), but that is now
1191 // done later, the same as it happens for the terminal. Hopefully that
1192 // works for everybody...
Bram Moolenaar071d4272004-06-13 20:20:40 +00001193
Bram Moolenaar30613902019-12-01 22:11:18 +01001194 // Check for special keys. Also do this when len == 1 (key has an ASCII
1195 // value) to detect backspace, delete and keypad keys.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196 if (len == 0 || len == 1)
1197 {
1198 for (i = 0; special_keys[i].key_sym != 0; i++)
1199 {
1200 if (special_keys[i].key_sym == key_sym)
1201 {
1202 string[0] = CSI;
1203 string[1] = special_keys[i].code0;
1204 string[2] = special_keys[i].code1;
1205 len = -3;
1206 break;
1207 }
1208 }
1209 }
1210
Bram Moolenaar30613902019-12-01 22:11:18 +01001211 if (len == 0) // Unrecognized key
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 return TRUE;
1213
Bram Moolenaarfd615a32020-05-16 14:01:51 +02001214 // Handle modifiers.
1215 modifiers = modifiers_gdk2vim(state);
1216
1217 // For some keys a shift modifier is translated into another key code.
1218 if (len == -3)
1219 key = TO_SPECIAL(string[1], string[2]);
1220 else
1221 key = string[0];
1222
1223 key = simplify_key(key, &modifiers);
1224 if (key == CSI)
1225 key = K_CSI;
1226 if (IS_SPECIAL(key))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001227 {
Bram Moolenaarfd615a32020-05-16 14:01:51 +02001228 string[0] = CSI;
1229 string[1] = K_SECOND(key);
1230 string[2] = K_THIRD(key);
1231 len = 3;
1232 }
1233 else
1234 {
Bram Moolenaarf4ae6b22020-05-30 19:52:46 +02001235 // <C-H> and <C-h> mean the same thing, always use "H"
1236 if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key))
1237 key = TOUPPER_ASC(key);
Bram Moolenaarfd615a32020-05-16 14:01:51 +02001238 string[0] = key;
1239 len = 1;
1240 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241
Bram Moolenaarfd615a32020-05-16 14:01:51 +02001242 if (modifiers != 0)
1243 {
1244 string2[0] = CSI;
1245 string2[1] = KS_MODIFIER;
1246 string2[2] = modifiers;
1247 add_to_input_buf(string2, 3);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 }
1249
1250 if (len == 1 && ((string[0] == Ctrl_C && ctrl_c_interrupts)
1251 || (string[0] == intr_char && intr_char != Ctrl_C)))
1252 {
1253 trash_input_buf();
1254 got_int = TRUE;
1255 }
1256
1257 add_to_input_buf(string, len);
1258
Bram Moolenaar30613902019-12-01 22:11:18 +01001259 // blank out the pointer if necessary
Bram Moolenaar071d4272004-06-13 20:20:40 +00001260 if (p_mh)
1261 gui_mch_mousehide(TRUE);
1262
Bram Moolenaar071d4272004-06-13 20:20:40 +00001263 return TRUE;
1264}
1265
Bram Moolenaar98921892016-02-23 17:14:37 +01001266#if defined(FEAT_XIM) || GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 static gboolean
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001268key_release_event(GtkWidget *widget UNUSED,
1269 GdkEventKey *event,
1270 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001271{
Bram Moolenaar98921892016-02-23 17:14:37 +01001272# if GTK_CHECK_VERSION(3,0,0)
1273 is_key_pressed = FALSE;
1274 gui_mch_start_blink();
1275# endif
1276# if defined(FEAT_XIM)
Bram Moolenaar20892c12011-06-26 04:49:00 +02001277 gui.event_time = event->time;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 /*
1279 * GTK+ 2 input methods may do fancy stuff on key release events too.
1280 * With the default IM for instance, you can enter any UCS code point
1281 * by holding down CTRL-SHIFT and typing hexadecimal digits.
1282 */
1283 return xim_queue_key_press_event(event, FALSE);
Bram Moolenaar98921892016-02-23 17:14:37 +01001284# else
1285 return TRUE;
1286# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001287}
1288#endif
1289
1290
Bram Moolenaar30613902019-12-01 22:11:18 +01001291/////////////////////////////////////////////////////////////////////////////
1292// Selection handlers:
Bram Moolenaar071d4272004-06-13 20:20:40 +00001293
Bram Moolenaar30613902019-12-01 22:11:18 +01001294// Remember when clip_lose_selection was called from here, we must not call
1295// gtk_selection_owner_set() then.
Bram Moolenaar29dfa5a2018-03-20 21:24:45 +01001296static int in_selection_clear_event = FALSE;
1297
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001299selection_clear_event(GtkWidget *widget UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300 GdkEventSelection *event,
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001301 gpointer user_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001302{
Bram Moolenaar29dfa5a2018-03-20 21:24:45 +01001303 in_selection_clear_event = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001304 if (event->selection == clip_plus.gtk_sel_atom)
1305 clip_lose_selection(&clip_plus);
1306 else
1307 clip_lose_selection(&clip_star);
Bram Moolenaar29dfa5a2018-03-20 21:24:45 +01001308 in_selection_clear_event = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309
Bram Moolenaar071d4272004-06-13 20:20:40 +00001310 return TRUE;
1311}
1312
Bram Moolenaar30613902019-12-01 22:11:18 +01001313#define RS_NONE 0 // selection_received_cb() not called yet
1314#define RS_OK 1 // selection_received_cb() called and OK
1315#define RS_FAIL 2 // selection_received_cb() called and failed
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316static int received_selection = RS_NONE;
1317
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001319selection_received_cb(GtkWidget *widget UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 GtkSelectionData *data,
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001321 guint time_ UNUSED,
1322 gpointer user_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001323{
Bram Moolenaar0554fa42019-06-14 21:36:54 +02001324 Clipboard_T *cbd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001325 char_u *text;
1326 char_u *tmpbuf = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 guchar *tmpbuf_utf8 = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001328 int len;
Bram Moolenaard44347f2011-06-19 01:14:29 +02001329 int motion_type = MAUTO;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001330
Bram Moolenaar98921892016-02-23 17:14:37 +01001331 if (gtk_selection_data_get_selection(data) == clip_plus.gtk_sel_atom)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332 cbd = &clip_plus;
1333 else
1334 cbd = &clip_star;
1335
Bram Moolenaar98921892016-02-23 17:14:37 +01001336 text = (char_u *)gtk_selection_data_get_data(data);
1337 len = gtk_selection_data_get_length(data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001338
1339 if (text == NULL || len <= 0)
1340 {
1341 received_selection = RS_FAIL;
Bram Moolenaar30613902019-12-01 22:11:18 +01001342 // clip_free_selection(cbd); ???
Bram Moolenaar071d4272004-06-13 20:20:40 +00001343
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344 return;
1345 }
1346
Bram Moolenaar98921892016-02-23 17:14:37 +01001347 if (gtk_selection_data_get_data_type(data) == vim_atom)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001348 {
1349 motion_type = *text++;
1350 --len;
1351 }
Bram Moolenaar98921892016-02-23 17:14:37 +01001352 else if (gtk_selection_data_get_data_type(data) == vimenc_atom)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001353 {
1354 char_u *enc;
1355 vimconv_T conv;
1356
1357 motion_type = *text++;
1358 --len;
1359
1360 enc = text;
1361 text += STRLEN(text) + 1;
1362 len -= text - enc;
1363
Bram Moolenaar30613902019-12-01 22:11:18 +01001364 // If the encoding of the text is different from 'encoding', attempt
1365 // converting it.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001366 conv.vc_type = CONV_NONE;
1367 convert_setup(&conv, enc, p_enc);
1368 if (conv.vc_type != CONV_NONE)
1369 {
1370 tmpbuf = string_convert(&conv, text, &len);
1371 if (tmpbuf != NULL)
1372 text = tmpbuf;
1373 convert_setup(&conv, NULL, NULL);
1374 }
1375 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376
Bram Moolenaar30613902019-12-01 22:11:18 +01001377 // gtk_selection_data_get_text() handles all the nasty details
1378 // and targets and encodings etc. This rocks so hard.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001379 else
1380 {
1381 tmpbuf_utf8 = gtk_selection_data_get_text(data);
1382 if (tmpbuf_utf8 != NULL)
1383 {
1384 len = STRLEN(tmpbuf_utf8);
1385 if (input_conv.vc_type != CONV_NONE)
1386 {
1387 tmpbuf = string_convert(&input_conv, tmpbuf_utf8, &len);
1388 if (tmpbuf != NULL)
1389 text = tmpbuf;
1390 }
1391 else
1392 text = tmpbuf_utf8;
1393 }
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00001394 else if (len >= 2 && text[0] == 0xff && text[1] == 0xfe)
1395 {
1396 vimconv_T conv;
1397
Bram Moolenaar30613902019-12-01 22:11:18 +01001398 // UTF-16, we get this for HTML
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00001399 conv.vc_type = CONV_NONE;
1400 convert_setup_ext(&conv, (char_u *)"utf-16le", FALSE, p_enc, TRUE);
1401
1402 if (conv.vc_type != CONV_NONE)
1403 {
1404 text += 2;
1405 len -= 2;
1406 tmpbuf = string_convert(&conv, text, &len);
1407 convert_setup(&conv, NULL, NULL);
1408 }
1409 if (tmpbuf != NULL)
1410 text = tmpbuf;
1411 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001412 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001413
Bram Moolenaar30613902019-12-01 22:11:18 +01001414 // Chop off any trailing NUL bytes. OpenOffice sends these.
Bram Moolenaara76638f2010-06-05 12:49:46 +02001415 while (len > 0 && text[len - 1] == NUL)
1416 --len;
1417
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 clip_yank_selection(motion_type, text, (long)len, cbd);
1419 received_selection = RS_OK;
1420 vim_free(tmpbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001421 g_free(tmpbuf_utf8);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422}
1423
1424/*
1425 * Prepare our selection data for passing it to the external selection
1426 * client.
1427 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001428 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001429selection_get_cb(GtkWidget *widget UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001430 GtkSelectionData *selection_data,
1431 guint info,
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001432 guint time_ UNUSED,
1433 gpointer user_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001434{
1435 char_u *string;
1436 char_u *tmpbuf;
1437 long_u tmplen;
1438 int length;
1439 int motion_type;
1440 GdkAtom type;
Bram Moolenaar0554fa42019-06-14 21:36:54 +02001441 Clipboard_T *cbd;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001442
Bram Moolenaar98921892016-02-23 17:14:37 +01001443 if (gtk_selection_data_get_selection(selection_data)
1444 == clip_plus.gtk_sel_atom)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001445 cbd = &clip_plus;
1446 else
1447 cbd = &clip_star;
1448
1449 if (!cbd->owned)
Bram Moolenaar30613902019-12-01 22:11:18 +01001450 return; // Shouldn't ever happen
Bram Moolenaar071d4272004-06-13 20:20:40 +00001451
1452 if (info != (guint)TARGET_STRING
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00001453 && (!clip_html || info != (guint)TARGET_HTML)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001454 && info != (guint)TARGET_UTF8_STRING
1455 && info != (guint)TARGET_VIMENC
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456 && info != (guint)TARGET_VIM
1457 && info != (guint)TARGET_COMPOUND_TEXT
1458 && info != (guint)TARGET_TEXT)
1459 return;
1460
Bram Moolenaar30613902019-12-01 22:11:18 +01001461 // get the selection from the '*'/'+' register
Bram Moolenaar071d4272004-06-13 20:20:40 +00001462 clip_get_selection(cbd);
1463
1464 motion_type = clip_convert_selection(&string, &tmplen, cbd);
1465 if (motion_type < 0 || string == NULL)
1466 return;
Bram Moolenaar30613902019-12-01 22:11:18 +01001467 // Due to int arguments we can't handle more than G_MAXINT. Also
1468 // reserve one extra byte for NUL or the motion type; just in case.
1469 // (Not that pasting 2G of text is ever going to work, but... ;-)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001470 length = MIN(tmplen, (long_u)(G_MAXINT - 1));
1471
1472 if (info == (guint)TARGET_VIM)
1473 {
Bram Moolenaar964b3742019-05-24 18:54:09 +02001474 tmpbuf = alloc(length + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001475 if (tmpbuf != NULL)
1476 {
1477 tmpbuf[0] = motion_type;
1478 mch_memmove(tmpbuf + 1, string, (size_t)length);
1479 }
Bram Moolenaar30613902019-12-01 22:11:18 +01001480 // For our own format, the first byte contains the motion type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001481 ++length;
1482 vim_free(string);
1483 string = tmpbuf;
1484 type = vim_atom;
1485 }
1486
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00001487 else if (info == (guint)TARGET_HTML)
1488 {
1489 vimconv_T conv;
1490
Bram Moolenaar30613902019-12-01 22:11:18 +01001491 // Since we get utf-16, we probably should set it as well.
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00001492 conv.vc_type = CONV_NONE;
1493 convert_setup_ext(&conv, p_enc, TRUE, (char_u *)"utf-16le", FALSE);
1494 if (conv.vc_type != CONV_NONE)
1495 {
1496 tmpbuf = string_convert(&conv, string, &length);
1497 convert_setup(&conv, NULL, NULL);
1498 vim_free(string);
1499 string = tmpbuf;
1500 }
1501
Bram Moolenaar30613902019-12-01 22:11:18 +01001502 // Prepend the BOM: "fffe"
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00001503 if (string != NULL)
1504 {
1505 tmpbuf = alloc(length + 2);
Bram Moolenaar6ee96582019-04-27 22:06:37 +02001506 if (tmpbuf != NULL)
1507 {
1508 tmpbuf[0] = 0xff;
1509 tmpbuf[1] = 0xfe;
1510 mch_memmove(tmpbuf + 2, string, (size_t)length);
1511 vim_free(string);
1512 string = tmpbuf;
1513 length += 2;
1514 }
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00001515
Bram Moolenaar98921892016-02-23 17:14:37 +01001516#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar30613902019-12-01 22:11:18 +01001517 // Looks redundant even for GTK2 because these values are
1518 // overwritten by gtk_selection_data_set() that follows.
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00001519 selection_data->type = selection_data->target;
Bram Moolenaar30613902019-12-01 22:11:18 +01001520 selection_data->format = 16; // 16 bits per char
Bram Moolenaar98921892016-02-23 17:14:37 +01001521#endif
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00001522 gtk_selection_data_set(selection_data, html_atom, 16,
1523 string, length);
1524 vim_free(string);
1525 }
1526 return;
1527 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001528 else if (info == (guint)TARGET_VIMENC)
1529 {
1530 int l = STRLEN(p_enc);
1531
Bram Moolenaar30613902019-12-01 22:11:18 +01001532 // contents: motion_type 'encoding' NUL text
Bram Moolenaar964b3742019-05-24 18:54:09 +02001533 tmpbuf = alloc(length + l + 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001534 if (tmpbuf != NULL)
1535 {
1536 tmpbuf[0] = motion_type;
1537 STRCPY(tmpbuf + 1, p_enc);
1538 mch_memmove(tmpbuf + l + 2, string, (size_t)length);
Bram Moolenaar6ee96582019-04-27 22:06:37 +02001539 length += l + 2;
1540 vim_free(string);
1541 string = tmpbuf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001542 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001543 type = vimenc_atom;
1544 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001545
Bram Moolenaar30613902019-12-01 22:11:18 +01001546 // gtk_selection_data_set_text() handles everything for us. This is
1547 // so easy and simple and cool, it'd be insane not to use it.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001548 else
1549 {
1550 if (output_conv.vc_type != CONV_NONE)
1551 {
1552 tmpbuf = string_convert(&output_conv, string, &length);
1553 vim_free(string);
1554 if (tmpbuf == NULL)
1555 return;
1556 string = tmpbuf;
1557 }
Bram Moolenaar30613902019-12-01 22:11:18 +01001558 // Validate the string to avoid runtime warnings
Bram Moolenaar071d4272004-06-13 20:20:40 +00001559 if (g_utf8_validate((const char *)string, (gssize)length, NULL))
1560 {
1561 gtk_selection_data_set_text(selection_data,
1562 (const char *)string, length);
1563 }
1564 vim_free(string);
1565 return;
1566 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001567
1568 if (string != NULL)
1569 {
Bram Moolenaar98921892016-02-23 17:14:37 +01001570#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar30613902019-12-01 22:11:18 +01001571 // Looks redundant even for GTK2 because these values are
1572 // overwritten by gtk_selection_data_set() that follows.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001573 selection_data->type = selection_data->target;
Bram Moolenaar30613902019-12-01 22:11:18 +01001574 selection_data->format = 8; // 8 bits per char
Bram Moolenaar98921892016-02-23 17:14:37 +01001575#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001576 gtk_selection_data_set(selection_data, type, 8, string, length);
1577 vim_free(string);
1578 }
1579}
1580
1581/*
Bram Moolenaar29695702012-05-18 17:03:18 +02001582 * Check if the GUI can be started. Called before gvimrc is sourced and
1583 * before fork().
1584 * Return OK or FAIL.
1585 */
1586 int
Bram Moolenaar717e1962016-08-10 21:28:44 +02001587gui_mch_early_init_check(int give_message)
Bram Moolenaar29695702012-05-18 17:03:18 +02001588{
1589 char_u *p;
1590
Bram Moolenaar30613902019-12-01 22:11:18 +01001591 // Guess that when $DISPLAY isn't set the GUI can't start.
Bram Moolenaar29695702012-05-18 17:03:18 +02001592 p = mch_getenv((char_u *)"DISPLAY");
1593 if (p == NULL || *p == NUL)
1594 {
1595 gui.dying = TRUE;
Bram Moolenaar717e1962016-08-10 21:28:44 +02001596 if (give_message)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001597 emsg(_((char *)e_opendisp));
Bram Moolenaar29695702012-05-18 17:03:18 +02001598 return FAIL;
1599 }
1600 return OK;
1601}
1602
1603/*
1604 * Check if the GUI can be started. Called before gvimrc is sourced but after
1605 * fork().
Bram Moolenaar071d4272004-06-13 20:20:40 +00001606 * Return OK or FAIL.
1607 */
1608 int
1609gui_mch_init_check(void)
1610{
Bram Moolenaar3a466a82016-01-19 17:47:25 +01001611#ifdef USE_GRESOURCE
1612 static int res_registered = FALSE;
1613
1614 if (!res_registered)
1615 {
Bram Moolenaar30613902019-12-01 22:11:18 +01001616 // Call this function in the GUI process; otherwise, the resources
1617 // won't be available. Don't call it twice.
Bram Moolenaar3a466a82016-01-19 17:47:25 +01001618 res_registered = TRUE;
1619 gui_gtk_register_resource();
1620 }
1621#endif
1622
Bram Moolenaarf80663f2016-04-05 21:56:06 +02001623#if GTK_CHECK_VERSION(3,10,0)
Bram Moolenaar30613902019-12-01 22:11:18 +01001624 // Vim currently assumes that Gtk means X11, so it cannot use native Gtk
1625 // support for other backends such as Wayland.
Bram Moolenaarf80663f2016-04-05 21:56:06 +02001626 gdk_set_allowed_backends ("x11");
1627#endif
1628
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629#ifdef FEAT_GUI_GNOME
1630 if (gtk_socket_id == 0)
1631 using_gnome = 1;
1632#endif
1633
Bram Moolenaar30613902019-12-01 22:11:18 +01001634 // This defaults to argv[0], but we want it to match the name of the
1635 // shipped gvim.desktop so that Vim's windows can be associated with this
1636 // file.
Bram Moolenaar8dd79012013-05-21 12:52:04 +02001637 g_set_prgname("gvim");
1638
Bram Moolenaar30613902019-12-01 22:11:18 +01001639 // Don't use gtk_init() or gnome_init(), it exits on failure.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001640 if (!gtk_init_check(&gui_argc, &gui_argv))
1641 {
1642 gui.dying = TRUE;
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001643 emsg(_((char *)e_opendisp));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001644 return FAIL;
1645 }
1646
1647 return OK;
1648}
1649
Bram Moolenaar30613902019-12-01 22:11:18 +01001650/////////////////////////////////////////////////////////////////////////////
1651// Mouse handling callbacks
Bram Moolenaar071d4272004-06-13 20:20:40 +00001652
1653
1654static guint mouse_click_timer = 0;
1655static int mouse_timed_out = TRUE;
1656
1657/*
1658 * Timer used to recognize multiple clicks of the mouse button
1659 */
Bram Moolenaar4ab79682017-08-27 14:50:47 +02001660 static timeout_cb_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661mouse_click_timer_cb(gpointer data)
1662{
Bram Moolenaar30613902019-12-01 22:11:18 +01001663 // we don't use this information currently
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664 int *timed_out = (int *) data;
1665
1666 *timed_out = TRUE;
Bram Moolenaar30613902019-12-01 22:11:18 +01001667 return FALSE; // don't happen again
Bram Moolenaar071d4272004-06-13 20:20:40 +00001668}
1669
Bram Moolenaar4ab79682017-08-27 14:50:47 +02001670static guint motion_repeat_timer = 0;
1671static int motion_repeat_offset = FALSE;
1672static timeout_cb_type motion_repeat_timer_cb(gpointer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001673
1674 static void
1675process_motion_notify(int x, int y, GdkModifierType state)
1676{
1677 int button;
1678 int_u vim_modifiers;
Bram Moolenaar98921892016-02-23 17:14:37 +01001679 GtkAllocation allocation;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001680
1681 button = (state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1682 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK |
1683 GDK_BUTTON5_MASK))
1684 ? MOUSE_DRAG : ' ';
1685
Bram Moolenaar30613902019-12-01 22:11:18 +01001686 // If our pointer is currently hidden, then we should show it.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001687 gui_mch_mousehide(FALSE);
1688
Bram Moolenaar30613902019-12-01 22:11:18 +01001689 // Just moving the rodent above the drawing area without any button
1690 // being pressed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001691 if (button != MOUSE_DRAG)
1692 {
1693 gui_mouse_moved(x, y);
1694 return;
1695 }
1696
Bram Moolenaar30613902019-12-01 22:11:18 +01001697 // translate modifier coding between the main engine and GTK
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001698 vim_modifiers = modifiers_gdk2mouse(state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001699
Bram Moolenaar30613902019-12-01 22:11:18 +01001700 // inform the editor engine about the occurrence of this event
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701 gui_send_mouse_event(button, x, y, FALSE, vim_modifiers);
1702
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703 /*
1704 * Auto repeat timer handling.
1705 */
Bram Moolenaar98921892016-02-23 17:14:37 +01001706 gtk_widget_get_allocation(gui.drawarea, &allocation);
1707
1708 if (x < 0 || y < 0
1709 || x >= allocation.width
1710 || y >= allocation.height)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001711 {
1712
1713 int dx;
1714 int dy;
1715 int offshoot;
1716 int delay = 10;
1717
Bram Moolenaar30613902019-12-01 22:11:18 +01001718 // Calculate the maximal distance of the cursor from the drawing area.
1719 // (offshoot can't become negative here!).
Bram Moolenaar98921892016-02-23 17:14:37 +01001720 dx = x < 0 ? -x : x - allocation.width;
1721 dy = y < 0 ? -y : y - allocation.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001722
1723 offshoot = dx > dy ? dx : dy;
1724
Bram Moolenaar30613902019-12-01 22:11:18 +01001725 // Make a linearly decaying timer delay with a threshold of 5 at a
1726 // distance of 127 pixels from the main window.
1727 //
1728 // One could think endlessly about the most ergonomic variant here.
1729 // For example it could make sense to calculate the distance from the
1730 // drags start instead...
1731 //
1732 // Maybe a parabolic interpolation would suite us better here too...
Bram Moolenaar071d4272004-06-13 20:20:40 +00001733 if (offshoot > 127)
1734 {
Bram Moolenaar30613902019-12-01 22:11:18 +01001735 // 5 appears to be somehow near to my perceptual limits :-).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736 delay = 5;
1737 }
1738 else
1739 {
1740 delay = (130 * (127 - offshoot)) / 127 + 5;
1741 }
1742
Bram Moolenaar30613902019-12-01 22:11:18 +01001743 // shoot again
Bram Moolenaar071d4272004-06-13 20:20:40 +00001744 if (!motion_repeat_timer)
Bram Moolenaar4ab79682017-08-27 14:50:47 +02001745 motion_repeat_timer = timeout_add(delay, motion_repeat_timer_cb,
1746 NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747 }
1748}
1749
Bram Moolenaar98921892016-02-23 17:14:37 +01001750#if GTK_CHECK_VERSION(3,0,0)
1751 static GdkDevice *
1752gui_gtk_get_pointer_device(GtkWidget *widget)
1753{
1754 GdkWindow * const win = gtk_widget_get_window(widget);
1755 GdkDisplay * const dpy = gdk_window_get_display(win);
Bram Moolenaar30e12d22016-04-17 20:49:53 +02001756# if GTK_CHECK_VERSION(3,20,0)
1757 GdkSeat * const seat = gdk_display_get_default_seat(dpy);
1758 return gdk_seat_get_pointer(seat);
1759# else
Bram Moolenaar98921892016-02-23 17:14:37 +01001760 GdkDeviceManager * const mngr = gdk_display_get_device_manager(dpy);
1761 return gdk_device_manager_get_client_pointer(mngr);
Bram Moolenaar30e12d22016-04-17 20:49:53 +02001762# endif
Bram Moolenaar98921892016-02-23 17:14:37 +01001763}
1764
1765 static GdkWindow *
1766gui_gtk_get_pointer(GtkWidget *widget,
1767 gint *x,
1768 gint *y,
1769 GdkModifierType *state)
1770{
1771 GdkWindow * const win = gtk_widget_get_window(widget);
1772 GdkDevice * const dev = gui_gtk_get_pointer_device(widget);
1773 return gdk_window_get_device_position(win, dev , x, y, state);
1774}
1775
Bram Moolenaar2369c152016-03-04 23:08:25 +01001776# if defined(FEAT_GUI_TABLINE) || defined(PROTO)
Bram Moolenaar98921892016-02-23 17:14:37 +01001777 static GdkWindow *
1778gui_gtk_window_at_position(GtkWidget *widget,
1779 gint *x,
1780 gint *y)
1781{
1782 GdkDevice * const dev = gui_gtk_get_pointer_device(widget);
1783 return gdk_device_get_window_at_position(dev, x, y);
1784}
Bram Moolenaar2369c152016-03-04 23:08:25 +01001785# endif
Bram Moolenaar30613902019-12-01 22:11:18 +01001786#else // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar664323e2018-09-18 22:30:07 +02001787# define gui_gtk_get_pointer(wid, x, y, s) \
1788 gdk_window_get_pointer((wid)->window, x, y, s)
1789# define gui_gtk_window_at_position(wid, x, y) gdk_window_at_pointer(x, y)
Bram Moolenaar98921892016-02-23 17:14:37 +01001790#endif
1791
Bram Moolenaar071d4272004-06-13 20:20:40 +00001792/*
1793 * Timer used to recognize multiple clicks of the mouse button.
1794 */
Bram Moolenaar4ab79682017-08-27 14:50:47 +02001795 static timeout_cb_type
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001796motion_repeat_timer_cb(gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797{
1798 int x;
1799 int y;
1800 GdkModifierType state;
1801
Bram Moolenaar98921892016-02-23 17:14:37 +01001802 gui_gtk_get_pointer(gui.drawarea, &x, &y, &state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001803
1804 if (!(state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK |
1805 GDK_BUTTON3_MASK | GDK_BUTTON4_MASK |
1806 GDK_BUTTON5_MASK)))
1807 {
1808 motion_repeat_timer = 0;
1809 return FALSE;
1810 }
1811
Bram Moolenaar30613902019-12-01 22:11:18 +01001812 // If there already is a mouse click in the input buffer, wait another
1813 // time (otherwise we would create a backlog of clicks)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 if (vim_used_in_input_buf() > 10)
1815 return TRUE;
1816
1817 motion_repeat_timer = 0;
1818
1819 /*
1820 * Fake a motion event.
1821 * Trick: Pretend the mouse moved to the next character on every other
1822 * event, otherwise drag events will be discarded, because they are still
1823 * in the same character.
1824 */
1825 if (motion_repeat_offset)
1826 x += gui.char_width;
1827
1828 motion_repeat_offset = !motion_repeat_offset;
1829 process_motion_notify(x, y, state);
1830
Bram Moolenaar30613902019-12-01 22:11:18 +01001831 // Don't happen again. We will get reinstalled in the synthetic event
1832 // if needed -- thus repeating should still work.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001833 return FALSE;
1834}
1835
Bram Moolenaar071d4272004-06-13 20:20:40 +00001836 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001837motion_notify_event(GtkWidget *widget,
1838 GdkEventMotion *event,
1839 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001840{
1841 if (event->is_hint)
1842 {
1843 int x;
1844 int y;
1845 GdkModifierType state;
1846
Bram Moolenaar98921892016-02-23 17:14:37 +01001847 gui_gtk_get_pointer(widget, &x, &y, &state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001848 process_motion_notify(x, y, state);
1849 }
1850 else
1851 {
1852 process_motion_notify((int)event->x, (int)event->y,
1853 (GdkModifierType)event->state);
1854 }
1855
Bram Moolenaar30613902019-12-01 22:11:18 +01001856 return TRUE; // handled
Bram Moolenaar071d4272004-06-13 20:20:40 +00001857}
1858
1859
1860/*
1861 * Mouse button handling. Note please that we are capturing multiple click's
1862 * by our own timeout mechanism instead of the one provided by GTK+ itself.
1863 * This is due to the way the generic VIM code is recognizing multiple clicks.
1864 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001865 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001866button_press_event(GtkWidget *widget,
1867 GdkEventButton *event,
1868 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001869{
1870 int button;
1871 int repeated_click = FALSE;
1872 int x, y;
1873 int_u vim_modifiers;
1874
Bram Moolenaar20892c12011-06-26 04:49:00 +02001875 gui.event_time = event->time;
Bram Moolenaar7cfea752010-06-22 06:07:12 +02001876
Bram Moolenaar30613902019-12-01 22:11:18 +01001877 // Make sure we have focus now we've been selected
Bram Moolenaar98921892016-02-23 17:14:37 +01001878 if (gtk_socket_id != 0 && !gtk_widget_has_focus(widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001879 gtk_widget_grab_focus(widget);
1880
1881 /*
1882 * Don't let additional events about multiple clicks send by GTK to us
1883 * after the initial button press event confuse us.
1884 */
1885 if (event->type != GDK_BUTTON_PRESS)
1886 return FALSE;
1887
1888 x = event->x;
1889 y = event->y;
1890
Bram Moolenaar30613902019-12-01 22:11:18 +01001891 // Handle multiple clicks
Bram Moolenaar071d4272004-06-13 20:20:40 +00001892 if (!mouse_timed_out && mouse_click_timer)
1893 {
Bram Moolenaar4ab79682017-08-27 14:50:47 +02001894 timeout_remove(mouse_click_timer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001895 mouse_click_timer = 0;
1896 repeated_click = TRUE;
1897 }
1898
1899 mouse_timed_out = FALSE;
Bram Moolenaar4ab79682017-08-27 14:50:47 +02001900 mouse_click_timer = timeout_add(p_mouset, mouse_click_timer_cb,
1901 &mouse_timed_out);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001902
1903 switch (event->button)
1904 {
Bram Moolenaar30613902019-12-01 22:11:18 +01001905 // Keep in sync with gui_x11.c.
1906 // Buttons 4-7 are handled in scroll_event()
Bram Moolenaar88e484b2015-11-24 15:38:44 +01001907 case 1: button = MOUSE_LEFT; break;
1908 case 2: button = MOUSE_MIDDLE; break;
1909 case 3: button = MOUSE_RIGHT; break;
1910 case 8: button = MOUSE_X1; break;
1911 case 9: button = MOUSE_X2; break;
1912 default:
Bram Moolenaar30613902019-12-01 22:11:18 +01001913 return FALSE; // Unknown button
Bram Moolenaar071d4272004-06-13 20:20:40 +00001914 }
1915
1916#ifdef FEAT_XIM
Bram Moolenaar30613902019-12-01 22:11:18 +01001917 // cancel any preediting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001918 if (im_is_preediting())
1919 xim_reset();
1920#endif
1921
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001922 vim_modifiers = modifiers_gdk2mouse(event->state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001923
1924 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001925
1926 return TRUE;
1927}
1928
Bram Moolenaar071d4272004-06-13 20:20:40 +00001929/*
Bram Moolenaar182c5be2010-06-25 05:37:59 +02001930 * GTK+ 2 abstracts scrolling via the GdkEventScroll.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001931 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001932 static gboolean
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001933scroll_event(GtkWidget *widget,
1934 GdkEventScroll *event,
1935 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001936{
1937 int button;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001938 int_u vim_modifiers;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001939
Bram Moolenaar98921892016-02-23 17:14:37 +01001940 if (gtk_socket_id != 0 && !gtk_widget_has_focus(widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001941 gtk_widget_grab_focus(widget);
1942
1943 switch (event->direction)
1944 {
1945 case GDK_SCROLL_UP:
1946 button = MOUSE_4;
1947 break;
1948 case GDK_SCROLL_DOWN:
1949 button = MOUSE_5;
1950 break;
Bram Moolenaar8d9b40e2010-07-25 15:49:07 +02001951 case GDK_SCROLL_LEFT:
1952 button = MOUSE_7;
1953 break;
1954 case GDK_SCROLL_RIGHT:
1955 button = MOUSE_6;
1956 break;
Bram Moolenaar30613902019-12-01 22:11:18 +01001957 default: // This shouldn't happen
Bram Moolenaar071d4272004-06-13 20:20:40 +00001958 return FALSE;
1959 }
1960
1961# ifdef FEAT_XIM
Bram Moolenaar30613902019-12-01 22:11:18 +01001962 // cancel any preediting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001963 if (im_is_preediting())
1964 xim_reset();
1965# endif
1966
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001967 vim_modifiers = modifiers_gdk2mouse(event->state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001968
1969 gui_send_mouse_event(button, (int)event->x, (int)event->y,
1970 FALSE, vim_modifiers);
1971
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972 return TRUE;
1973}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001974
1975
Bram Moolenaar071d4272004-06-13 20:20:40 +00001976 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +00001977button_release_event(GtkWidget *widget UNUSED,
1978 GdkEventButton *event,
1979 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001980{
1981 int x, y;
1982 int_u vim_modifiers;
1983
Bram Moolenaar20892c12011-06-26 04:49:00 +02001984 gui.event_time = event->time;
Bram Moolenaar7cfea752010-06-22 06:07:12 +02001985
Bram Moolenaar30613902019-12-01 22:11:18 +01001986 // Remove any motion "machine gun" timers used for automatic further
1987 // extension of allocation areas if outside of the applications window
1988 // area .
Bram Moolenaar071d4272004-06-13 20:20:40 +00001989 if (motion_repeat_timer)
1990 {
Bram Moolenaar4ab79682017-08-27 14:50:47 +02001991 timeout_remove(motion_repeat_timer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001992 motion_repeat_timer = 0;
1993 }
1994
1995 x = event->x;
1996 y = event->y;
1997
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001998 vim_modifiers = modifiers_gdk2mouse(event->state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001999
2000 gui_send_mouse_event(MOUSE_RELEASE, x, y, FALSE, vim_modifiers);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002001
2002 return TRUE;
2003}
2004
2005
2006#ifdef FEAT_DND
Bram Moolenaar30613902019-12-01 22:11:18 +01002007/////////////////////////////////////////////////////////////////////////////
2008// Drag aNd Drop support handlers.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002009
2010/*
2011 * Count how many items there may be and separate them with a NUL.
2012 * Apparently the items are separated with \r\n. This is not documented,
2013 * thus be careful not to go past the end. Also allow separation with
2014 * NUL characters.
2015 */
2016 static int
2017count_and_decode_uri_list(char_u *out, char_u *raw, int len)
2018{
2019 int i;
2020 char_u *p = out;
2021 int count = 0;
2022
2023 for (i = 0; i < len; ++i)
2024 {
2025 if (raw[i] == NUL || raw[i] == '\n' || raw[i] == '\r')
2026 {
2027 if (p > out && p[-1] != NUL)
2028 {
2029 ++count;
2030 *p++ = NUL;
2031 }
2032 }
2033 else if (raw[i] == '%' && i + 2 < len && hexhex2nr(raw + i + 1) > 0)
2034 {
2035 *p++ = hexhex2nr(raw + i + 1);
2036 i += 2;
2037 }
2038 else
2039 *p++ = raw[i];
2040 }
2041 if (p > out && p[-1] != NUL)
2042 {
Bram Moolenaar30613902019-12-01 22:11:18 +01002043 *p = NUL; // last item didn't have \r or \n
Bram Moolenaar071d4272004-06-13 20:20:40 +00002044 ++count;
2045 }
2046 return count;
2047}
2048
2049/*
2050 * Parse NUL separated "src" strings. Make it an array "outlist" form. On
2051 * this process, URI which protocol is not "file:" are removed. Return
2052 * length of array (less than "max").
2053 */
2054 static int
2055filter_uri_list(char_u **outlist, int max, char_u *src)
2056{
2057 int i, j;
2058
2059 for (i = j = 0; i < max; ++i)
2060 {
2061 outlist[i] = NULL;
2062 if (STRNCMP(src, "file:", 5) == 0)
2063 {
2064 src += 5;
2065 if (STRNCMP(src, "//localhost", 11) == 0)
2066 src += 11;
2067 while (src[0] == '/' && src[1] == '/')
2068 ++src;
2069 outlist[j++] = vim_strsave(src);
2070 }
2071 src += STRLEN(src) + 1;
2072 }
2073 return j;
2074}
2075
2076 static char_u **
2077parse_uri_list(int *count, char_u *data, int len)
2078{
2079 int n = 0;
2080 char_u *tmp = NULL;
Bram Moolenaar945ec092016-06-08 21:17:43 +02002081 char_u **array = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002082
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002083 if (data != NULL && len > 0 && (tmp = alloc(len + 1)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002084 {
2085 n = count_and_decode_uri_list(tmp, data, len);
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002086 if (n > 0 && (array = ALLOC_MULT(char_u *, n)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002087 n = filter_uri_list(array, n, tmp);
2088 }
2089 vim_free(tmp);
2090 *count = n;
2091 return array;
2092}
2093
2094 static void
2095drag_handle_uri_list(GdkDragContext *context,
2096 GtkSelectionData *data,
2097 guint time_,
2098 GdkModifierType state,
2099 gint x,
2100 gint y)
2101{
2102 char_u **fnames;
2103 int nfiles = 0;
2104
Bram Moolenaar98921892016-02-23 17:14:37 +01002105 fnames = parse_uri_list(&nfiles,
2106 (char_u *)gtk_selection_data_get_data(data),
2107 gtk_selection_data_get_length(data));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002108
2109 if (fnames != NULL && nfiles > 0)
2110 {
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002111 int_u modifiers;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112
Bram Moolenaar30613902019-12-01 22:11:18 +01002113 gtk_drag_finish(context, TRUE, FALSE, time_); // accept
Bram Moolenaar071d4272004-06-13 20:20:40 +00002114
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002115 modifiers = modifiers_gdk2mouse(state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002116
2117 gui_handle_drop(x, y, modifiers, fnames, nfiles);
2118 }
Bram Moolenaareb3593b2006-04-22 22:33:57 +00002119 else
2120 vim_free(fnames);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002121}
2122
2123 static void
2124drag_handle_text(GdkDragContext *context,
2125 GtkSelectionData *data,
2126 guint time_,
2127 GdkModifierType state)
2128{
2129 char_u dropkey[6] = {CSI, KS_MODIFIER, 0, CSI, KS_EXTRA, (char_u)KE_DROP};
2130 char_u *text;
2131 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002132 char_u *tmpbuf = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002133
Bram Moolenaar98921892016-02-23 17:14:37 +01002134 text = (char_u *)gtk_selection_data_get_data(data);
2135 len = gtk_selection_data_get_length(data);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002136
Bram Moolenaar98921892016-02-23 17:14:37 +01002137 if (gtk_selection_data_get_data_type(data) == utf8_string_atom)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002138 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00002139 if (input_conv.vc_type != CONV_NONE)
2140 tmpbuf = string_convert(&input_conv, text, &len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002141 if (tmpbuf != NULL)
2142 text = tmpbuf;
2143 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002144
2145 dnd_yank_drag_data(text, (long)len);
Bram Moolenaar30613902019-12-01 22:11:18 +01002146 gtk_drag_finish(context, TRUE, FALSE, time_); // accept
Bram Moolenaar071d4272004-06-13 20:20:40 +00002147 vim_free(tmpbuf);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002148
Bram Moolenaar19a09a12005-03-04 23:39:37 +00002149 dropkey[2] = modifiers_gdk2vim(state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002150
2151 if (dropkey[2] != 0)
2152 add_to_input_buf(dropkey, (int)sizeof(dropkey));
2153 else
2154 add_to_input_buf(dropkey + 3, (int)(sizeof(dropkey) - 3));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002155}
2156
2157/*
2158 * DND receiver.
2159 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002160 static void
2161drag_data_received_cb(GtkWidget *widget,
2162 GdkDragContext *context,
2163 gint x,
2164 gint y,
2165 GtkSelectionData *data,
2166 guint info,
2167 guint time_,
Bram Moolenaarb85cb212009-05-17 14:24:23 +00002168 gpointer user_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002169{
2170 GdkModifierType state;
2171
Bram Moolenaar30613902019-12-01 22:11:18 +01002172 // Guard against trash
Bram Moolenaar98921892016-02-23 17:14:37 +01002173 const guchar * const data_data = gtk_selection_data_get_data(data);
2174 const gint data_length = gtk_selection_data_get_length(data);
2175 const gint data_format = gtk_selection_data_get_format(data);
2176
2177 if (data_data == NULL
2178 || data_length <= 0
2179 || data_format != 8
2180 || data_data[data_length] != '\0')
Bram Moolenaar071d4272004-06-13 20:20:40 +00002181 {
2182 gtk_drag_finish(context, FALSE, FALSE, time_);
2183 return;
2184 }
2185
Bram Moolenaar30613902019-12-01 22:11:18 +01002186 // Get the current modifier state for proper distinguishment between
2187 // different operations later.
Bram Moolenaar98921892016-02-23 17:14:37 +01002188 gui_gtk_get_pointer(widget, NULL, NULL, &state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002189
Bram Moolenaar30613902019-12-01 22:11:18 +01002190 // Not sure about the role of "text/plain" here...
Bram Moolenaar071d4272004-06-13 20:20:40 +00002191 if (info == (guint)TARGET_TEXT_URI_LIST)
2192 drag_handle_uri_list(context, data, time_, state, x, y);
2193 else
2194 drag_handle_text(context, data, time_, state);
2195
2196}
Bram Moolenaar30613902019-12-01 22:11:18 +01002197#endif // FEAT_DND
Bram Moolenaar071d4272004-06-13 20:20:40 +00002198
2199
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +02002200#if defined(USE_GNOME_SESSION)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002201/*
2202 * GnomeClient interact callback. Check for unsaved buffers that cannot
2203 * be abandoned and pop up a dialog asking the user for confirmation if
2204 * necessary.
2205 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002206 static void
Bram Moolenaar30bb4142010-05-17 22:07:15 +02002207sm_client_check_changed_any(GnomeClient *client UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002208 gint key,
Bram Moolenaar30bb4142010-05-17 22:07:15 +02002209 GnomeDialogType type UNUSED,
2210 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002211{
2212 cmdmod_T save_cmdmod;
2213 gboolean shutdown_cancelled;
2214
2215 save_cmdmod = cmdmod;
2216
2217# ifdef FEAT_BROWSE
2218 cmdmod.browse = TRUE;
2219# endif
2220# if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
2221 cmdmod.confirm = TRUE;
2222# endif
2223 /*
2224 * If there are changed buffers, present the user with
2225 * a dialog if possible, otherwise give an error message.
2226 */
Bram Moolenaar027387f2016-01-02 22:25:52 +01002227 shutdown_cancelled = check_changed_any(FALSE, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002228
2229 exiting = FALSE;
2230 cmdmod = save_cmdmod;
Bram Moolenaar30613902019-12-01 22:11:18 +01002231 setcursor(); // position the cursor
Bram Moolenaar071d4272004-06-13 20:20:40 +00002232 out_flush();
2233 /*
2234 * If the user hit the [Cancel] button the whole shutdown
2235 * will be cancelled. Wow, quite powerful feature (:
2236 */
2237 gnome_interaction_key_return(key, shutdown_cancelled);
2238}
2239
2240/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002241 * "save_yourself" signal handler. Initiate an interaction to ask the user
2242 * for confirmation if necessary. Save the current editing session and tell
2243 * the session manager how to restart Vim.
2244 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002245 static gboolean
2246sm_client_save_yourself(GnomeClient *client,
Bram Moolenaar30bb4142010-05-17 22:07:15 +02002247 gint phase UNUSED,
2248 GnomeSaveStyle save_style UNUSED,
2249 gboolean shutdown UNUSED,
Bram Moolenaar071d4272004-06-13 20:20:40 +00002250 GnomeInteractStyle interact_style,
Bram Moolenaar30bb4142010-05-17 22:07:15 +02002251 gboolean fast UNUSED,
2252 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002253{
2254 static const char suffix[] = "-session.vim";
2255 char *session_file;
2256 unsigned int len;
2257 gboolean success;
2258
Bram Moolenaar30613902019-12-01 22:11:18 +01002259 // Always request an interaction if possible. check_changed_any()
2260 // won't actually show a dialog unless any buffers have been modified.
2261 // There doesn't seem to be an obvious way to check that without
2262 // automatically firing the dialog. Anyway, it works just fine.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002263 if (interact_style == GNOME_INTERACT_ANY)
2264 gnome_client_request_interaction(client, GNOME_DIALOG_NORMAL,
2265 &sm_client_check_changed_any,
2266 NULL);
2267 out_flush();
Bram Moolenaar30613902019-12-01 22:11:18 +01002268 ml_sync_all(FALSE, FALSE); // preserve all swap files
Bram Moolenaar071d4272004-06-13 20:20:40 +00002269
Bram Moolenaar30613902019-12-01 22:11:18 +01002270 // The path is unique for each session save. We do neither know nor care
2271 // which session script will actually be used later. This decision is in
2272 // the domain of the session manager.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002273 session_file = gnome_config_get_real_path(
2274 gnome_client_get_config_prefix(client));
2275 len = strlen(session_file);
2276
2277 if (len > 0 && session_file[len-1] == G_DIR_SEPARATOR)
Bram Moolenaar30613902019-12-01 22:11:18 +01002278 --len; // get rid of the superfluous trailing '/'
Bram Moolenaar071d4272004-06-13 20:20:40 +00002279
2280 session_file = g_renew(char, session_file, len + sizeof(suffix));
2281 memcpy(session_file + len, suffix, sizeof(suffix));
2282
2283 success = write_session_file((char_u *)session_file);
2284
2285 if (success)
2286 {
2287 const char *argv[8];
2288 int i;
2289
Bram Moolenaar30613902019-12-01 22:11:18 +01002290 // Tell the session manager how to wipe out the stored session data.
2291 // This isn't as dangerous as it looks, don't worry :) session_file
2292 // is a unique absolute filename. Usually it'll be something like
2293 // `/home/user/.gnome2/vim-XXXXXX-session.vim'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002294 i = 0;
2295 argv[i++] = "rm";
2296 argv[i++] = session_file;
2297 argv[i] = NULL;
2298
2299 gnome_client_set_discard_command(client, i, (char **)argv);
2300
Bram Moolenaar30613902019-12-01 22:11:18 +01002301 // Tell the session manager how to restore the just saved session.
2302 // This is easily done thanks to Vim's -S option. Pass the -f flag
2303 // since there's no need to fork -- it might even cause confusion.
2304 // Also pass the window role to give the WM something to match on.
2305 // The role is set in gui_mch_open(), thus should _never_ be NULL.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002306 i = 0;
2307 argv[i++] = restart_command;
2308 argv[i++] = "-f";
2309 argv[i++] = "-g";
Bram Moolenaar071d4272004-06-13 20:20:40 +00002310 argv[i++] = "--role";
2311 argv[i++] = gtk_window_get_role(GTK_WINDOW(gui.mainwin));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002312 argv[i++] = "-S";
2313 argv[i++] = session_file;
2314 argv[i] = NULL;
2315
2316 gnome_client_set_restart_command(client, i, (char **)argv);
2317 gnome_client_set_clone_command(client, 0, NULL);
2318 }
2319
2320 g_free(session_file);
2321
2322 return success;
2323}
2324
2325/*
2326 * Called when the session manager wants us to die. There isn't much to save
2327 * here since "save_yourself" has been emitted before (unless serious trouble
2328 * is happening).
2329 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002330 static void
Bram Moolenaar30bb4142010-05-17 22:07:15 +02002331sm_client_die(GnomeClient *client UNUSED, gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002332{
Bram Moolenaar30613902019-12-01 22:11:18 +01002333 // Don't write messages to the GUI anymore
Bram Moolenaar071d4272004-06-13 20:20:40 +00002334 full_screen = FALSE;
2335
Bram Moolenaarc93e7912008-07-08 10:46:08 +00002336 vim_strncpy(IObuff, (char_u *)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002337 _("Vim: Received \"die\" request from session manager\n"),
Bram Moolenaarc93e7912008-07-08 10:46:08 +00002338 IOSIZE - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002339 preserve_exit();
2340}
2341
2342/*
2343 * Connect our signal handlers to be notified on session save and shutdown.
2344 */
2345 static void
2346setup_save_yourself(void)
2347{
2348 GnomeClient *client;
2349
2350 client = gnome_master_client();
2351
2352 if (client != NULL)
2353 {
Bram Moolenaar30613902019-12-01 22:11:18 +01002354 // Must use the deprecated gtk_signal_connect() for compatibility
2355 // with GNOME 1. Arrgh, zombies!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002356 gtk_signal_connect(GTK_OBJECT(client), "save_yourself",
2357 GTK_SIGNAL_FUNC(&sm_client_save_yourself), NULL);
2358 gtk_signal_connect(GTK_OBJECT(client), "die",
2359 GTK_SIGNAL_FUNC(&sm_client_die), NULL);
2360 }
2361}
2362
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +02002363#else // !USE_GNOME_SESSION
Bram Moolenaar071d4272004-06-13 20:20:40 +00002364
2365# ifdef USE_XSMP
2366/*
2367 * GTK tells us that XSMP needs attention
2368 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002369 static gboolean
Bram Moolenaar66f948e2016-01-30 16:39:25 +01002370local_xsmp_handle_requests(
2371 GIOChannel *source UNUSED,
2372 GIOCondition condition,
2373 gpointer data)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002374{
2375 if (condition == G_IO_IN)
2376 {
Bram Moolenaar30613902019-12-01 22:11:18 +01002377 // Do stuff; maybe close connection
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378 if (xsmp_handle_requests() == FAIL)
2379 g_io_channel_unref((GIOChannel *)data);
2380 return TRUE;
2381 }
Bram Moolenaar30613902019-12-01 22:11:18 +01002382 // Error
Bram Moolenaar071d4272004-06-13 20:20:40 +00002383 g_io_channel_unref((GIOChannel *)data);
2384 xsmp_close();
2385 return TRUE;
2386}
Bram Moolenaar30613902019-12-01 22:11:18 +01002387# endif // USE_XSMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002388
2389/*
2390 * Setup the WM_PROTOCOLS to indicate we want the WM_SAVE_YOURSELF event.
2391 * This is an ugly use of X functions. GTK doesn't offer an alternative.
2392 */
2393 static void
2394setup_save_yourself(void)
2395{
2396 Atom *existing_atoms = NULL;
2397 int count = 0;
2398
Bram Moolenaar98921892016-02-23 17:14:37 +01002399# ifdef USE_XSMP
Bram Moolenaar071d4272004-06-13 20:20:40 +00002400 if (xsmp_icefd != -1)
2401 {
2402 /*
2403 * Use XSMP is preference to legacy WM_SAVE_YOURSELF;
2404 * set up GTK IO monitor
2405 */
2406 GIOChannel *g_io = g_io_channel_unix_new(xsmp_icefd);
2407
2408 g_io_add_watch(g_io, G_IO_IN | G_IO_ERR | G_IO_HUP,
2409 local_xsmp_handle_requests, (gpointer)g_io);
Bram Moolenaar98921892016-02-23 17:14:37 +01002410 g_io_channel_unref(g_io);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002411 }
2412 else
Bram Moolenaar98921892016-02-23 17:14:37 +01002413# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002414 {
Bram Moolenaar30613902019-12-01 22:11:18 +01002415 // Fall back to old method
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416
Bram Moolenaar30613902019-12-01 22:11:18 +01002417 // first get the existing value
Bram Moolenaar98921892016-02-23 17:14:37 +01002418 GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin);
2419
2420 if (XGetWMProtocols(GDK_WINDOW_XDISPLAY(mainwin_win),
2421 GDK_WINDOW_XID(mainwin_win),
2422 &existing_atoms, &count))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002423 {
2424 Atom *new_atoms;
2425 Atom save_yourself_xatom;
2426 int i;
2427
2428 save_yourself_xatom = GET_X_ATOM(save_yourself_atom);
2429
Bram Moolenaar30613902019-12-01 22:11:18 +01002430 // check if WM_SAVE_YOURSELF isn't there yet
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 for (i = 0; i < count; ++i)
2432 if (existing_atoms[i] == save_yourself_xatom)
2433 break;
2434
2435 if (i == count)
2436 {
Bram Moolenaar30613902019-12-01 22:11:18 +01002437 // allocate an Atoms array which is one item longer
Bram Moolenaarc799fe22019-05-28 23:08:19 +02002438 new_atoms = ALLOC_MULT(Atom, count + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002439 if (new_atoms != NULL)
2440 {
2441 memcpy(new_atoms, existing_atoms, count * sizeof(Atom));
2442 new_atoms[count] = save_yourself_xatom;
Bram Moolenaar98921892016-02-23 17:14:37 +01002443 XSetWMProtocols(GDK_WINDOW_XDISPLAY(mainwin_win),
2444 GDK_WINDOW_XID(mainwin_win),
Bram Moolenaar071d4272004-06-13 20:20:40 +00002445 new_atoms, count + 1);
2446 vim_free(new_atoms);
2447 }
2448 }
2449 XFree(existing_atoms);
2450 }
2451 }
2452}
2453
Bram Moolenaar071d4272004-06-13 20:20:40 +00002454/*
2455 * Installing a global event filter seems to be the only way to catch
2456 * client messages of type WM_PROTOCOLS without overriding GDK's own
2457 * client message event filter. Well, that's still better than trying
2458 * to guess what the GDK filter had done if it had been invoked instead
Bram Moolenaar071d4272004-06-13 20:20:40 +00002459 *
2460 * GTK2_FIXME: This doesn't seem to work. For some reason we never
2461 * receive WM_SAVE_YOURSELF even though everything is set up correctly.
2462 * I have the nasty feeling modern session managers just don't send this
2463 * deprecated message anymore. Addition: confirmed by several people.
2464 *
2465 * The GNOME session support is much cooler anyway. Unlike this ugly
2466 * WM_SAVE_YOURSELF hack it actually stores the session... And yes,
2467 * it should work with KDE as well.
2468 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002469 static GdkFilterReturn
Bram Moolenaarb85cb212009-05-17 14:24:23 +00002470global_event_filter(GdkXEvent *xev,
2471 GdkEvent *event UNUSED,
2472 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002473{
2474 XEvent *xevent = (XEvent *)xev;
2475
2476 if (xevent != NULL
2477 && xevent->type == ClientMessage
2478 && xevent->xclient.message_type == GET_X_ATOM(wm_protocols_atom)
Bram Moolenaarb85cb212009-05-17 14:24:23 +00002479 && (long_u)xevent->xclient.data.l[0]
2480 == GET_X_ATOM(save_yourself_atom))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002481 {
2482 out_flush();
Bram Moolenaar30613902019-12-01 22:11:18 +01002483 ml_sync_all(FALSE, FALSE); // preserve all swap files
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484 /*
2485 * Set the window's WM_COMMAND property, to let the window manager
2486 * know we are done saving ourselves. We don't want to be
2487 * restarted, thus set argv to NULL.
2488 */
Bram Moolenaar98921892016-02-23 17:14:37 +01002489 XSetCommand(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
2490 GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin)),
Bram Moolenaar071d4272004-06-13 20:20:40 +00002491 NULL, 0);
2492 return GDK_FILTER_REMOVE;
2493 }
2494
2495 return GDK_FILTER_CONTINUE;
2496}
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +02002497#endif // !USE_GNOME_SESSION
Bram Moolenaar071d4272004-06-13 20:20:40 +00002498
2499
2500/*
2501 * Setup the window icon & xcmdsrv comm after the main window has been realized.
2502 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002503 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +00002504mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002505{
Bram Moolenaar30613902019-12-01 22:11:18 +01002506// If you get an error message here, you still need to unpack the runtime
2507// archive!
Bram Moolenaar071d4272004-06-13 20:20:40 +00002508#ifdef magick
2509# undef magick
2510#endif
Bram Moolenaar30613902019-12-01 22:11:18 +01002511 // A bit hackish, but avoids casting later and allows optimization
Bram Moolenaar071d4272004-06-13 20:20:40 +00002512# define static static const
Bram Moolenaar071d4272004-06-13 20:20:40 +00002513#define magick vim32x32
2514#include "../runtime/vim32x32.xpm"
2515#undef magick
2516#define magick vim16x16
2517#include "../runtime/vim16x16.xpm"
2518#undef magick
2519#define magick vim48x48
2520#include "../runtime/vim48x48.xpm"
2521#undef magick
Bram Moolenaar071d4272004-06-13 20:20:40 +00002522# undef static
Bram Moolenaar071d4272004-06-13 20:20:40 +00002523
Bram Moolenaar98921892016-02-23 17:14:37 +01002524 GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin);
Bram Moolenaar98921892016-02-23 17:14:37 +01002525
Bram Moolenaar30613902019-12-01 22:11:18 +01002526 // When started with "--echo-wid" argument, write window ID on stdout.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002527 if (echo_wid_arg)
2528 {
Bram Moolenaar98921892016-02-23 17:14:37 +01002529 printf("WID: %ld\n", (long)GDK_WINDOW_XID(mainwin_win));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002530 fflush(stdout);
2531 }
2532
2533 if (vim_strchr(p_go, GO_ICON) != NULL)
2534 {
2535 /*
2536 * Add an icon to the main window. For fun and convenience of the user.
2537 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002538 GList *icons = NULL;
2539
2540 icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim16x16));
2541 icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim32x32));
2542 icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim48x48));
2543
2544 gtk_window_set_icon_list(GTK_WINDOW(gui.mainwin), icons);
2545
2546 g_list_foreach(icons, (GFunc)&g_object_unref, NULL);
2547 g_list_free(icons);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002548 }
2549
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +02002550#if !defined(USE_GNOME_SESSION)
Bram Moolenaar30613902019-12-01 22:11:18 +01002551 // Register a handler for WM_SAVE_YOURSELF with GDK's low-level X I/F
Bram Moolenaar071d4272004-06-13 20:20:40 +00002552 gdk_window_add_filter(NULL, &global_event_filter, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002553#endif
Bram Moolenaar30613902019-12-01 22:11:18 +01002554 // Setup to indicate to the window manager that we want to catch the
2555 // WM_SAVE_YOURSELF event. For GNOME, this connects to the session
2556 // manager instead.
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +02002557#if defined(USE_GNOME_SESSION)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002558 if (using_gnome)
2559#endif
2560 setup_save_yourself();
2561
2562#ifdef FEAT_CLIENTSERVER
2563 if (serverName == NULL && serverDelayedStartName != NULL)
2564 {
Bram Moolenaar30613902019-12-01 22:11:18 +01002565 // This is a :gui command in a plain vim with no previous server
Bram Moolenaar98921892016-02-23 17:14:37 +01002566 commWindow = GDK_WINDOW_XID(mainwin_win);
2567
2568 (void)serverRegisterName(GDK_WINDOW_XDISPLAY(mainwin_win),
2569 serverDelayedStartName);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002570 }
2571 else
2572 {
2573 /*
2574 * Cannot handle "XLib-only" windows with gtk event routines, we'll
2575 * have to change the "server" registration to that of the main window
Bram Moolenaarb463e8d2017-06-05 15:07:09 +02002576 * If we have not registered a name yet, remember the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002577 */
Bram Moolenaar98921892016-02-23 17:14:37 +01002578 serverChangeRegisteredWindow(GDK_WINDOW_XDISPLAY(mainwin_win),
2579 GDK_WINDOW_XID(mainwin_win));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002580 }
2581 gtk_widget_add_events(gui.mainwin, GDK_PROPERTY_CHANGE_MASK);
Bram Moolenaar98921892016-02-23 17:14:37 +01002582 g_signal_connect(G_OBJECT(gui.mainwin), "property-notify-event",
2583 G_CALLBACK(property_event), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002584#endif
2585}
2586
2587 static GdkCursor *
2588create_blank_pointer(void)
2589{
2590 GdkWindow *root_window = NULL;
Bram Moolenaar98921892016-02-23 17:14:37 +01002591#if GTK_CHECK_VERSION(3,0,0)
2592 GdkPixbuf *blank_mask;
2593#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002594 GdkPixmap *blank_mask;
Bram Moolenaar98921892016-02-23 17:14:37 +01002595#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002596 GdkCursor *cursor;
Bram Moolenaar36edf062016-07-21 22:10:12 +02002597#if GTK_CHECK_VERSION(3,0,0)
2598 GdkRGBA color = { 0.0, 0.0, 0.0, 1.0 };
2599#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002600 GdkColor color = { 0, 0, 0, 0 };
2601 char blank_data[] = { 0x0 };
Bram Moolenaar98921892016-02-23 17:14:37 +01002602#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002603
Bram Moolenaarb463e8d2017-06-05 15:07:09 +02002604#if GTK_CHECK_VERSION(3,12,0)
Bram Moolenaar98921892016-02-23 17:14:37 +01002605 {
2606 GdkWindow * const win = gtk_widget_get_window(gui.mainwin);
2607 GdkScreen * const scrn = gdk_window_get_screen(win);
2608 root_window = gdk_screen_get_root_window(scrn);
2609 }
Bram Moolenaarb463e8d2017-06-05 15:07:09 +02002610#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002611 root_window = gtk_widget_get_root_window(gui.mainwin);
2612#endif
2613
Bram Moolenaar30613902019-12-01 22:11:18 +01002614 // Create a pseudo blank pointer, which is in fact one pixel by one pixel
2615 // in size.
Bram Moolenaar98921892016-02-23 17:14:37 +01002616#if GTK_CHECK_VERSION(3,0,0)
2617 {
2618 cairo_surface_t *surf;
2619 cairo_t *cr;
2620
2621 surf = cairo_image_surface_create(CAIRO_FORMAT_A1, 1, 1);
2622 cr = cairo_create(surf);
2623
Bram Moolenaar36edf062016-07-21 22:10:12 +02002624 cairo_set_source_rgba(cr,
2625 color.red,
2626 color.green,
2627 color.blue,
2628 color.alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01002629 cairo_rectangle(cr, 0, 0, 1, 1);
2630 cairo_fill(cr);
2631 cairo_destroy(cr);
2632
2633 blank_mask = gdk_pixbuf_get_from_surface(surf, 0, 0, 1, 1);
2634 cairo_surface_destroy(surf);
2635
2636 cursor = gdk_cursor_new_from_pixbuf(gdk_window_get_display(root_window),
2637 blank_mask, 0, 0);
2638 g_object_unref(blank_mask);
2639 }
2640#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002641 blank_mask = gdk_bitmap_create_from_data(root_window, blank_data, 1, 1);
2642 cursor = gdk_cursor_new_from_pixmap(blank_mask, blank_mask,
2643 &color, &color, 0, 0);
2644 gdk_bitmap_unref(blank_mask);
Bram Moolenaar98921892016-02-23 17:14:37 +01002645#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646
2647 return cursor;
2648}
2649
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650 static void
2651mainwin_screen_changed_cb(GtkWidget *widget,
Bram Moolenaarb85cb212009-05-17 14:24:23 +00002652 GdkScreen *previous_screen UNUSED,
2653 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002654{
2655 if (!gtk_widget_has_screen(widget))
2656 return;
2657
2658 /*
Bram Moolenaar49325942007-05-10 19:19:59 +00002659 * Recreate the invisible mouse cursor.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002660 */
2661 if (gui.blank_pointer != NULL)
Bram Moolenaarb463e8d2017-06-05 15:07:09 +02002662#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar98921892016-02-23 17:14:37 +01002663 g_object_unref(G_OBJECT(gui.blank_pointer));
Bram Moolenaarb463e8d2017-06-05 15:07:09 +02002664#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665 gdk_cursor_unref(gui.blank_pointer);
Bram Moolenaarb463e8d2017-06-05 15:07:09 +02002666#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002667
2668 gui.blank_pointer = create_blank_pointer();
2669
Bram Moolenaar98921892016-02-23 17:14:37 +01002670 if (gui.pointer_hidden && gtk_widget_get_window(gui.drawarea) != NULL)
2671 gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea),
2672 gui.blank_pointer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673
2674 /*
2675 * Create a new PangoContext for this screen, and initialize it
2676 * with the current font if necessary.
2677 */
2678 if (gui.text_context != NULL)
2679 g_object_unref(gui.text_context);
2680
2681 gui.text_context = gtk_widget_create_pango_context(widget);
2682 pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR);
2683
2684 if (gui.norm_font != NULL)
2685 {
Bram Moolenaar46c9c732004-12-12 11:37:09 +00002686 gui_mch_init_font(p_guifont, FALSE);
Bram Moolenaar8ac44152017-11-09 18:33:29 +01002687 gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002688 }
2689}
Bram Moolenaar071d4272004-06-13 20:20:40 +00002690
2691/*
2692 * After the drawing area comes up, we calculate all colors and create the
2693 * dummy blank cursor.
2694 *
2695 * Don't try to set any VIM scrollbar sizes anywhere here. I'm relying on the
2696 * fact that the main VIM engine doesn't take them into account anywhere.
2697 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002698 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +00002699drawarea_realize_cb(GtkWidget *widget, gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700{
2701 GtkWidget *sbar;
Bram Moolenaar98921892016-02-23 17:14:37 +01002702 GtkAllocation allocation;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002703
2704#ifdef FEAT_XIM
2705 xim_init();
2706#endif
2707 gui_mch_new_colors();
Bram Moolenaar98921892016-02-23 17:14:37 +01002708#if GTK_CHECK_VERSION(3,0,0)
2709 gui.surface = gdk_window_create_similar_surface(
2710 gtk_widget_get_window(widget),
2711 CAIRO_CONTENT_COLOR_ALPHA,
2712 gtk_widget_get_allocated_width(widget),
2713 gtk_widget_get_allocated_height(widget));
2714#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002715 gui.text_gc = gdk_gc_new(gui.drawarea->window);
Bram Moolenaar98921892016-02-23 17:14:37 +01002716#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002717
2718 gui.blank_pointer = create_blank_pointer();
2719 if (gui.pointer_hidden)
Bram Moolenaar98921892016-02-23 17:14:37 +01002720 gdk_window_set_cursor(gtk_widget_get_window(widget), gui.blank_pointer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002721
Bram Moolenaar30613902019-12-01 22:11:18 +01002722 // get the actual size of the scrollbars, if they are realized
Bram Moolenaar071d4272004-06-13 20:20:40 +00002723 sbar = firstwin->w_scrollbars[SBAR_LEFT].id;
2724 if (!sbar || (!gui.which_scrollbars[SBAR_LEFT]
2725 && firstwin->w_scrollbars[SBAR_RIGHT].id))
2726 sbar = firstwin->w_scrollbars[SBAR_RIGHT].id;
Bram Moolenaar98921892016-02-23 17:14:37 +01002727 gtk_widget_get_allocation(sbar, &allocation);
2728 if (sbar && gtk_widget_get_realized(sbar) && allocation.width)
2729 gui.scrollbar_width = allocation.width;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002730
2731 sbar = gui.bottom_sbar.id;
Bram Moolenaar98921892016-02-23 17:14:37 +01002732 if (sbar && gtk_widget_get_realized(sbar) && allocation.height)
2733 gui.scrollbar_height = allocation.height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002734}
2735
2736/*
2737 * Properly clean up on shutdown.
2738 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002739 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +00002740drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002741{
Bram Moolenaar30613902019-12-01 22:11:18 +01002742 // Don't write messages to the GUI anymore
Bram Moolenaar071d4272004-06-13 20:20:40 +00002743 full_screen = FALSE;
2744
2745#ifdef FEAT_XIM
2746 im_shutdown();
2747#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002748 if (gui.ascii_glyphs != NULL)
2749 {
2750 pango_glyph_string_free(gui.ascii_glyphs);
2751 gui.ascii_glyphs = NULL;
2752 }
2753 if (gui.ascii_font != NULL)
2754 {
2755 g_object_unref(gui.ascii_font);
2756 gui.ascii_font = NULL;
2757 }
2758 g_object_unref(gui.text_context);
2759 gui.text_context = NULL;
2760
Bram Moolenaar98921892016-02-23 17:14:37 +01002761#if GTK_CHECK_VERSION(3,0,0)
2762 if (gui.surface != NULL)
2763 {
2764 cairo_surface_destroy(gui.surface);
2765 gui.surface = NULL;
2766 }
2767#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002768 g_object_unref(gui.text_gc);
2769 gui.text_gc = NULL;
Bram Moolenaar98921892016-02-23 17:14:37 +01002770#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002771
Bram Moolenaar98921892016-02-23 17:14:37 +01002772#if GTK_CHECK_VERSION(3,0,0)
2773 g_object_unref(G_OBJECT(gui.blank_pointer));
2774#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 gdk_cursor_unref(gui.blank_pointer);
Bram Moolenaar98921892016-02-23 17:14:37 +01002776#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002777 gui.blank_pointer = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002778}
2779
Bram Moolenaara859f042016-11-17 19:11:55 +01002780#if GTK_CHECK_VERSION(3,22,2)
2781 static void
2782drawarea_style_updated_cb(GtkWidget *widget UNUSED,
2783 gpointer data UNUSED)
2784#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00002785 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +00002786drawarea_style_set_cb(GtkWidget *widget UNUSED,
2787 GtkStyle *previous_style UNUSED,
2788 gpointer data UNUSED)
Bram Moolenaara859f042016-11-17 19:11:55 +01002789#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002790{
2791 gui_mch_new_colors();
2792}
2793
Bram Moolenaar98921892016-02-23 17:14:37 +01002794#if GTK_CHECK_VERSION(3,0,0)
2795 static gboolean
2796drawarea_configure_event_cb(GtkWidget *widget,
2797 GdkEventConfigure *event,
2798 gpointer data UNUSED)
2799{
2800 static int cur_width = 0;
2801 static int cur_height = 0;
2802
2803 g_return_val_if_fail(event
2804 && event->width >= 1 && event->height >= 1, TRUE);
2805
Bram Moolenaar182707a2016-11-21 20:55:58 +01002806# if GTK_CHECK_VERSION(3,22,2) && !GTK_CHECK_VERSION(3,22,4)
Bram Moolenaar30613902019-12-01 22:11:18 +01002807 // As of 3.22.2, GdkWindows have started distributing configure events to
2808 // their "native" children (https://git.gnome.org/browse/gtk+/commit/?h=gtk-3-22&id=12579fe71b3b8f79eb9c1b80e429443bcc437dd0).
2809 //
2810 // As can be seen from the implementation of move_native_children() and
2811 // configure_native_child() in gdkwindow.c, those functions actually
2812 // propagate configure events to every child, failing to distinguish
2813 // "native" one from non-native one.
2814 //
2815 // Naturally, configure events propagated to here like that are fallacious
2816 // and, as a matter of fact, they trigger a geometric collapse of
2817 // gui.drawarea in fullscreen and maximized modes.
2818 //
2819 // To filter out such nuisance events, we are making use of the fact that
2820 // the field send_event of such GdkEventConfigures is set to FALSE in
2821 // configure_native_child().
2822 //
2823 // Obviously, this is a terrible hack making GVim depend on GTK's
2824 // implementation details. Therefore, watch out any relevant internal
2825 // changes happening in GTK in the feature (sigh).
2826 //
2827 // Follow-up
2828 // After a few weeks later, the GdkWindow change mentioned above was
2829 // reverted (https://git.gnome.org/browse/gtk+/commit/?h=gtk-3-22&id=f70039cb9603a02d2369fec4038abf40a1711155).
2830 // The corresponding official release is 3.22.4.
Bram Moolenaara859f042016-11-17 19:11:55 +01002831 if (event->send_event == FALSE)
2832 return TRUE;
2833# endif
2834
Bram Moolenaar98921892016-02-23 17:14:37 +01002835 if (event->width == cur_width && event->height == cur_height)
2836 return TRUE;
2837
2838 cur_width = event->width;
2839 cur_height = event->height;
2840
2841 if (gui.surface != NULL)
2842 cairo_surface_destroy(gui.surface);
2843
2844 gui.surface = gdk_window_create_similar_surface(
2845 gtk_widget_get_window(widget),
2846 CAIRO_CONTENT_COLOR_ALPHA,
2847 event->width, event->height);
2848
2849 gtk_widget_queue_draw(widget);
2850
2851 return TRUE;
2852}
2853#endif
2854
Bram Moolenaar071d4272004-06-13 20:20:40 +00002855/*
2856 * Callback routine for the "delete_event" signal on the toplevel window.
2857 * Tries to vim gracefully, or refuses to exit with changed buffers.
2858 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002859 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +00002860delete_event_cb(GtkWidget *widget UNUSED,
2861 GdkEventAny *event UNUSED,
2862 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002863{
2864 gui_shell_closed();
2865 return TRUE;
2866}
2867
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002868#if defined(FEAT_MENU) || defined(FEAT_TOOLBAR) || defined(FEAT_GUI_TABLINE)
2869 static int
2870get_item_dimensions(GtkWidget *widget, GtkOrientation orientation)
2871{
Bram Moolenaarc4a249a2017-01-30 22:56:48 +01002872# ifdef FEAT_GUI_GNOME
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002873 GtkOrientation item_orientation = GTK_ORIENTATION_HORIZONTAL;
2874
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002875 if (using_gnome && widget != NULL)
2876 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00002877 GtkWidget *parent;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002878 BonoboDockItem *dockitem;
2879
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00002880 parent = gtk_widget_get_parent(widget);
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00002881 if (G_TYPE_FROM_INSTANCE(parent) == BONOBO_TYPE_DOCK_ITEM)
2882 {
Bram Moolenaar30613902019-12-01 22:11:18 +01002883 // Only menu & toolbar are dock items. Could tabline be?
2884 // Seem to be only the 2 defined in GNOME
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00002885 widget = parent;
2886 dockitem = BONOBO_DOCK_ITEM(widget);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002887
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00002888 if (dockitem == NULL || dockitem->is_floating)
2889 return 0;
2890 item_orientation = bonobo_dock_item_get_orientation(dockitem);
2891 }
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002892 }
Bram Moolenaarc4a249a2017-01-30 22:56:48 +01002893# else
2894# define item_orientation GTK_ORIENTATION_HORIZONTAL
Bram Moolenaar98921892016-02-23 17:14:37 +01002895# endif
Bram Moolenaarc4a249a2017-01-30 22:56:48 +01002896
Bram Moolenaar98921892016-02-23 17:14:37 +01002897 if (widget != NULL
2898 && item_orientation == orientation
2899 && gtk_widget_get_realized(widget)
2900 && gtk_widget_get_visible(widget))
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002901 {
Bram Moolenaar664323e2018-09-18 22:30:07 +02002902# if GTK_CHECK_VERSION(3,0,0) || !defined(FEAT_GUI_GNOME)
Bram Moolenaar98921892016-02-23 17:14:37 +01002903 GtkAllocation allocation;
2904
2905 gtk_widget_get_allocation(widget, &allocation);
Bram Moolenaarc4a249a2017-01-30 22:56:48 +01002906 return allocation.height;
Bram Moolenaar98921892016-02-23 17:14:37 +01002907# else
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002908 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2909 return widget->allocation.height;
2910 else
2911 return widget->allocation.width;
Bram Moolenaar98921892016-02-23 17:14:37 +01002912# endif
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002913 }
2914 return 0;
2915}
2916#endif
2917
2918 static int
2919get_menu_tool_width(void)
2920{
2921 int width = 0;
2922
Bram Moolenaar30613902019-12-01 22:11:18 +01002923#ifdef FEAT_GUI_GNOME // these are never vertical without GNOME
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002924# ifdef FEAT_MENU
2925 width += get_item_dimensions(gui.menubar, GTK_ORIENTATION_VERTICAL);
2926# endif
2927# ifdef FEAT_TOOLBAR
2928 width += get_item_dimensions(gui.toolbar, GTK_ORIENTATION_VERTICAL);
2929# endif
2930# ifdef FEAT_GUI_TABLINE
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00002931 if (gui.tabline != NULL)
2932 width += get_item_dimensions(gui.tabline, GTK_ORIENTATION_VERTICAL);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002933# endif
2934#endif
2935
2936 return width;
2937}
2938
2939 static int
2940get_menu_tool_height(void)
2941{
2942 int height = 0;
2943
2944#ifdef FEAT_MENU
2945 height += get_item_dimensions(gui.menubar, GTK_ORIENTATION_HORIZONTAL);
2946#endif
2947#ifdef FEAT_TOOLBAR
2948 height += get_item_dimensions(gui.toolbar, GTK_ORIENTATION_HORIZONTAL);
2949#endif
2950#ifdef FEAT_GUI_TABLINE
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00002951 if (gui.tabline != NULL)
2952 height += get_item_dimensions(gui.tabline, GTK_ORIENTATION_HORIZONTAL);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002953#endif
2954
2955 return height;
2956}
2957
Bram Moolenaar30613902019-12-01 22:11:18 +01002958// This controls whether we can set the real window hints at
2959// start-up when in a GtkPlug.
2960// 0 = normal processing (default)
2961// 1 = init. hints set, no-one's tried to reset since last check
2962// 2 = init. hints set, attempt made to change hints
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00002963static int init_window_hints_state = 0;
2964
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002965 static void
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00002966update_window_manager_hints(int force_width, int force_height)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002967{
2968 static int old_width = 0;
2969 static int old_height = 0;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00002970 static int old_min_width = 0;
2971 static int old_min_height = 0;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002972 static int old_char_width = 0;
2973 static int old_char_height = 0;
2974
2975 int width;
2976 int height;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00002977 int min_width;
2978 int min_height;
2979
Bram Moolenaar30613902019-12-01 22:11:18 +01002980 // At start-up, don't try to set the hints until the initial
2981 // values have been used (those that dictate our initial size)
2982 // Let forced (i.e., correct) values through always.
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00002983 if (!(force_width && force_height) && init_window_hints_state > 0)
2984 {
Bram Moolenaar30613902019-12-01 22:11:18 +01002985 // Don't do it!
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00002986 init_window_hints_state = 2;
2987 return;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00002988 }
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002989
Bram Moolenaar30613902019-12-01 22:11:18 +01002990 // This also needs to be done when the main window isn't there yet,
2991 // otherwise the hints don't work.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002992 width = gui_get_base_width();
2993 height = gui_get_base_height();
2994# ifdef FEAT_MENU
2995 height += tabline_height() * gui.char_height;
2996# endif
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002997 width += get_menu_tool_width();
2998 height += get_menu_tool_height();
Bram Moolenaar32466aa2006-02-24 23:53:04 +00002999
Bram Moolenaar30613902019-12-01 22:11:18 +01003000 // GtkSockets use GtkPlug's [gui,mainwin] min-size hints to determine
3001 // their actual widget size. When we set our size ourselves (e.g.,
3002 // 'set columns=' or init. -geom) we briefly set the min. to the size
3003 // we wish to be instead of the legitimate minimum so that we actually
3004 // resize correctly.
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003005 if (force_width && force_height)
3006 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00003007 min_width = force_width;
3008 min_height = force_height;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003009 }
3010 else
3011 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00003012 min_width = width + MIN_COLUMNS * gui.char_width;
3013 min_height = height + MIN_LINES * gui.char_height;
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003014 }
3015
Bram Moolenaar30613902019-12-01 22:11:18 +01003016 // Avoid an expose event when the size didn't change.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003017 if (width != old_width
3018 || height != old_height
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00003019 || min_width != old_min_width
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003020 || min_height != old_min_height
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003021 || gui.char_width != old_char_width
3022 || gui.char_height != old_char_height)
3023 {
3024 GdkGeometry geometry;
3025 GdkWindowHints geometry_mask;
3026
3027 geometry.width_inc = gui.char_width;
3028 geometry.height_inc = gui.char_height;
3029 geometry.base_width = width;
3030 geometry.base_height = height;
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00003031 geometry.min_width = min_width;
3032 geometry.min_height = min_height;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003033 geometry_mask = GDK_HINT_BASE_SIZE|GDK_HINT_RESIZE_INC
3034 |GDK_HINT_MIN_SIZE;
Bram Moolenaar30613902019-12-01 22:11:18 +01003035 // Using gui.formwin as geometry widget doesn't work as expected
3036 // with GTK+ 2 -- dunno why. Presumably all the resizing hacks
3037 // in Vim confuse GTK+.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003038 gtk_window_set_geometry_hints(GTK_WINDOW(gui.mainwin), gui.mainwin,
3039 &geometry, geometry_mask);
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00003040 old_width = width;
3041 old_height = height;
3042 old_min_width = min_width;
3043 old_min_height = min_height;
3044 old_char_width = gui.char_width;
3045 old_char_height = gui.char_height;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003046 }
3047}
3048
Bram Moolenaar50bf7ce2019-09-15 13:17:00 +02003049#if defined(FEAT_GUI_DARKTHEME) || defined(PROTO)
3050 void
3051gui_mch_set_dark_theme(int dark)
3052{
3053# if GTK_CHECK_VERSION(3,0,0)
3054 GtkSettings *gtk_settings;
3055
3056 gtk_settings = gtk_settings_get_for_screen(gdk_screen_get_default());
3057 g_object_set(gtk_settings, "gtk-application-prefer-dark-theme", (gboolean)dark, NULL);
3058# endif
3059}
Bram Moolenaar30613902019-12-01 22:11:18 +01003060#endif // FEAT_GUI_DARKTHEME
Bram Moolenaar50bf7ce2019-09-15 13:17:00 +02003061
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062#ifdef FEAT_TOOLBAR
3063
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064/*
3065 * This extra effort wouldn't be necessary if we only used stock icons in the
3066 * toolbar, as we do for all builtin icons. But user-defined toolbar icons
3067 * shouldn't be treated differently, thus we do need this.
3068 */
3069 static void
3070icon_size_changed_foreach(GtkWidget *widget, gpointer user_data)
3071{
3072 if (GTK_IS_IMAGE(widget))
3073 {
3074 GtkImage *image = (GtkImage *)widget;
3075
Bram Moolenaar98921892016-02-23 17:14:37 +01003076# if GTK_CHECK_VERSION(3,10,0)
3077 if (gtk_image_get_storage_type(image) == GTK_IMAGE_ICON_NAME)
3078 {
3079 const GtkIconSize icon_size = GPOINTER_TO_INT(user_data);
3080 const gchar *icon_name;
3081
3082 gtk_image_get_icon_name(image, &icon_name, NULL);
3083
3084 gtk_image_set_from_icon_name(image, icon_name, icon_size);
3085 }
3086# else
Bram Moolenaar30613902019-12-01 22:11:18 +01003087 // User-defined icons are stored in a GtkIconSet
Bram Moolenaar071d4272004-06-13 20:20:40 +00003088 if (gtk_image_get_storage_type(image) == GTK_IMAGE_ICON_SET)
3089 {
3090 GtkIconSet *icon_set;
3091 GtkIconSize icon_size;
3092
3093 gtk_image_get_icon_set(image, &icon_set, &icon_size);
3094 icon_size = (GtkIconSize)(long)user_data;
3095
3096 gtk_icon_set_ref(icon_set);
3097 gtk_image_set_from_icon_set(image, icon_set, icon_size);
3098 gtk_icon_set_unref(icon_set);
3099 }
Bram Moolenaar98921892016-02-23 17:14:37 +01003100# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003101 }
3102 else if (GTK_IS_CONTAINER(widget))
3103 {
3104 gtk_container_foreach((GtkContainer *)widget,
3105 &icon_size_changed_foreach,
3106 user_data);
3107 }
3108}
Bram Moolenaar071d4272004-06-13 20:20:40 +00003109
3110 static void
3111set_toolbar_style(GtkToolbar *toolbar)
3112{
3113 GtkToolbarStyle style;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003114 GtkIconSize size;
3115 GtkIconSize oldsize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003116
Bram Moolenaar071d4272004-06-13 20:20:40 +00003117 if ((toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS | TOOLBAR_HORIZ))
3118 == (TOOLBAR_TEXT | TOOLBAR_ICONS | TOOLBAR_HORIZ))
3119 style = GTK_TOOLBAR_BOTH_HORIZ;
Bram Moolenaar182c5be2010-06-25 05:37:59 +02003120 else if ((toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003121 == (TOOLBAR_TEXT | TOOLBAR_ICONS))
3122 style = GTK_TOOLBAR_BOTH;
3123 else if (toolbar_flags & TOOLBAR_TEXT)
3124 style = GTK_TOOLBAR_TEXT;
3125 else
3126 style = GTK_TOOLBAR_ICONS;
3127
3128 gtk_toolbar_set_style(toolbar, style);
Bram Moolenaar98921892016-02-23 17:14:37 +01003129# if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003130 gtk_toolbar_set_tooltips(toolbar, (toolbar_flags & TOOLBAR_TOOLTIPS) != 0);
Bram Moolenaar98921892016-02-23 17:14:37 +01003131# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003132
Bram Moolenaar071d4272004-06-13 20:20:40 +00003133 switch (tbis_flags)
3134 {
3135 case TBIS_TINY: size = GTK_ICON_SIZE_MENU; break;
3136 case TBIS_SMALL: size = GTK_ICON_SIZE_SMALL_TOOLBAR; break;
3137 case TBIS_MEDIUM: size = GTK_ICON_SIZE_BUTTON; break;
3138 case TBIS_LARGE: size = GTK_ICON_SIZE_LARGE_TOOLBAR; break;
Bram Moolenaarbeb003b2016-03-08 22:47:17 +01003139 case TBIS_HUGE: size = GTK_ICON_SIZE_DND; break;
3140 case TBIS_GIANT: size = GTK_ICON_SIZE_DIALOG; break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003141 default: size = GTK_ICON_SIZE_INVALID; break;
3142 }
3143 oldsize = gtk_toolbar_get_icon_size(toolbar);
3144
3145 if (size == GTK_ICON_SIZE_INVALID)
3146 {
Bram Moolenaar30613902019-12-01 22:11:18 +01003147 // Let global user preferences decide the icon size.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003148 gtk_toolbar_unset_icon_size(toolbar);
3149 size = gtk_toolbar_get_icon_size(toolbar);
3150 }
3151 if (size != oldsize)
3152 {
3153 gtk_container_foreach(GTK_CONTAINER(toolbar),
3154 &icon_size_changed_foreach,
3155 GINT_TO_POINTER((int)size));
3156 }
3157 gtk_toolbar_set_icon_size(toolbar, size);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003158}
3159
Bram Moolenaar30613902019-12-01 22:11:18 +01003160#endif // FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00003161
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003162#if defined(FEAT_GUI_TABLINE) || defined(PROTO)
3163static int ignore_tabline_evt = FALSE;
Bram Moolenaara5621492006-02-25 21:55:24 +00003164static GtkWidget *tabline_menu;
Bram Moolenaar98921892016-02-23 17:14:37 +01003165# if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003166static GtkTooltips *tabline_tooltip;
Bram Moolenaar98921892016-02-23 17:14:37 +01003167# endif
Bram Moolenaar30613902019-12-01 22:11:18 +01003168static int clicked_page; // page clicked in tab line
Bram Moolenaara5621492006-02-25 21:55:24 +00003169
3170/*
3171 * Handle selecting an item in the tab line popup menu.
3172 */
Bram Moolenaara5621492006-02-25 21:55:24 +00003173 static void
Bram Moolenaarb85cb212009-05-17 14:24:23 +00003174tabline_menu_handler(GtkMenuItem *item UNUSED, gpointer user_data)
Bram Moolenaara5621492006-02-25 21:55:24 +00003175{
Bram Moolenaar30613902019-12-01 22:11:18 +01003176 // Add the string cmd into input buffer
Bram Moolenaarc6fe9192006-04-09 21:54:49 +00003177 send_tabline_menu_event(clicked_page, (int)(long)user_data);
Bram Moolenaara5621492006-02-25 21:55:24 +00003178}
3179
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003180 static void
3181add_tabline_menu_item(GtkWidget *menu, char_u *text, int resp)
3182{
3183 GtkWidget *item;
3184 char_u *utf_text;
3185
3186 utf_text = CONVERT_TO_UTF8(text);
3187 item = gtk_menu_item_new_with_label((const char *)utf_text);
3188 gtk_widget_show(item);
3189 CONVERT_TO_UTF8_FREE(utf_text);
3190
3191 gtk_container_add(GTK_CONTAINER(menu), item);
Bram Moolenaar98921892016-02-23 17:14:37 +01003192 g_signal_connect(G_OBJECT(item), "activate",
3193 G_CALLBACK(tabline_menu_handler),
3194 GINT_TO_POINTER(resp));
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003195}
3196
Bram Moolenaara5621492006-02-25 21:55:24 +00003197/*
Bram Moolenaara5621492006-02-25 21:55:24 +00003198 * Create a menu for the tab line.
3199 */
3200 static GtkWidget *
3201create_tabline_menu(void)
3202{
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003203 GtkWidget *menu;
Bram Moolenaara5621492006-02-25 21:55:24 +00003204
3205 menu = gtk_menu_new();
Bram Moolenaar29547192018-12-11 20:39:19 +01003206 add_tabline_menu_item(menu, (char_u *)_("Close tab"), TABLINE_MENU_CLOSE);
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003207 add_tabline_menu_item(menu, (char_u *)_("New tab"), TABLINE_MENU_NEW);
3208 add_tabline_menu_item(menu, (char_u *)_("Open Tab..."), TABLINE_MENU_OPEN);
Bram Moolenaara5621492006-02-25 21:55:24 +00003209
3210 return menu;
3211}
3212
3213 static gboolean
3214on_tabline_menu(GtkWidget *widget, GdkEvent *event)
3215{
Bram Moolenaar30613902019-12-01 22:11:18 +01003216 // Was this button press event ?
Bram Moolenaara5621492006-02-25 21:55:24 +00003217 if (event->type == GDK_BUTTON_PRESS)
3218 {
3219 GdkEventButton *bevent = (GdkEventButton *)event;
3220 int x = bevent->x;
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003221 int y = bevent->y;
3222 GtkWidget *tabwidget;
3223 GdkWindow *tabwin;
Bram Moolenaara5621492006-02-25 21:55:24 +00003224
Bram Moolenaar30613902019-12-01 22:11:18 +01003225 // When ignoring events return TRUE so that the selected page doesn't
3226 // change.
Bram Moolenaarf193fff2006-04-27 00:02:13 +00003227 if (hold_gui_events
3228# ifdef FEAT_CMDWIN
3229 || cmdwin_type != 0
3230# endif
3231 )
3232 return TRUE;
3233
Bram Moolenaar98921892016-02-23 17:14:37 +01003234 tabwin = gui_gtk_window_at_position(gui.mainwin, &x, &y);
Bram Moolenaar98921892016-02-23 17:14:37 +01003235
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003236 gdk_window_get_user_data(tabwin, (gpointer)&tabwidget);
Bram Moolenaar98921892016-02-23 17:14:37 +01003237 clicked_page = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tabwidget),
3238 "tab_num"));
Bram Moolenaara5621492006-02-25 21:55:24 +00003239
Bram Moolenaar30613902019-12-01 22:11:18 +01003240 // If the event was generated for 3rd button popup the menu.
Bram Moolenaara5621492006-02-25 21:55:24 +00003241 if (bevent->button == 3)
3242 {
Bram Moolenaara859f042016-11-17 19:11:55 +01003243# if GTK_CHECK_VERSION(3,22,2)
3244 gtk_menu_popup_at_pointer(GTK_MENU(widget), event);
3245# else
Bram Moolenaara5621492006-02-25 21:55:24 +00003246 gtk_menu_popup(GTK_MENU(widget), NULL, NULL, NULL, NULL,
3247 bevent->button, bevent->time);
Bram Moolenaara859f042016-11-17 19:11:55 +01003248# endif
Bram Moolenaar30613902019-12-01 22:11:18 +01003249 // We handled the event.
Bram Moolenaara5621492006-02-25 21:55:24 +00003250 return TRUE;
3251 }
Bram Moolenaar96351572006-05-05 21:16:59 +00003252 else if (bevent->button == 1)
Bram Moolenaareddf53b2006-02-27 00:11:10 +00003253 {
Bram Moolenaar96351572006-05-05 21:16:59 +00003254 if (clicked_page == 0)
3255 {
Bram Moolenaar30613902019-12-01 22:11:18 +01003256 // Click after all tabs moves to next tab page. When "x" is
3257 // small guess it's the left button.
Bram Moolenaara3f41662010-07-11 19:01:06 +02003258 send_tabline_event(x < 50 ? -1 : 0);
Bram Moolenaar96351572006-05-05 21:16:59 +00003259 }
Bram Moolenaareddf53b2006-02-27 00:11:10 +00003260 }
Bram Moolenaara5621492006-02-25 21:55:24 +00003261 }
Bram Moolenaarf193fff2006-04-27 00:02:13 +00003262
Bram Moolenaar30613902019-12-01 22:11:18 +01003263 // We didn't handle the event.
Bram Moolenaara5621492006-02-25 21:55:24 +00003264 return FALSE;
3265}
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003266
3267/*
3268 * Handle selecting one of the tabs.
3269 */
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003270 static void
3271on_select_tab(
Bram Moolenaarb85cb212009-05-17 14:24:23 +00003272 GtkNotebook *notebook UNUSED,
Bram Moolenaar98921892016-02-23 17:14:37 +01003273 gpointer *page UNUSED,
Bram Moolenaar89d40322006-08-29 15:30:07 +00003274 gint idx,
Bram Moolenaarb85cb212009-05-17 14:24:23 +00003275 gpointer data UNUSED)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003276{
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003277 if (!ignore_tabline_evt)
Bram Moolenaara3f41662010-07-11 19:01:06 +02003278 send_tabline_event(idx + 1);
Bram Moolenaarca05aa22017-10-22 15:36:14 +02003279}
3280
Bram Moolenaar92cbf622018-09-19 22:40:03 +02003281# if GTK_CHECK_VERSION(2,10,0)
Bram Moolenaarca05aa22017-10-22 15:36:14 +02003282/*
3283 * Handle reordering the tabs (using D&D).
3284 */
3285 static void
3286on_tab_reordered(
3287 GtkNotebook *notebook UNUSED,
Bram Moolenaarca05aa22017-10-22 15:36:14 +02003288 gpointer *page UNUSED,
Bram Moolenaarca05aa22017-10-22 15:36:14 +02003289 gint idx,
3290 gpointer data UNUSED)
3291{
3292 if (!ignore_tabline_evt)
3293 {
3294 if ((tabpage_index(curtab) - 1) < idx)
3295 tabpage_move(idx + 1);
3296 else
3297 tabpage_move(idx);
Bram Moolenaareddf53b2006-02-27 00:11:10 +00003298 }
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003299}
Bram Moolenaar92cbf622018-09-19 22:40:03 +02003300# endif
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003301
3302/*
3303 * Show or hide the tabline.
3304 */
3305 void
3306gui_mch_show_tabline(int showit)
3307{
3308 if (gui.tabline == NULL)
3309 return;
3310
3311 if (!showit != !gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline)))
3312 {
Bram Moolenaar30613902019-12-01 22:11:18 +01003313 // Note: this may cause a resize event
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003314 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), showit);
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003315 update_window_manager_hints(0, 0);
Bram Moolenaar96351572006-05-05 21:16:59 +00003316 if (showit)
Bram Moolenaar98921892016-02-23 17:14:37 +01003317 gtk_widget_set_can_focus(GTK_WIDGET(gui.tabline), FALSE);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003318 }
Bram Moolenaar96351572006-05-05 21:16:59 +00003319
3320 gui_mch_update();
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003321}
3322
3323/*
Bram Moolenaar3517bb12006-03-03 22:58:45 +00003324 * Return TRUE when tabline is displayed.
3325 */
3326 int
3327gui_mch_showing_tabline(void)
3328{
3329 return gui.tabline != NULL
Bram Moolenaar182c5be2010-06-25 05:37:59 +02003330 && gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline));
Bram Moolenaar3517bb12006-03-03 22:58:45 +00003331}
3332
3333/*
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003334 * Update the labels of the tabline.
3335 */
3336 void
3337gui_mch_update_tabline(void)
3338{
3339 GtkWidget *page;
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003340 GtkWidget *event_box;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003341 GtkWidget *label;
3342 tabpage_T *tp;
3343 int nr = 0;
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003344 int tab_num;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003345 int curtabidx = 0;
Bram Moolenaarc1e37902006-04-18 21:55:01 +00003346 char_u *labeltext;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003347
3348 if (gui.tabline == NULL)
3349 return;
3350
3351 ignore_tabline_evt = TRUE;
3352
Bram Moolenaar30613902019-12-01 22:11:18 +01003353 // Add a label for each tab page. They all contain the same text area.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003354 for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
3355 {
3356 if (tp == curtab)
3357 curtabidx = nr;
3358
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003359 tab_num = nr + 1;
3360
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003361 page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr);
3362 if (page == NULL)
3363 {
Bram Moolenaar30613902019-12-01 22:11:18 +01003364 // Add notebook page
Bram Moolenaar98921892016-02-23 17:14:37 +01003365# if GTK_CHECK_VERSION(3,2,0)
3366 page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3367 gtk_box_set_homogeneous(GTK_BOX(page), FALSE);
3368# else
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003369 page = gtk_vbox_new(FALSE, 0);
Bram Moolenaar98921892016-02-23 17:14:37 +01003370# endif
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003371 gtk_widget_show(page);
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003372 event_box = gtk_event_box_new();
3373 gtk_widget_show(event_box);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003374 label = gtk_label_new("-Empty-");
Bram Moolenaar98921892016-02-23 17:14:37 +01003375# if !GTK_CHECK_VERSION(3,14,0)
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003376 gtk_misc_set_padding(GTK_MISC(label), 2, 2);
Bram Moolenaar98921892016-02-23 17:14:37 +01003377# endif
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003378 gtk_container_add(GTK_CONTAINER(event_box), label);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003379 gtk_widget_show(label);
3380 gtk_notebook_insert_page(GTK_NOTEBOOK(gui.tabline),
3381 page,
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003382 event_box,
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003383 nr++);
Bram Moolenaar92cbf622018-09-19 22:40:03 +02003384# if GTK_CHECK_VERSION(2,10,0)
Bram Moolenaarca05aa22017-10-22 15:36:14 +02003385 gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(gui.tabline),
3386 page,
3387 TRUE);
Bram Moolenaar92cbf622018-09-19 22:40:03 +02003388# endif
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003389 }
3390
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003391 event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(gui.tabline), page);
Bram Moolenaar98921892016-02-23 17:14:37 +01003392 g_object_set_data(G_OBJECT(event_box), "tab_num",
3393 GINT_TO_POINTER(tab_num));
Bram Moolenaar98921892016-02-23 17:14:37 +01003394 label = gtk_bin_get_child(GTK_BIN(event_box));
Bram Moolenaar57657d82006-04-21 22:12:41 +00003395 get_tabline_label(tp, FALSE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00003396 labeltext = CONVERT_TO_UTF8(NameBuff);
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003397 gtk_label_set_text(GTK_LABEL(label), (const char *)labeltext);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00003398 CONVERT_TO_UTF8_FREE(labeltext);
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003399
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003400 get_tabline_label(tp, TRUE);
3401 labeltext = CONVERT_TO_UTF8(NameBuff);
Bram Moolenaar98921892016-02-23 17:14:37 +01003402# if GTK_CHECK_VERSION(3,0,0)
3403 gtk_widget_set_tooltip_text(event_box, (const gchar *)labeltext);
3404# else
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003405 gtk_tooltips_set_tip(GTK_TOOLTIPS(tabline_tooltip), event_box,
3406 (const char *)labeltext, NULL);
Bram Moolenaar98921892016-02-23 17:14:37 +01003407# endif
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003408 CONVERT_TO_UTF8_FREE(labeltext);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003409 }
3410
Bram Moolenaar30613902019-12-01 22:11:18 +01003411 // Remove any old labels.
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003412 while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr) != NULL)
3413 gtk_notebook_remove_page(GTK_NOTEBOOK(gui.tabline), nr);
3414
Bram Moolenaar98921892016-02-23 17:14:37 +01003415 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(gui.tabline)) != curtabidx)
3416 gtk_notebook_set_current_page(GTK_NOTEBOOK(gui.tabline), curtabidx);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003417
Bram Moolenaar30613902019-12-01 22:11:18 +01003418 // Make sure everything is in place before drawing text.
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003419 gui_mch_update();
3420
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003421 ignore_tabline_evt = FALSE;
3422}
3423
3424/*
3425 * Set the current tab to "nr". First tab is 1.
3426 */
3427 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01003428gui_mch_set_curtab(int nr)
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003429{
3430 if (gui.tabline == NULL)
3431 return;
3432
3433 ignore_tabline_evt = TRUE;
Bram Moolenaar98921892016-02-23 17:14:37 +01003434 if (gtk_notebook_get_current_page(GTK_NOTEBOOK(gui.tabline)) != nr - 1)
3435 gtk_notebook_set_current_page(GTK_NOTEBOOK(gui.tabline), nr - 1);
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003436 ignore_tabline_evt = FALSE;
3437}
3438
Bram Moolenaar30613902019-12-01 22:11:18 +01003439#endif // FEAT_GUI_TABLINE
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003440
Bram Moolenaar071d4272004-06-13 20:20:40 +00003441/*
Bram Moolenaara76638f2010-06-05 12:49:46 +02003442 * Add selection targets for PRIMARY and CLIPBOARD selections.
3443 */
3444 void
3445gui_gtk_set_selection_targets(void)
3446{
3447 int i, j = 0;
3448 int n_targets = N_SELECTION_TARGETS;
3449 GtkTargetEntry targets[N_SELECTION_TARGETS];
3450
3451 for (i = 0; i < (int)N_SELECTION_TARGETS; ++i)
3452 {
Bram Moolenaar30613902019-12-01 22:11:18 +01003453 // OpenOffice tries to use TARGET_HTML and fails when we don't
3454 // return something, instead of trying another target. Therefore only
3455 // offer TARGET_HTML when it works.
Bram Moolenaara76638f2010-06-05 12:49:46 +02003456 if (!clip_html && selection_targets[i].info == TARGET_HTML)
3457 n_targets--;
3458 else
Bram Moolenaara76638f2010-06-05 12:49:46 +02003459 targets[j++] = selection_targets[i];
3460 }
3461
3462 gtk_selection_clear_targets(gui.drawarea, (GdkAtom)GDK_SELECTION_PRIMARY);
3463 gtk_selection_clear_targets(gui.drawarea, (GdkAtom)clip_plus.gtk_sel_atom);
3464 gtk_selection_add_targets(gui.drawarea,
3465 (GdkAtom)GDK_SELECTION_PRIMARY,
3466 targets, n_targets);
3467 gtk_selection_add_targets(gui.drawarea,
3468 (GdkAtom)clip_plus.gtk_sel_atom,
3469 targets, n_targets);
3470}
3471
3472/*
3473 * Set up for receiving DND items.
3474 */
3475 void
3476gui_gtk_set_dnd_targets(void)
3477{
3478 int i, j = 0;
3479 int n_targets = N_DND_TARGETS;
3480 GtkTargetEntry targets[N_DND_TARGETS];
3481
3482 for (i = 0; i < (int)N_DND_TARGETS; ++i)
3483 {
Bram Moolenaarb931d742011-10-26 11:36:25 +02003484 if (!clip_html && dnd_targets[i].info == TARGET_HTML)
Bram Moolenaara76638f2010-06-05 12:49:46 +02003485 n_targets--;
3486 else
Bram Moolenaara76638f2010-06-05 12:49:46 +02003487 targets[j++] = dnd_targets[i];
3488 }
3489
3490 gtk_drag_dest_unset(gui.drawarea);
3491 gtk_drag_dest_set(gui.drawarea,
3492 GTK_DEST_DEFAULT_ALL,
3493 targets, n_targets,
Bram Moolenaarba7cc9f2011-02-25 17:10:27 +01003494 GDK_ACTION_COPY | GDK_ACTION_MOVE);
Bram Moolenaara76638f2010-06-05 12:49:46 +02003495}
3496
3497/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003498 * Initialize the GUI. Create all the windows, set up all the callbacks etc.
3499 * Returns OK for success, FAIL when the GUI can't be started.
3500 */
3501 int
3502gui_mch_init(void)
3503{
3504 GtkWidget *vbox;
3505
3506#ifdef FEAT_GUI_GNOME
Bram Moolenaar30613902019-12-01 22:11:18 +01003507 // Initialize the GNOME libraries. gnome_program_init()/gnome_init()
3508 // exits on failure, but that's a non-issue because we already called
3509 // gtk_init_check() in gui_mch_init_check().
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510 if (using_gnome)
Bram Moolenaar3be71ce2013-01-23 16:00:11 +01003511 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512 gnome_program_init(VIMPACKAGE, VIM_VERSION_SHORT,
3513 LIBGNOMEUI_MODULE, gui_argc, gui_argv, NULL);
Bram Moolenaar3be71ce2013-01-23 16:00:11 +01003514# if defined(FEAT_FLOAT) && defined(LC_NUMERIC)
Bram Moolenaar1ff32c52014-04-29 15:11:43 +02003515 {
3516 char *p = setlocale(LC_NUMERIC, NULL);
3517
Bram Moolenaar30613902019-12-01 22:11:18 +01003518 // Make sure strtod() uses a decimal point, not a comma. Gnome
3519 // init may change it.
Bram Moolenaar1ff32c52014-04-29 15:11:43 +02003520 if (p == NULL || strcmp(p, "C") != 0)
3521 setlocale(LC_NUMERIC, "C");
3522 }
Bram Moolenaar3be71ce2013-01-23 16:00:11 +01003523# endif
3524 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525#endif
Bram Moolenaard23a8232018-02-10 18:45:26 +01003526 VIM_CLEAR(gui_argv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527
Bram Moolenaar182c5be2010-06-25 05:37:59 +02003528#if GLIB_CHECK_VERSION(2,1,3)
Bram Moolenaar30613902019-12-01 22:11:18 +01003529 // Set the human-readable application name
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530 g_set_application_name("Vim");
Bram Moolenaar182c5be2010-06-25 05:37:59 +02003531#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003532 /*
3533 * Force UTF-8 output no matter what the value of 'encoding' is.
3534 * did_set_string_option() in option.c prohibits changing 'termencoding'
3535 * to something else than UTF-8 if the GUI is in use.
3536 */
3537 set_option_value((char_u *)"termencoding", 0L, (char_u *)"utf-8", 0);
3538
Bram Moolenaar182c5be2010-06-25 05:37:59 +02003539#ifdef FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00003540 gui_gtk_register_stock_icons();
Bram Moolenaar182c5be2010-06-25 05:37:59 +02003541#endif
Bram Moolenaar30613902019-12-01 22:11:18 +01003542 // FIXME: Need to install the classic icons and a gtkrc.classic file.
3543 // The hard part is deciding install locations and the Makefile magic.
Bram Moolenaar98921892016-02-23 17:14:37 +01003544#if !GTK_CHECK_VERSION(3,0,0)
3545# if 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00003546 gtk_rc_parse("gtkrc");
Bram Moolenaar98921892016-02-23 17:14:37 +01003547# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003548#endif
3549
Bram Moolenaar30613902019-12-01 22:11:18 +01003550 // Initialize values
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551 gui.border_width = 2;
3552 gui.scrollbar_width = SB_DEFAULT_WIDTH;
3553 gui.scrollbar_height = SB_DEFAULT_WIDTH;
Bram Moolenaar36edf062016-07-21 22:10:12 +02003554#if GTK_CHECK_VERSION(3,0,0)
3555 gui.fgcolor = g_new(GdkRGBA, 1);
3556 gui.bgcolor = g_new(GdkRGBA, 1);
3557 gui.spcolor = g_new(GdkRGBA, 1);
3558#else
Bram Moolenaar30613902019-12-01 22:11:18 +01003559 // LINTED: avoid warning: conversion to 'unsigned long'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003560 gui.fgcolor = g_new0(GdkColor, 1);
Bram Moolenaar30613902019-12-01 22:11:18 +01003561 // LINTED: avoid warning: conversion to 'unsigned long'
Bram Moolenaar071d4272004-06-13 20:20:40 +00003562 gui.bgcolor = g_new0(GdkColor, 1);
Bram Moolenaar30613902019-12-01 22:11:18 +01003563 // LINTED: avoid warning: conversion to 'unsigned long'
Bram Moolenaarf36d3692005-03-15 22:48:14 +00003564 gui.spcolor = g_new0(GdkColor, 1);
Bram Moolenaar36edf062016-07-21 22:10:12 +02003565#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003566
Bram Moolenaar30613902019-12-01 22:11:18 +01003567 // Initialise atoms
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00003568 html_atom = gdk_atom_intern("text/html", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003569 utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003570
Bram Moolenaar30613902019-12-01 22:11:18 +01003571 // Set default foreground and background colors.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003572 gui.norm_pixel = gui.def_norm_pixel;
3573 gui.back_pixel = gui.def_back_pixel;
3574
3575 if (gtk_socket_id != 0)
3576 {
3577 GtkWidget *plug;
3578
Bram Moolenaar30613902019-12-01 22:11:18 +01003579 // Use GtkSocket from another app.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003580 plug = gtk_plug_new_for_display(gdk_display_get_default(),
3581 gtk_socket_id);
Bram Moolenaar98921892016-02-23 17:14:37 +01003582 if (plug != NULL && gtk_plug_get_socket_window(GTK_PLUG(plug)) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583 {
3584 gui.mainwin = plug;
3585 }
3586 else
3587 {
3588 g_warning("Connection to GTK+ socket (ID %u) failed",
3589 (unsigned int)gtk_socket_id);
Bram Moolenaar30613902019-12-01 22:11:18 +01003590 // Pretend we never wanted it if it failed (get own window)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591 gtk_socket_id = 0;
3592 }
3593 }
3594
3595 if (gtk_socket_id == 0)
3596 {
3597#ifdef FEAT_GUI_GNOME
3598 if (using_gnome)
3599 {
3600 gui.mainwin = gnome_app_new("Vim", NULL);
3601# ifdef USE_XSMP
Bram Moolenaar30613902019-12-01 22:11:18 +01003602 // Use the GNOME save-yourself functionality now.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003603 xsmp_close();
3604# endif
3605 }
3606 else
3607#endif
3608 gui.mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3609 }
3610
3611 gtk_widget_set_name(gui.mainwin, "vim-main-window");
3612
Bram Moolenaar30613902019-12-01 22:11:18 +01003613 // Create the PangoContext used for drawing all text.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614 gui.text_context = gtk_widget_create_pango_context(gui.mainwin);
3615 pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003616
Bram Moolenaar98921892016-02-23 17:14:37 +01003617 gtk_container_set_border_width(GTK_CONTAINER(gui.mainwin), 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003618 gtk_widget_add_events(gui.mainwin, GDK_VISIBILITY_NOTIFY_MASK);
3619
Bram Moolenaar98921892016-02-23 17:14:37 +01003620 g_signal_connect(G_OBJECT(gui.mainwin), "delete-event",
3621 G_CALLBACK(&delete_event_cb), NULL);
3622
3623 g_signal_connect(G_OBJECT(gui.mainwin), "realize",
3624 G_CALLBACK(&mainwin_realize), NULL);
Bram Moolenaarb463e8d2017-06-05 15:07:09 +02003625
3626 g_signal_connect(G_OBJECT(gui.mainwin), "screen-changed",
Bram Moolenaar071d4272004-06-13 20:20:40 +00003627 G_CALLBACK(&mainwin_screen_changed_cb), NULL);
Bram Moolenaarb463e8d2017-06-05 15:07:09 +02003628
Bram Moolenaar071d4272004-06-13 20:20:40 +00003629 gui.accel_group = gtk_accel_group_new();
3630 gtk_window_add_accel_group(GTK_WINDOW(gui.mainwin), gui.accel_group);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003631
Bram Moolenaar30613902019-12-01 22:11:18 +01003632 // A vertical box holds the menubar, toolbar and main text window.
Bram Moolenaar98921892016-02-23 17:14:37 +01003633#if GTK_CHECK_VERSION(3,2,0)
3634 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3635 gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE);
3636#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003637 vbox = gtk_vbox_new(FALSE, 0);
Bram Moolenaar98921892016-02-23 17:14:37 +01003638#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003639
3640#ifdef FEAT_GUI_GNOME
3641 if (using_gnome)
3642 {
Bram Moolenaar182c5be2010-06-25 05:37:59 +02003643# if defined(FEAT_MENU)
Bram Moolenaar30613902019-12-01 22:11:18 +01003644 // automagically restore menubar/toolbar placement
Bram Moolenaar071d4272004-06-13 20:20:40 +00003645 gnome_app_enable_layout_config(GNOME_APP(gui.mainwin), TRUE);
3646# endif
3647 gnome_app_set_contents(GNOME_APP(gui.mainwin), vbox);
3648 }
3649 else
3650#endif
3651 {
3652 gtk_container_add(GTK_CONTAINER(gui.mainwin), vbox);
3653 gtk_widget_show(vbox);
3654 }
3655
3656#ifdef FEAT_MENU
3657 /*
3658 * Create the menubar and handle
3659 */
3660 gui.menubar = gtk_menu_bar_new();
3661 gtk_widget_set_name(gui.menubar, "vim-menubar");
3662
Bram Moolenaar30613902019-12-01 22:11:18 +01003663 // Avoid that GTK takes <F10> away from us.
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00003664 {
3665 GtkSettings *gtk_settings;
3666
3667 gtk_settings = gtk_settings_get_for_screen(gdk_screen_get_default());
3668 g_object_set(gtk_settings, "gtk-menu-bar-accel", NULL, NULL);
3669 }
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00003670
3671
Bram Moolenaar071d4272004-06-13 20:20:40 +00003672# ifdef FEAT_GUI_GNOME
3673 if (using_gnome)
3674 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675 BonoboDockItem *dockitem;
3676
3677 gnome_app_set_menus(GNOME_APP(gui.mainwin), GTK_MENU_BAR(gui.menubar));
3678 dockitem = gnome_app_get_dock_item_by_name(GNOME_APP(gui.mainwin),
3679 GNOME_APP_MENUBAR_NAME);
Bram Moolenaar30613902019-12-01 22:11:18 +01003680 // We don't want the menu to float.
Bram Moolenaardb552d602006-03-23 22:59:57 +00003681 bonobo_dock_item_set_behavior(dockitem,
3682 bonobo_dock_item_get_behavior(dockitem)
3683 | BONOBO_DOCK_ITEM_BEH_NEVER_FLOATING);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003684 gui.menubar_h = GTK_WIDGET(dockitem);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003685 }
3686 else
Bram Moolenaar30613902019-12-01 22:11:18 +01003687# endif // FEAT_GUI_GNOME
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688 {
Bram Moolenaar30613902019-12-01 22:11:18 +01003689 // Always show the menubar, otherwise <F10> doesn't work. It may be
3690 // disabled in gui_init() later.
Bram Moolenaar18144c82006-04-12 21:52:12 +00003691 gtk_widget_show(gui.menubar);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003692 gtk_box_pack_start(GTK_BOX(vbox), gui.menubar, FALSE, FALSE, 0);
3693 }
Bram Moolenaar30613902019-12-01 22:11:18 +01003694#endif // FEAT_MENU
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695
3696#ifdef FEAT_TOOLBAR
3697 /*
3698 * Create the toolbar and handle
3699 */
Bram Moolenaar30613902019-12-01 22:11:18 +01003700 // some aesthetics on the toolbar
Bram Moolenaar98921892016-02-23 17:14:37 +01003701# ifdef USE_GTK3
Bram Moolenaar30613902019-12-01 22:11:18 +01003702 // TODO: Add GTK+ 3 code here using GtkCssProvider if necessary.
3703 // N.B. Since the default value of GtkToolbar::button-relief is
3704 // GTK_RELIEF_NONE, there's no need to specify that, probably.
Bram Moolenaar98921892016-02-23 17:14:37 +01003705# else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003706 gtk_rc_parse_string(
3707 "style \"vim-toolbar-style\" {\n"
3708 " GtkToolbar::button_relief = GTK_RELIEF_NONE\n"
3709 "}\n"
3710 "widget \"*.vim-toolbar\" style \"vim-toolbar-style\"\n");
Bram Moolenaar98921892016-02-23 17:14:37 +01003711# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 gui.toolbar = gtk_toolbar_new();
3713 gtk_widget_set_name(gui.toolbar, "vim-toolbar");
Bram Moolenaar071d4272004-06-13 20:20:40 +00003714 set_toolbar_style(GTK_TOOLBAR(gui.toolbar));
3715
3716# ifdef FEAT_GUI_GNOME
3717 if (using_gnome)
3718 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003719 BonoboDockItem *dockitem;
3720
3721 gnome_app_set_toolbar(GNOME_APP(gui.mainwin), GTK_TOOLBAR(gui.toolbar));
3722 dockitem = gnome_app_get_dock_item_by_name(GNOME_APP(gui.mainwin),
3723 GNOME_APP_TOOLBAR_NAME);
3724 gui.toolbar_h = GTK_WIDGET(dockitem);
Bram Moolenaar30613902019-12-01 22:11:18 +01003725 // When the toolbar is floating it gets stuck. So long as that isn't
3726 // fixed let's disallow floating.
Bram Moolenaare580b0c2006-03-21 21:33:03 +00003727 bonobo_dock_item_set_behavior(dockitem,
Bram Moolenaardb552d602006-03-23 22:59:57 +00003728 bonobo_dock_item_get_behavior(dockitem)
3729 | BONOBO_DOCK_ITEM_BEH_NEVER_FLOATING);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730 gtk_container_set_border_width(GTK_CONTAINER(gui.toolbar), 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003731 }
3732 else
Bram Moolenaar30613902019-12-01 22:11:18 +01003733# endif // FEAT_GUI_GNOME
Bram Moolenaar071d4272004-06-13 20:20:40 +00003734 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00003735 if (vim_strchr(p_go, GO_TOOLBAR) != NULL
3736 && (toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS)))
3737 gtk_widget_show(gui.toolbar);
3738 gtk_box_pack_start(GTK_BOX(vbox), gui.toolbar, FALSE, FALSE, 0);
3739 }
Bram Moolenaar30613902019-12-01 22:11:18 +01003740#endif // FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00003741
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003742#ifdef FEAT_GUI_TABLINE
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00003743 /*
3744 * Use a Notebook for the tab pages labels. The labels are hidden by
3745 * default.
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00003746 */
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003747 gui.tabline = gtk_notebook_new();
3748 gtk_widget_show(gui.tabline);
3749 gtk_box_pack_start(GTK_BOX(vbox), gui.tabline, FALSE, FALSE, 0);
3750 gtk_notebook_set_show_border(GTK_NOTEBOOK(gui.tabline), FALSE);
3751 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), FALSE);
Bram Moolenaar4a85b412006-04-23 22:40:29 +00003752 gtk_notebook_set_scrollable(GTK_NOTEBOOK(gui.tabline), TRUE);
Bram Moolenaar98921892016-02-23 17:14:37 +01003753# if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar96351572006-05-05 21:16:59 +00003754 gtk_notebook_set_tab_border(GTK_NOTEBOOK(gui.tabline), FALSE);
Bram Moolenaar98921892016-02-23 17:14:37 +01003755# endif
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003756
Bram Moolenaar98921892016-02-23 17:14:37 +01003757# if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003758 tabline_tooltip = gtk_tooltips_new();
3759 gtk_tooltips_enable(GTK_TOOLTIPS(tabline_tooltip));
Bram Moolenaar98921892016-02-23 17:14:37 +01003760# endif
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003761
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003762 {
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003763 GtkWidget *page, *label, *event_box;
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003764
Bram Moolenaar30613902019-12-01 22:11:18 +01003765 // Add the first tab.
Bram Moolenaar98921892016-02-23 17:14:37 +01003766# if GTK_CHECK_VERSION(3,2,0)
3767 page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3768 gtk_box_set_homogeneous(GTK_BOX(page), FALSE);
3769# else
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003770 page = gtk_vbox_new(FALSE, 0);
Bram Moolenaar98921892016-02-23 17:14:37 +01003771# endif
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003772 gtk_widget_show(page);
3773 gtk_container_add(GTK_CONTAINER(gui.tabline), page);
3774 label = gtk_label_new("-Empty-");
3775 gtk_widget_show(label);
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003776 event_box = gtk_event_box_new();
3777 gtk_widget_show(event_box);
Bram Moolenaar98921892016-02-23 17:14:37 +01003778 g_object_set_data(G_OBJECT(event_box), "tab_num", GINT_TO_POINTER(1L));
Bram Moolenaar98921892016-02-23 17:14:37 +01003779# if !GTK_CHECK_VERSION(3,14,0)
Bram Moolenaar8ea91232006-04-28 22:41:43 +00003780 gtk_misc_set_padding(GTK_MISC(label), 2, 2);
Bram Moolenaar98921892016-02-23 17:14:37 +01003781# endif
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003782 gtk_container_add(GTK_CONTAINER(event_box), label);
Bram Moolenaar437df8f2006-04-27 21:47:44 +00003783 gtk_notebook_set_tab_label(GTK_NOTEBOOK(gui.tabline), page, event_box);
Bram Moolenaar92cbf622018-09-19 22:40:03 +02003784# if GTK_CHECK_VERSION(2,10,0)
Bram Moolenaarca05aa22017-10-22 15:36:14 +02003785 gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(gui.tabline), page, TRUE);
Bram Moolenaar92cbf622018-09-19 22:40:03 +02003786# endif
Bram Moolenaar97b2ad32006-03-18 21:40:56 +00003787 }
Bram Moolenaar54a709e2006-05-04 21:57:11 +00003788
Bram Moolenaar98921892016-02-23 17:14:37 +01003789 g_signal_connect(G_OBJECT(gui.tabline), "switch-page",
3790 G_CALLBACK(on_select_tab), NULL);
Bram Moolenaar92cbf622018-09-19 22:40:03 +02003791# if GTK_CHECK_VERSION(2,10,0)
Bram Moolenaarca05aa22017-10-22 15:36:14 +02003792 g_signal_connect(G_OBJECT(gui.tabline), "page-reordered",
3793 G_CALLBACK(on_tab_reordered), NULL);
Bram Moolenaar92cbf622018-09-19 22:40:03 +02003794# endif
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003795
Bram Moolenaar30613902019-12-01 22:11:18 +01003796 // Create a popup menu for the tab line and connect it.
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003797 tabline_menu = create_tabline_menu();
Bram Moolenaar98921892016-02-23 17:14:37 +01003798 g_signal_connect_swapped(G_OBJECT(gui.tabline), "button-press-event",
3799 G_CALLBACK(on_tabline_menu), G_OBJECT(tabline_menu));
Bram Moolenaar30613902019-12-01 22:11:18 +01003800#endif // FEAT_GUI_TABLINE
Bram Moolenaar32466aa2006-02-24 23:53:04 +00003801
Bram Moolenaar071d4272004-06-13 20:20:40 +00003802 gui.formwin = gtk_form_new();
Bram Moolenaar98921892016-02-23 17:14:37 +01003803 gtk_container_set_border_width(GTK_CONTAINER(gui.formwin), 0);
Bram Moolenaar98921892016-02-23 17:14:37 +01003804#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003805 gtk_widget_set_events(gui.formwin, GDK_EXPOSURE_MASK);
Bram Moolenaar98921892016-02-23 17:14:37 +01003806#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003807
3808 gui.drawarea = gtk_drawing_area_new();
Bram Moolenaara859f042016-11-17 19:11:55 +01003809#if GTK_CHECK_VERSION(3,22,2)
3810 gtk_widget_set_name(gui.drawarea, "vim-gui-drawarea");
3811#endif
Bram Moolenaar98921892016-02-23 17:14:37 +01003812#if GTK_CHECK_VERSION(3,0,0)
3813 gui.surface = NULL;
3814 gui.by_signal = FALSE;
3815#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003816
Bram Moolenaar30613902019-12-01 22:11:18 +01003817 // Determine which events we will filter.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818 gtk_widget_set_events(gui.drawarea,
3819 GDK_EXPOSURE_MASK |
3820 GDK_ENTER_NOTIFY_MASK |
3821 GDK_LEAVE_NOTIFY_MASK |
3822 GDK_BUTTON_PRESS_MASK |
3823 GDK_BUTTON_RELEASE_MASK |
Bram Moolenaar071d4272004-06-13 20:20:40 +00003824 GDK_SCROLL_MASK |
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 GDK_KEY_PRESS_MASK |
3826 GDK_KEY_RELEASE_MASK |
3827 GDK_POINTER_MOTION_MASK |
3828 GDK_POINTER_MOTION_HINT_MASK);
3829
3830 gtk_widget_show(gui.drawarea);
3831 gtk_form_put(GTK_FORM(gui.formwin), gui.drawarea, 0, 0);
3832 gtk_widget_show(gui.formwin);
3833 gtk_box_pack_start(GTK_BOX(vbox), gui.formwin, TRUE, TRUE, 0);
3834
Bram Moolenaar30613902019-12-01 22:11:18 +01003835 // For GtkSockets, key-presses must go to the focus widget (drawarea)
3836 // and not the window.
Bram Moolenaar98921892016-02-23 17:14:37 +01003837 g_signal_connect((gtk_socket_id == 0) ? G_OBJECT(gui.mainwin)
3838 : G_OBJECT(gui.drawarea),
3839 "key-press-event",
3840 G_CALLBACK(key_press_event), NULL);
Bram Moolenaar98921892016-02-23 17:14:37 +01003841#if defined(FEAT_XIM) || GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar30613902019-12-01 22:11:18 +01003842 // Also forward key release events for the benefit of GTK+ 2 input
3843 // modules. Try CTRL-SHIFT-xdigits to enter a Unicode code point.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003844 g_signal_connect((gtk_socket_id == 0) ? G_OBJECT(gui.mainwin)
3845 : G_OBJECT(gui.drawarea),
Bram Moolenaar98921892016-02-23 17:14:37 +01003846 "key-release-event",
Bram Moolenaar071d4272004-06-13 20:20:40 +00003847 G_CALLBACK(&key_release_event), NULL);
3848#endif
Bram Moolenaar98921892016-02-23 17:14:37 +01003849 g_signal_connect(G_OBJECT(gui.drawarea), "realize",
3850 G_CALLBACK(drawarea_realize_cb), NULL);
3851 g_signal_connect(G_OBJECT(gui.drawarea), "unrealize",
3852 G_CALLBACK(drawarea_unrealize_cb), NULL);
Bram Moolenaar664323e2018-09-18 22:30:07 +02003853#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar98921892016-02-23 17:14:37 +01003854 g_signal_connect(G_OBJECT(gui.drawarea), "configure-event",
3855 G_CALLBACK(drawarea_configure_event_cb), NULL);
Bram Moolenaar664323e2018-09-18 22:30:07 +02003856#endif
3857#if GTK_CHECK_VERSION(3,22,2)
Bram Moolenaara859f042016-11-17 19:11:55 +01003858 g_signal_connect_after(G_OBJECT(gui.drawarea), "style-updated",
3859 G_CALLBACK(&drawarea_style_updated_cb), NULL);
Bram Moolenaar664323e2018-09-18 22:30:07 +02003860#else
Bram Moolenaar98921892016-02-23 17:14:37 +01003861 g_signal_connect_after(G_OBJECT(gui.drawarea), "style-set",
3862 G_CALLBACK(&drawarea_style_set_cb), NULL);
Bram Moolenaar98921892016-02-23 17:14:37 +01003863#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864
Bram Moolenaar98921892016-02-23 17:14:37 +01003865#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866 gui.visibility = GDK_VISIBILITY_UNOBSCURED;
Bram Moolenaar98921892016-02-23 17:14:37 +01003867#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003868
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +02003869#if !defined(USE_GNOME_SESSION)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870 wm_protocols_atom = gdk_atom_intern("WM_PROTOCOLS", FALSE);
3871 save_yourself_atom = gdk_atom_intern("WM_SAVE_YOURSELF", FALSE);
3872#endif
3873
3874 if (gtk_socket_id != 0)
Bram Moolenaar30613902019-12-01 22:11:18 +01003875 // make sure keyboard input can go to the drawarea
Bram Moolenaar98921892016-02-23 17:14:37 +01003876 gtk_widget_set_can_focus(gui.drawarea, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003877
3878 /*
3879 * Set clipboard specific atoms
3880 */
3881 vim_atom = gdk_atom_intern(VIM_ATOM_NAME, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003882 vimenc_atom = gdk_atom_intern(VIMENC_ATOM_NAME, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883 clip_star.gtk_sel_atom = GDK_SELECTION_PRIMARY;
3884 clip_plus.gtk_sel_atom = gdk_atom_intern("CLIPBOARD", FALSE);
3885
3886 /*
3887 * Start out by adding the configured border width into the border offset.
3888 */
3889 gui.border_offset = gui.border_width;
3890
Bram Moolenaar98921892016-02-23 17:14:37 +01003891#if GTK_CHECK_VERSION(3,0,0)
3892 g_signal_connect(G_OBJECT(gui.drawarea), "draw",
3893 G_CALLBACK(draw_event), NULL);
3894#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895 gtk_signal_connect(GTK_OBJECT(gui.mainwin), "visibility_notify_event",
3896 GTK_SIGNAL_FUNC(visibility_event), NULL);
3897 gtk_signal_connect(GTK_OBJECT(gui.drawarea), "expose_event",
3898 GTK_SIGNAL_FUNC(expose_event), NULL);
Bram Moolenaar98921892016-02-23 17:14:37 +01003899#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900
3901 /*
3902 * Only install these enter/leave callbacks when 'p' in 'guioptions'.
3903 * Only needed for some window managers.
3904 */
3905 if (vim_strchr(p_go, GO_POINTER) != NULL)
3906 {
Bram Moolenaar98921892016-02-23 17:14:37 +01003907 g_signal_connect(G_OBJECT(gui.drawarea), "leave-notify-event",
3908 G_CALLBACK(leave_notify_event), NULL);
3909 g_signal_connect(G_OBJECT(gui.drawarea), "enter-notify-event",
3910 G_CALLBACK(enter_notify_event), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 }
3912
Bram Moolenaar30613902019-12-01 22:11:18 +01003913 // Real windows can get focus ... GtkPlug, being a mere container can't,
3914 // only its widgets. Arguably, this could be common code and we not use
3915 // the window focus at all, but let's be safe.
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003916 if (gtk_socket_id == 0)
3917 {
Bram Moolenaar98921892016-02-23 17:14:37 +01003918 g_signal_connect(G_OBJECT(gui.mainwin), "focus-out-event",
3919 G_CALLBACK(focus_out_event), NULL);
3920 g_signal_connect(G_OBJECT(gui.mainwin), "focus-in-event",
3921 G_CALLBACK(focus_in_event), NULL);
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003922 }
3923 else
3924 {
Bram Moolenaar98921892016-02-23 17:14:37 +01003925 g_signal_connect(G_OBJECT(gui.drawarea), "focus-out-event",
3926 G_CALLBACK(focus_out_event), NULL);
3927 g_signal_connect(G_OBJECT(gui.drawarea), "focus-in-event",
3928 G_CALLBACK(focus_in_event), NULL);
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003929#ifdef FEAT_GUI_TABLINE
Bram Moolenaar98921892016-02-23 17:14:37 +01003930 g_signal_connect(G_OBJECT(gui.tabline), "focus-out-event",
3931 G_CALLBACK(focus_out_event), NULL);
3932 g_signal_connect(G_OBJECT(gui.tabline), "focus-in-event",
3933 G_CALLBACK(focus_in_event), NULL);
Bram Moolenaar30613902019-12-01 22:11:18 +01003934#endif // FEAT_GUI_TABLINE
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00003935 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003936
Bram Moolenaar98921892016-02-23 17:14:37 +01003937 g_signal_connect(G_OBJECT(gui.drawarea), "motion-notify-event",
3938 G_CALLBACK(motion_notify_event), NULL);
3939 g_signal_connect(G_OBJECT(gui.drawarea), "button-press-event",
3940 G_CALLBACK(button_press_event), NULL);
3941 g_signal_connect(G_OBJECT(gui.drawarea), "button-release-event",
3942 G_CALLBACK(button_release_event), NULL);
3943 g_signal_connect(G_OBJECT(gui.drawarea), "scroll-event",
3944 G_CALLBACK(&scroll_event), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003945
3946 /*
3947 * Add selection handler functions.
3948 */
Bram Moolenaar98921892016-02-23 17:14:37 +01003949 g_signal_connect(G_OBJECT(gui.drawarea), "selection-clear-event",
3950 G_CALLBACK(selection_clear_event), NULL);
3951 g_signal_connect(G_OBJECT(gui.drawarea), "selection-received",
3952 G_CALLBACK(selection_received_cb), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003953
Bram Moolenaara76638f2010-06-05 12:49:46 +02003954 gui_gtk_set_selection_targets();
Bram Moolenaar071d4272004-06-13 20:20:40 +00003955
Bram Moolenaar98921892016-02-23 17:14:37 +01003956 g_signal_connect(G_OBJECT(gui.drawarea), "selection-get",
3957 G_CALLBACK(selection_get_cb), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003958
Bram Moolenaar30613902019-12-01 22:11:18 +01003959 // Pretend we don't have input focus, we will get an event if we do.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960 gui.in_focus = FALSE;
3961
Bram Moolenaar7ebf4e12018-08-07 20:01:40 +02003962 // Handle changes to the "Xft/DPI" setting.
3963 {
3964 GtkSettings *gtk_settings =
3965 gtk_settings_get_for_screen(gdk_screen_get_default());
3966
3967 g_signal_connect(gtk_settings, "notify::gtk-xft-dpi",
3968 G_CALLBACK(gtk_settings_xft_dpi_changed_cb), NULL);
3969 }
3970
Bram Moolenaar071d4272004-06-13 20:20:40 +00003971 return OK;
3972}
3973
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +02003974#if defined(USE_GNOME_SESSION) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975/*
3976 * This is called from gui_start() after a fork() has been done.
3977 * We have to tell the session manager our new PID.
3978 */
3979 void
3980gui_mch_forked(void)
3981{
3982 if (using_gnome)
3983 {
3984 GnomeClient *client;
3985
3986 client = gnome_master_client();
3987
3988 if (client != NULL)
3989 gnome_client_set_process_id(client, getpid());
3990 }
3991}
Bram Moolenaarf96ae0b2019-07-28 15:21:55 +02003992#endif // USE_GNOME_SESSION
Bram Moolenaar071d4272004-06-13 20:20:40 +00003993
Bram Moolenaar98921892016-02-23 17:14:37 +01003994#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar36edf062016-07-21 22:10:12 +02003995 static GdkRGBA
3996color_to_rgba(guicolor_T color)
Bram Moolenaar98921892016-02-23 17:14:37 +01003997{
Bram Moolenaar36edf062016-07-21 22:10:12 +02003998 GdkRGBA rgba;
3999 rgba.red = ((color & 0xff0000) >> 16) / 255.0;
4000 rgba.green = ((color & 0xff00) >> 8) / 255.0;
4001 rgba.blue = ((color & 0xff)) / 255.0;
4002 rgba.alpha = 1.0;
4003 return rgba;
Bram Moolenaar98921892016-02-23 17:14:37 +01004004}
4005
4006 static void
Bram Moolenaar36edf062016-07-21 22:10:12 +02004007set_cairo_source_rgba_from_color(cairo_t *cr, guicolor_T color)
Bram Moolenaar98921892016-02-23 17:14:37 +01004008{
Bram Moolenaar36edf062016-07-21 22:10:12 +02004009 const GdkRGBA rgba = color_to_rgba(color);
4010 cairo_set_source_rgba(cr, rgba.red, rgba.green, rgba.blue, rgba.alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01004011}
Bram Moolenaar30613902019-12-01 22:11:18 +01004012#endif // GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar98921892016-02-23 17:14:37 +01004013
Bram Moolenaar071d4272004-06-13 20:20:40 +00004014/*
4015 * Called when the foreground or background color has been changed.
4016 * This used to change the graphics contexts directly but we are
4017 * currently manipulating them where desired.
4018 */
4019 void
4020gui_mch_new_colors(void)
4021{
Bram Moolenaar98921892016-02-23 17:14:37 +01004022 if (gui.drawarea != NULL && gtk_widget_get_window(gui.drawarea) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004023 {
Bram Moolenaar664323e2018-09-18 22:30:07 +02004024#if !GTK_CHECK_VERSION(3,22,2)
4025 GdkWindow * const da_win = gtk_widget_get_window(gui.drawarea);
4026#endif
4027
Bram Moolenaara859f042016-11-17 19:11:55 +01004028#if GTK_CHECK_VERSION(3,22,2)
4029 GtkStyleContext * const context
4030 = gtk_widget_get_style_context(gui.drawarea);
4031 GtkCssProvider * const provider = gtk_css_provider_new();
4032 gchar * const css = g_strdup_printf(
4033 "widget#vim-gui-drawarea {\n"
4034 " background-color: #%.2lx%.2lx%.2lx;\n"
4035 "}\n",
4036 (gui.back_pixel >> 16) & 0xff,
4037 (gui.back_pixel >> 8) & 0xff,
4038 gui.back_pixel & 0xff);
4039
4040 gtk_css_provider_load_from_data(provider, css, -1, NULL);
4041 gtk_style_context_add_provider(context,
4042 GTK_STYLE_PROVIDER(provider), G_MAXUINT);
4043
4044 g_free(css);
4045 g_object_unref(provider);
Bram Moolenaar30613902019-12-01 22:11:18 +01004046#elif GTK_CHECK_VERSION(3,4,0) // !GTK_CHECK_VERSION(3,22,2)
Bram Moolenaar36edf062016-07-21 22:10:12 +02004047 GdkRGBA rgba;
Bram Moolenaar98921892016-02-23 17:14:37 +01004048
Bram Moolenaar36edf062016-07-21 22:10:12 +02004049 rgba = color_to_rgba(gui.back_pixel);
Bram Moolenaar98921892016-02-23 17:14:37 +01004050 {
4051 cairo_pattern_t * const pat = cairo_pattern_create_rgba(
Bram Moolenaar36edf062016-07-21 22:10:12 +02004052 rgba.red, rgba.green, rgba.blue, rgba.alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01004053 if (pat != NULL)
4054 {
4055 gdk_window_set_background_pattern(da_win, pat);
4056 cairo_pattern_destroy(pat);
4057 }
4058 else
Bram Moolenaar36edf062016-07-21 22:10:12 +02004059 gdk_window_set_background_rgba(da_win, &rgba);
Bram Moolenaar98921892016-02-23 17:14:37 +01004060 }
Bram Moolenaar30613902019-12-01 22:11:18 +01004061#else // !GTK_CHECK_VERSION(3,4,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004062 GdkColor color = { 0, 0, 0, 0 };
4063
4064 color.pixel = gui.back_pixel;
Bram Moolenaar98921892016-02-23 17:14:37 +01004065 gdk_window_set_background(da_win, &color);
Bram Moolenaar30613902019-12-01 22:11:18 +01004066#endif // !GTK_CHECK_VERSION(3,22,2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004067 }
4068}
4069
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070/*
4071 * This signal informs us about the need to rearrange our sub-widgets.
4072 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073 static gint
Bram Moolenaarb85cb212009-05-17 14:24:23 +00004074form_configure_event(GtkWidget *widget UNUSED,
4075 GdkEventConfigure *event,
4076 gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004077{
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004078 int usable_height = event->height;
4079
Bram Moolenaar182707a2016-11-21 20:55:58 +01004080#if GTK_CHECK_VERSION(3,22,2) && !GTK_CHECK_VERSION(3,22,4)
Bram Moolenaar30613902019-12-01 22:11:18 +01004081 // As of 3.22.2, GdkWindows have started distributing configure events to
4082 // their "native" children (https://git.gnome.org/browse/gtk+/commit/?h=gtk-3-22&id=12579fe71b3b8f79eb9c1b80e429443bcc437dd0).
4083 //
4084 // As can be seen from the implementation of move_native_children() and
4085 // configure_native_child() in gdkwindow.c, those functions actually
4086 // propagate configure events to every child, failing to distinguish
4087 // "native" one from non-native one.
4088 //
4089 // Naturally, configure events propagated to here like that are fallacious
4090 // and, as a matter of fact, they trigger a geometric collapse of
4091 // gui.formwin.
4092 //
4093 // To filter out such fallacious events, check if the given event is the
4094 // one that was sent out to the right place. Ignore it if not.
4095 //
4096 // Follow-up
4097 // After a few weeks later, the GdkWindow change mentioned above was
4098 // reverted (https://git.gnome.org/browse/gtk+/commit/?h=gtk-3-22&id=f70039cb9603a02d2369fec4038abf40a1711155).
4099 // The corresponding official release is 3.22.4.
Bram Moolenaara859f042016-11-17 19:11:55 +01004100 if (event->window != gtk_widget_get_window(gui.formwin))
4101 return TRUE;
4102#endif
4103
Bram Moolenaar30613902019-12-01 22:11:18 +01004104 // When in a GtkPlug, we can't guarantee valid heights (as a round
4105 // no. of char-heights), so we have to manually sanitise them.
4106 // Widths seem to sort themselves out, don't ask me why.
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004107 if (gtk_socket_id != 0)
Bram Moolenaar30613902019-12-01 22:11:18 +01004108 usable_height -= (gui.char_height - (gui.char_height/2)); // sic.
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004109
Bram Moolenaar071d4272004-06-13 20:20:40 +00004110 gtk_form_freeze(GTK_FORM(gui.formwin));
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004111 gui_resize_shell(event->width, usable_height);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112 gtk_form_thaw(GTK_FORM(gui.formwin));
4113
4114 return TRUE;
4115}
4116
4117/*
4118 * Function called when window already closed.
4119 * We can't do much more here than to trying to preserve what had been done,
4120 * since the window is already inevitably going away.
4121 */
Bram Moolenaar98921892016-02-23 17:14:37 +01004122 static void
4123mainwin_destroy_cb(GObject *object UNUSED, gpointer data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124{
Bram Moolenaar30613902019-12-01 22:11:18 +01004125 // Don't write messages to the GUI anymore
Bram Moolenaar071d4272004-06-13 20:20:40 +00004126 full_screen = FALSE;
4127
4128 gui.mainwin = NULL;
4129 gui.drawarea = NULL;
4130
Bram Moolenaar30613902019-12-01 22:11:18 +01004131 if (!exiting) // only do anything if the destroy was unexpected
Bram Moolenaar071d4272004-06-13 20:20:40 +00004132 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00004133 vim_strncpy(IObuff,
4134 (char_u *)_("Vim: Main window unexpectedly destroyed\n"),
4135 IOSIZE - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004136 preserve_exit();
4137 }
Bram Moolenaar36e294c2015-12-29 18:55:46 +01004138#ifdef USE_GRESOURCE
4139 gui_gtk_unregister_resource();
4140#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004141}
4142
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004143
4144/*
4145 * Bit of a hack to ensure we start GtkPlug windows with the correct window
4146 * hints (and thus the required size from -geom), but that after that we
4147 * put the hints back to normal (the actual minimum size) so we may
4148 * subsequently be resized smaller. GtkSocket (the parent end) uses the
Bram Moolenaarc4568ab2018-11-16 16:21:05 +01004149 * plug's window 'min hints to set *its* minimum size, but that's also the
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004150 * only way we have of making ourselves bigger (by set lines/columns).
4151 * Thus set hints at start-up to ensure correct init. size, then a
Bram Moolenaar3f2a5d82016-02-27 22:08:16 +01004152 * second after the final attempt to reset the real minimum hints (done by
4153 * scrollbar init.), actually do the standard hints and stop the timer.
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004154 * We'll not let the default hints be set while this timer's active.
4155 */
Bram Moolenaar4ab79682017-08-27 14:50:47 +02004156 static timeout_cb_type
Bram Moolenaarb85cb212009-05-17 14:24:23 +00004157check_startup_plug_hints(gpointer data UNUSED)
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004158{
4159 if (init_window_hints_state == 1)
4160 {
Bram Moolenaar30613902019-12-01 22:11:18 +01004161 // Safe to use normal hints now
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00004162 init_window_hints_state = 0;
4163 update_window_manager_hints(0, 0);
Bram Moolenaar30613902019-12-01 22:11:18 +01004164 return FALSE; // stop timer
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004165 }
4166
Bram Moolenaar30613902019-12-01 22:11:18 +01004167 // Keep on trying
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004168 init_window_hints_state = 1;
4169 return TRUE;
4170}
4171
Bram Moolenaar071d4272004-06-13 20:20:40 +00004172/*
4173 * Open the GUI window which was created by a call to gui_mch_init().
4174 */
4175 int
4176gui_mch_open(void)
4177{
4178 guicolor_T fg_pixel = INVALCOLOR;
4179 guicolor_T bg_pixel = INVALCOLOR;
Bram Moolenaar79ef6d62009-09-23 15:35:48 +00004180 guint pixel_width;
4181 guint pixel_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183 /*
4184 * Allow setting a window role on the command line, or invent one
4185 * if none was specified. This is mainly useful for GNOME session
4186 * support; allowing the WM to restore window placement.
4187 */
4188 if (role_argument != NULL)
4189 {
4190 gtk_window_set_role(GTK_WINDOW(gui.mainwin), role_argument);
4191 }
4192 else
4193 {
4194 char *role;
4195
Bram Moolenaar30613902019-12-01 22:11:18 +01004196 // Invent a unique-enough ID string for the role
Bram Moolenaar071d4272004-06-13 20:20:40 +00004197 role = g_strdup_printf("vim-%u-%u-%u",
4198 (unsigned)mch_get_pid(),
4199 (unsigned)g_random_int(),
4200 (unsigned)time(NULL));
4201
4202 gtk_window_set_role(GTK_WINDOW(gui.mainwin), role);
4203 g_free(role);
4204 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004205
4206 if (gui_win_x != -1 && gui_win_y != -1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004207 gtk_window_move(GTK_WINDOW(gui.mainwin), gui_win_x, gui_win_y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004208
Bram Moolenaar30613902019-12-01 22:11:18 +01004209 // Determine user specified geometry, if present.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004210 if (gui.geom != NULL)
4211 {
4212 int mask;
4213 unsigned int w, h;
4214 int x = 0;
4215 int y = 0;
4216
4217 mask = XParseGeometry((char *)gui.geom, &x, &y, &w, &h);
4218
4219 if (mask & WidthValue)
4220 Columns = w;
4221 if (mask & HeightValue)
Bram Moolenaard68071d2006-05-02 22:08:30 +00004222 {
Bram Moolenaarb85cb212009-05-17 14:24:23 +00004223 if (p_window > (long)h - 1 || !option_was_set((char_u *)"window"))
Bram Moolenaard68071d2006-05-02 22:08:30 +00004224 p_window = h - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225 Rows = h;
Bram Moolenaard68071d2006-05-02 22:08:30 +00004226 }
Bram Moolenaare057d402013-06-30 17:51:51 +02004227 limit_screen_size();
Bram Moolenaar2dd8b522007-10-19 12:33:44 +00004228
4229 pixel_width = (guint)(gui_get_base_width() + Columns * gui.char_width);
4230 pixel_height = (guint)(gui_get_base_height() + Rows * gui.char_height);
4231
Bram Moolenaar2dd8b522007-10-19 12:33:44 +00004232 pixel_width += get_menu_tool_width();
4233 pixel_height += get_menu_tool_height();
Bram Moolenaar2dd8b522007-10-19 12:33:44 +00004234
Bram Moolenaar071d4272004-06-13 20:20:40 +00004235 if (mask & (XValue | YValue))
Bram Moolenaar2dd8b522007-10-19 12:33:44 +00004236 {
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +00004237 int ww, hh;
4238 gui_mch_get_screen_dimensions(&ww, &hh);
4239 hh += p_ghr + get_menu_tool_height();
4240 ww += get_menu_tool_width();
Bram Moolenaar2dd8b522007-10-19 12:33:44 +00004241 if (mask & XNegative)
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +00004242 x += ww - pixel_width;
Bram Moolenaar2dd8b522007-10-19 12:33:44 +00004243 if (mask & YNegative)
Bram Moolenaarfe86f2d2008-11-28 20:29:07 +00004244 y += hh - pixel_height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 gtk_window_move(GTK_WINDOW(gui.mainwin), x, y);
Bram Moolenaar2dd8b522007-10-19 12:33:44 +00004246 }
Bram Moolenaard23a8232018-02-10 18:45:26 +01004247 VIM_CLEAR(gui.geom);
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004248
Bram Moolenaar30613902019-12-01 22:11:18 +01004249 // From now until everyone's stopped trying to set the window hints
4250 // to their correct minimum values, stop them being set as we need
4251 // them to remain at our required size for the parent GtkSocket to
4252 // give us the right initial size.
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004253 if (gtk_socket_id != 0 && (mask & WidthValue || mask & HeightValue))
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00004254 {
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00004255 update_window_manager_hints(pixel_width, pixel_height);
4256 init_window_hints_state = 1;
Bram Moolenaar4ab79682017-08-27 14:50:47 +02004257 timeout_add(1000, check_startup_plug_hints, NULL);
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00004258 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004259 }
4260
Bram Moolenaar79ef6d62009-09-23 15:35:48 +00004261 pixel_width = (guint)(gui_get_base_width() + Columns * gui.char_width);
4262 pixel_height = (guint)(gui_get_base_height() + Rows * gui.char_height);
Bram Moolenaar30613902019-12-01 22:11:18 +01004263 // For GTK2 changing the size of the form widget doesn't cause window
4264 // resizing.
Bram Moolenaardebe25a2010-06-06 17:41:24 +02004265 if (gtk_socket_id == 0)
Bram Moolenaar79ef6d62009-09-23 15:35:48 +00004266 gtk_window_resize(GTK_WINDOW(gui.mainwin), pixel_width, pixel_height);
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004267 update_window_manager_hints(0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268
4269 if (foreground_argument != NULL)
4270 fg_pixel = gui_get_color((char_u *)foreground_argument);
4271 if (fg_pixel == INVALCOLOR)
4272 fg_pixel = gui_get_color((char_u *)"Black");
4273
4274 if (background_argument != NULL)
4275 bg_pixel = gui_get_color((char_u *)background_argument);
4276 if (bg_pixel == INVALCOLOR)
4277 bg_pixel = gui_get_color((char_u *)"White");
4278
4279 if (found_reverse_arg)
4280 {
4281 gui.def_norm_pixel = bg_pixel;
4282 gui.def_back_pixel = fg_pixel;
4283 }
4284 else
4285 {
4286 gui.def_norm_pixel = fg_pixel;
4287 gui.def_back_pixel = bg_pixel;
4288 }
4289
Bram Moolenaar30613902019-12-01 22:11:18 +01004290 // Get the colors from the "Normal" and "Menu" group (set in syntax.c or
4291 // in a vimrc file)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004292 set_normal_colors();
4293
Bram Moolenaar30613902019-12-01 22:11:18 +01004294 // Check that none of the colors are the same as the background color
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295 gui_check_colors();
4296
Bram Moolenaar30613902019-12-01 22:11:18 +01004297 // Get the colors for the highlight groups (gui_check_colors() might have
4298 // changed them).
4299 highlight_gui_started(); // re-init colors and fonts
Bram Moolenaar071d4272004-06-13 20:20:40 +00004300
Bram Moolenaar98921892016-02-23 17:14:37 +01004301 g_signal_connect(G_OBJECT(gui.mainwin), "destroy",
4302 G_CALLBACK(mainwin_destroy_cb), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004303
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304 /*
4305 * Notify the fixed area about the need to resize the contents of the
4306 * gui.formwin, which we use for random positioning of the included
4307 * components.
4308 *
4309 * We connect this signal deferred finally after anything is in place,
4310 * since this is intended to handle resizements coming from the window
4311 * manager upon us and should not interfere with what VIM is requesting
4312 * upon startup.
4313 */
Bram Moolenaar98921892016-02-23 17:14:37 +01004314 g_signal_connect(G_OBJECT(gui.formwin), "configure-event",
4315 G_CALLBACK(form_configure_event), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316
4317#ifdef FEAT_DND
Bram Moolenaar30613902019-12-01 22:11:18 +01004318 // Set up for receiving DND items.
Bram Moolenaara76638f2010-06-05 12:49:46 +02004319 gui_gtk_set_dnd_targets();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004320
Bram Moolenaar98921892016-02-23 17:14:37 +01004321 g_signal_connect(G_OBJECT(gui.drawarea), "drag-data-received",
4322 G_CALLBACK(drag_data_received_cb), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004323#endif
4324
Bram Moolenaar30613902019-12-01 22:11:18 +01004325 // With GTK+ 2, we need to iconify the window before calling show()
4326 // to avoid mapping the window for a short time.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327 if (found_iconic_arg && gtk_socket_id == 0)
4328 gui_mch_iconify();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329
4330 {
Bram Moolenaar182c5be2010-06-25 05:37:59 +02004331#if defined(FEAT_GUI_GNOME) && defined(FEAT_MENU)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004332 unsigned long menu_handler = 0;
4333# ifdef FEAT_TOOLBAR
4334 unsigned long tool_handler = 0;
4335# endif
4336 /*
4337 * Urgh hackish :/ For some reason BonoboDockLayout always forces a
4338 * show when restoring the saved layout configuration. We can't just
4339 * hide the widgets again after gtk_widget_show(gui.mainwin) since it's
4340 * a toplevel window and thus will be realized immediately. Instead,
4341 * connect signal handlers to hide the widgets just after they've been
4342 * marked visible, but before the main window is realized.
4343 */
4344 if (using_gnome && vim_strchr(p_go, GO_MENUS) == NULL)
4345 menu_handler = g_signal_connect_after(gui.menubar_h, "show",
4346 G_CALLBACK(&gtk_widget_hide),
4347 NULL);
4348# ifdef FEAT_TOOLBAR
4349 if (using_gnome && vim_strchr(p_go, GO_TOOLBAR) == NULL
4350 && (toolbar_flags & (TOOLBAR_TEXT | TOOLBAR_ICONS)))
4351 tool_handler = g_signal_connect_after(gui.toolbar_h, "show",
4352 G_CALLBACK(&gtk_widget_hide),
4353 NULL);
4354# endif
4355#endif
4356 gtk_widget_show(gui.mainwin);
4357
Bram Moolenaar182c5be2010-06-25 05:37:59 +02004358#if defined(FEAT_GUI_GNOME) && defined(FEAT_MENU)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359 if (menu_handler != 0)
4360 g_signal_handler_disconnect(gui.menubar_h, menu_handler);
4361# ifdef FEAT_TOOLBAR
4362 if (tool_handler != 0)
4363 g_signal_handler_disconnect(gui.toolbar_h, tool_handler);
4364# endif
4365#endif
4366 }
4367
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 return OK;
4369}
4370
4371
Bram Moolenaar071d4272004-06-13 20:20:40 +00004372 void
Bram Moolenaarb85cb212009-05-17 14:24:23 +00004373gui_mch_exit(int rc UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374{
4375 if (gui.mainwin != NULL)
4376 gtk_widget_destroy(gui.mainwin);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377}
4378
4379/*
4380 * Get the position of the top left corner of the window.
4381 */
4382 int
4383gui_mch_get_winpos(int *x, int *y)
4384{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004385 gtk_window_get_position(GTK_WINDOW(gui.mainwin), x, y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 return OK;
4387}
4388
4389/*
4390 * Set the position of the top left corner of the window to the given
4391 * coordinates.
4392 */
4393 void
4394gui_mch_set_winpos(int x, int y)
4395{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004396 gtk_window_move(GTK_WINDOW(gui.mainwin), x, y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004397}
4398
Bram Moolenaar98921892016-02-23 17:14:37 +01004399#if !GTK_CHECK_VERSION(3,0,0)
4400# if 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401static int resize_idle_installed = FALSE;
4402/*
4403 * Idle handler to force resize. Used by gui_mch_set_shellsize() to ensure
4404 * the shell size doesn't exceed the window size, i.e. if the window manager
4405 * ignored our size request. Usually this happens if the window is maximized.
4406 *
4407 * FIXME: It'd be nice if we could find a little more orthodox solution.
4408 * See also the remark below in gui_mch_set_shellsize().
4409 *
4410 * DISABLED: When doing ":set lines+=1" this function would first invoke
4411 * gui_resize_shell() with the old size, then the normal callback would
4412 * report the new size through form_configure_event(). That caused the window
4413 * layout to be messed up.
4414 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415 static gboolean
4416force_shell_resize_idle(gpointer data)
4417{
4418 if (gui.mainwin != NULL
4419 && GTK_WIDGET_REALIZED(gui.mainwin)
4420 && GTK_WIDGET_VISIBLE(gui.mainwin))
4421 {
4422 int width;
4423 int height;
4424
4425 gtk_window_get_size(GTK_WINDOW(gui.mainwin), &width, &height);
4426
4427 width -= get_menu_tool_width();
4428 height -= get_menu_tool_height();
4429
4430 gui_resize_shell(width, height);
4431 }
4432
4433 resize_idle_installed = FALSE;
Bram Moolenaar30613902019-12-01 22:11:18 +01004434 return FALSE; // don't call me again
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435}
Bram Moolenaar98921892016-02-23 17:14:37 +01004436# endif
Bram Moolenaar30613902019-12-01 22:11:18 +01004437#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438
Bram Moolenaar09736232009-09-23 16:14:49 +00004439/*
4440 * Return TRUE if the main window is maximized.
4441 */
4442 int
Bram Moolenaar66f948e2016-01-30 16:39:25 +01004443gui_mch_maximized(void)
Bram Moolenaar09736232009-09-23 16:14:49 +00004444{
Bram Moolenaar98921892016-02-23 17:14:37 +01004445 return (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL
4446 && (gdk_window_get_state(gtk_widget_get_window(gui.mainwin))
4447 & GDK_WINDOW_STATE_MAXIMIZED));
Bram Moolenaar09736232009-09-23 16:14:49 +00004448}
4449
4450/*
4451 * Unmaximize the main window
4452 */
4453 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01004454gui_mch_unmaximize(void)
Bram Moolenaar09736232009-09-23 16:14:49 +00004455{
4456 if (gui.mainwin != NULL)
4457 gtk_window_unmaximize(GTK_WINDOW(gui.mainwin));
4458}
Bram Moolenaar09736232009-09-23 16:14:49 +00004459
Bram Moolenaar071d4272004-06-13 20:20:40 +00004460/*
Bram Moolenaar8ac44152017-11-09 18:33:29 +01004461 * Called when the font changed while the window is maximized or GO_KEEPWINSIZE
4462 * is set. Compute the new Rows and Columns. This is like resizing the
4463 * window.
Bram Moolenaar12bc1b52011-08-10 17:44:45 +02004464 */
4465 void
Bram Moolenaar66f948e2016-01-30 16:39:25 +01004466gui_mch_newfont(void)
Bram Moolenaar12bc1b52011-08-10 17:44:45 +02004467{
4468 int w, h;
4469
4470 gtk_window_get_size(GTK_WINDOW(gui.mainwin), &w, &h);
4471 w -= get_menu_tool_width();
4472 h -= get_menu_tool_height();
4473 gui_resize_shell(w, h);
4474}
4475
4476/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 * Set the windows size.
4478 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479 void
4480gui_mch_set_shellsize(int width, int height,
Bram Moolenaarb85cb212009-05-17 14:24:23 +00004481 int min_width UNUSED, int min_height UNUSED,
4482 int base_width UNUSED, int base_height UNUSED,
4483 int direction UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484{
Bram Moolenaar30613902019-12-01 22:11:18 +01004485 // give GTK+ a chance to put all widget's into place
Bram Moolenaar071d4272004-06-13 20:20:40 +00004486 gui_mch_update();
4487
Bram Moolenaar30613902019-12-01 22:11:18 +01004488 // this will cause the proper resizement to happen too
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004489 if (gtk_socket_id == 0)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00004490 update_window_manager_hints(0, 0);
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004491
Bram Moolenaar30613902019-12-01 22:11:18 +01004492 // With GTK+ 2, changing the size of the form widget doesn't resize
4493 // the window. So let's do it the other way around and resize the
4494 // main window instead.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004495 width += get_menu_tool_width();
4496 height += get_menu_tool_height();
4497
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004498 if (gtk_socket_id == 0)
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00004499 gtk_window_resize(GTK_WINDOW(gui.mainwin), width, height);
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004500 else
Bram Moolenaarc9b4b052006-04-30 18:54:39 +00004501 update_window_manager_hints(width, height);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004502
Bram Moolenaar98921892016-02-23 17:14:37 +01004503# if !GTK_CHECK_VERSION(3,0,0)
4504# if 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00004505 if (!resize_idle_installed)
4506 {
4507 g_idle_add_full(GDK_PRIORITY_EVENTS + 10,
4508 &force_shell_resize_idle, NULL, NULL);
4509 resize_idle_installed = TRUE;
4510 }
Bram Moolenaar98921892016-02-23 17:14:37 +01004511# endif
Bram Moolenaar30613902019-12-01 22:11:18 +01004512# endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513 /*
4514 * Wait until all events are processed to prevent a crash because the
4515 * real size of the drawing area doesn't reflect Vim's internal ideas.
4516 *
4517 * This is a bit of a hack, since Vim is a terminal application with a GUI
4518 * on top, while the GUI expects to be the boss.
4519 */
4520 gui_mch_update();
Bram Moolenaar071d4272004-06-13 20:20:40 +00004521}
4522
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004523 void
Bram Moolenaar3f6a16f2018-08-19 22:58:45 +02004524gui_gtk_get_screen_geom_of_win(
4525 GtkWidget *wid,
4526 int *screen_x,
4527 int *screen_y,
4528 int *width,
4529 int *height)
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004530{
Bram Moolenaar3f6a16f2018-08-19 22:58:45 +02004531 GdkRectangle geometry;
4532 GdkWindow *win = gtk_widget_get_window(wid);
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004533#if GTK_CHECK_VERSION(3,22,0)
Bram Moolenaar8696bba2017-09-09 23:00:56 +02004534 GdkDisplay *dpy = gtk_widget_get_display(wid);
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004535 GdkMonitor *monitor = gdk_display_get_monitor_at_window(dpy, win);
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004536
4537 gdk_monitor_get_geometry(monitor, &geometry);
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004538#else
4539 GdkScreen* screen;
Bram Moolenaar3f6a16f2018-08-19 22:58:45 +02004540 int monitor;
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004541
Bram Moolenaar8696bba2017-09-09 23:00:56 +02004542 if (wid != NULL && gtk_widget_has_screen(wid))
4543 screen = gtk_widget_get_screen(wid);
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004544 else
4545 screen = gdk_screen_get_default();
Bram Moolenaar3f6a16f2018-08-19 22:58:45 +02004546 monitor = gdk_screen_get_monitor_at_window(screen, win);
4547 gdk_screen_get_monitor_geometry(screen, monitor, &geometry);
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004548#endif
Bram Moolenaar3f6a16f2018-08-19 22:58:45 +02004549 *screen_x = geometry.x;
4550 *screen_y = geometry.y;
4551 *width = geometry.width;
4552 *height = geometry.height;
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004553}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004554
4555/*
4556 * The screen size is used to make sure the initial window doesn't get bigger
4557 * than the screen. This subtracts some room for menubar, toolbar and window
4558 * decorations.
4559 */
4560 void
4561gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
4562{
Bram Moolenaar3f6a16f2018-08-19 22:58:45 +02004563 int x, y;
4564
4565 gui_gtk_get_screen_geom_of_win(gui.mainwin, &x, &y, screen_w, screen_h);
Bram Moolenaara859f042016-11-17 19:11:55 +01004566
Bram Moolenaar30613902019-12-01 22:11:18 +01004567 // Subtract 'guiheadroom' from the height to allow some room for the
4568 // window manager (task list and window title bar).
Bram Moolenaar7be9b502017-09-09 18:45:26 +02004569 *screen_h -= p_ghr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570
4571 /*
4572 * FIXME: dirty trick: Because the gui_get_base_height() doesn't include
4573 * the toolbar and menubar for GTK, we subtract them from the screen
Bram Moolenaar64404472010-06-26 06:24:45 +02004574 * height, so that the window size can be made to fit on the screen.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 * This should be completely changed later.
4576 */
4577 *screen_w -= get_menu_tool_width();
4578 *screen_h -= get_menu_tool_height();
4579}
4580
4581#if defined(FEAT_TITLE) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004582 void
Bram Moolenaarb85cb212009-05-17 14:24:23 +00004583gui_mch_settitle(char_u *title, char_u *icon UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004584{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004585 if (title != NULL && output_conv.vc_type != CONV_NONE)
4586 title = string_convert(&output_conv, title, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004587
4588 gtk_window_set_title(GTK_WINDOW(gui.mainwin), (const char *)title);
4589
Bram Moolenaar071d4272004-06-13 20:20:40 +00004590 if (output_conv.vc_type != CONV_NONE)
4591 vim_free(title);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004592}
Bram Moolenaar30613902019-12-01 22:11:18 +01004593#endif // FEAT_TITLE
Bram Moolenaar071d4272004-06-13 20:20:40 +00004594
4595#if defined(FEAT_MENU) || defined(PROTO)
4596 void
4597gui_mch_enable_menu(int showit)
4598{
4599 GtkWidget *widget;
4600
4601# ifdef FEAT_GUI_GNOME
4602 if (using_gnome)
4603 widget = gui.menubar_h;
4604 else
4605# endif
4606 widget = gui.menubar;
4607
Bram Moolenaar30613902019-12-01 22:11:18 +01004608 // Do not disable the menu while starting up, otherwise F10 doesn't work.
Bram Moolenaar98921892016-02-23 17:14:37 +01004609 if (!showit != !gtk_widget_get_visible(widget) && !gui.starting)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004610 {
4611 if (showit)
4612 gtk_widget_show(widget);
4613 else
4614 gtk_widget_hide(widget);
4615
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004616 update_window_manager_hints(0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004617 }
4618}
Bram Moolenaar30613902019-12-01 22:11:18 +01004619#endif // FEAT_MENU
Bram Moolenaar071d4272004-06-13 20:20:40 +00004620
4621#if defined(FEAT_TOOLBAR) || defined(PROTO)
4622 void
4623gui_mch_show_toolbar(int showit)
4624{
4625 GtkWidget *widget;
4626
4627 if (gui.toolbar == NULL)
4628 return;
4629
4630# ifdef FEAT_GUI_GNOME
4631 if (using_gnome)
4632 widget = gui.toolbar_h;
4633 else
4634# endif
4635 widget = gui.toolbar;
4636
4637 if (showit)
4638 set_toolbar_style(GTK_TOOLBAR(gui.toolbar));
4639
Bram Moolenaar98921892016-02-23 17:14:37 +01004640 if (!showit != !gtk_widget_get_visible(widget))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004641 {
4642 if (showit)
4643 gtk_widget_show(widget);
4644 else
4645 gtk_widget_hide(widget);
4646
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00004647 update_window_manager_hints(0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004648 }
4649}
Bram Moolenaar30613902019-12-01 22:11:18 +01004650#endif // FEAT_TOOLBAR
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651
Bram Moolenaar071d4272004-06-13 20:20:40 +00004652/*
4653 * Check if a given font is a CJK font. This is done in a very crude manner. It
4654 * just see if U+04E00 for zh and ja and U+AC00 for ko are covered in a given
4655 * font. Consequently, this function cannot be used as a general purpose check
4656 * for CJK-ness for which fontconfig APIs should be used. This is only used by
4657 * gui_mch_init_font() to deal with 'CJK fixed width fonts'.
4658 */
4659 static int
4660is_cjk_font(PangoFontDescription *font_desc)
4661{
4662 static const char * const cjk_langs[] =
4663 {"zh_CN", "zh_TW", "zh_HK", "ja", "ko"};
4664
4665 PangoFont *font;
4666 unsigned i;
4667 int is_cjk = FALSE;
4668
4669 font = pango_context_load_font(gui.text_context, font_desc);
4670
4671 if (font == NULL)
4672 return FALSE;
4673
4674 for (i = 0; !is_cjk && i < G_N_ELEMENTS(cjk_langs); ++i)
4675 {
4676 PangoCoverage *coverage;
4677 gunichar uc;
4678
Bram Moolenaar14285cb2020-03-27 20:58:37 +01004679 // Valgrind reports a leak for pango_language_from_string(), but the
4680 // documentation says "This is owned by Pango and should not be freed".
Bram Moolenaar071d4272004-06-13 20:20:40 +00004681 coverage = pango_font_get_coverage(
4682 font, pango_language_from_string(cjk_langs[i]));
4683
4684 if (coverage != NULL)
4685 {
4686 uc = (cjk_langs[i][0] == 'k') ? 0xAC00 : 0x4E00;
4687 is_cjk = (pango_coverage_get(coverage, uc) == PANGO_COVERAGE_EXACT);
4688 pango_coverage_unref(coverage);
4689 }
4690 }
4691
4692 g_object_unref(font);
4693
4694 return is_cjk;
4695}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004696
Bram Moolenaar231334e2005-07-25 20:46:57 +00004697/*
4698 * Adjust gui.char_height (after 'linespace' was changed).
4699 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700 int
Bram Moolenaar231334e2005-07-25 20:46:57 +00004701gui_mch_adjust_charheight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004702{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703 PangoFontMetrics *metrics;
4704 int ascent;
4705 int descent;
4706
4707 metrics = pango_context_get_metrics(gui.text_context, gui.norm_font,
4708 pango_context_get_language(gui.text_context));
4709 ascent = pango_font_metrics_get_ascent(metrics);
4710 descent = pango_font_metrics_get_descent(metrics);
4711
4712 pango_font_metrics_unref(metrics);
4713
Bram Moolenaar5cd1cb92020-05-31 13:53:04 +02004714 // Round up, but not when the value is very close (e.g. 15.0009).
4715 gui.char_height = (ascent + descent + PANGO_SCALE - 3) / PANGO_SCALE
Bram Moolenaar231334e2005-07-25 20:46:57 +00004716 + p_linespace;
Bram Moolenaar30613902019-12-01 22:11:18 +01004717 // LINTED: avoid warning: bitwise operation on signed value
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718 gui.char_ascent = PANGO_PIXELS(ascent + p_linespace * PANGO_SCALE / 2);
4719
Bram Moolenaar30613902019-12-01 22:11:18 +01004720 // A not-positive value of char_height may crash Vim. Only happens
4721 // if 'linespace' is negative (which does make sense sometimes).
Bram Moolenaar071d4272004-06-13 20:20:40 +00004722 gui.char_ascent = MAX(gui.char_ascent, 0);
4723 gui.char_height = MAX(gui.char_height, gui.char_ascent + 1);
4724
4725 return OK;
4726}
4727
Bram Moolenaar98921892016-02-23 17:14:37 +01004728#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar30613902019-12-01 22:11:18 +01004729// Callback function used in gui_mch_font_dialog()
Bram Moolenaar98921892016-02-23 17:14:37 +01004730 static gboolean
4731font_filter(const PangoFontFamily *family,
4732 const PangoFontFace *face UNUSED,
4733 gpointer data UNUSED)
4734{
4735 return pango_font_family_is_monospace((PangoFontFamily *)family);
4736}
4737#endif
4738
Bram Moolenaar071d4272004-06-13 20:20:40 +00004739/*
4740 * Put up a font dialog and return the selected font name in allocated memory.
4741 * "oldval" is the previous value. Return NULL when cancelled.
4742 * This should probably go into gui_gtk.c. Hmm.
4743 * FIXME:
4744 * The GTK2 font selection dialog has no filtering API. So we could either
4745 * a) implement our own (possibly copying the code from somewhere else) or
4746 * b) just live with it.
4747 */
4748 char_u *
4749gui_mch_font_dialog(char_u *oldval)
4750{
4751 GtkWidget *dialog;
4752 int response;
4753 char_u *fontname = NULL;
4754 char_u *oldname;
4755
Bram Moolenaar98921892016-02-23 17:14:37 +01004756#if GTK_CHECK_VERSION(3,2,0)
4757 dialog = gtk_font_chooser_dialog_new(NULL, NULL);
4758 gtk_font_chooser_set_filter_func(GTK_FONT_CHOOSER(dialog), font_filter,
4759 NULL, NULL);
4760#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761 dialog = gtk_font_selection_dialog_new(NULL);
Bram Moolenaar98921892016-02-23 17:14:37 +01004762#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004763
4764 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(gui.mainwin));
4765 gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
4766
4767 if (oldval != NULL && oldval[0] != NUL)
4768 {
4769 if (output_conv.vc_type != CONV_NONE)
4770 oldname = string_convert(&output_conv, oldval, NULL);
4771 else
4772 oldname = oldval;
4773
Bram Moolenaar30613902019-12-01 22:11:18 +01004774 // Annoying bug in GTK (or Pango): if the font name does not include a
4775 // size, zero is used. Use default point size ten.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004776 if (!vim_isdigit(oldname[STRLEN(oldname) - 1]))
4777 {
4778 char_u *p = vim_strnsave(oldname, STRLEN(oldname) + 3);
4779
4780 if (p != NULL)
4781 {
4782 STRCPY(p + STRLEN(p), " 10");
4783 if (oldname != oldval)
4784 vim_free(oldname);
4785 oldname = p;
4786 }
4787 }
4788
Bram Moolenaar98921892016-02-23 17:14:37 +01004789#if GTK_CHECK_VERSION(3,2,0)
4790 gtk_font_chooser_set_font(
4791 GTK_FONT_CHOOSER(dialog), (const gchar *)oldname);
4792#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793 gtk_font_selection_dialog_set_font_name(
4794 GTK_FONT_SELECTION_DIALOG(dialog), (const char *)oldname);
Bram Moolenaar98921892016-02-23 17:14:37 +01004795#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796
4797 if (oldname != oldval)
Bram Moolenaar8c711452005-01-14 21:53:12 +00004798 vim_free(oldname);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 }
Bram Moolenaarbef9d832009-09-11 13:46:41 +00004800 else
Bram Moolenaar98921892016-02-23 17:14:37 +01004801#if GTK_CHECK_VERSION(3,2,0)
4802 gtk_font_chooser_set_font(
4803 GTK_FONT_CHOOSER(dialog), DEFAULT_FONT);
4804#else
Bram Moolenaarbef9d832009-09-11 13:46:41 +00004805 gtk_font_selection_dialog_set_font_name(
4806 GTK_FONT_SELECTION_DIALOG(dialog), DEFAULT_FONT);
Bram Moolenaar98921892016-02-23 17:14:37 +01004807#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004808
4809 response = gtk_dialog_run(GTK_DIALOG(dialog));
4810
4811 if (response == GTK_RESPONSE_OK)
4812 {
4813 char *name;
4814
Bram Moolenaar98921892016-02-23 17:14:37 +01004815#if GTK_CHECK_VERSION(3,2,0)
4816 name = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
4817#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818 name = gtk_font_selection_dialog_get_font_name(
4819 GTK_FONT_SELECTION_DIALOG(dialog));
Bram Moolenaar98921892016-02-23 17:14:37 +01004820#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00004821 if (name != NULL)
4822 {
Bram Moolenaar362e1a32006-03-06 23:29:24 +00004823 char_u *p;
4824
Bram Moolenaar30613902019-12-01 22:11:18 +01004825 // Apparently some font names include a comma, need to escape
4826 // that, because in 'guifont' it separates names.
Bram Moolenaar362e1a32006-03-06 23:29:24 +00004827 p = vim_strsave_escaped((char_u *)name, (char_u *)",");
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828 g_free(name);
Bram Moolenaareb3593b2006-04-22 22:33:57 +00004829 if (p != NULL && input_conv.vc_type != CONV_NONE)
Bram Moolenaar362e1a32006-03-06 23:29:24 +00004830 {
4831 fontname = string_convert(&input_conv, p, NULL);
4832 vim_free(p);
4833 }
4834 else
4835 fontname = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004836 }
4837 }
4838
4839 if (response != GTK_RESPONSE_NONE)
4840 gtk_widget_destroy(dialog);
4841
4842 return fontname;
4843}
4844
4845/*
4846 * Some monospace fonts don't support a bold weight, and fall back
4847 * silently to the regular weight. But this is no good since our text
4848 * drawing function can emulate bold by overstriking. So let's try
4849 * to detect whether bold weight is actually available and emulate it
4850 * otherwise.
4851 *
4852 * Note that we don't need to check for italic style since Xft can
4853 * emulate italic on its own, provided you have a proper fontconfig
4854 * setup. We wouldn't be able to emulate it in Vim anyway.
4855 */
4856 static void
4857get_styled_font_variants(void)
4858{
4859 PangoFontDescription *bold_font_desc;
4860 PangoFont *plain_font;
4861 PangoFont *bold_font;
4862
4863 gui.font_can_bold = FALSE;
4864
4865 plain_font = pango_context_load_font(gui.text_context, gui.norm_font);
4866
4867 if (plain_font == NULL)
4868 return;
4869
4870 bold_font_desc = pango_font_description_copy_static(gui.norm_font);
4871 pango_font_description_set_weight(bold_font_desc, PANGO_WEIGHT_BOLD);
4872
4873 bold_font = pango_context_load_font(gui.text_context, bold_font_desc);
4874 /*
4875 * The comparison relies on the unique handle nature of a PangoFont*,
4876 * i.e. it's assumed that a different PangoFont* won't refer to the
4877 * same font. Seems to work, and failing here isn't critical anyway.
4878 */
4879 if (bold_font != NULL)
4880 {
4881 gui.font_can_bold = (bold_font != plain_font);
4882 g_object_unref(bold_font);
4883 }
4884
4885 pango_font_description_free(bold_font_desc);
4886 g_object_unref(plain_font);
4887}
4888
Bram Moolenaar071d4272004-06-13 20:20:40 +00004889static PangoEngineShape *default_shape_engine = NULL;
4890
4891/*
4892 * Create a map from ASCII characters in the range [32,126] to glyphs
4893 * of the current font. This is used by gui_gtk2_draw_string() to skip
4894 * the itemize and shaping process for the most common case.
4895 */
4896 static void
4897ascii_glyph_table_init(void)
4898{
Bram Moolenaar16350cb2016-08-14 20:27:34 +02004899 char_u ascii_chars[2 * 128];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004900 PangoAttrList *attr_list;
4901 GList *item_list;
4902 int i;
4903
4904 if (gui.ascii_glyphs != NULL)
4905 pango_glyph_string_free(gui.ascii_glyphs);
4906 if (gui.ascii_font != NULL)
4907 g_object_unref(gui.ascii_font);
4908
4909 gui.ascii_glyphs = NULL;
4910 gui.ascii_font = NULL;
4911
Bram Moolenaar30613902019-12-01 22:11:18 +01004912 // For safety, fill in question marks for the control characters.
4913 // Put a space between characters to avoid shaping.
Bram Moolenaar16350cb2016-08-14 20:27:34 +02004914 for (i = 0; i < 128; ++i)
4915 {
4916 if (i >= 32 && i < 127)
4917 ascii_chars[2 * i] = i;
4918 else
4919 ascii_chars[2 * i] = '?';
4920 ascii_chars[2 * i + 1] = ' ';
4921 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004922
4923 attr_list = pango_attr_list_new();
4924 item_list = pango_itemize(gui.text_context, (const char *)ascii_chars,
4925 0, sizeof(ascii_chars), attr_list, NULL);
4926
Bram Moolenaar30613902019-12-01 22:11:18 +01004927 if (item_list != NULL && item_list->next == NULL) // play safe
Bram Moolenaar071d4272004-06-13 20:20:40 +00004928 {
4929 PangoItem *item;
4930 int width;
4931
4932 item = (PangoItem *)item_list->data;
4933 width = gui.char_width * PANGO_SCALE;
4934
Bram Moolenaar30613902019-12-01 22:11:18 +01004935 // Remember the shape engine used for ASCII.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004936 default_shape_engine = item->analysis.shape_engine;
4937
4938 gui.ascii_font = item->analysis.font;
4939 g_object_ref(gui.ascii_font);
4940
4941 gui.ascii_glyphs = pango_glyph_string_new();
4942
4943 pango_shape((const char *)ascii_chars, sizeof(ascii_chars),
4944 &item->analysis, gui.ascii_glyphs);
4945
4946 g_return_if_fail(gui.ascii_glyphs->num_glyphs == sizeof(ascii_chars));
4947
4948 for (i = 0; i < gui.ascii_glyphs->num_glyphs; ++i)
4949 {
4950 PangoGlyphGeometry *geom;
4951
4952 geom = &gui.ascii_glyphs->glyphs[i].geometry;
4953 geom->x_offset += MAX(0, width - geom->width) / 2;
4954 geom->width = width;
4955 }
4956 }
4957
4958 g_list_foreach(item_list, (GFunc)&pango_item_free, NULL);
4959 g_list_free(item_list);
4960 pango_attr_list_unref(attr_list);
4961}
Bram Moolenaar071d4272004-06-13 20:20:40 +00004962
4963/*
4964 * Initialize Vim to use the font or fontset with the given name.
4965 * Return FAIL if the font could not be loaded, OK otherwise.
4966 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004967 int
Bram Moolenaarb85cb212009-05-17 14:24:23 +00004968gui_mch_init_font(char_u *font_name, int fontset UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004969{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004970 PangoFontDescription *font_desc;
4971 PangoLayout *layout;
4972 int width;
4973
Bram Moolenaar30613902019-12-01 22:11:18 +01004974 // If font_name is NULL, this means to use the default, which should
4975 // be present on all proper Pango/fontconfig installations.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004976 if (font_name == NULL)
4977 font_name = (char_u *)DEFAULT_FONT;
4978
4979 font_desc = gui_mch_get_font(font_name, FALSE);
4980
4981 if (font_desc == NULL)
4982 return FAIL;
4983
4984 gui_mch_free_font(gui.norm_font);
4985 gui.norm_font = font_desc;
4986
4987 pango_context_set_font_description(gui.text_context, font_desc);
4988
4989 layout = pango_layout_new(gui.text_context);
4990 pango_layout_set_text(layout, "MW", 2);
4991 pango_layout_get_size(layout, &width, NULL);
4992 /*
4993 * Set char_width to half the width obtained from pango_layout_get_size()
4994 * for CJK fixed_width/bi-width fonts. An unpatched version of Xft leads
4995 * Pango to use the same width for both non-CJK characters (e.g. Latin
4996 * letters and numbers) and CJK characters. This results in 's p a c e d
4997 * o u t' rendering when a CJK 'fixed width' font is used. To work around
4998 * that, divide the width returned by Pango by 2 if cjk_width is equal to
4999 * width for CJK fonts.
5000 *
5001 * For related bugs, see:
5002 * http://bugzilla.gnome.org/show_bug.cgi?id=106618
5003 * http://bugzilla.gnome.org/show_bug.cgi?id=106624
5004 *
5005 * With this, for all four of the following cases, Vim works fine:
5006 * guifont=CJK_fixed_width_font
5007 * guifont=Non_CJK_fixed_font
5008 * guifont=Non_CJK_fixed_font,CJK_Fixed_font
5009 * guifont=Non_CJK_fixed_font guifontwide=CJK_fixed_font
5010 */
5011 if (is_cjk_font(gui.norm_font))
5012 {
5013 int cjk_width;
5014
Bram Moolenaar30613902019-12-01 22:11:18 +01005015 // Measure the text extent of U+4E00 and U+4E8C
Bram Moolenaar071d4272004-06-13 20:20:40 +00005016 pango_layout_set_text(layout, "\344\270\200\344\272\214", -1);
5017 pango_layout_get_size(layout, &cjk_width, NULL);
5018
Bram Moolenaar30613902019-12-01 22:11:18 +01005019 if (width == cjk_width) // Xft not patched
Bram Moolenaar071d4272004-06-13 20:20:40 +00005020 width /= 2;
5021 }
5022 g_object_unref(layout);
5023
5024 gui.char_width = (width / 2 + PANGO_SCALE - 1) / PANGO_SCALE;
5025
Bram Moolenaar30613902019-12-01 22:11:18 +01005026 // A zero width may cause a crash. Happens for semi-invalid fontsets.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005027 if (gui.char_width <= 0)
5028 gui.char_width = 8;
5029
Bram Moolenaar231334e2005-07-25 20:46:57 +00005030 gui_mch_adjust_charheight();
Bram Moolenaar071d4272004-06-13 20:20:40 +00005031
Bram Moolenaar30613902019-12-01 22:11:18 +01005032 // Set the fontname, which will be used for information purposes
Bram Moolenaar071d4272004-06-13 20:20:40 +00005033 hl_set_font_name(font_name);
5034
5035 get_styled_font_variants();
5036 ascii_glyph_table_init();
5037
Bram Moolenaar30613902019-12-01 22:11:18 +01005038 // Avoid unnecessary overhead if 'guifontwide' is equal to 'guifont'.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005039 if (gui.wide_font != NULL
5040 && pango_font_description_equal(gui.norm_font, gui.wide_font))
5041 {
5042 pango_font_description_free(gui.wide_font);
5043 gui.wide_font = NULL;
5044 }
5045
Bram Moolenaare161c792009-11-03 17:13:59 +00005046 if (gui_mch_maximized())
5047 {
Bram Moolenaar30613902019-12-01 22:11:18 +01005048 // Update lines and columns in accordance with the new font, keep the
5049 // window maximized.
Bram Moolenaar12bc1b52011-08-10 17:44:45 +02005050 gui_mch_newfont();
Bram Moolenaare161c792009-11-03 17:13:59 +00005051 }
5052 else
Bram Moolenaare161c792009-11-03 17:13:59 +00005053 {
Bram Moolenaar30613902019-12-01 22:11:18 +01005054 // Preserve the logical dimensions of the screen.
Bram Moolenaare161c792009-11-03 17:13:59 +00005055 update_window_manager_hints(0, 0);
5056 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005057
5058 return OK;
5059}
5060
5061/*
5062 * Get a reference to the font "name".
5063 * Return zero for failure.
5064 */
5065 GuiFont
5066gui_mch_get_font(char_u *name, int report_error)
5067{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005068 PangoFontDescription *font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005069
Bram Moolenaar30613902019-12-01 22:11:18 +01005070 // can't do this when GUI is not running
Bram Moolenaar071d4272004-06-13 20:20:40 +00005071 if (!gui.in_use || name == NULL)
5072 return NULL;
5073
Bram Moolenaar071d4272004-06-13 20:20:40 +00005074 if (output_conv.vc_type != CONV_NONE)
5075 {
5076 char_u *buf;
5077
5078 buf = string_convert(&output_conv, name, NULL);
5079 if (buf != NULL)
5080 {
5081 font = pango_font_description_from_string((const char *)buf);
5082 vim_free(buf);
5083 }
5084 else
5085 font = NULL;
5086 }
5087 else
5088 font = pango_font_description_from_string((const char *)name);
5089
5090 if (font != NULL)
5091 {
5092 PangoFont *real_font;
5093
Bram Moolenaar30613902019-12-01 22:11:18 +01005094 // pango_context_load_font() bails out if no font size is set
Bram Moolenaar071d4272004-06-13 20:20:40 +00005095 if (pango_font_description_get_size(font) <= 0)
5096 pango_font_description_set_size(font, 10 * PANGO_SCALE);
5097
5098 real_font = pango_context_load_font(gui.text_context, font);
5099
5100 if (real_font == NULL)
5101 {
5102 pango_font_description_free(font);
5103 font = NULL;
5104 }
5105 else
5106 g_object_unref(real_font);
5107 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005108
5109 if (font == NULL)
5110 {
5111 if (report_error)
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01005112 semsg(_((char *)e_font), name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005113 return NULL;
5114 }
5115
Bram Moolenaar071d4272004-06-13 20:20:40 +00005116 return font;
5117}
5118
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005119#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005120/*
5121 * Return the name of font "font" in allocated memory.
5122 */
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005123 char_u *
Bram Moolenaarb85cb212009-05-17 14:24:23 +00005124gui_mch_get_fontname(GuiFont font, char_u *name UNUSED)
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005125{
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005126 if (font != NOFONT)
5127 {
Bram Moolenaar89d40322006-08-29 15:30:07 +00005128 char *pangoname = pango_font_description_to_string(font);
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005129
Bram Moolenaar89d40322006-08-29 15:30:07 +00005130 if (pangoname != NULL)
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005131 {
Bram Moolenaar89d40322006-08-29 15:30:07 +00005132 char_u *s = vim_strsave((char_u *)pangoname);
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005133
Bram Moolenaar89d40322006-08-29 15:30:07 +00005134 g_free(pangoname);
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005135 return s;
5136 }
5137 }
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005138 return NULL;
5139}
Bram Moolenaar1cd871b2004-12-19 22:46:22 +00005140#endif
Bram Moolenaar46c9c732004-12-12 11:37:09 +00005141
Bram Moolenaar071d4272004-06-13 20:20:40 +00005142/*
5143 * If a font is not going to be used, free its structure.
5144 */
5145 void
5146gui_mch_free_font(GuiFont font)
5147{
5148 if (font != NOFONT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005149 pango_font_description_free(font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005150}
5151
Bram Moolenaar071d4272004-06-13 20:20:40 +00005152/*
Bram Moolenaar36edf062016-07-21 22:10:12 +02005153 * Return the Pixel value (color) for the given color name.
5154 *
Bram Moolenaar071d4272004-06-13 20:20:40 +00005155 * Return INVALCOLOR for error.
5156 */
5157 guicolor_T
5158gui_mch_get_color(char_u *name)
5159{
Bram Moolenaar2e324952018-04-14 14:37:07 +02005160 guicolor_T color = INVALCOLOR;
5161
Bram Moolenaar30613902019-12-01 22:11:18 +01005162 if (!gui.in_use) // can't do this when GUI not running
Bram Moolenaar2e324952018-04-14 14:37:07 +02005163 return color;
5164
5165 if (name != NULL)
5166 color = gui_get_color_cmn(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005167
Bram Moolenaar98921892016-02-23 17:14:37 +01005168#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar2e324952018-04-14 14:37:07 +02005169 return color;
Bram Moolenaar98921892016-02-23 17:14:37 +01005170#else
Bram Moolenaar36edf062016-07-21 22:10:12 +02005171 if (color == INVALCOLOR)
5172 return INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005173
Bram Moolenaar26af85d2017-07-23 16:45:10 +02005174 return gui_mch_get_rgb_color(
5175 (color & 0xff0000) >> 16,
5176 (color & 0xff00) >> 8,
5177 color & 0xff);
5178#endif
5179}
5180
5181/*
5182 * Return the Pixel value (color) for the given RGB values.
5183 * Return INVALCOLOR for error.
5184 */
5185 guicolor_T
5186gui_mch_get_rgb_color(int r, int g, int b)
5187{
5188#if GTK_CHECK_VERSION(3,0,0)
5189 return gui_get_rgb_color_cmn(r, g, b);
5190#else
5191 GdkColor gcolor;
5192 int ret;
5193
5194 gcolor.red = (guint16)(r / 255.0 * 65535 + 0.5);
5195 gcolor.green = (guint16)(g / 255.0 * 65535 + 0.5);
5196 gcolor.blue = (guint16)(b / 255.0 * 65535 + 0.5);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005197
Bram Moolenaar36edf062016-07-21 22:10:12 +02005198 ret = gdk_colormap_alloc_color(gtk_widget_get_colormap(gui.drawarea),
5199 &gcolor, FALSE, TRUE);
5200
5201 return ret != 0 ? (guicolor_T)gcolor.pixel : INVALCOLOR;
5202#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005203}
5204
5205/*
5206 * Set the current text foreground color.
5207 */
5208 void
5209gui_mch_set_fg_color(guicolor_T color)
5210{
Bram Moolenaar36edf062016-07-21 22:10:12 +02005211#if GTK_CHECK_VERSION(3,0,0)
5212 *gui.fgcolor = color_to_rgba(color);
5213#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005214 gui.fgcolor->pixel = (unsigned long)color;
Bram Moolenaar36edf062016-07-21 22:10:12 +02005215#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005216}
5217
5218/*
5219 * Set the current text background color.
5220 */
5221 void
5222gui_mch_set_bg_color(guicolor_T color)
5223{
Bram Moolenaar36edf062016-07-21 22:10:12 +02005224#if GTK_CHECK_VERSION(3,0,0)
5225 *gui.bgcolor = color_to_rgba(color);
5226#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005227 gui.bgcolor->pixel = (unsigned long)color;
Bram Moolenaar36edf062016-07-21 22:10:12 +02005228#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005229}
5230
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005231/*
5232 * Set the current text special color.
5233 */
5234 void
5235gui_mch_set_sp_color(guicolor_T color)
5236{
Bram Moolenaar36edf062016-07-21 22:10:12 +02005237#if GTK_CHECK_VERSION(3,0,0)
5238 *gui.spcolor = color_to_rgba(color);
5239#else
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005240 gui.spcolor->pixel = (unsigned long)color;
Bram Moolenaar36edf062016-07-21 22:10:12 +02005241#endif
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005242}
5243
Bram Moolenaar071d4272004-06-13 20:20:40 +00005244/*
5245 * Function-like convenience macro for the sake of efficiency.
5246 */
5247#define INSERT_PANGO_ATTR(Attribute, AttrList, Start, End) \
5248 G_STMT_START{ \
5249 PangoAttribute *tmp_attr_; \
5250 tmp_attr_ = (Attribute); \
5251 tmp_attr_->start_index = (Start); \
5252 tmp_attr_->end_index = (End); \
5253 pango_attr_list_insert((AttrList), tmp_attr_); \
5254 }G_STMT_END
5255
5256 static void
5257apply_wide_font_attr(char_u *s, int len, PangoAttrList *attr_list)
5258{
5259 char_u *start = NULL;
5260 char_u *p;
5261 int uc;
5262
5263 for (p = s; p < s + len; p += utf_byte2len(*p))
5264 {
5265 uc = utf_ptr2char(p);
5266
5267 if (start == NULL)
5268 {
5269 if (uc >= 0x80 && utf_char2cells(uc) == 2)
5270 start = p;
5271 }
Bram Moolenaar30613902019-12-01 22:11:18 +01005272 else if (uc < 0x80 // optimization shortcut
Bram Moolenaar071d4272004-06-13 20:20:40 +00005273 || (utf_char2cells(uc) != 2 && !utf_iscomposing(uc)))
5274 {
5275 INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font),
5276 attr_list, start - s, p - s);
5277 start = NULL;
5278 }
5279 }
5280
5281 if (start != NULL)
5282 INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font),
5283 attr_list, start - s, len);
5284}
5285
5286 static int
5287count_cluster_cells(char_u *s, PangoItem *item,
5288 PangoGlyphString* glyphs, int i,
5289 int *cluster_width,
5290 int *last_glyph_rbearing)
5291{
5292 char_u *p;
Bram Moolenaar30613902019-12-01 22:11:18 +01005293 int next; // glyph start index of next cluster
5294 int start, end; // string segment of current cluster
5295 int width; // real cluster width in Pango units
Bram Moolenaar071d4272004-06-13 20:20:40 +00005296 int uc;
5297 int cellcount = 0;
5298
5299 width = glyphs->glyphs[i].geometry.width;
5300
5301 for (next = i + 1; next < glyphs->num_glyphs; ++next)
5302 {
5303 if (glyphs->glyphs[next].attr.is_cluster_start)
5304 break;
5305 else if (glyphs->glyphs[next].geometry.width > width)
5306 width = glyphs->glyphs[next].geometry.width;
5307 }
5308
5309 start = item->offset + glyphs->log_clusters[i];
5310 end = item->offset + ((next < glyphs->num_glyphs) ?
5311 glyphs->log_clusters[next] : item->length);
5312
5313 for (p = s + start; p < s + end; p += utf_byte2len(*p))
5314 {
5315 uc = utf_ptr2char(p);
5316 if (uc < 0x80)
5317 ++cellcount;
5318 else if (!utf_iscomposing(uc))
5319 cellcount += utf_char2cells(uc);
5320 }
5321
5322 if (last_glyph_rbearing != NULL
5323 && cellcount > 0 && next == glyphs->num_glyphs)
5324 {
5325 PangoRectangle ink_rect;
5326 /*
5327 * If a certain combining mark had to be taken from a non-monospace
5328 * font, we have to compensate manually by adapting x_offset according
5329 * to the ink extents of the previous glyph.
5330 */
5331 pango_font_get_glyph_extents(item->analysis.font,
5332 glyphs->glyphs[i].glyph,
5333 &ink_rect, NULL);
5334
5335 if (PANGO_RBEARING(ink_rect) > 0)
5336 *last_glyph_rbearing = PANGO_RBEARING(ink_rect);
5337 }
5338
5339 if (cellcount > 0)
5340 *cluster_width = width;
5341
5342 return cellcount;
5343}
5344
5345/*
5346 * If there are only combining characters in the cluster, we cannot just
5347 * change the width of the previous glyph since there is none. Therefore
5348 * some guesswork is needed.
5349 *
5350 * If ink_rect.x is negative Pango apparently has taken care of the composing
5351 * by itself. Actually setting x_offset = 0 should be sufficient then, but due
5352 * to problems with composing from different fonts we still need to fine-tune
Bram Moolenaar64404472010-06-26 06:24:45 +02005353 * x_offset to avoid ugliness.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005354 *
5355 * If ink_rect.x is not negative, force overstriking by pointing x_offset to
5356 * the position of the previous glyph. Apparently this happens only with old
5357 * X fonts which don't provide the special combining information needed by
5358 * Pango.
5359 */
5360 static void
5361setup_zero_width_cluster(PangoItem *item, PangoGlyphInfo *glyph,
5362 int last_cellcount, int last_cluster_width,
5363 int last_glyph_rbearing)
5364{
5365 PangoRectangle ink_rect;
5366 PangoRectangle logical_rect;
5367 int width;
5368
5369 width = last_cellcount * gui.char_width * PANGO_SCALE;
5370 glyph->geometry.x_offset = -width + MAX(0, width - last_cluster_width) / 2;
5371 glyph->geometry.width = 0;
5372
5373 pango_font_get_glyph_extents(item->analysis.font,
5374 glyph->glyph,
5375 &ink_rect, &logical_rect);
5376 if (ink_rect.x < 0)
5377 {
5378 glyph->geometry.x_offset += last_glyph_rbearing;
5379 glyph->geometry.y_offset = logical_rect.height
5380 - (gui.char_height - p_linespace) * PANGO_SCALE;
5381 }
Bram Moolenaar706e84b2010-08-07 15:46:45 +02005382 else
Bram Moolenaar30613902019-12-01 22:11:18 +01005383 // If the accent width is smaller than the cluster width, position it
5384 // in the middle.
Bram Moolenaar706e84b2010-08-07 15:46:45 +02005385 glyph->geometry.x_offset = -width + MAX(0, width - ink_rect.width) / 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005386}
5387
Bram Moolenaar98921892016-02-23 17:14:37 +01005388#if GTK_CHECK_VERSION(3,0,0)
5389 static void
5390draw_glyph_string(int row, int col, int num_cells, int flags,
5391 PangoFont *font, PangoGlyphString *glyphs,
5392 cairo_t *cr)
5393#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005394 static void
5395draw_glyph_string(int row, int col, int num_cells, int flags,
5396 PangoFont *font, PangoGlyphString *glyphs)
Bram Moolenaar98921892016-02-23 17:14:37 +01005397#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005398{
5399 if (!(flags & DRAW_TRANSP))
5400 {
Bram Moolenaar98921892016-02-23 17:14:37 +01005401#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar36edf062016-07-21 22:10:12 +02005402 cairo_set_source_rgba(cr,
5403 gui.bgcolor->red, gui.bgcolor->green, gui.bgcolor->blue,
5404 gui.bgcolor->alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01005405 cairo_rectangle(cr,
5406 FILL_X(col), FILL_Y(row),
5407 num_cells * gui.char_width, gui.char_height);
5408 cairo_fill(cr);
5409#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005410 gdk_gc_set_foreground(gui.text_gc, gui.bgcolor);
5411
5412 gdk_draw_rectangle(gui.drawarea->window,
5413 gui.text_gc,
5414 TRUE,
5415 FILL_X(col),
5416 FILL_Y(row),
5417 num_cells * gui.char_width,
5418 gui.char_height);
Bram Moolenaar98921892016-02-23 17:14:37 +01005419#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005420 }
5421
Bram Moolenaar98921892016-02-23 17:14:37 +01005422#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar36edf062016-07-21 22:10:12 +02005423 cairo_set_source_rgba(cr,
5424 gui.fgcolor->red, gui.fgcolor->green, gui.fgcolor->blue,
5425 gui.fgcolor->alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01005426 cairo_move_to(cr, TEXT_X(col), TEXT_Y(row));
5427 pango_cairo_show_glyph_string(cr, font, glyphs);
5428#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005429 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
5430
5431 gdk_draw_glyphs(gui.drawarea->window,
5432 gui.text_gc,
5433 font,
5434 TEXT_X(col),
5435 TEXT_Y(row),
5436 glyphs);
Bram Moolenaar98921892016-02-23 17:14:37 +01005437#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005438
Bram Moolenaar30613902019-12-01 22:11:18 +01005439 // redraw the contents with an offset of 1 to emulate bold
Bram Moolenaar071d4272004-06-13 20:20:40 +00005440 if ((flags & DRAW_BOLD) && !gui.font_can_bold)
Bram Moolenaar98921892016-02-23 17:14:37 +01005441#if GTK_CHECK_VERSION(3,0,0)
5442 {
Bram Moolenaar36edf062016-07-21 22:10:12 +02005443 cairo_set_source_rgba(cr,
5444 gui.fgcolor->red, gui.fgcolor->green, gui.fgcolor->blue,
5445 gui.fgcolor->alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01005446 cairo_move_to(cr, TEXT_X(col) + 1, TEXT_Y(row));
5447 pango_cairo_show_glyph_string(cr, font, glyphs);
5448 }
5449#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005450 gdk_draw_glyphs(gui.drawarea->window,
5451 gui.text_gc,
5452 font,
5453 TEXT_X(col) + 1,
5454 TEXT_Y(row),
5455 glyphs);
Bram Moolenaar98921892016-02-23 17:14:37 +01005456#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005457}
5458
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005459/*
5460 * Draw underline and undercurl at the bottom of the character cell.
5461 */
Bram Moolenaar98921892016-02-23 17:14:37 +01005462#if GTK_CHECK_VERSION(3,0,0)
5463 static void
5464draw_under(int flags, int row, int col, int cells, cairo_t *cr)
5465#else
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005466 static void
5467draw_under(int flags, int row, int col, int cells)
Bram Moolenaar98921892016-02-23 17:14:37 +01005468#endif
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005469{
5470 int i;
5471 int offset;
Bram Moolenaarb85cb212009-05-17 14:24:23 +00005472 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005473 int y = FILL_Y(row + 1) - 1;
5474
Bram Moolenaar30613902019-12-01 22:11:18 +01005475 // Undercurl: draw curl at the bottom of the character cell.
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005476 if (flags & DRAW_UNDERC)
5477 {
Bram Moolenaar98921892016-02-23 17:14:37 +01005478#if GTK_CHECK_VERSION(3,0,0)
5479 cairo_set_line_width(cr, 1.0);
5480 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
Bram Moolenaar36edf062016-07-21 22:10:12 +02005481 cairo_set_source_rgba(cr,
5482 gui.spcolor->red, gui.spcolor->green, gui.spcolor->blue,
5483 gui.spcolor->alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01005484 for (i = FILL_X(col); i < FILL_X(col + cells); ++i)
5485 {
5486 offset = val[i % 8];
5487 cairo_line_to(cr, i, y - offset + 0.5);
5488 }
5489 cairo_stroke(cr);
5490#else
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005491 gdk_gc_set_foreground(gui.text_gc, gui.spcolor);
5492 for (i = FILL_X(col); i < FILL_X(col + cells); ++i)
5493 {
5494 offset = val[i % 8];
5495 gdk_draw_point(gui.drawarea->window, gui.text_gc, i, y - offset);
5496 }
5497 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
Bram Moolenaar98921892016-02-23 17:14:37 +01005498#endif
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005499 }
5500
Bram Moolenaar30613902019-12-01 22:11:18 +01005501 // Draw a strikethrough line
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02005502 if (flags & DRAW_STRIKE)
5503 {
5504#if GTK_CHECK_VERSION(3,0,0)
5505 cairo_set_line_width(cr, 1.0);
5506 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
5507 cairo_set_source_rgba(cr,
5508 gui.spcolor->red, gui.spcolor->green, gui.spcolor->blue,
5509 gui.spcolor->alpha);
5510 cairo_move_to(cr, FILL_X(col), y + 1 - gui.char_height/2 + 0.5);
5511 cairo_line_to(cr, FILL_X(col + cells), y + 1 - gui.char_height/2 + 0.5);
5512 cairo_stroke(cr);
5513#else
5514 gdk_gc_set_foreground(gui.text_gc, gui.spcolor);
5515 gdk_draw_line(gui.drawarea->window, gui.text_gc,
5516 FILL_X(col), y + 1 - gui.char_height/2,
5517 FILL_X(col + cells), y + 1 - gui.char_height/2);
5518 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
5519#endif
5520 }
5521
Bram Moolenaar30613902019-12-01 22:11:18 +01005522 // Underline: draw a line at the bottom of the character cell.
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005523 if (flags & DRAW_UNDERL)
5524 {
Bram Moolenaar30613902019-12-01 22:11:18 +01005525 // When p_linespace is 0, overwrite the bottom row of pixels.
5526 // Otherwise put the line just below the character.
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005527 if (p_linespace > 1)
5528 y -= p_linespace - 1;
Bram Moolenaar98921892016-02-23 17:14:37 +01005529#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaarcf4b00c2017-09-02 18:33:56 +02005530 cairo_set_line_width(cr, 1.0);
5531 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
5532 cairo_set_source_rgba(cr,
5533 gui.fgcolor->red, gui.fgcolor->green, gui.fgcolor->blue,
5534 gui.fgcolor->alpha);
5535 cairo_move_to(cr, FILL_X(col), y + 0.5);
5536 cairo_line_to(cr, FILL_X(col + cells), y + 0.5);
5537 cairo_stroke(cr);
Bram Moolenaar98921892016-02-23 17:14:37 +01005538#else
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005539 gdk_draw_line(gui.drawarea->window, gui.text_gc,
5540 FILL_X(col), y,
5541 FILL_X(col + cells) - 1, y);
Bram Moolenaar98921892016-02-23 17:14:37 +01005542#endif
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005543 }
5544}
5545
Bram Moolenaar071d4272004-06-13 20:20:40 +00005546 int
5547gui_gtk2_draw_string(int row, int col, char_u *s, int len, int flags)
5548{
Bram Moolenaar30613902019-12-01 22:11:18 +01005549 GdkRectangle area; // area for clip mask
5550 PangoGlyphString *glyphs; // glyphs of current item
5551 int column_offset = 0; // column offset in cells
Bram Moolenaar071d4272004-06-13 20:20:40 +00005552 int i;
Bram Moolenaar30613902019-12-01 22:11:18 +01005553 char_u *conv_buf = NULL; // result of UTF-8 conversion
Bram Moolenaar071d4272004-06-13 20:20:40 +00005554 char_u *new_conv_buf;
5555 int convlen;
5556 char_u *sp, *bp;
5557 int plen;
Bram Moolenaar98921892016-02-23 17:14:37 +01005558#if GTK_CHECK_VERSION(3,0,0)
5559 cairo_t *cr;
5560#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005561
Bram Moolenaar98921892016-02-23 17:14:37 +01005562 if (gui.text_context == NULL || gtk_widget_get_window(gui.drawarea) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005563 return len;
5564
5565 if (output_conv.vc_type != CONV_NONE)
5566 {
5567 /*
5568 * Convert characters from 'encoding' to 'termencoding', which is set
5569 * to UTF-8 by gui_mch_init(). did_set_string_option() in option.c
5570 * prohibits changing this to something else than UTF-8 if the GUI is
5571 * in use.
5572 */
5573 convlen = len;
5574 conv_buf = string_convert(&output_conv, s, &convlen);
5575 g_return_val_if_fail(conv_buf != NULL, len);
5576
Bram Moolenaar30613902019-12-01 22:11:18 +01005577 // Correct for differences in char width: some chars are
5578 // double-wide in 'encoding' but single-wide in utf-8. Add a space to
5579 // compensate for that.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005580 for (sp = s, bp = conv_buf; sp < s + len && bp < conv_buf + convlen; )
5581 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00005582 plen = utf_ptr2len(bp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005583 if ((*mb_ptr2cells)(sp) == 2 && utf_ptr2cells(bp) == 1)
5584 {
5585 new_conv_buf = alloc(convlen + 2);
5586 if (new_conv_buf == NULL)
5587 return len;
5588 plen += bp - conv_buf;
5589 mch_memmove(new_conv_buf, conv_buf, plen);
5590 new_conv_buf[plen] = ' ';
5591 mch_memmove(new_conv_buf + plen + 1, conv_buf + plen,
5592 convlen - plen + 1);
5593 vim_free(conv_buf);
5594 conv_buf = new_conv_buf;
5595 ++convlen;
5596 bp = conv_buf + plen;
5597 plen = 1;
5598 }
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00005599 sp += (*mb_ptr2len)(sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005600 bp += plen;
5601 }
5602 s = conv_buf;
5603 len = convlen;
5604 }
5605
5606 /*
5607 * Restrict all drawing to the current screen line in order to prevent
5608 * fuzzy font lookups from messing up the screen.
5609 */
5610 area.x = gui.border_offset;
5611 area.y = FILL_Y(row);
5612 area.width = gui.num_cols * gui.char_width;
5613 area.height = gui.char_height;
5614
Bram Moolenaar98921892016-02-23 17:14:37 +01005615#if GTK_CHECK_VERSION(3,0,0)
5616 cr = cairo_create(gui.surface);
5617 cairo_rectangle(cr, area.x, area.y, area.width, area.height);
5618 cairo_clip(cr);
5619#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005620 gdk_gc_set_clip_origin(gui.text_gc, 0, 0);
5621 gdk_gc_set_clip_rectangle(gui.text_gc, &area);
Bram Moolenaar98921892016-02-23 17:14:37 +01005622#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005623
5624 glyphs = pango_glyph_string_new();
5625
5626 /*
5627 * Optimization hack: If possible, skip the itemize and shaping process
5628 * for pure ASCII strings. This optimization is particularly effective
5629 * because Vim draws space characters to clear parts of the screen.
5630 */
5631 if (!(flags & DRAW_ITALIC)
5632 && !((flags & DRAW_BOLD) && gui.font_can_bold)
5633 && gui.ascii_glyphs != NULL)
5634 {
5635 char_u *p;
5636
5637 for (p = s; p < s + len; ++p)
5638 if (*p & 0x80)
5639 goto not_ascii;
5640
5641 pango_glyph_string_set_size(glyphs, len);
5642
5643 for (i = 0; i < len; ++i)
5644 {
Bram Moolenaar16350cb2016-08-14 20:27:34 +02005645 glyphs->glyphs[i] = gui.ascii_glyphs->glyphs[2 * s[i]];
Bram Moolenaar071d4272004-06-13 20:20:40 +00005646 glyphs->log_clusters[i] = i;
5647 }
5648
Bram Moolenaar98921892016-02-23 17:14:37 +01005649#if GTK_CHECK_VERSION(3,0,0)
5650 draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs, cr);
5651#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005652 draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs);
Bram Moolenaar98921892016-02-23 17:14:37 +01005653#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005654
5655 column_offset = len;
5656 }
5657 else
5658not_ascii:
5659 {
5660 PangoAttrList *attr_list;
5661 GList *item_list;
5662 int cluster_width;
5663 int last_glyph_rbearing;
Bram Moolenaar30613902019-12-01 22:11:18 +01005664 int cells = 0; // cells occupied by current cluster
Bram Moolenaar071d4272004-06-13 20:20:40 +00005665
Bram Moolenaar30613902019-12-01 22:11:18 +01005666 // Safety check: pango crashes when invoked with invalid utf-8
5667 // characters.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005668 if (!utf_valid_string(s, s + len))
5669 {
5670 column_offset = len;
5671 goto skipitall;
5672 }
5673
Bram Moolenaar30613902019-12-01 22:11:18 +01005674 // original width of the current cluster
Bram Moolenaar071d4272004-06-13 20:20:40 +00005675 cluster_width = PANGO_SCALE * gui.char_width;
5676
Bram Moolenaar30613902019-12-01 22:11:18 +01005677 // right bearing of the last non-composing glyph
Bram Moolenaar071d4272004-06-13 20:20:40 +00005678 last_glyph_rbearing = PANGO_SCALE * gui.char_width;
5679
5680 attr_list = pango_attr_list_new();
5681
Bram Moolenaar30613902019-12-01 22:11:18 +01005682 // If 'guifontwide' is set then use that for double-width characters.
5683 // Otherwise just go with 'guifont' and let Pango do its thing.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005684 if (gui.wide_font != NULL)
5685 apply_wide_font_attr(s, len, attr_list);
5686
5687 if ((flags & DRAW_BOLD) && gui.font_can_bold)
5688 INSERT_PANGO_ATTR(pango_attr_weight_new(PANGO_WEIGHT_BOLD),
5689 attr_list, 0, len);
5690 if (flags & DRAW_ITALIC)
5691 INSERT_PANGO_ATTR(pango_attr_style_new(PANGO_STYLE_ITALIC),
5692 attr_list, 0, len);
5693 /*
5694 * Break the text into segments with consistent directional level
5695 * and shaping engine. Pure Latin text needs only a single segment,
5696 * so there's no need to worry about the loop's efficiency. Better
5697 * try to optimize elsewhere, e.g. reducing exposes and stuff :)
5698 */
5699 item_list = pango_itemize(gui.text_context,
5700 (const char *)s, 0, len, attr_list, NULL);
5701
5702 while (item_list != NULL)
5703 {
5704 PangoItem *item;
Bram Moolenaar30613902019-12-01 22:11:18 +01005705 int item_cells = 0; // item length in cells
Bram Moolenaar071d4272004-06-13 20:20:40 +00005706
5707 item = (PangoItem *)item_list->data;
5708 item_list = g_list_delete_link(item_list, item_list);
5709 /*
5710 * Increment the bidirectional embedding level by 1 if it is not
5711 * even. An odd number means the output will be RTL, but we don't
5712 * want that since Vim handles right-to-left text on its own. It
5713 * would probably be sufficient to just set level = 0, but you can
5714 * never know :)
5715 *
5716 * Unfortunately we can't take advantage of Pango's ability to
5717 * render both LTR and RTL at the same time. In order to support
5718 * that, Vim's main screen engine would have to make use of Pango
5719 * functionality.
5720 */
5721 item->analysis.level = (item->analysis.level + 1) & (~1U);
5722
Bram Moolenaar30613902019-12-01 22:11:18 +01005723 // HACK: Overrule the shape engine, we don't want shaping to be
5724 // done, because drawing the cursor would change the display.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005725 item->analysis.shape_engine = default_shape_engine;
5726
Bram Moolenaar3cbe0c02015-09-08 20:00:22 +02005727#ifdef HAVE_PANGO_SHAPE_FULL
Bram Moolenaar7e2ec002015-09-08 16:31:06 +02005728 pango_shape_full((const char *)s + item->offset, item->length,
5729 (const char *)s, len, &item->analysis, glyphs);
Bram Moolenaar3cbe0c02015-09-08 20:00:22 +02005730#else
5731 pango_shape((const char *)s + item->offset, item->length,
5732 &item->analysis, glyphs);
5733#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005734 /*
5735 * Fixed-width hack: iterate over the array and assign a fixed
5736 * width to each glyph, thus overriding the choice made by the
5737 * shaping engine. We use utf_char2cells() to determine the
5738 * number of cells needed.
5739 *
5740 * Also perform all kind of dark magic to get composing
5741 * characters right (and pretty too of course).
5742 */
5743 for (i = 0; i < glyphs->num_glyphs; ++i)
5744 {
5745 PangoGlyphInfo *glyph;
5746
5747 glyph = &glyphs->glyphs[i];
5748
5749 if (glyph->attr.is_cluster_start)
5750 {
5751 int cellcount;
5752
5753 cellcount = count_cluster_cells(
5754 s, item, glyphs, i, &cluster_width,
5755 (item_list != NULL) ? &last_glyph_rbearing : NULL);
5756
5757 if (cellcount > 0)
5758 {
5759 int width;
5760
5761 width = cellcount * gui.char_width * PANGO_SCALE;
5762 glyph->geometry.x_offset +=
5763 MAX(0, width - cluster_width) / 2;
5764 glyph->geometry.width = width;
5765 }
5766 else
5767 {
Bram Moolenaar30613902019-12-01 22:11:18 +01005768 // If there are only combining characters in the
5769 // cluster, we cannot just change the width of the
5770 // previous glyph since there is none. Therefore
5771 // some guesswork is needed.
Bram Moolenaar071d4272004-06-13 20:20:40 +00005772 setup_zero_width_cluster(item, glyph, cells,
5773 cluster_width,
5774 last_glyph_rbearing);
5775 }
5776
5777 item_cells += cellcount;
5778 cells = cellcount;
5779 }
5780 else if (i > 0)
5781 {
5782 int width;
5783
Bram Moolenaar30613902019-12-01 22:11:18 +01005784 // There is a previous glyph, so we deal with combining
5785 // characters the canonical way.
5786 // In some circumstances Pango uses a positive x_offset,
5787 // then use the width of the previous glyph for this one
5788 // and set the previous width to zero.
5789 // Otherwise we get a negative x_offset, Pango has already
5790 // positioned the combining char, keep the widths as they
5791 // are.
5792 // For both adjust the x_offset to position the glyph in
5793 // the middle.
Bram Moolenaar706e84b2010-08-07 15:46:45 +02005794 if (glyph->geometry.x_offset >= 0)
Bram Moolenaar96118f32010-08-08 14:40:37 +02005795 {
5796 glyphs->glyphs[i].geometry.width =
5797 glyphs->glyphs[i - 1].geometry.width;
Bram Moolenaar706e84b2010-08-07 15:46:45 +02005798 glyphs->glyphs[i - 1].geometry.width = 0;
Bram Moolenaar96118f32010-08-08 14:40:37 +02005799 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005800 width = cells * gui.char_width * PANGO_SCALE;
5801 glyph->geometry.x_offset +=
5802 MAX(0, width - cluster_width) / 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005803 }
Bram Moolenaar30613902019-12-01 22:11:18 +01005804 else // i == 0 "cannot happen"
Bram Moolenaar071d4272004-06-13 20:20:40 +00005805 {
5806 glyph->geometry.width = 0;
5807 }
5808 }
5809
Bram Moolenaar30613902019-12-01 22:11:18 +01005810 //// Aaaaand action! **
Bram Moolenaar98921892016-02-23 17:14:37 +01005811#if GTK_CHECK_VERSION(3,0,0)
5812 draw_glyph_string(row, col + column_offset, item_cells,
5813 flags, item->analysis.font, glyphs,
5814 cr);
5815#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005816 draw_glyph_string(row, col + column_offset, item_cells,
5817 flags, item->analysis.font, glyphs);
Bram Moolenaar98921892016-02-23 17:14:37 +01005818#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005819
5820 pango_item_free(item);
5821
5822 column_offset += item_cells;
5823 }
5824
5825 pango_attr_list_unref(attr_list);
5826 }
5827
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00005828skipitall:
Bram Moolenaar30613902019-12-01 22:11:18 +01005829 // Draw underline and undercurl.
Bram Moolenaar98921892016-02-23 17:14:37 +01005830#if GTK_CHECK_VERSION(3,0,0)
5831 draw_under(flags, row, col, column_offset, cr);
5832#else
Bram Moolenaarf36d3692005-03-15 22:48:14 +00005833 draw_under(flags, row, col, column_offset);
Bram Moolenaar98921892016-02-23 17:14:37 +01005834#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005835
5836 pango_glyph_string_free(glyphs);
5837 vim_free(conv_buf);
5838
Bram Moolenaar98921892016-02-23 17:14:37 +01005839#if GTK_CHECK_VERSION(3,0,0)
5840 cairo_destroy(cr);
5841 if (!gui.by_signal)
5842 gdk_window_invalidate_rect(gtk_widget_get_window(gui.drawarea),
5843 &area, FALSE);
5844#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005845 gdk_gc_set_clip_rectangle(gui.text_gc, NULL);
Bram Moolenaar98921892016-02-23 17:14:37 +01005846#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005847
5848 return column_offset;
5849}
Bram Moolenaar071d4272004-06-13 20:20:40 +00005850
5851/*
5852 * Return OK if the key with the termcap name "name" is supported.
5853 */
5854 int
5855gui_mch_haskey(char_u *name)
5856{
5857 int i;
5858
5859 for (i = 0; special_keys[i].key_sym != 0; i++)
5860 if (name[0] == special_keys[i].code0
5861 && name[1] == special_keys[i].code1)
5862 return OK;
5863 return FAIL;
5864}
5865
Bram Moolenaarb2c5a5a2013-02-14 22:11:39 +01005866#if defined(FEAT_TITLE) || defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005867/*
5868 * Return the text window-id and display. Only required for X-based GUI's
5869 */
5870 int
5871gui_get_x11_windis(Window *win, Display **dis)
5872{
Bram Moolenaar98921892016-02-23 17:14:37 +01005873 if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005874 {
Bram Moolenaar98921892016-02-23 17:14:37 +01005875 *dis = GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin));
5876 *win = GDK_WINDOW_XID(gtk_widget_get_window(gui.mainwin));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005877 return OK;
5878 }
5879
5880 *dis = NULL;
5881 *win = 0;
5882 return FAIL;
5883}
5884#endif
5885
5886#if defined(FEAT_CLIENTSERVER) \
5887 || (defined(FEAT_X11) && defined(FEAT_CLIPBOARD)) || defined(PROTO)
5888
5889 Display *
5890gui_mch_get_display(void)
5891{
Bram Moolenaar98921892016-02-23 17:14:37 +01005892 if (gui.mainwin != NULL && gtk_widget_get_window(gui.mainwin) != NULL)
5893 return GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005894 else
5895 return NULL;
5896}
5897#endif
5898
5899 void
5900gui_mch_beep(void)
5901{
Bram Moolenaar071d4272004-06-13 20:20:40 +00005902 GdkDisplay *display;
5903
Bram Moolenaar98921892016-02-23 17:14:37 +01005904 if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005905 display = gtk_widget_get_display(gui.mainwin);
5906 else
5907 display = gdk_display_get_default();
5908
5909 if (display != NULL)
5910 gdk_display_beep(display);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005911}
5912
5913 void
5914gui_mch_flash(int msec)
5915{
Bram Moolenaar98921892016-02-23 17:14:37 +01005916#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar30613902019-12-01 22:11:18 +01005917 // TODO Replace GdkGC with Cairo
Bram Moolenaar98921892016-02-23 17:14:37 +01005918 (void)msec;
5919#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005920 GdkGCValues values;
5921 GdkGC *invert_gc;
5922
5923 if (gui.drawarea->window == NULL)
5924 return;
5925
5926 values.foreground.pixel = gui.norm_pixel ^ gui.back_pixel;
5927 values.background.pixel = gui.norm_pixel ^ gui.back_pixel;
5928 values.function = GDK_XOR;
5929 invert_gc = gdk_gc_new_with_values(gui.drawarea->window,
5930 &values,
5931 GDK_GC_FOREGROUND |
5932 GDK_GC_BACKGROUND |
5933 GDK_GC_FUNCTION);
5934 gdk_gc_set_exposures(invert_gc,
5935 gui.visibility != GDK_VISIBILITY_UNOBSCURED);
5936 /*
5937 * Do a visual beep by changing back and forth in some undetermined way,
5938 * the foreground and background colors. This is due to the fact that
5939 * there can't be really any prediction about the effects of XOR on
5940 * arbitrary X11 servers. However this seems to be enough for what we
5941 * intend it to do.
5942 */
5943 gdk_draw_rectangle(gui.drawarea->window, invert_gc,
5944 TRUE,
5945 0, 0,
5946 FILL_X((int)Columns) + gui.border_offset,
5947 FILL_Y((int)Rows) + gui.border_offset);
5948
5949 gui_mch_flush();
Bram Moolenaar30613902019-12-01 22:11:18 +01005950 ui_delay((long)msec, TRUE); // wait so many msec
Bram Moolenaar071d4272004-06-13 20:20:40 +00005951
5952 gdk_draw_rectangle(gui.drawarea->window, invert_gc,
5953 TRUE,
5954 0, 0,
5955 FILL_X((int)Columns) + gui.border_offset,
5956 FILL_Y((int)Rows) + gui.border_offset);
5957
5958 gdk_gc_destroy(invert_gc);
Bram Moolenaar98921892016-02-23 17:14:37 +01005959#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005960}
5961
5962/*
5963 * Invert a rectangle from row r, column c, for nr rows and nc columns.
5964 */
5965 void
5966gui_mch_invert_rectangle(int r, int c, int nr, int nc)
5967{
Bram Moolenaar98921892016-02-23 17:14:37 +01005968#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar4fc563b2016-03-12 12:40:58 +01005969 const GdkRectangle rect = {
5970 FILL_X(c), FILL_Y(r), nc * gui.char_width, nr * gui.char_height
5971 };
5972 cairo_t * const cr = cairo_create(gui.surface);
5973
Bram Moolenaar36edf062016-07-21 22:10:12 +02005974 set_cairo_source_rgba_from_color(cr, gui.norm_pixel ^ gui.back_pixel);
Bram Moolenaar4fc563b2016-03-12 12:40:58 +01005975# if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1,9,2)
5976 cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE);
5977# else
Bram Moolenaar30613902019-12-01 22:11:18 +01005978 // Give an implementation for older cairo versions if necessary.
Bram Moolenaar4fc563b2016-03-12 12:40:58 +01005979# endif
5980 gdk_cairo_rectangle(cr, &rect);
5981 cairo_fill(cr);
5982
5983 cairo_destroy(cr);
5984
5985 if (!gui.by_signal)
5986 gtk_widget_queue_draw_area(gui.drawarea, rect.x, rect.y,
5987 rect.width, rect.height);
Bram Moolenaar98921892016-02-23 17:14:37 +01005988#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00005989 GdkGCValues values;
5990 GdkGC *invert_gc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005991
5992 if (gui.drawarea->window == NULL)
5993 return;
5994
Bram Moolenaar89d40322006-08-29 15:30:07 +00005995 values.foreground.pixel = gui.norm_pixel ^ gui.back_pixel;
5996 values.background.pixel = gui.norm_pixel ^ gui.back_pixel;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005997 values.function = GDK_XOR;
5998 invert_gc = gdk_gc_new_with_values(gui.drawarea->window,
5999 &values,
6000 GDK_GC_FOREGROUND |
6001 GDK_GC_BACKGROUND |
6002 GDK_GC_FUNCTION);
Bram Moolenaar89d40322006-08-29 15:30:07 +00006003 gdk_gc_set_exposures(invert_gc, gui.visibility !=
6004 GDK_VISIBILITY_UNOBSCURED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006005 gdk_draw_rectangle(gui.drawarea->window, invert_gc,
6006 TRUE,
6007 FILL_X(c), FILL_Y(r),
6008 (nc) * gui.char_width, (nr) * gui.char_height);
6009 gdk_gc_destroy(invert_gc);
Bram Moolenaar98921892016-02-23 17:14:37 +01006010#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006011}
6012
6013/*
6014 * Iconify the GUI window.
6015 */
6016 void
6017gui_mch_iconify(void)
6018{
Bram Moolenaar071d4272004-06-13 20:20:40 +00006019 gtk_window_iconify(GTK_WINDOW(gui.mainwin));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006020}
6021
6022#if defined(FEAT_EVAL) || defined(PROTO)
6023/*
6024 * Bring the Vim window to the foreground.
6025 */
6026 void
6027gui_mch_set_foreground(void)
6028{
Bram Moolenaar071d4272004-06-13 20:20:40 +00006029 gtk_window_present(GTK_WINDOW(gui.mainwin));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006030}
6031#endif
6032
6033/*
6034 * Draw a cursor without focus.
6035 */
6036 void
6037gui_mch_draw_hollow_cursor(guicolor_T color)
6038{
6039 int i = 1;
Bram Moolenaar98921892016-02-23 17:14:37 +01006040#if GTK_CHECK_VERSION(3,0,0)
6041 cairo_t *cr;
6042#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006043
Bram Moolenaar98921892016-02-23 17:14:37 +01006044 if (gtk_widget_get_window(gui.drawarea) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006045 return;
6046
Bram Moolenaar98921892016-02-23 17:14:37 +01006047#if GTK_CHECK_VERSION(3,0,0)
6048 cr = cairo_create(gui.surface);
6049#endif
6050
Bram Moolenaar071d4272004-06-13 20:20:40 +00006051 gui_mch_set_fg_color(color);
6052
Bram Moolenaar98921892016-02-23 17:14:37 +01006053#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar36edf062016-07-21 22:10:12 +02006054 cairo_set_source_rgba(cr,
6055 gui.fgcolor->red, gui.fgcolor->green, gui.fgcolor->blue,
6056 gui.fgcolor->alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01006057#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006058 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
Bram Moolenaar98921892016-02-23 17:14:37 +01006059#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006060 if (mb_lefthalve(gui.row, gui.col))
6061 i = 2;
Bram Moolenaar98921892016-02-23 17:14:37 +01006062#if GTK_CHECK_VERSION(3,0,0)
6063 cairo_set_line_width(cr, 1.0);
6064 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
6065 cairo_rectangle(cr,
6066 FILL_X(gui.col) + 0.5, FILL_Y(gui.row) + 0.5,
6067 i * gui.char_width - 1, gui.char_height - 1);
6068 cairo_stroke(cr);
6069 cairo_destroy(cr);
6070#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006071 gdk_draw_rectangle(gui.drawarea->window, gui.text_gc,
6072 FALSE,
6073 FILL_X(gui.col), FILL_Y(gui.row),
6074 i * gui.char_width - 1, gui.char_height - 1);
Bram Moolenaar98921892016-02-23 17:14:37 +01006075#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006076}
6077
6078/*
6079 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
6080 * color "color".
6081 */
6082 void
6083gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
6084{
Bram Moolenaar98921892016-02-23 17:14:37 +01006085 if (gtk_widget_get_window(gui.drawarea) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006086 return;
6087
6088 gui_mch_set_fg_color(color);
6089
Bram Moolenaar98921892016-02-23 17:14:37 +01006090#if GTK_CHECK_VERSION(3,0,0)
6091 {
6092 cairo_t *cr;
6093
6094 cr = cairo_create(gui.surface);
Bram Moolenaar36edf062016-07-21 22:10:12 +02006095 cairo_set_source_rgba(cr,
6096 gui.fgcolor->red, gui.fgcolor->green, gui.fgcolor->blue,
6097 gui.fgcolor->alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01006098 cairo_rectangle(cr,
6099# ifdef FEAT_RIGHTLEFT
Bram Moolenaar30613902019-12-01 22:11:18 +01006100 // vertical line should be on the right of current point
Bram Moolenaar98921892016-02-23 17:14:37 +01006101 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
6102# endif
6103 FILL_X(gui.col), FILL_Y(gui.row) + gui.char_height - h,
6104 w, h);
6105 cairo_fill(cr);
6106 cairo_destroy(cr);
6107 }
Bram Moolenaar30613902019-12-01 22:11:18 +01006108#else // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006109 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
6110 gdk_draw_rectangle(gui.drawarea->window, gui.text_gc,
6111 TRUE,
Bram Moolenaar98921892016-02-23 17:14:37 +01006112# ifdef FEAT_RIGHTLEFT
Bram Moolenaar30613902019-12-01 22:11:18 +01006113 // vertical line should be on the right of current point
Bram Moolenaar071d4272004-06-13 20:20:40 +00006114 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
Bram Moolenaar98921892016-02-23 17:14:37 +01006115# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006116 FILL_X(gui.col),
6117 FILL_Y(gui.row) + gui.char_height - h,
6118 w, h);
Bram Moolenaar30613902019-12-01 22:11:18 +01006119#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006120}
6121
6122
6123/*
6124 * Catch up with any queued X11 events. This may put keyboard input into the
6125 * input buffer, call resize call-backs, trigger timers etc. If there is
6126 * nothing in the X11 event queue (& no timers pending), then we return
6127 * immediately.
6128 */
6129 void
6130gui_mch_update(void)
6131{
Bram Moolenaara3f41662010-07-11 19:01:06 +02006132 while (g_main_context_pending(NULL) && !vim_is_input_buf_full())
6133 g_main_context_iteration(NULL, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006134}
6135
Bram Moolenaar4ab79682017-08-27 14:50:47 +02006136 static timeout_cb_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00006137input_timer_cb(gpointer data)
6138{
6139 int *timed_out = (int *) data;
6140
Bram Moolenaar30613902019-12-01 22:11:18 +01006141 // Just inform the caller about the occurrence of it
Bram Moolenaar071d4272004-06-13 20:20:40 +00006142 *timed_out = TRUE;
6143
Bram Moolenaar30613902019-12-01 22:11:18 +01006144 return FALSE; // don't happen again
Bram Moolenaar071d4272004-06-13 20:20:40 +00006145}
6146
Bram Moolenaar4ab79682017-08-27 14:50:47 +02006147#ifdef FEAT_JOB_CHANNEL
6148 static timeout_cb_type
6149channel_poll_cb(gpointer data UNUSED)
6150{
Bram Moolenaar30613902019-12-01 22:11:18 +01006151 // Using an event handler for a channel that may be disconnected does
6152 // not work, it hangs. Instead poll for messages.
Bram Moolenaar4ab79682017-08-27 14:50:47 +02006153 channel_handle_events(TRUE);
6154 parse_queued_messages();
6155
Bram Moolenaar30613902019-12-01 22:11:18 +01006156 return TRUE; // repeat
Bram Moolenaar4ab79682017-08-27 14:50:47 +02006157}
6158#endif
6159
Bram Moolenaar071d4272004-06-13 20:20:40 +00006160/*
6161 * GUI input routine called by gui_wait_for_chars(). Waits for a character
6162 * from the keyboard.
6163 * wtime == -1 Wait forever.
6164 * wtime == 0 This should never happen.
6165 * wtime > 0 Wait wtime milliseconds for a character.
6166 * Returns OK if a character was found to be available within the given time,
6167 * or FAIL otherwise.
6168 */
6169 int
6170gui_mch_wait_for_chars(long wtime)
6171{
Bram Moolenaar4231da42016-06-02 14:30:04 +02006172 int focus;
6173 guint timer;
6174 static int timed_out;
6175 int retval = FAIL;
Bram Moolenaar4ab79682017-08-27 14:50:47 +02006176#ifdef FEAT_JOB_CHANNEL
6177 guint channel_timer = 0;
6178#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006179
6180 timed_out = FALSE;
6181
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +01006182 // This timeout makes sure that we will return if no characters arrived in
6183 // time. If "wtime" is zero just use one.
6184 if (wtime >= 0)
Bram Moolenaar34a58742019-02-03 15:28:28 +01006185 timer = timeout_add(wtime == 0 ? 1L : wtime,
Bram Moolenaar12dfc9e2019-01-28 22:32:58 +01006186 input_timer_cb, &timed_out);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006187 else
6188 timer = 0;
6189
Bram Moolenaar4ab79682017-08-27 14:50:47 +02006190#ifdef FEAT_JOB_CHANNEL
Bram Moolenaar30613902019-12-01 22:11:18 +01006191 // If there is a channel with the keep_open flag we need to poll for input
6192 // on them.
Bram Moolenaar4ab79682017-08-27 14:50:47 +02006193 if (channel_any_keep_open())
6194 channel_timer = timeout_add(20, channel_poll_cb, NULL);
6195#endif
6196
Bram Moolenaar071d4272004-06-13 20:20:40 +00006197 focus = gui.in_focus;
6198
6199 do
6200 {
Bram Moolenaar30613902019-12-01 22:11:18 +01006201 // Stop or start blinking when focus changes
Bram Moolenaar071d4272004-06-13 20:20:40 +00006202 if (gui.in_focus != focus)
6203 {
6204 if (gui.in_focus)
6205 gui_mch_start_blink();
6206 else
Bram Moolenaar1dd45fb2018-01-31 21:10:01 +01006207 gui_mch_stop_blink(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006208 focus = gui.in_focus;
6209 }
6210
Bram Moolenaar93c88e02015-09-15 14:12:05 +02006211#ifdef MESSAGE_QUEUE
Bram Moolenaar4231da42016-06-02 14:30:04 +02006212# ifdef FEAT_TIMERS
6213 did_add_timer = FALSE;
6214# endif
Bram Moolenaar93c88e02015-09-15 14:12:05 +02006215 parse_queued_messages();
Bram Moolenaar4231da42016-06-02 14:30:04 +02006216# ifdef FEAT_TIMERS
6217 if (did_add_timer)
Bram Moolenaar30613902019-12-01 22:11:18 +01006218 // Need to recompute the waiting time.
Bram Moolenaar4231da42016-06-02 14:30:04 +02006219 goto theend;
6220# endif
Bram Moolenaar8c8de832008-06-24 22:58:06 +00006221#endif
6222
Bram Moolenaar071d4272004-06-13 20:20:40 +00006223 /*
6224 * Loop in GTK+ processing until a timeout or input occurs.
Bram Moolenaard4755bb2004-09-02 19:12:26 +00006225 * Skip this if input is available anyway (can happen in rare
6226 * situations, sort of race condition).
Bram Moolenaar071d4272004-06-13 20:20:40 +00006227 */
Bram Moolenaard4755bb2004-09-02 19:12:26 +00006228 if (!input_available())
Bram Moolenaara3f41662010-07-11 19:01:06 +02006229 g_main_context_iteration(NULL, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006230
Bram Moolenaar30613902019-12-01 22:11:18 +01006231 // Got char, return immediately
Bram Moolenaar071d4272004-06-13 20:20:40 +00006232 if (input_available())
6233 {
Bram Moolenaar4231da42016-06-02 14:30:04 +02006234 retval = OK;
6235 goto theend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006236 }
6237 } while (wtime < 0 || !timed_out);
6238
6239 /*
6240 * Flush all eventually pending (drawing) events.
6241 */
6242 gui_mch_update();
6243
Bram Moolenaar4231da42016-06-02 14:30:04 +02006244theend:
6245 if (timer != 0 && !timed_out)
Bram Moolenaar4ab79682017-08-27 14:50:47 +02006246 timeout_remove(timer);
6247#ifdef FEAT_JOB_CHANNEL
6248 if (channel_timer != 0)
6249 timeout_remove(channel_timer);
Bram Moolenaar4231da42016-06-02 14:30:04 +02006250#endif
6251
6252 return retval;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006253}
6254
6255
Bram Moolenaar30613902019-12-01 22:11:18 +01006256/////////////////////////////////////////////////////////////////////////////
6257// Output drawing routines.
6258//
Bram Moolenaar071d4272004-06-13 20:20:40 +00006259
6260
Bram Moolenaar30613902019-12-01 22:11:18 +01006261// Flush any output to the screen
Bram Moolenaar071d4272004-06-13 20:20:40 +00006262 void
6263gui_mch_flush(void)
6264{
Bram Moolenaar98921892016-02-23 17:14:37 +01006265 if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin))
Bram Moolenaar92cbf622018-09-19 22:40:03 +02006266#if GTK_CHECK_VERSION(2,4,0)
Bram Moolenaarb463e8d2017-06-05 15:07:09 +02006267 gdk_display_flush(gtk_widget_get_display(gui.mainwin));
Bram Moolenaar92cbf622018-09-19 22:40:03 +02006268#else
6269 gdk_display_sync(gtk_widget_get_display(gui.mainwin));
6270#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006271}
6272
6273/*
6274 * Clear a rectangular region of the screen from text pos (row1, col1) to
6275 * (row2, col2) inclusive.
6276 */
6277 void
Bram Moolenaarfe344a92017-02-23 12:20:35 +01006278gui_mch_clear_block(int row1arg, int col1arg, int row2arg, int col2arg)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006279{
Bram Moolenaarfe344a92017-02-23 12:20:35 +01006280
6281 int col1 = check_col(col1arg);
6282 int col2 = check_col(col2arg);
6283 int row1 = check_row(row1arg);
6284 int row2 = check_row(row2arg);
6285
Bram Moolenaar98921892016-02-23 17:14:37 +01006286#if GTK_CHECK_VERSION(3,0,0)
6287 if (gtk_widget_get_window(gui.drawarea) == NULL)
6288 return;
6289#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006290 GdkColor color;
6291
6292 if (gui.drawarea->window == NULL)
6293 return;
6294
6295 color.pixel = gui.back_pixel;
Bram Moolenaar98921892016-02-23 17:14:37 +01006296#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006297
Bram Moolenaar98921892016-02-23 17:14:37 +01006298#if GTK_CHECK_VERSION(3,0,0)
6299 {
Bram Moolenaar30613902019-12-01 22:11:18 +01006300 // Add one pixel to the far right column in case a double-stroked
6301 // bold glyph may sit there.
Bram Moolenaar98921892016-02-23 17:14:37 +01006302 const GdkRectangle rect = {
6303 FILL_X(col1), FILL_Y(row1),
6304 (col2 - col1 + 1) * gui.char_width + (col2 == Columns - 1),
6305 (row2 - row1 + 1) * gui.char_height
6306 };
6307 GdkWindow * const win = gtk_widget_get_window(gui.drawarea);
6308 cairo_t * const cr = cairo_create(gui.surface);
Bram Moolenaara859f042016-11-17 19:11:55 +01006309# if GTK_CHECK_VERSION(3,22,2)
6310 set_cairo_source_rgba_from_color(cr, gui.back_pixel);
6311# else
Bram Moolenaar98921892016-02-23 17:14:37 +01006312 cairo_pattern_t * const pat = gdk_window_get_background_pattern(win);
6313 if (pat != NULL)
6314 cairo_set_source(cr, pat);
6315 else
Bram Moolenaar36edf062016-07-21 22:10:12 +02006316 set_cairo_source_rgba_from_color(cr, gui.back_pixel);
Bram Moolenaara859f042016-11-17 19:11:55 +01006317# endif
Bram Moolenaar98921892016-02-23 17:14:37 +01006318 gdk_cairo_rectangle(cr, &rect);
6319 cairo_fill(cr);
6320 cairo_destroy(cr);
6321
6322 if (!gui.by_signal)
6323 gdk_window_invalidate_rect(win, &rect, FALSE);
6324 }
Bram Moolenaar30613902019-12-01 22:11:18 +01006325#else // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006326 gdk_gc_set_foreground(gui.text_gc, &color);
6327
Bram Moolenaar30613902019-12-01 22:11:18 +01006328 // Clear one extra pixel at the far right, for when bold characters have
6329 // spilled over to the window border.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006330 gdk_draw_rectangle(gui.drawarea->window, gui.text_gc, TRUE,
6331 FILL_X(col1), FILL_Y(row1),
6332 (col2 - col1 + 1) * gui.char_width
6333 + (col2 == Columns - 1),
6334 (row2 - row1 + 1) * gui.char_height);
Bram Moolenaar30613902019-12-01 22:11:18 +01006335#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336}
6337
Bram Moolenaar98921892016-02-23 17:14:37 +01006338#if GTK_CHECK_VERSION(3,0,0)
6339 static void
6340gui_gtk_window_clear(GdkWindow *win)
6341{
6342 const GdkRectangle rect = {
6343 0, 0, gdk_window_get_width(win), gdk_window_get_height(win)
6344 };
6345 cairo_t * const cr = cairo_create(gui.surface);
Bram Moolenaara859f042016-11-17 19:11:55 +01006346# if GTK_CHECK_VERSION(3,22,2)
6347 set_cairo_source_rgba_from_color(cr, gui.back_pixel);
6348# else
Bram Moolenaar98921892016-02-23 17:14:37 +01006349 cairo_pattern_t * const pat = gdk_window_get_background_pattern(win);
6350 if (pat != NULL)
6351 cairo_set_source(cr, pat);
6352 else
Bram Moolenaar36edf062016-07-21 22:10:12 +02006353 set_cairo_source_rgba_from_color(cr, gui.back_pixel);
Bram Moolenaara859f042016-11-17 19:11:55 +01006354# endif
Bram Moolenaar98921892016-02-23 17:14:37 +01006355 gdk_cairo_rectangle(cr, &rect);
6356 cairo_fill(cr);
6357 cairo_destroy(cr);
6358
6359 if (!gui.by_signal)
6360 gdk_window_invalidate_rect(win, &rect, FALSE);
6361}
Bram Moolenaar25328e32018-09-11 21:30:09 +02006362#else
6363# define gui_gtk_window_clear(win) gdk_window_clear(win)
Bram Moolenaar98921892016-02-23 17:14:37 +01006364#endif
6365
Bram Moolenaar071d4272004-06-13 20:20:40 +00006366 void
6367gui_mch_clear_all(void)
6368{
Bram Moolenaar98921892016-02-23 17:14:37 +01006369 if (gtk_widget_get_window(gui.drawarea) != NULL)
6370 gui_gtk_window_clear(gtk_widget_get_window(gui.drawarea));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006371}
6372
Bram Moolenaar98921892016-02-23 17:14:37 +01006373#if !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006374/*
6375 * Redraw any text revealed by scrolling up/down.
6376 */
6377 static void
6378check_copy_area(void)
6379{
6380 GdkEvent *event;
6381 int expose_count;
6382
6383 if (gui.visibility != GDK_VISIBILITY_PARTIAL)
6384 return;
6385
Bram Moolenaar30613902019-12-01 22:11:18 +01006386 // Avoid redrawing the cursor while scrolling or it'll end up where
6387 // we don't want it to be. I'm not sure if it's correct to call
6388 // gui_dont_update_cursor() at this point but it works as a quick
6389 // fix for now.
Bram Moolenaar107abd22016-08-12 14:08:25 +02006390 gui_dont_update_cursor(TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006391
6392 do
6393 {
Bram Moolenaar30613902019-12-01 22:11:18 +01006394 // Wait to check whether the scroll worked or not.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006395 event = gdk_event_get_graphics_expose(gui.drawarea->window);
6396
6397 if (event == NULL)
Bram Moolenaar30613902019-12-01 22:11:18 +01006398 break; // received NoExpose event
Bram Moolenaar071d4272004-06-13 20:20:40 +00006399
6400 gui_redraw(event->expose.area.x, event->expose.area.y,
6401 event->expose.area.width, event->expose.area.height);
6402
6403 expose_count = event->expose.count;
6404 gdk_event_free(event);
6405 }
Bram Moolenaar30613902019-12-01 22:11:18 +01006406 while (expose_count > 0); // more events follow
Bram Moolenaar071d4272004-06-13 20:20:40 +00006407
6408 gui_can_update_cursor();
6409}
Bram Moolenaar30613902019-12-01 22:11:18 +01006410#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar98921892016-02-23 17:14:37 +01006411
6412#if GTK_CHECK_VERSION(3,0,0)
6413 static void
6414gui_gtk_surface_copy_rect(int dest_x, int dest_y,
6415 int src_x, int src_y,
6416 int width, int height)
6417{
6418 cairo_t * const cr = cairo_create(gui.surface);
6419
6420 cairo_rectangle(cr, dest_x, dest_y, width, height);
6421 cairo_clip(cr);
6422 cairo_push_group(cr);
6423 cairo_set_source_surface(cr, gui.surface, dest_x - src_x, dest_y - src_y);
6424 cairo_paint(cr);
6425 cairo_pop_group_to_source(cr);
6426 cairo_paint(cr);
6427
6428 cairo_destroy(cr);
6429}
6430#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006431
6432/*
6433 * Delete the given number of lines from the given row, scrolling up any
6434 * text further down within the scroll region.
6435 */
6436 void
6437gui_mch_delete_lines(int row, int num_lines)
6438{
Bram Moolenaar98921892016-02-23 17:14:37 +01006439#if GTK_CHECK_VERSION(3,0,0)
6440 const int ncols = gui.scroll_region_right - gui.scroll_region_left + 1;
6441 const int nrows = gui.scroll_region_bot - row + 1;
6442 const int src_nrows = nrows - num_lines;
6443
6444 gui_gtk_surface_copy_rect(
6445 FILL_X(gui.scroll_region_left), FILL_Y(row),
6446 FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
6447 gui.char_width * ncols + 1, gui.char_height * src_nrows);
6448 gui_clear_block(
6449 gui.scroll_region_bot - num_lines + 1, gui.scroll_region_left,
6450 gui.scroll_region_bot, gui.scroll_region_right);
6451 gui_gtk3_redraw(
6452 FILL_X(gui.scroll_region_left), FILL_Y(row),
6453 gui.char_width * ncols + 1, gui.char_height * nrows);
6454 if (!gui.by_signal)
6455 gtk_widget_queue_draw_area(gui.drawarea,
6456 FILL_X(gui.scroll_region_left), FILL_Y(row),
6457 gui.char_width * ncols + 1, gui.char_height * nrows);
6458#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006459 if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
Bram Moolenaar30613902019-12-01 22:11:18 +01006460 return; // Can't see the window
Bram Moolenaar071d4272004-06-13 20:20:40 +00006461
6462 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
6463 gdk_gc_set_background(gui.text_gc, gui.bgcolor);
6464
Bram Moolenaar30613902019-12-01 22:11:18 +01006465 // copy one extra pixel, for when bold has spilled over
Bram Moolenaar071d4272004-06-13 20:20:40 +00006466 gdk_window_copy_area(gui.drawarea->window, gui.text_gc,
6467 FILL_X(gui.scroll_region_left), FILL_Y(row),
6468 gui.drawarea->window,
6469 FILL_X(gui.scroll_region_left),
6470 FILL_Y(row + num_lines),
6471 gui.char_width * (gui.scroll_region_right
6472 - gui.scroll_region_left + 1) + 1,
6473 gui.char_height * (gui.scroll_region_bot - row - num_lines + 1));
6474
6475 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
6476 gui.scroll_region_left,
6477 gui.scroll_region_bot, gui.scroll_region_right);
6478 check_copy_area();
Bram Moolenaar30613902019-12-01 22:11:18 +01006479#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006480}
6481
6482/*
6483 * Insert the given number of lines before the given row, scrolling down any
6484 * following text within the scroll region.
6485 */
6486 void
6487gui_mch_insert_lines(int row, int num_lines)
6488{
Bram Moolenaar98921892016-02-23 17:14:37 +01006489#if GTK_CHECK_VERSION(3,0,0)
6490 const int ncols = gui.scroll_region_right - gui.scroll_region_left + 1;
6491 const int nrows = gui.scroll_region_bot - row + 1;
6492 const int src_nrows = nrows - num_lines;
6493
6494 gui_gtk_surface_copy_rect(
6495 FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
6496 FILL_X(gui.scroll_region_left), FILL_Y(row),
6497 gui.char_width * ncols + 1, gui.char_height * src_nrows);
6498 gui_mch_clear_block(
6499 row, gui.scroll_region_left,
6500 row + num_lines - 1, gui.scroll_region_right);
6501 gui_gtk3_redraw(
6502 FILL_X(gui.scroll_region_left), FILL_Y(row),
6503 gui.char_width * ncols + 1, gui.char_height * nrows);
6504 if (!gui.by_signal)
6505 gtk_widget_queue_draw_area(gui.drawarea,
6506 FILL_X(gui.scroll_region_left), FILL_Y(row),
6507 gui.char_width * ncols + 1, gui.char_height * nrows);
6508#else
Bram Moolenaar071d4272004-06-13 20:20:40 +00006509 if (gui.visibility == GDK_VISIBILITY_FULLY_OBSCURED)
Bram Moolenaar30613902019-12-01 22:11:18 +01006510 return; // Can't see the window
Bram Moolenaar071d4272004-06-13 20:20:40 +00006511
6512 gdk_gc_set_foreground(gui.text_gc, gui.fgcolor);
6513 gdk_gc_set_background(gui.text_gc, gui.bgcolor);
6514
Bram Moolenaar30613902019-12-01 22:11:18 +01006515 // copy one extra pixel, for when bold has spilled over
Bram Moolenaar071d4272004-06-13 20:20:40 +00006516 gdk_window_copy_area(gui.drawarea->window, gui.text_gc,
6517 FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
6518 gui.drawarea->window,
6519 FILL_X(gui.scroll_region_left), FILL_Y(row),
6520 gui.char_width * (gui.scroll_region_right
6521 - gui.scroll_region_left + 1) + 1,
6522 gui.char_height * (gui.scroll_region_bot - row - num_lines + 1));
6523
6524 gui_clear_block(row, gui.scroll_region_left,
6525 row + num_lines - 1, gui.scroll_region_right);
6526 check_copy_area();
Bram Moolenaar30613902019-12-01 22:11:18 +01006527#endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006528}
6529
6530/*
6531 * X Selection stuff, for cutting and pasting text to other windows.
6532 */
6533 void
Bram Moolenaar0554fa42019-06-14 21:36:54 +02006534clip_mch_request_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006535{
6536 GdkAtom target;
6537 unsigned i;
Bram Moolenaar51b5ab92008-01-06 14:17:07 +00006538 time_t start;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006539
6540 for (i = 0; i < N_SELECTION_TARGETS; ++i)
6541 {
Bram Moolenaar3a6eaa52009-06-16 13:23:06 +00006542 if (!clip_html && selection_targets[i].info == TARGET_HTML)
6543 continue;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006544 received_selection = RS_NONE;
6545 target = gdk_atom_intern(selection_targets[i].target, FALSE);
6546
6547 gtk_selection_convert(gui.drawarea,
6548 cbd->gtk_sel_atom, target,
6549 (guint32)GDK_CURRENT_TIME);
6550
Bram Moolenaar30613902019-12-01 22:11:18 +01006551 // Hack: Wait up to three seconds for the selection. A hang was
6552 // noticed here when using the netrw plugin combined with ":gui"
6553 // during the FocusGained event.
Bram Moolenaar51b5ab92008-01-06 14:17:07 +00006554 start = time(NULL);
6555 while (received_selection == RS_NONE && time(NULL) < start + 3)
Bram Moolenaar30613902019-12-01 22:11:18 +01006556 g_main_context_iteration(NULL, TRUE); // wait for selection_received_cb
Bram Moolenaar071d4272004-06-13 20:20:40 +00006557
6558 if (received_selection != RS_FAIL)
6559 return;
6560 }
6561
Bram Moolenaar30613902019-12-01 22:11:18 +01006562 // Final fallback position - use the X CUT_BUFFER0 store
Bram Moolenaar98921892016-02-23 17:14:37 +01006563 yank_cut_buffer0(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.mainwin)),
6564 cbd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006565}
6566
6567/*
6568 * Disown the selection.
6569 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006570 void
Bram Moolenaar0554fa42019-06-14 21:36:54 +02006571clip_mch_lose_selection(Clipboard_T *cbd UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006572{
Bram Moolenaar29dfa5a2018-03-20 21:24:45 +01006573 if (!in_selection_clear_event)
6574 {
6575 gtk_selection_owner_set(NULL, cbd->gtk_sel_atom, gui.event_time);
6576 gui_mch_update();
6577 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006578}
6579
6580/*
6581 * Own the selection and return OK if it worked.
6582 */
6583 int
Bram Moolenaar0554fa42019-06-14 21:36:54 +02006584clip_mch_own_selection(Clipboard_T *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006585{
6586 int success;
6587
6588 success = gtk_selection_owner_set(gui.drawarea, cbd->gtk_sel_atom,
Bram Moolenaar20892c12011-06-26 04:49:00 +02006589 gui.event_time);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006590 gui_mch_update();
6591 return (success) ? OK : FAIL;
6592}
6593
6594/*
6595 * Send the current selection to the clipboard. Do nothing for X because we
6596 * will fill in the selection only when requested by another app.
6597 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006598 void
Bram Moolenaar0554fa42019-06-14 21:36:54 +02006599clip_mch_set_selection(Clipboard_T *cbd UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006600{
6601}
6602
Bram Moolenaar113e1072019-01-20 15:30:40 +01006603#if (defined(FEAT_XCLIPBOARD) && defined(USE_SYSTEM)) || defined(PROTO)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01006604 int
Bram Moolenaar0554fa42019-06-14 21:36:54 +02006605clip_gtk_owner_exists(Clipboard_T *cbd)
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01006606{
6607 return gdk_selection_owner_get(cbd->gtk_sel_atom) != NULL;
6608}
Bram Moolenaar113e1072019-01-20 15:30:40 +01006609#endif
Bram Moolenaar1a0316c2013-03-13 17:50:25 +01006610
Bram Moolenaar071d4272004-06-13 20:20:40 +00006611
6612#if defined(FEAT_MENU) || defined(PROTO)
6613/*
6614 * Make a menu item appear either active or not active (grey or not grey).
6615 */
6616 void
6617gui_mch_menu_grey(vimmenu_T *menu, int grey)
6618{
6619 if (menu->id == NULL)
6620 return;
6621
6622 if (menu_is_separator(menu->name))
6623 grey = TRUE;
6624
6625 gui_mch_menu_hidden(menu, FALSE);
Bram Moolenaar30613902019-12-01 22:11:18 +01006626 // Be clever about bitfields versus true booleans here!
Bram Moolenaar98921892016-02-23 17:14:37 +01006627 if (!gtk_widget_get_sensitive(menu->id) == !grey)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006628 {
6629 gtk_widget_set_sensitive(menu->id, !grey);
6630 gui_mch_update();
6631 }
6632}
6633
6634/*
6635 * Make menu item hidden or not hidden.
6636 */
6637 void
6638gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
6639{
6640 if (menu->id == 0)
6641 return;
6642
6643 if (hidden)
6644 {
Bram Moolenaar98921892016-02-23 17:14:37 +01006645 if (gtk_widget_get_visible(menu->id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006646 {
6647 gtk_widget_hide(menu->id);
6648 gui_mch_update();
6649 }
6650 }
6651 else
6652 {
Bram Moolenaar98921892016-02-23 17:14:37 +01006653 if (!gtk_widget_get_visible(menu->id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006654 {
6655 gtk_widget_show(menu->id);
6656 gui_mch_update();
6657 }
6658 }
6659}
6660
6661/*
6662 * This is called after setting all the menus to grey/hidden or not.
6663 */
6664 void
6665gui_mch_draw_menubar(void)
6666{
Bram Moolenaar30613902019-12-01 22:11:18 +01006667 // just make sure that the visual changes get effect immediately
Bram Moolenaar071d4272004-06-13 20:20:40 +00006668 gui_mch_update();
6669}
Bram Moolenaar30613902019-12-01 22:11:18 +01006670#endif // FEAT_MENU
Bram Moolenaar071d4272004-06-13 20:20:40 +00006671
6672/*
6673 * Scrollbar stuff.
6674 */
6675 void
6676gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
6677{
6678 if (sb->id == NULL)
6679 return;
6680
Bram Moolenaar98921892016-02-23 17:14:37 +01006681 gtk_widget_set_visible(sb->id, flag);
Bram Moolenaarb3656ed2006-03-20 21:59:49 +00006682 update_window_manager_hints(0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006683}
6684
6685
6686/*
6687 * Return the RGB value of a pixel as long.
6688 */
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02006689 guicolor_T
Bram Moolenaar071d4272004-06-13 20:20:40 +00006690gui_mch_get_rgb(guicolor_T pixel)
6691{
Bram Moolenaar98921892016-02-23 17:14:37 +01006692#if GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar36edf062016-07-21 22:10:12 +02006693 return (long_u)pixel;
Bram Moolenaar98921892016-02-23 17:14:37 +01006694#else
Bram Moolenaar36edf062016-07-21 22:10:12 +02006695 GdkColor color;
6696
Bram Moolenaar071d4272004-06-13 20:20:40 +00006697 gdk_colormap_query_color(gtk_widget_get_colormap(gui.drawarea),
6698 (unsigned long)pixel, &color);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006699
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02006700 return (guicolor_T)(
6701 (((unsigned)color.red & 0xff00) << 8)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006702 | ((unsigned)color.green & 0xff00)
Bram Moolenaar1b58cdd2016-08-22 23:04:33 +02006703 | (((unsigned)color.blue & 0xff00) >> 8));
Bram Moolenaar36edf062016-07-21 22:10:12 +02006704#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006705}
6706
6707/*
Bram Moolenaar6cc16192005-01-08 21:49:45 +00006708 * Get current mouse coordinates in text window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006709 */
Bram Moolenaar6cc16192005-01-08 21:49:45 +00006710 void
6711gui_mch_getmouse(int *x, int *y)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006712{
Bram Moolenaar98921892016-02-23 17:14:37 +01006713 gui_gtk_get_pointer(gui.drawarea, x, y, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006714}
6715
6716 void
6717gui_mch_setmouse(int x, int y)
6718{
Bram Moolenaar30613902019-12-01 22:11:18 +01006719 // Sorry for the Xlib call, but we can't avoid it, since there is no
6720 // internal GDK mechanism present to accomplish this. (and for good
6721 // reason...)
Bram Moolenaar98921892016-02-23 17:14:37 +01006722 XWarpPointer(GDK_WINDOW_XDISPLAY(gtk_widget_get_window(gui.drawarea)),
6723 (Window)0, GDK_WINDOW_XID(gtk_widget_get_window(gui.drawarea)),
6724 0, 0, 0U, 0U, x, y);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006725}
6726
6727
6728#ifdef FEAT_MOUSESHAPE
Bram Moolenaar30613902019-12-01 22:11:18 +01006729// The last set mouse pointer shape is remembered, to be used when it goes
6730// from hidden to not hidden.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006731static int last_shape = 0;
6732#endif
6733
6734/*
6735 * Use the blank mouse pointer or not.
6736 *
6737 * hide: TRUE = use blank ptr, FALSE = use parent ptr
6738 */
6739 void
6740gui_mch_mousehide(int hide)
6741{
6742 if (gui.pointer_hidden != hide)
6743 {
6744 gui.pointer_hidden = hide;
Bram Moolenaar98921892016-02-23 17:14:37 +01006745 if (gtk_widget_get_window(gui.drawarea) && gui.blank_pointer != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006746 {
6747 if (hide)
Bram Moolenaar98921892016-02-23 17:14:37 +01006748 gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea),
6749 gui.blank_pointer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006750 else
6751#ifdef FEAT_MOUSESHAPE
6752 mch_set_mouse_shape(last_shape);
6753#else
Bram Moolenaar25328e32018-09-11 21:30:09 +02006754 gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea), NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006755#endif
6756 }
6757 }
6758}
6759
6760#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
6761
Bram Moolenaar30613902019-12-01 22:11:18 +01006762// Table for shape IDs. Keep in sync with the mshape_names[] table in
6763// misc2.c!
Bram Moolenaar071d4272004-06-13 20:20:40 +00006764static const int mshape_ids[] =
6765{
Bram Moolenaar30613902019-12-01 22:11:18 +01006766 GDK_LEFT_PTR, // arrow
6767 GDK_CURSOR_IS_PIXMAP, // blank
6768 GDK_XTERM, // beam
6769 GDK_SB_V_DOUBLE_ARROW, // updown
6770 GDK_SIZING, // udsizing
6771 GDK_SB_H_DOUBLE_ARROW, // leftright
6772 GDK_SIZING, // lrsizing
6773 GDK_WATCH, // busy
6774 GDK_X_CURSOR, // no
6775 GDK_CROSSHAIR, // crosshair
6776 GDK_HAND1, // hand1
6777 GDK_HAND2, // hand2
6778 GDK_PENCIL, // pencil
6779 GDK_QUESTION_ARROW, // question
6780 GDK_RIGHT_PTR, // right-arrow
6781 GDK_CENTER_PTR, // up-arrow
6782 GDK_LEFT_PTR // last one
Bram Moolenaar071d4272004-06-13 20:20:40 +00006783};
6784
6785 void
6786mch_set_mouse_shape(int shape)
6787{
6788 int id;
6789 GdkCursor *c;
6790
Bram Moolenaar98921892016-02-23 17:14:37 +01006791 if (gtk_widget_get_window(gui.drawarea) == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006792 return;
6793
6794 if (shape == MSHAPE_HIDE || gui.pointer_hidden)
Bram Moolenaar98921892016-02-23 17:14:37 +01006795 gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea),
6796 gui.blank_pointer);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006797 else
6798 {
6799 if (shape >= MSHAPE_NUMBERED)
6800 {
6801 id = shape - MSHAPE_NUMBERED;
6802 if (id >= GDK_LAST_CURSOR)
6803 id = GDK_LEFT_PTR;
6804 else
Bram Moolenaar30613902019-12-01 22:11:18 +01006805 id &= ~1; // they are always even (why?)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006806 }
Bram Moolenaarb85cb212009-05-17 14:24:23 +00006807 else if (shape < (int)(sizeof(mshape_ids) / sizeof(int)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006808 id = mshape_ids[shape];
Bram Moolenaarf9393ef2006-04-24 19:47:27 +00006809 else
6810 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006811 c = gdk_cursor_new_for_display(
Bram Moolenaarb71ec9f2005-01-25 22:22:02 +00006812 gtk_widget_get_display(gui.drawarea), (GdkCursorType)id);
Bram Moolenaar98921892016-02-23 17:14:37 +01006813 gdk_window_set_cursor(gtk_widget_get_window(gui.drawarea), c);
Bram Moolenaar98921892016-02-23 17:14:37 +01006814# if GTK_CHECK_VERSION(3,0,0)
6815 g_object_unref(G_OBJECT(c));
6816# else
Bram Moolenaar30613902019-12-01 22:11:18 +01006817 gdk_cursor_destroy(c); // Unref, actually. Bloody GTK+ 1.
Bram Moolenaar98921892016-02-23 17:14:37 +01006818# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006819 }
6820 if (shape != MSHAPE_HIDE)
6821 last_shape = shape;
6822}
Bram Moolenaar30613902019-12-01 22:11:18 +01006823#endif // FEAT_MOUSESHAPE
Bram Moolenaar071d4272004-06-13 20:20:40 +00006824
6825
6826#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
6827/*
6828 * Signs are currently always 2 chars wide. With GTK+ 2, the image will be
6829 * scaled down if the current font is not big enough, or scaled up if the image
6830 * size is less than 3/4 of the maximum sign size. With GTK+ 1, the pixmap
6831 * will be cut off if the current font is not big enough, or centered if it's
6832 * too small.
6833 */
6834# define SIGN_WIDTH (2 * gui.char_width)
6835# define SIGN_HEIGHT (gui.char_height)
6836# define SIGN_ASPECT ((double)SIGN_HEIGHT / (double)SIGN_WIDTH)
6837
Bram Moolenaar071d4272004-06-13 20:20:40 +00006838 void
6839gui_mch_drawsign(int row, int col, int typenr)
6840{
6841 GdkPixbuf *sign;
6842
6843 sign = (GdkPixbuf *)sign_get_image(typenr);
6844
Bram Moolenaar98921892016-02-23 17:14:37 +01006845 if (sign != NULL && gui.drawarea != NULL
6846 && gtk_widget_get_window(gui.drawarea) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006847 {
6848 int width;
6849 int height;
6850 int xoffset;
6851 int yoffset;
6852 int need_scale;
6853
6854 width = gdk_pixbuf_get_width(sign);
6855 height = gdk_pixbuf_get_height(sign);
6856 /*
6857 * Decide whether we need to scale. Allow one pixel of border
6858 * width to be cut off, in order to avoid excessive scaling for
6859 * tiny differences in font size.
Bram Moolenaar58cbc912014-06-17 18:47:02 +02006860 * Do scale to fit the height to avoid gaps because of linespacing.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006861 */
6862 need_scale = (width > SIGN_WIDTH + 2
Bram Moolenaar58cbc912014-06-17 18:47:02 +02006863 || height != SIGN_HEIGHT
Bram Moolenaar071d4272004-06-13 20:20:40 +00006864 || (width < 3 * SIGN_WIDTH / 4
6865 && height < 3 * SIGN_HEIGHT / 4));
6866 if (need_scale)
6867 {
Bram Moolenaar58cbc912014-06-17 18:47:02 +02006868 double aspect;
6869 int w = width;
6870 int h = height;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006871
Bram Moolenaar30613902019-12-01 22:11:18 +01006872 // Keep the original aspect ratio
Bram Moolenaar071d4272004-06-13 20:20:40 +00006873 aspect = (double)height / (double)width;
6874 width = (double)SIGN_WIDTH * SIGN_ASPECT / aspect;
6875 width = MIN(width, SIGN_WIDTH);
Bram Moolenaar58cbc912014-06-17 18:47:02 +02006876 if (((double)(MAX(height, SIGN_HEIGHT)) /
6877 (double)(MIN(height, SIGN_HEIGHT))) < 1.15)
6878 {
Bram Moolenaar30613902019-12-01 22:11:18 +01006879 // Change the aspect ratio by at most 15% to fill the
6880 // available space completely.
Bram Moolenaar58cbc912014-06-17 18:47:02 +02006881 height = (double)SIGN_HEIGHT * SIGN_ASPECT / aspect;
6882 height = MIN(height, SIGN_HEIGHT);
6883 }
6884 else
6885 height = (double)width * aspect;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006886
Bram Moolenaar58cbc912014-06-17 18:47:02 +02006887 if (w == width && h == height)
6888 {
Bram Moolenaar30613902019-12-01 22:11:18 +01006889 // no change in dimensions; don't decrease reference counter
6890 // (below)
Bram Moolenaar58cbc912014-06-17 18:47:02 +02006891 need_scale = FALSE;
6892 }
6893 else
6894 {
Bram Moolenaar30613902019-12-01 22:11:18 +01006895 // This doesn't seem to be worth caching, and doing so would
6896 // complicate the code quite a bit.
Bram Moolenaar58cbc912014-06-17 18:47:02 +02006897 sign = gdk_pixbuf_scale_simple(sign, width, height,
6898 GDK_INTERP_BILINEAR);
6899 if (sign == NULL)
Bram Moolenaar30613902019-12-01 22:11:18 +01006900 return; // out of memory
Bram Moolenaar58cbc912014-06-17 18:47:02 +02006901 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006902 }
6903
Bram Moolenaar30613902019-12-01 22:11:18 +01006904 // The origin is the upper-left corner of the pixmap. Therefore
6905 // these offset may become negative if the pixmap is smaller than
6906 // the 2x1 cells reserved for the sign icon.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006907 xoffset = (width - SIGN_WIDTH) / 2;
6908 yoffset = (height - SIGN_HEIGHT) / 2;
6909
Bram Moolenaar98921892016-02-23 17:14:37 +01006910# if GTK_CHECK_VERSION(3,0,0)
6911 {
6912 cairo_t *cr;
6913 cairo_surface_t *bg_surf;
6914 cairo_t *bg_cr;
6915 cairo_surface_t *sign_surf;
6916 cairo_t *sign_cr;
6917
6918 cr = cairo_create(gui.surface);
6919
6920 bg_surf = cairo_surface_create_similar(gui.surface,
6921 cairo_surface_get_content(gui.surface),
6922 SIGN_WIDTH, SIGN_HEIGHT);
6923 bg_cr = cairo_create(bg_surf);
Bram Moolenaar36edf062016-07-21 22:10:12 +02006924 cairo_set_source_rgba(bg_cr,
6925 gui.bgcolor->red, gui.bgcolor->green, gui.bgcolor->blue,
6926 gui.bgcolor->alpha);
Bram Moolenaar98921892016-02-23 17:14:37 +01006927 cairo_paint(bg_cr);
6928
6929 sign_surf = cairo_surface_create_similar(gui.surface,
6930 cairo_surface_get_content(gui.surface),
6931 SIGN_WIDTH, SIGN_HEIGHT);
6932 sign_cr = cairo_create(sign_surf);
6933 gdk_cairo_set_source_pixbuf(sign_cr, sign, -xoffset, -yoffset);
6934 cairo_paint(sign_cr);
6935
6936 cairo_set_operator(sign_cr, CAIRO_OPERATOR_DEST_OVER);
6937 cairo_set_source_surface(sign_cr, bg_surf, 0, 0);
6938 cairo_paint(sign_cr);
6939
6940 cairo_set_source_surface(cr, sign_surf, FILL_X(col), FILL_Y(row));
6941 cairo_paint(cr);
6942
6943 cairo_destroy(sign_cr);
6944 cairo_surface_destroy(sign_surf);
6945 cairo_destroy(bg_cr);
6946 cairo_surface_destroy(bg_surf);
6947 cairo_destroy(cr);
6948
6949 if (!gui.by_signal)
6950 gtk_widget_queue_draw_area(gui.drawarea,
6951 FILL_X(col), FILL_Y(col), width, height);
6952
6953 }
Bram Moolenaar30613902019-12-01 22:11:18 +01006954# else // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006955 gdk_gc_set_foreground(gui.text_gc, gui.bgcolor);
6956
6957 gdk_draw_rectangle(gui.drawarea->window,
6958 gui.text_gc,
6959 TRUE,
6960 FILL_X(col),
6961 FILL_Y(row),
6962 SIGN_WIDTH,
6963 SIGN_HEIGHT);
6964
Bram Moolenaar071d4272004-06-13 20:20:40 +00006965 gdk_pixbuf_render_to_drawable_alpha(sign,
6966 gui.drawarea->window,
6967 MAX(0, xoffset),
6968 MAX(0, yoffset),
6969 FILL_X(col) - MIN(0, xoffset),
6970 FILL_Y(row) - MIN(0, yoffset),
6971 MIN(width, SIGN_WIDTH),
6972 MIN(height, SIGN_HEIGHT),
6973 GDK_PIXBUF_ALPHA_BILEVEL,
6974 127,
6975 GDK_RGB_DITHER_NORMAL,
6976 0, 0);
Bram Moolenaar30613902019-12-01 22:11:18 +01006977# endif // !GTK_CHECK_VERSION(3,0,0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006978 if (need_scale)
6979 g_object_unref(sign);
6980 }
6981}
6982
6983 void *
6984gui_mch_register_sign(char_u *signfile)
6985{
6986 if (signfile[0] != NUL && signfile[0] != '-' && gui.in_use)
6987 {
6988 GdkPixbuf *sign;
6989 GError *error = NULL;
6990 char_u *message;
6991
6992 sign = gdk_pixbuf_new_from_file((const char *)signfile, &error);
6993
6994 if (error == NULL)
6995 return sign;
6996
6997 message = (char_u *)error->message;
6998
6999 if (message != NULL && input_conv.vc_type != CONV_NONE)
7000 message = string_convert(&input_conv, message, NULL);
7001
7002 if (message != NULL)
7003 {
Bram Moolenaar30613902019-12-01 22:11:18 +01007004 // The error message is already translated and will be more
7005 // descriptive than anything we could possibly do ourselves.
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01007006 semsg("E255: %s", message);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007007
7008 if (input_conv.vc_type != CONV_NONE)
7009 vim_free(message);
7010 }
7011 g_error_free(error);
7012 }
7013
7014 return NULL;
7015}
7016
7017 void
7018gui_mch_destroy_sign(void *sign)
7019{
7020 if (sign != NULL)
7021 g_object_unref(sign);
7022}
7023
Bram Moolenaar30613902019-12-01 22:11:18 +01007024#endif // FEAT_SIGN_ICONS