blob: b525cf7a68449d821897ac1c030def4556c8313c [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 * GUI/Motif support by Robert Webb
5 *
6 * Do ":help uganda" in Vim to read copying and usage conditions.
7 * Do ":help credits" in Vim to see a list of people who contributed.
8 * See README.txt for an overview of the Vim source code.
9 */
10/*
11 * Common code for the Motif and Athena GUI.
12 * Not used for GTK.
13 */
14
15#include <X11/keysym.h>
16#include <X11/Xatom.h>
17#include <X11/StringDefs.h>
18#include <X11/Intrinsic.h>
19#include <X11/Shell.h>
20#include <X11/cursorfont.h>
21
22#include "vim.h"
23
24/*
25 * For Workshop XpmP.h is preferred, because it makes the signs drawn with a
26 * transparent background instead of black.
27 */
28#if defined(HAVE_XM_XPMP_H) && defined(FEAT_GUI_MOTIF) \
29 && (!defined(HAVE_X11_XPM_H) || defined(FEAT_SUN_WORKSHOP))
30# include <Xm/XpmP.h>
31#else
32# ifdef HAVE_X11_XPM_H
33# include <X11/xpm.h>
34# endif
35#endif
36
37#ifdef FEAT_XFONTSET
38# ifdef X_LOCALE
39# include <X11/Xlocale.h>
40# else
41# include <locale.h>
42# endif
43#endif
44
45#ifdef HAVE_X11_SUNKEYSYM_H
46# include <X11/Sunkeysym.h>
47#endif
48
49#ifdef HAVE_X11_XMU_EDITRES_H
50# include <X11/Xmu/Editres.h>
51#endif
52
53#ifdef FEAT_BEVAL_TIP
54# include "gui_beval.h"
55#endif
56
57#define VIM_NAME "vim"
58#define VIM_CLASS "Vim"
59
60/* Default resource values */
61#define DFLT_FONT "7x13"
62#ifdef FONTSET_ALWAYS
63# define DFLT_MENU_FONT XtDefaultFontSet
64#else
65# define DFLT_MENU_FONT XtDefaultFont
66#endif
67#define DFLT_TOOLTIP_FONT XtDefaultFontSet
68
69#ifdef FEAT_GUI_ATHENA
70# define DFLT_MENU_BG_COLOR "gray77"
71# define DFLT_MENU_FG_COLOR "black"
72# define DFLT_SCROLL_BG_COLOR "gray60"
73# define DFLT_SCROLL_FG_COLOR "gray77"
74# define DFLT_TOOLTIP_BG_COLOR "#ffffffff9191"
75# define DFLT_TOOLTIP_FG_COLOR "#000000000000"
76#else
77/* use the default (CDE) colors */
78# define DFLT_MENU_BG_COLOR ""
79# define DFLT_MENU_FG_COLOR ""
80# define DFLT_SCROLL_BG_COLOR ""
81# define DFLT_SCROLL_FG_COLOR ""
82# define DFLT_TOOLTIP_BG_COLOR "#ffffffff9191"
83# define DFLT_TOOLTIP_FG_COLOR "#000000000000"
84#endif
85
86Widget vimShell = (Widget)0;
87
88static Atom wm_atoms[2]; /* Window Manager Atoms */
89#define DELETE_WINDOW_IDX 0 /* index in wm_atoms[] for WM_DELETE_WINDOW */
90#define SAVE_YOURSELF_IDX 1 /* index in wm_atoms[] for WM_SAVE_YOURSELF */
91
92#ifdef FEAT_XFONTSET
93/*
94 * We either draw with a fontset (when current_fontset != NULL) or with a
95 * normal font (current_fontset == NULL, use gui.text_gc and gui.back_gc).
96 */
97static XFontSet current_fontset = NULL;
98
99#define XDrawString(dpy, win, gc, x, y, str, n) \
100 do \
101 { \
102 if (current_fontset != NULL) \
103 XmbDrawString(dpy, win, current_fontset, gc, x, y, str, n); \
104 else \
105 XDrawString(dpy, win, gc, x, y, str, n); \
106 } while (0)
107
108#define XDrawString16(dpy, win, gc, x, y, str, n) \
109 do \
110 { \
111 if (current_fontset != NULL) \
112 XwcDrawString(dpy, win, current_fontset, gc, x, y, (wchar_t *)str, n); \
113 else \
Bram Moolenaara3227e22006-03-08 21:32:40 +0000114 XDrawString16(dpy, win, gc, x, y, (XChar2b *)str, n); \
115 } while (0)
116
117#define XDrawImageString16(dpy, win, gc, x, y, str, n) \
118 do \
119 { \
120 if (current_fontset != NULL) \
121 XwcDrawImageString(dpy, win, current_fontset, gc, x, y, (wchar_t *)str, n); \
122 else \
123 XDrawImageString16(dpy, win, gc, x, y, (XChar2b *)str, n); \
Bram Moolenaar071d4272004-06-13 20:20:40 +0000124 } while (0)
125
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100126static int check_fontset_sanity(XFontSet fs);
127static int fontset_width(XFontSet fs);
128static int fontset_ascent(XFontSet fs);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000129#endif
130
131static guicolor_T prev_fg_color = INVALCOLOR;
132static guicolor_T prev_bg_color = INVALCOLOR;
Bram Moolenaarfb269802005-03-15 22:46:30 +0000133static guicolor_T prev_sp_color = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000134
135#if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
136static XButtonPressedEvent last_mouse_event;
137#endif
138
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100139static int find_closest_color(Colormap colormap, XColor *colorPtr);
140static void gui_x11_timer_cb(XtPointer timed_out, XtIntervalId *interval_id);
141static void gui_x11_visibility_cb(Widget w, XtPointer dud, XEvent *event, Boolean *dum);
142static void gui_x11_expose_cb(Widget w, XtPointer dud, XEvent *event, Boolean *dum);
143static void gui_x11_resize_window_cb(Widget w, XtPointer dud, XEvent *event, Boolean *dum);
144static void gui_x11_focus_change_cb(Widget w, XtPointer data, XEvent *event, Boolean *dum);
145static void gui_x11_enter_cb(Widget w, XtPointer data, XEvent *event, Boolean *dum);
146static void gui_x11_leave_cb(Widget w, XtPointer data, XEvent *event, Boolean *dum);
147static void gui_x11_mouse_cb(Widget w, XtPointer data, XEvent *event, Boolean *dum);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100148static void gui_x11_check_copy_area(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000149#ifdef FEAT_CLIENTSERVER
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100150static void gui_x11_send_event_handler(Widget, XtPointer, XEvent *, Boolean *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000151#endif
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +0100152static void gui_x11_wm_protocol_handler(Widget, XtPointer, XEvent *, Boolean *);
153static void gui_x11_blink_cb(XtPointer timed_out, XtIntervalId *interval_id);
154static Cursor gui_x11_create_blank_mouse(void);
155static void draw_curl(int row, int col, int cells);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000156
157
158/*
159 * Keycodes recognized by vim.
160 * NOTE: when changing this, the table in gui_gtk_x11.c probably needs the
161 * same change!
162 */
163static struct specialkey
164{
165 KeySym key_sym;
166 char_u vim_code0;
167 char_u vim_code1;
168} special_keys[] =
169{
170 {XK_Up, 'k', 'u'},
171 {XK_Down, 'k', 'd'},
172 {XK_Left, 'k', 'l'},
173 {XK_Right, 'k', 'r'},
174
175 {XK_F1, 'k', '1'},
176 {XK_F2, 'k', '2'},
177 {XK_F3, 'k', '3'},
178 {XK_F4, 'k', '4'},
179 {XK_F5, 'k', '5'},
180 {XK_F6, 'k', '6'},
181 {XK_F7, 'k', '7'},
182 {XK_F8, 'k', '8'},
183 {XK_F9, 'k', '9'},
184 {XK_F10, 'k', ';'},
185
186 {XK_F11, 'F', '1'},
187 {XK_F12, 'F', '2'},
188 {XK_F13, 'F', '3'},
189 {XK_F14, 'F', '4'},
190 {XK_F15, 'F', '5'},
191 {XK_F16, 'F', '6'},
192 {XK_F17, 'F', '7'},
193 {XK_F18, 'F', '8'},
194 {XK_F19, 'F', '9'},
195 {XK_F20, 'F', 'A'},
196
197 {XK_F21, 'F', 'B'},
198 {XK_F22, 'F', 'C'},
199 {XK_F23, 'F', 'D'},
200 {XK_F24, 'F', 'E'},
201 {XK_F25, 'F', 'F'},
202 {XK_F26, 'F', 'G'},
203 {XK_F27, 'F', 'H'},
204 {XK_F28, 'F', 'I'},
205 {XK_F29, 'F', 'J'},
206 {XK_F30, 'F', 'K'},
207
208 {XK_F31, 'F', 'L'},
209 {XK_F32, 'F', 'M'},
210 {XK_F33, 'F', 'N'},
211 {XK_F34, 'F', 'O'},
212 {XK_F35, 'F', 'P'}, /* keysymdef.h defines up to F35 */
213#ifdef SunXK_F36
214 {SunXK_F36, 'F', 'Q'},
215 {SunXK_F37, 'F', 'R'},
216#endif
217
218 {XK_Help, '%', '1'},
219 {XK_Undo, '&', '8'},
220 {XK_BackSpace, 'k', 'b'},
221 {XK_Insert, 'k', 'I'},
222 {XK_Delete, 'k', 'D'},
223 {XK_Home, 'k', 'h'},
224 {XK_End, '@', '7'},
225 {XK_Prior, 'k', 'P'},
226 {XK_Next, 'k', 'N'},
227 {XK_Print, '%', '9'},
228
229 /* Keypad keys: */
230#ifdef XK_KP_Left
231 {XK_KP_Left, 'k', 'l'},
232 {XK_KP_Right, 'k', 'r'},
233 {XK_KP_Up, 'k', 'u'},
234 {XK_KP_Down, 'k', 'd'},
235 {XK_KP_Insert, KS_EXTRA, (char_u)KE_KINS},
236 {XK_KP_Delete, KS_EXTRA, (char_u)KE_KDEL},
237 {XK_KP_Home, 'K', '1'},
238 {XK_KP_End, 'K', '4'},
239 {XK_KP_Prior, 'K', '3'},
240 {XK_KP_Next, 'K', '5'},
241
242 {XK_KP_Add, 'K', '6'},
243 {XK_KP_Subtract, 'K', '7'},
244 {XK_KP_Divide, 'K', '8'},
245 {XK_KP_Multiply, 'K', '9'},
246 {XK_KP_Enter, 'K', 'A'},
247 {XK_KP_Decimal, 'K', 'B'},
248
249 {XK_KP_0, 'K', 'C'},
250 {XK_KP_1, 'K', 'D'},
251 {XK_KP_2, 'K', 'E'},
252 {XK_KP_3, 'K', 'F'},
253 {XK_KP_4, 'K', 'G'},
254 {XK_KP_5, 'K', 'H'},
255 {XK_KP_6, 'K', 'I'},
256 {XK_KP_7, 'K', 'J'},
257 {XK_KP_8, 'K', 'K'},
258 {XK_KP_9, 'K', 'L'},
259#endif
260
261 /* End of list marker: */
262 {(KeySym)0, 0, 0}
263};
264
265#define XtNboldFont "boldFont"
266#define XtCBoldFont "BoldFont"
267#define XtNitalicFont "italicFont"
268#define XtCItalicFont "ItalicFont"
269#define XtNboldItalicFont "boldItalicFont"
270#define XtCBoldItalicFont "BoldItalicFont"
271#define XtNscrollbarWidth "scrollbarWidth"
272#define XtCScrollbarWidth "ScrollbarWidth"
273#define XtNmenuHeight "menuHeight"
274#define XtCMenuHeight "MenuHeight"
275#define XtNmenuFont "menuFont"
276#define XtCMenuFont "MenuFont"
277#define XtNmenuFontSet "menuFontSet"
278#define XtCMenuFontSet "MenuFontSet"
279
280
281/* Resources for setting the foreground and background colors of menus */
282#define XtNmenuBackground "menuBackground"
283#define XtCMenuBackground "MenuBackground"
284#define XtNmenuForeground "menuForeground"
285#define XtCMenuForeground "MenuForeground"
286
287/* Resources for setting the foreground and background colors of scrollbars */
288#define XtNscrollBackground "scrollBackground"
289#define XtCScrollBackground "ScrollBackground"
290#define XtNscrollForeground "scrollForeground"
291#define XtCScrollForeground "ScrollForeground"
292
293/* Resources for setting the foreground and background colors of tooltip */
294#define XtNtooltipBackground "tooltipBackground"
295#define XtCTooltipBackground "TooltipBackground"
296#define XtNtooltipForeground "tooltipForeground"
297#define XtCTooltipForeground "TooltipForeground"
298#define XtNtooltipFont "tooltipFont"
299#define XtCTooltipFont "TooltipFont"
300
301/*
302 * X Resources:
303 */
304static XtResource vim_resources[] =
305{
306 {
307 XtNforeground,
308 XtCForeground,
309 XtRPixel,
310 sizeof(Pixel),
311 XtOffsetOf(gui_T, def_norm_pixel),
312 XtRString,
313 XtDefaultForeground
314 },
315 {
316 XtNbackground,
317 XtCBackground,
318 XtRPixel,
319 sizeof(Pixel),
320 XtOffsetOf(gui_T, def_back_pixel),
321 XtRString,
322 XtDefaultBackground
323 },
324 {
325 XtNfont,
326 XtCFont,
327 XtRString,
328 sizeof(String *),
329 XtOffsetOf(gui_T, rsrc_font_name),
330 XtRImmediate,
331 XtDefaultFont
332 },
333 {
334 XtNboldFont,
335 XtCBoldFont,
336 XtRString,
337 sizeof(String *),
338 XtOffsetOf(gui_T, rsrc_bold_font_name),
339 XtRImmediate,
340 ""
341 },
342 {
343 XtNitalicFont,
344 XtCItalicFont,
345 XtRString,
346 sizeof(String *),
347 XtOffsetOf(gui_T, rsrc_ital_font_name),
348 XtRImmediate,
349 ""
350 },
351 {
352 XtNboldItalicFont,
353 XtCBoldItalicFont,
354 XtRString,
355 sizeof(String *),
356 XtOffsetOf(gui_T, rsrc_boldital_font_name),
357 XtRImmediate,
358 ""
359 },
360 {
361 XtNgeometry,
362 XtCGeometry,
363 XtRString,
364 sizeof(String *),
365 XtOffsetOf(gui_T, geom),
366 XtRImmediate,
367 ""
368 },
369 {
370 XtNreverseVideo,
371 XtCReverseVideo,
372 XtRBool,
373 sizeof(Bool),
374 XtOffsetOf(gui_T, rsrc_rev_video),
375 XtRImmediate,
376 (XtPointer)False
377 },
378 {
379 XtNborderWidth,
380 XtCBorderWidth,
381 XtRInt,
382 sizeof(int),
383 XtOffsetOf(gui_T, border_width),
384 XtRImmediate,
385 (XtPointer)2
386 },
387 {
388 XtNscrollbarWidth,
389 XtCScrollbarWidth,
390 XtRInt,
391 sizeof(int),
392 XtOffsetOf(gui_T, scrollbar_width),
393 XtRImmediate,
394 (XtPointer)SB_DEFAULT_WIDTH
395 },
396#ifdef FEAT_MENU
397# ifdef FEAT_GUI_ATHENA /* with Motif the height is always computed */
398 {
399 XtNmenuHeight,
400 XtCMenuHeight,
401 XtRInt,
402 sizeof(int),
403 XtOffsetOf(gui_T, menu_height),
404 XtRImmediate,
405 (XtPointer)MENU_DEFAULT_HEIGHT /* Should figure out at run time */
406 },
407# endif
408 {
409# ifdef FONTSET_ALWAYS
410 XtNmenuFontSet,
411 XtCMenuFontSet,
412#else
413 XtNmenuFont,
414 XtCMenuFont,
415#endif
416 XtRString,
417 sizeof(char *),
418 XtOffsetOf(gui_T, rsrc_menu_font_name),
419 XtRString,
420 DFLT_MENU_FONT
421 },
422#endif
423 {
424 XtNmenuForeground,
425 XtCMenuForeground,
426 XtRString,
427 sizeof(char *),
428 XtOffsetOf(gui_T, rsrc_menu_fg_name),
429 XtRString,
430 DFLT_MENU_FG_COLOR
431 },
432 {
433 XtNmenuBackground,
434 XtCMenuBackground,
435 XtRString,
436 sizeof(char *),
437 XtOffsetOf(gui_T, rsrc_menu_bg_name),
438 XtRString,
439 DFLT_MENU_BG_COLOR
440 },
441 {
442 XtNscrollForeground,
443 XtCScrollForeground,
444 XtRString,
445 sizeof(char *),
446 XtOffsetOf(gui_T, rsrc_scroll_fg_name),
447 XtRString,
448 DFLT_SCROLL_FG_COLOR
449 },
450 {
451 XtNscrollBackground,
452 XtCScrollBackground,
453 XtRString,
454 sizeof(char *),
455 XtOffsetOf(gui_T, rsrc_scroll_bg_name),
456 XtRString,
457 DFLT_SCROLL_BG_COLOR
458 },
459#ifdef FEAT_BEVAL
460 {
461 XtNtooltipForeground,
462 XtCTooltipForeground,
463 XtRString,
464 sizeof(char *),
465 XtOffsetOf(gui_T, rsrc_tooltip_fg_name),
466 XtRString,
467 DFLT_TOOLTIP_FG_COLOR
468 },
469 {
470 XtNtooltipBackground,
471 XtCTooltipBackground,
472 XtRString,
473 sizeof(char *),
474 XtOffsetOf(gui_T, rsrc_tooltip_bg_name),
475 XtRString,
476 DFLT_TOOLTIP_BG_COLOR
477 },
478 {
479 XtNtooltipFont,
480 XtCTooltipFont,
481 XtRString,
482 sizeof(char *),
483 XtOffsetOf(gui_T, rsrc_tooltip_font_name),
484 XtRString,
485 DFLT_TOOLTIP_FONT
486 },
487 /* This one isn't really needed, keep for Sun Workshop? */
488 {
489 "balloonEvalFontSet",
490 XtCFontSet,
491 XtRFontSet,
492 sizeof(XFontSet),
493 XtOffsetOf(gui_T, tooltip_fontset),
494 XtRImmediate,
495 (XtPointer)NOFONTSET
496 },
497#endif /* FEAT_BEVAL */
498#ifdef FEAT_XIM
499 {
500 "preeditType",
501 "PreeditType",
502 XtRString,
503 sizeof(char*),
504 XtOffsetOf(gui_T, rsrc_preedit_type_name),
505 XtRString,
506 (XtPointer)"OverTheSpot,OffTheSpot,Root"
507 },
508 {
509 "inputMethod",
510 "InputMethod",
511 XtRString,
512 sizeof(char*),
513 XtOffsetOf(gui_T, rsrc_input_method),
514 XtRString,
515 NULL
516 },
517#endif /* FEAT_XIM */
518};
519
520/*
521 * This table holds all the X GUI command line options allowed. This includes
522 * the standard ones so that we can skip them when vim is started without the
523 * GUI (but the GUI might start up later).
524 * When changing this, also update doc/vim_gui.txt and the usage message!!!
525 */
526static XrmOptionDescRec cmdline_options[] =
527{
528 /* We handle these options ourselves */
529 {"-bg", ".background", XrmoptionSepArg, NULL},
530 {"-background", ".background", XrmoptionSepArg, NULL},
531 {"-fg", ".foreground", XrmoptionSepArg, NULL},
532 {"-foreground", ".foreground", XrmoptionSepArg, NULL},
533 {"-fn", ".font", XrmoptionSepArg, NULL},
534 {"-font", ".font", XrmoptionSepArg, NULL},
535 {"-boldfont", ".boldFont", XrmoptionSepArg, NULL},
536 {"-italicfont", ".italicFont", XrmoptionSepArg, NULL},
537 {"-geom", ".geometry", XrmoptionSepArg, NULL},
538 {"-geometry", ".geometry", XrmoptionSepArg, NULL},
539 {"-reverse", "*reverseVideo", XrmoptionNoArg, "True"},
540 {"-rv", "*reverseVideo", XrmoptionNoArg, "True"},
541 {"+reverse", "*reverseVideo", XrmoptionNoArg, "False"},
542 {"+rv", "*reverseVideo", XrmoptionNoArg, "False"},
543 {"-display", ".display", XrmoptionSepArg, NULL},
Bram Moolenaara5792f52005-11-23 21:25:05 +0000544 {"-iconic", ".iconic", XrmoptionNoArg, "True"},
Bram Moolenaar071d4272004-06-13 20:20:40 +0000545 {"-name", ".name", XrmoptionSepArg, NULL},
546 {"-bw", ".borderWidth", XrmoptionSepArg, NULL},
547 {"-borderwidth", ".borderWidth", XrmoptionSepArg, NULL},
548 {"-sw", ".scrollbarWidth", XrmoptionSepArg, NULL},
549 {"-scrollbarwidth", ".scrollbarWidth", XrmoptionSepArg, NULL},
550 {"-mh", ".menuHeight", XrmoptionSepArg, NULL},
551 {"-menuheight", ".menuHeight", XrmoptionSepArg, NULL},
552#ifdef FONTSET_ALWAYS
553 {"-mf", ".menuFontSet", XrmoptionSepArg, NULL},
554 {"-menufont", ".menuFontSet", XrmoptionSepArg, NULL},
555 {"-menufontset", ".menuFontSet", XrmoptionSepArg, NULL},
556#else
557 {"-mf", ".menuFont", XrmoptionSepArg, NULL},
558 {"-menufont", ".menuFont", XrmoptionSepArg, NULL},
559#endif
560 {"-xrm", NULL, XrmoptionResArg, NULL}
561};
562
563static int gui_argc = 0;
564static char **gui_argv = NULL;
565
566/*
567 * Call-back routines.
568 */
569
Bram Moolenaar071d4272004-06-13 20:20:40 +0000570 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100571gui_x11_timer_cb(
572 XtPointer timed_out,
573 XtIntervalId *interval_id UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000574{
575 *((int *)timed_out) = TRUE;
576}
577
Bram Moolenaar071d4272004-06-13 20:20:40 +0000578 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100579gui_x11_visibility_cb(
580 Widget w UNUSED,
581 XtPointer dud UNUSED,
582 XEvent *event,
583 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000584{
585 if (event->type != VisibilityNotify)
586 return;
587
588 gui.visibility = event->xvisibility.state;
589
590 /*
591 * When we do an XCopyArea(), and the window is partially obscured, we want
592 * to receive an event to tell us whether it worked or not.
593 */
594 XSetGraphicsExposures(gui.dpy, gui.text_gc,
595 gui.visibility != VisibilityUnobscured);
596
597 /* This is needed for when redrawing is slow. */
598 gui_mch_update();
599}
600
Bram Moolenaar071d4272004-06-13 20:20:40 +0000601 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100602gui_x11_expose_cb(
603 Widget w UNUSED,
604 XtPointer dud UNUSED,
605 XEvent *event,
606 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000607{
608 XExposeEvent *gevent;
609 int new_x;
610
611 if (event->type != Expose)
612 return;
613
614 out_flush(); /* make sure all output has been processed */
615
616 gevent = (XExposeEvent *)event;
617 gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height);
618
619 new_x = FILL_X(0);
620
621 /* Clear the border areas if needed */
622 if (gevent->x < new_x)
623 XClearArea(gui.dpy, gui.wid, 0, 0, new_x, 0, False);
624 if (gevent->y < FILL_Y(0))
625 XClearArea(gui.dpy, gui.wid, 0, 0, 0, FILL_Y(0), False);
626 if (gevent->x > FILL_X(Columns))
627 XClearArea(gui.dpy, gui.wid, FILL_X((int)Columns), 0, 0, 0, False);
628 if (gevent->y > FILL_Y(Rows))
629 XClearArea(gui.dpy, gui.wid, 0, FILL_Y((int)Rows), 0, 0, False);
630
631 /* This is needed for when redrawing is slow. */
632 gui_mch_update();
633}
634
Bram Moolenaar67c53842010-05-22 18:28:27 +0200635#if ((defined(FEAT_NETBEANS_INTG) || defined(FEAT_SUN_WORKSHOP)) \
636 && defined(FEAT_GUI_MOTIF)) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637/*
Bram Moolenaar231334e2005-07-25 20:46:57 +0000638 * This function fills in the XRectangle object with the current x,y
639 * coordinates and height, width so that an XtVaSetValues to the same shell of
Bram Moolenaar49325942007-05-10 19:19:59 +0000640 * those resources will restore the window to its former position and
Bram Moolenaar231334e2005-07-25 20:46:57 +0000641 * dimensions.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000642 *
Bram Moolenaar231334e2005-07-25 20:46:57 +0000643 * Note: This function may fail, in which case the XRectangle will be
644 * unchanged. Be sure to have the XRectangle set with the proper values for a
645 * failed condition prior to calling this function.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000646 */
647 static void
648shellRectangle(Widget shell, XRectangle *r)
649{
650 Window rootw, shellw, child, parentw;
651 int absx, absy;
652 XWindowAttributes a;
653 Window *children;
654 unsigned int childrenCount;
655
656 shellw = XtWindow(shell);
657 if (shellw == 0)
658 return;
659 for (;;)
660 {
661 XQueryTree(XtDisplay(shell), shellw, &rootw, &parentw,
662 &children, &childrenCount);
663 XFree(children);
664 if (parentw == rootw)
665 break;
666 shellw = parentw;
667 }
668 XGetWindowAttributes(XtDisplay(shell), shellw, &a);
669 XTranslateCoordinates(XtDisplay(shell), shellw, a.root, 0, 0,
670 &absx, &absy, &child);
671 r->x = absx;
672 r->y = absy;
673 XtVaGetValues(shell, XmNheight, &r->height, XmNwidth, &r->width, NULL);
674}
675#endif
676
Bram Moolenaar071d4272004-06-13 20:20:40 +0000677 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100678gui_x11_resize_window_cb(
679 Widget w UNUSED,
680 XtPointer dud UNUSED,
681 XEvent *event,
682 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000683{
684 static int lastWidth, lastHeight;
685
686 if (event->type != ConfigureNotify)
687 return;
688
689 if (event->xconfigure.width != lastWidth
690 || event->xconfigure.height != lastHeight)
691 {
692 lastWidth = event->xconfigure.width;
693 lastHeight = event->xconfigure.height;
694 gui_resize_shell(event->xconfigure.width, event->xconfigure.height
695#ifdef FEAT_XIM
696 - xim_get_status_area_height()
697#endif
698 );
699 }
700#ifdef FEAT_SUN_WORKSHOP
701 if (usingSunWorkShop)
702 {
703 XRectangle rec;
704
705 shellRectangle(w, &rec);
706 workshop_frame_moved(rec.x, rec.y, rec.width, rec.height);
707 }
708#endif
Bram Moolenaar67c53842010-05-22 18:28:27 +0200709#if defined(FEAT_NETBEANS_INTG) && defined(FEAT_GUI_MOTIF)
Bram Moolenaarb26e6322010-05-22 21:34:09 +0200710 if (netbeans_active())
Bram Moolenaar071d4272004-06-13 20:20:40 +0000711 {
712 XRectangle rec;
713
714 shellRectangle(w, &rec);
715 netbeans_frame_moved(rec.x, rec.y);
716 }
717#endif
718#ifdef FEAT_XIM
719 xim_set_preedit();
720#endif
721}
722
Bram Moolenaar071d4272004-06-13 20:20:40 +0000723 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100724gui_x11_focus_change_cb(
725 Widget w UNUSED,
726 XtPointer data UNUSED,
727 XEvent *event,
728 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000729{
730 gui_focus_change(event->type == FocusIn);
731}
732
Bram Moolenaar071d4272004-06-13 20:20:40 +0000733 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100734gui_x11_enter_cb(
735 Widget w UNUSED,
736 XtPointer data UNUSED,
737 XEvent *event UNUSED,
738 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000739{
740 gui_focus_change(TRUE);
741}
742
Bram Moolenaar071d4272004-06-13 20:20:40 +0000743 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100744gui_x11_leave_cb(
745 Widget w UNUSED,
746 XtPointer data UNUSED,
747 XEvent *event UNUSED,
748 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000749{
750 gui_focus_change(FALSE);
751}
752
753#if defined(X_HAVE_UTF8_STRING) && defined(FEAT_MBYTE)
754# if X_HAVE_UTF8_STRING
755# define USE_UTF8LOOKUP
756# endif
757#endif
758
Bram Moolenaar071d4272004-06-13 20:20:40 +0000759 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +0100760gui_x11_key_hit_cb(
761 Widget w UNUSED,
762 XtPointer dud UNUSED,
763 XEvent *event,
764 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765{
766 XKeyPressedEvent *ev_press;
767#ifdef FEAT_XIM
768 char_u string2[256];
769 char_u string_shortbuf[256];
770 char_u *string = string_shortbuf;
771 Boolean string_alloced = False;
772 Status status;
773#else
774 char_u string[4], string2[3];
775#endif
776 KeySym key_sym, key_sym2;
777 int len, len2;
778 int i;
779 int modifiers;
780 int key;
781
782 ev_press = (XKeyPressedEvent *)event;
783
784#ifdef FEAT_XIM
785 if (xic)
786 {
787# ifdef USE_UTF8LOOKUP
788 /* XFree86 4.0.2 or newer: Be able to get UTF-8 characters even when
789 * the locale isn't utf-8. */
790 if (enc_utf8)
791 len = Xutf8LookupString(xic, ev_press, (char *)string,
792 sizeof(string_shortbuf), &key_sym, &status);
793 else
794# endif
795 len = XmbLookupString(xic, ev_press, (char *)string,
796 sizeof(string_shortbuf), &key_sym, &status);
797 if (status == XBufferOverflow)
798 {
799 string = (char_u *)XtMalloc(len + 1);
800 string_alloced = True;
801# ifdef USE_UTF8LOOKUP
802 /* XFree86 4.0.2 or newer: Be able to get UTF-8 characters even
803 * when the locale isn't utf-8. */
804 if (enc_utf8)
805 len = Xutf8LookupString(xic, ev_press, (char *)string,
806 len, &key_sym, &status);
807 else
808# endif
809 len = XmbLookupString(xic, ev_press, (char *)string,
810 len, &key_sym, &status);
811 }
812 if (status == XLookupNone || status == XLookupChars)
813 key_sym = XK_VoidSymbol;
814
815# ifdef FEAT_MBYTE
816 /* Do conversion from 'termencoding' to 'encoding'. When using
817 * Xutf8LookupString() it has already been done. */
818 if (len > 0 && input_conv.vc_type != CONV_NONE
819# ifdef USE_UTF8LOOKUP
820 && !enc_utf8
821# endif
822 )
823 {
824 int maxlen = len * 4 + 40; /* guessed */
825 char_u *p = (char_u *)XtMalloc(maxlen);
826
827 mch_memmove(p, string, len);
828 if (string_alloced)
829 XtFree((char *)string);
830 string = p;
831 string_alloced = True;
832 len = convert_input(p, len, maxlen);
833 }
834# endif
835
836 /* Translate CSI to K_CSI, otherwise it could be recognized as the
837 * start of a special key. */
838 for (i = 0; i < len; ++i)
839 if (string[i] == CSI)
840 {
841 char_u *p = (char_u *)XtMalloc(len + 3);
842
843 mch_memmove(p, string, i + 1);
844 p[i + 1] = KS_EXTRA;
845 p[i + 2] = (int)KE_CSI;
846 mch_memmove(p + i + 3, string + i + 1, len - i);
847 if (string_alloced)
848 XtFree((char *)string);
849 string = p;
850 string_alloced = True;
851 i += 2;
852 len += 2;
853 }
854 }
855 else
856#endif
857 len = XLookupString(ev_press, (char *)string, sizeof(string),
858 &key_sym, NULL);
859
860#ifdef SunXK_F36
861 /*
862 * These keys have bogus lookup strings, and trapping them here is
863 * easier than trying to XRebindKeysym() on them with every possible
864 * combination of modifiers.
865 */
866 if (key_sym == SunXK_F36 || key_sym == SunXK_F37)
867 len = 0;
868#endif
869
870#ifdef FEAT_HANGULIN
871 if ((key_sym == XK_space) && (ev_press->state & ShiftMask))
872 {
873 hangul_input_state_toggle();
874 goto theend;
875 }
876#endif
877
878 if (key_sym == XK_space)
879 string[0] = ' '; /* Otherwise Ctrl-Space doesn't work */
880
881 /*
882 * Only on some machines ^_ requires Ctrl+Shift+minus. For consistency,
883 * allow just Ctrl+minus too.
884 */
885 if (key_sym == XK_minus && (ev_press->state & ControlMask))
886 string[0] = Ctrl__;
887
888#ifdef XK_ISO_Left_Tab
889 /* why do we get XK_ISO_Left_Tab instead of XK_Tab for shift-tab? */
890 if (key_sym == XK_ISO_Left_Tab)
891 {
892 key_sym = XK_Tab;
893 string[0] = TAB;
894 len = 1;
895 }
896#endif
897
898 /* Check for Alt/Meta key (Mod1Mask), but not for a BS, DEL or character
899 * that already has the 8th bit set. And not when using a double-byte
900 * encoding, setting the 8th bit may make it the lead byte of a
901 * double-byte character. */
902 if (len == 1
903 && (ev_press->state & Mod1Mask)
904 && !(key_sym == XK_BackSpace || key_sym == XK_Delete)
905 && (string[0] & 0x80) == 0
906#ifdef FEAT_MBYTE
907 && !enc_dbcs
908#endif
909 )
910 {
911#if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
912 /* Ignore ALT keys when they are used for the menu only */
913 if (gui.menu_is_active
914 && (p_wak[0] == 'y'
915 || (p_wak[0] == 'm' && gui_is_menu_shortcut(string[0]))))
916 goto theend;
917#endif
918 /*
919 * Before we set the 8th bit, check to make sure the user doesn't
920 * already have a mapping defined for this sequence. We determine this
921 * by checking to see if the input would be the same without the
922 * Alt/Meta key.
923 * Don't do this for <S-M-Tab>, that should become K_S_TAB with ALT.
924 */
925 ev_press->state &= ~Mod1Mask;
926 len2 = XLookupString(ev_press, (char *)string2, sizeof(string2),
927 &key_sym2, NULL);
928 if (key_sym2 == XK_space)
929 string2[0] = ' '; /* Otherwise Meta-Ctrl-Space doesn't work */
930 if ( len2 == 1
931 && string[0] == string2[0]
932 && !(key_sym == XK_Tab && (ev_press->state & ShiftMask)))
933 {
934 string[0] |= 0x80;
935#ifdef FEAT_MBYTE
936 if (enc_utf8) /* convert to utf-8 */
937 {
938 string[1] = string[0] & 0xbf;
939 string[0] = ((unsigned)string[0] >> 6) + 0xc0;
940 if (string[1] == CSI)
941 {
942 string[2] = KS_EXTRA;
943 string[3] = (int)KE_CSI;
944 len = 4;
945 }
946 else
947 len = 2;
948 }
949#endif
950 }
951 else
952 ev_press->state |= Mod1Mask;
953 }
954
955 if (len == 1 && string[0] == CSI)
956 {
957 string[1] = KS_EXTRA;
958 string[2] = (int)KE_CSI;
959 len = -3;
960 }
961
962 /* Check for special keys. Also do this when len == 1 (key has an ASCII
963 * value) to detect backspace, delete and keypad keys. */
964 if (len == 0 || len == 1)
965 {
966 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
967 {
968 if (special_keys[i].key_sym == key_sym)
969 {
970 string[0] = CSI;
971 string[1] = special_keys[i].vim_code0;
972 string[2] = special_keys[i].vim_code1;
973 len = -3;
974 break;
975 }
976 }
977 }
978
979 /* Unrecognised key is ignored. */
980 if (len == 0)
981 goto theend;
982
983 /* Special keys (and a few others) may have modifiers. Also when using a
984 * double-byte encoding (can't set the 8th bit). */
985 if (len == -3 || key_sym == XK_space || key_sym == XK_Tab
986 || key_sym == XK_Return || key_sym == XK_Linefeed
987 || key_sym == XK_Escape
988#ifdef FEAT_MBYTE
989 || (enc_dbcs && len == 1 && (ev_press->state & Mod1Mask))
990#endif
991 )
992 {
993 modifiers = 0;
994 if (ev_press->state & ShiftMask)
995 modifiers |= MOD_MASK_SHIFT;
996 if (ev_press->state & ControlMask)
997 modifiers |= MOD_MASK_CTRL;
998 if (ev_press->state & Mod1Mask)
999 modifiers |= MOD_MASK_ALT;
Bram Moolenaar19a09a12005-03-04 23:39:37 +00001000 if (ev_press->state & Mod4Mask)
1001 modifiers |= MOD_MASK_META;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001002
1003 /*
1004 * For some keys a shift modifier is translated into another key
1005 * code.
1006 */
1007 if (len == -3)
1008 key = TO_SPECIAL(string[1], string[2]);
1009 else
1010 key = string[0];
1011 key = simplify_key(key, &modifiers);
1012 if (key == CSI)
1013 key = K_CSI;
1014 if (IS_SPECIAL(key))
1015 {
1016 string[0] = CSI;
1017 string[1] = K_SECOND(key);
1018 string[2] = K_THIRD(key);
1019 len = 3;
1020 }
1021 else
1022 {
1023 string[0] = key;
1024 len = 1;
1025 }
1026
1027 if (modifiers != 0)
1028 {
1029 string2[0] = CSI;
1030 string2[1] = KS_MODIFIER;
1031 string2[2] = modifiers;
1032 add_to_input_buf(string2, 3);
1033 }
1034 }
1035
1036 if (len == 1 && ((string[0] == Ctrl_C && ctrl_c_interrupts)
1037#ifdef UNIX
1038 || (intr_char != 0 && string[0] == intr_char)
1039#endif
1040 ))
1041 {
1042 trash_input_buf();
1043 got_int = TRUE;
1044 }
1045
1046 add_to_input_buf(string, len);
1047
1048 /*
1049 * blank out the pointer if necessary
1050 */
1051 if (p_mh)
1052 gui_mch_mousehide(TRUE);
1053
1054#if defined(FEAT_BEVAL_TIP)
1055 {
1056 BalloonEval *be;
1057
1058 if ((be = gui_mch_currently_showing_beval()) != NULL)
1059 gui_mch_unpost_balloon(be);
1060 }
1061#endif
1062theend:
1063 {} /* some compilers need a statement here */
1064#ifdef FEAT_XIM
1065 if (string_alloced)
1066 XtFree((char *)string);
1067#endif
1068}
1069
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001071gui_x11_mouse_cb(
1072 Widget w UNUSED,
1073 XtPointer dud UNUSED,
1074 XEvent *event,
1075 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001076{
1077 static XtIntervalId timer = (XtIntervalId)0;
1078 static int timed_out = TRUE;
1079
1080 int button;
1081 int repeated_click = FALSE;
1082 int x, y;
1083 int_u x_modifiers;
1084 int_u vim_modifiers;
1085
1086 if (event->type == MotionNotify)
1087 {
1088 /* Get the latest position, avoids lagging behind on a drag. */
1089 x = event->xmotion.x;
1090 y = event->xmotion.y;
1091 x_modifiers = event->xmotion.state;
1092 button = (x_modifiers & (Button1Mask | Button2Mask | Button3Mask))
1093 ? MOUSE_DRAG : ' ';
1094
1095 /*
1096 * if our pointer is currently hidden, then we should show it.
1097 */
1098 gui_mch_mousehide(FALSE);
1099
1100 if (button != MOUSE_DRAG) /* just moving the rodent */
1101 {
1102#ifdef FEAT_MENU
1103 if (dud) /* moved in vimForm */
1104 y -= gui.menu_height;
1105#endif
1106 gui_mouse_moved(x, y);
1107 return;
1108 }
1109 }
1110 else
1111 {
1112 x = event->xbutton.x;
1113 y = event->xbutton.y;
1114 if (event->type == ButtonPress)
1115 {
1116 /* Handle multiple clicks */
1117 if (!timed_out)
1118 {
1119 XtRemoveTimeOut(timer);
1120 repeated_click = TRUE;
1121 }
1122 timed_out = FALSE;
1123 timer = XtAppAddTimeOut(app_context, (long_u)p_mouset,
1124 gui_x11_timer_cb, &timed_out);
1125 switch (event->xbutton.button)
1126 {
Bram Moolenaar88e484b2015-11-24 15:38:44 +01001127 /* keep in sync with gui_gtk_x11.c */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001128 case Button1: button = MOUSE_LEFT; break;
1129 case Button2: button = MOUSE_MIDDLE; break;
1130 case Button3: button = MOUSE_RIGHT; break;
1131 case Button4: button = MOUSE_4; break;
1132 case Button5: button = MOUSE_5; break;
Bram Moolenaar88e484b2015-11-24 15:38:44 +01001133 case 6: button = MOUSE_7; break;
1134 case 7: button = MOUSE_6; break;
1135 case 8: button = MOUSE_X1; break;
1136 case 9: button = MOUSE_X2; break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137 default:
1138 return; /* Unknown button */
1139 }
1140 }
1141 else if (event->type == ButtonRelease)
1142 button = MOUSE_RELEASE;
1143 else
1144 return; /* Unknown mouse event type */
1145
1146 x_modifiers = event->xbutton.state;
1147#if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
1148 last_mouse_event = event->xbutton;
1149#endif
1150 }
1151
1152 vim_modifiers = 0x0;
1153 if (x_modifiers & ShiftMask)
1154 vim_modifiers |= MOUSE_SHIFT;
1155 if (x_modifiers & ControlMask)
1156 vim_modifiers |= MOUSE_CTRL;
1157 if (x_modifiers & Mod1Mask) /* Alt or Meta key */
1158 vim_modifiers |= MOUSE_ALT;
1159
1160 gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
1161}
1162
Bram Moolenaar071d4272004-06-13 20:20:40 +00001163/*
1164 * End of call-back routines
1165 */
1166
1167/*
1168 * Parse the GUI related command-line arguments. Any arguments used are
1169 * deleted from argv, and *argc is decremented accordingly. This is called
1170 * when vim is started, whether or not the GUI has been started.
1171 */
1172 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001173gui_mch_prepare(int *argc, char **argv)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174{
1175 int arg;
1176 int i;
1177
1178 /*
1179 * Move all the entries in argv which are relevant to X into gui_argv.
1180 */
1181 gui_argc = 0;
1182 gui_argv = (char **)lalloc((long_u)(*argc * sizeof(char *)), FALSE);
1183 if (gui_argv == NULL)
1184 return;
1185 gui_argv[gui_argc++] = argv[0];
1186 arg = 1;
1187 while (arg < *argc)
1188 {
1189 /* Look for argv[arg] in cmdline_options[] table */
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00001190 for (i = 0; i < (int)XtNumber(cmdline_options); i++)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191 if (strcmp(argv[arg], cmdline_options[i].option) == 0)
1192 break;
1193
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00001194 if (i < (int)XtNumber(cmdline_options))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195 {
1196 /* Remember finding "-rv" or "-reverse" */
1197 if (strcmp("-rv", argv[arg]) == 0
1198 || strcmp("-reverse", argv[arg]) == 0)
1199 found_reverse_arg = TRUE;
1200 else if ((strcmp("-fn", argv[arg]) == 0
1201 || strcmp("-font", argv[arg]) == 0)
1202 && arg + 1 < *argc)
1203 font_argument = argv[arg + 1];
1204
1205 /* Found match in table, so move it into gui_argv */
1206 gui_argv[gui_argc++] = argv[arg];
1207 if (--*argc > arg)
1208 {
1209 mch_memmove(&argv[arg], &argv[arg + 1], (*argc - arg)
1210 * sizeof(char *));
1211 if (cmdline_options[i].argKind != XrmoptionNoArg)
1212 {
1213 /* Move the options argument as well */
1214 gui_argv[gui_argc++] = argv[arg];
1215 if (--*argc > arg)
1216 mch_memmove(&argv[arg], &argv[arg + 1], (*argc - arg)
1217 * sizeof(char *));
1218 }
1219 }
1220 argv[*argc] = NULL;
1221 }
1222 else
1223#ifdef FEAT_SUN_WORKSHOP
1224 if (strcmp("-ws", argv[arg]) == 0)
1225 {
1226 usingSunWorkShop++;
1227 p_acd = TRUE;
1228 gui.dofork = FALSE; /* don't fork() when starting GUI */
1229 mch_memmove(&argv[arg], &argv[arg + 1],
1230 (--*argc - arg) * sizeof(char *));
1231 argv[*argc] = NULL;
1232# ifdef WSDEBUG
1233 wsdebug_wait(WT_ENV | WT_WAIT | WT_STOP, "SPRO_GVIM_WAIT", 20);
1234 wsdebug_log_init("SPRO_GVIM_DEBUG", "SPRO_GVIM_DLEVEL");
1235# endif
1236 }
1237 else
1238#endif
1239#ifdef FEAT_NETBEANS_INTG
1240 if (strncmp("-nb", argv[arg], 3) == 0)
1241 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 gui.dofork = FALSE; /* don't fork() when starting GUI */
1243 netbeansArg = argv[arg];
1244 mch_memmove(&argv[arg], &argv[arg + 1],
1245 (--*argc - arg) * sizeof(char *));
1246 argv[*argc] = NULL;
1247 }
1248 else
1249#endif
1250 arg++;
1251 }
1252}
1253
1254#ifndef XtSpecificationRelease
1255# define CARDINAL (Cardinal *)
1256#else
1257# if XtSpecificationRelease == 4
1258# define CARDINAL (Cardinal *)
1259# else
1260# define CARDINAL (int *)
1261# endif
1262#endif
1263
1264/*
1265 * Check if the GUI can be started. Called before gvimrc is sourced.
1266 * Return OK or FAIL.
1267 */
1268 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001269gui_mch_init_check(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270{
1271#ifdef FEAT_XIM
1272 XtSetLanguageProc(NULL, NULL, NULL);
1273#endif
1274 open_app_context();
1275 if (app_context != NULL)
1276 gui.dpy = XtOpenDisplay(app_context, 0, VIM_NAME, VIM_CLASS,
Bram Moolenaar1c2fda22005-01-02 11:43:19 +00001277 cmdline_options, XtNumber(cmdline_options),
1278 CARDINAL &gui_argc, gui_argv);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001279
1280 if (app_context == NULL || gui.dpy == NULL)
1281 {
1282 gui.dying = TRUE;
1283 EMSG(_(e_opendisp));
1284 return FAIL;
1285 }
1286 return OK;
1287}
1288
1289
1290#ifdef USE_XSMP
1291/*
1292 * Handle XSMP processing, de-registering the attachment upon error
1293 */
1294static XtInputId _xsmp_xtinputid;
1295
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +01001296static void local_xsmp_handle_requests(XtPointer c, int *s, XtInputId *i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001299local_xsmp_handle_requests(
1300 XtPointer c UNUSED,
1301 int *s UNUSED,
1302 XtInputId *i UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001303{
1304 if (xsmp_handle_requests() == FAIL)
1305 XtRemoveInput(_xsmp_xtinputid);
1306}
1307#endif
1308
1309
1310/*
1311 * Initialise the X GUI. Create all the windows, set up all the call-backs etc.
1312 * Returns OK for success, FAIL when the GUI can't be started.
1313 */
1314 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001315gui_mch_init(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316{
1317 XtGCMask gc_mask;
1318 XGCValues gc_vals;
1319 int x, y, mask;
1320 unsigned w, h;
1321
1322#if 0
1323 /* Uncomment this to enable synchronous mode for debugging */
1324 XSynchronize(gui.dpy, True);
1325#endif
1326
1327 vimShell = XtVaAppCreateShell(VIM_NAME, VIM_CLASS,
1328 applicationShellWidgetClass, gui.dpy, NULL);
1329
1330 /*
1331 * Get the application resources
1332 */
1333 XtVaGetApplicationResources(vimShell, (XtPointer)&gui,
1334 vim_resources, XtNumber(vim_resources), NULL);
1335
1336 gui.scrollbar_height = gui.scrollbar_width;
1337
1338 /*
1339 * Get the colors ourselves. Using the automatic conversion doesn't
1340 * handle looking for approximate colors.
1341 */
1342 /* NOTE: These next few lines are an exact duplicate of gui_athena.c's
1343 * gui_mch_def_colors(). Why?
1344 */
1345 gui.menu_fg_pixel = gui_get_color((char_u *)gui.rsrc_menu_fg_name);
1346 gui.menu_bg_pixel = gui_get_color((char_u *)gui.rsrc_menu_bg_name);
1347 gui.scroll_fg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_fg_name);
1348 gui.scroll_bg_pixel = gui_get_color((char_u *)gui.rsrc_scroll_bg_name);
1349#ifdef FEAT_BEVAL
1350 gui.tooltip_fg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_fg_name);
1351 gui.tooltip_bg_pixel = gui_get_color((char_u *)gui.rsrc_tooltip_bg_name);
1352#endif
1353
1354#if defined(FEAT_MENU) && defined(FEAT_GUI_ATHENA)
1355 /* If the menu height was set, don't change it at runtime */
1356 if (gui.menu_height != MENU_DEFAULT_HEIGHT)
1357 gui.menu_height_fixed = TRUE;
1358#endif
1359
1360 /* Set default foreground and background colours */
1361 gui.norm_pixel = gui.def_norm_pixel;
1362 gui.back_pixel = gui.def_back_pixel;
1363
1364 /* Check if reverse video needs to be applied (on Sun it's done by X) */
1365 if (gui.rsrc_rev_video && gui_get_lightness(gui.back_pixel)
1366 > gui_get_lightness(gui.norm_pixel))
1367 {
1368 gui.norm_pixel = gui.def_back_pixel;
1369 gui.back_pixel = gui.def_norm_pixel;
1370 gui.def_norm_pixel = gui.norm_pixel;
1371 gui.def_back_pixel = gui.back_pixel;
1372 }
1373
1374 /* Get the colors from the "Normal", "Tooltip", "Scrollbar" and "Menu"
1375 * group (set in syntax.c or in a vimrc file) */
1376 set_normal_colors();
1377
1378 /*
1379 * Check that none of the colors are the same as the background color
1380 */
1381 gui_check_colors();
1382
1383 /*
1384 * Set up the GCs. The font attributes will be set in gui_init_font().
1385 */
1386 gc_mask = GCForeground | GCBackground;
1387 gc_vals.foreground = gui.norm_pixel;
1388 gc_vals.background = gui.back_pixel;
1389 gui.text_gc = XtGetGC(vimShell, gc_mask, &gc_vals);
1390
1391 gc_vals.foreground = gui.back_pixel;
1392 gc_vals.background = gui.norm_pixel;
1393 gui.back_gc = XtGetGC(vimShell, gc_mask, &gc_vals);
1394
1395 gc_mask |= GCFunction;
1396 gc_vals.foreground = gui.norm_pixel ^ gui.back_pixel;
1397 gc_vals.background = gui.norm_pixel ^ gui.back_pixel;
1398 gc_vals.function = GXxor;
1399 gui.invert_gc = XtGetGC(vimShell, gc_mask, &gc_vals);
1400
1401 gui.visibility = VisibilityUnobscured;
1402 x11_setup_atoms(gui.dpy);
1403
1404 if (gui_win_x != -1 && gui_win_y != -1)
1405 gui_mch_set_winpos(gui_win_x, gui_win_y);
1406
1407 /* Now adapt the supplied(?) geometry-settings */
1408 /* Added by Kjetil Jacobsen <kjetilja@stud.cs.uit.no> */
1409 if (gui.geom != NULL && *gui.geom != NUL)
1410 {
1411 mask = XParseGeometry((char *)gui.geom, &x, &y, &w, &h);
1412 if (mask & WidthValue)
1413 Columns = w;
1414 if (mask & HeightValue)
Bram Moolenaard68071d2006-05-02 22:08:30 +00001415 {
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00001416 if (p_window > (long)h - 1 || !option_was_set((char_u *)"window"))
Bram Moolenaard68071d2006-05-02 22:08:30 +00001417 p_window = h - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001418 Rows = h;
Bram Moolenaard68071d2006-05-02 22:08:30 +00001419 }
Bram Moolenaarc33916a2013-07-01 21:43:08 +02001420 limit_screen_size();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001421 /*
1422 * Set the (x,y) position of the main window only if specified in the
1423 * users geometry, so we get good defaults when they don't. This needs
1424 * to be done before the shell is popped up.
1425 */
1426 if (mask & (XValue|YValue))
1427 XtVaSetValues(vimShell, XtNgeometry, gui.geom, NULL);
1428 }
1429
1430 gui_x11_create_widgets();
1431
1432 /*
1433 * Add an icon to Vim (Marcel Douben: 11 May 1998).
1434 */
1435 if (vim_strchr(p_go, GO_ICON) != NULL)
1436 {
1437#ifndef HAVE_XPM
1438# include "vim_icon.xbm"
1439# include "vim_mask.xbm"
1440
1441 Arg arg[2];
1442
1443 XtSetArg(arg[0], XtNiconPixmap,
1444 XCreateBitmapFromData(gui.dpy,
1445 DefaultRootWindow(gui.dpy),
1446 (char *)vim_icon_bits,
1447 vim_icon_width,
1448 vim_icon_height));
1449 XtSetArg(arg[1], XtNiconMask,
1450 XCreateBitmapFromData(gui.dpy,
1451 DefaultRootWindow(gui.dpy),
1452 (char *)vim_mask_icon_bits,
1453 vim_mask_icon_width,
1454 vim_mask_icon_height));
1455 XtSetValues(vimShell, arg, (Cardinal)2);
1456#else
1457/* Use Pixmaps, looking much nicer. */
1458
1459/* If you get an error message here, you still need to unpack the runtime
1460 * archive! */
1461# ifdef magick
1462# undef magick
1463# endif
1464# define magick vim32x32
1465# include "../runtime/vim32x32.xpm"
1466# undef magick
1467# define magick vim16x16
1468# include "../runtime/vim16x16.xpm"
1469# undef magick
1470# define magick vim48x48
1471# include "../runtime/vim48x48.xpm"
1472# undef magick
1473
1474 static Pixmap icon = 0;
1475 static Pixmap icon_mask = 0;
1476 static char **magick = vim32x32;
1477 Window root_window;
1478 XIconSize *size;
1479 int number_sizes;
1480 Display *dsp;
1481 Screen *scr;
1482 XpmAttributes attr;
1483 Colormap cmap;
1484
1485 /*
1486 * Adjust the icon to the preferences of the actual window manager.
1487 */
1488 root_window = XRootWindowOfScreen(XtScreen(vimShell));
1489 if (XGetIconSizes(XtDisplay(vimShell), root_window,
1490 &size, &number_sizes) != 0)
1491 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001492 if (number_sizes > 0)
1493 {
Bram Moolenaar6f292662013-07-14 15:06:50 +02001494 if (size->max_height >= 48 && size->max_width >= 48)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 magick = vim48x48;
Bram Moolenaar6f292662013-07-14 15:06:50 +02001496 else if (size->max_height >= 32 && size->max_width >= 32)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001497 magick = vim32x32;
Bram Moolenaar6f292662013-07-14 15:06:50 +02001498 else if (size->max_height >= 16 && size->max_width >= 16)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001499 magick = vim16x16;
1500 }
1501 }
1502
1503 dsp = XtDisplay(vimShell);
1504 scr = XtScreen(vimShell);
1505
1506 cmap = DefaultColormap(dsp, DefaultScreen(dsp));
1507 XtVaSetValues(vimShell, XtNcolormap, cmap, NULL);
1508
1509 attr.valuemask = 0L;
1510 attr.valuemask = XpmCloseness | XpmReturnPixels | XpmColormap | XpmDepth;
1511 attr.closeness = 65535; /* accuracy isn't crucial */
1512 attr.colormap = cmap;
1513 attr.depth = DefaultDepthOfScreen(scr);
1514
1515 if (!icon)
Bram Moolenaare8208012008-06-20 09:59:25 +00001516 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001517 XpmCreatePixmapFromData(dsp, root_window, magick, &icon,
1518 &icon_mask, &attr);
Bram Moolenaare8208012008-06-20 09:59:25 +00001519 XpmFreeAttributes(&attr);
1520 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001521
1522# ifdef FEAT_GUI_ATHENA
1523 XtVaSetValues(vimShell, XtNiconPixmap, icon, XtNiconMask, icon_mask, NULL);
1524# else
1525 XtVaSetValues(vimShell, XmNiconPixmap, icon, XmNiconMask, icon_mask, NULL);
1526# endif
1527#endif
1528 }
1529
1530 if (gui.color_approx)
1531 EMSG(_("Vim E458: Cannot allocate colormap entry, some colors may be incorrect"));
1532
1533#ifdef FEAT_SUN_WORKSHOP
1534 if (usingSunWorkShop)
1535 workshop_connect(app_context);
1536#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001537
1538#ifdef FEAT_BEVAL
1539 gui_init_tooltip_font();
1540#endif
1541#ifdef FEAT_MENU
1542 gui_init_menu_font();
1543#endif
1544
1545#ifdef USE_XSMP
1546 /* Attach listener on ICE connection */
1547 if (-1 != xsmp_icefd)
1548 _xsmp_xtinputid = XtAppAddInput(app_context, xsmp_icefd,
1549 (XtPointer)XtInputReadMask, local_xsmp_handle_requests, NULL);
1550#endif
1551
1552 return OK;
1553}
1554
1555/*
1556 * Called when starting the GUI fails after calling gui_mch_init().
1557 */
1558 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001559gui_mch_uninit(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001560{
1561 gui_x11_destroy_widgets();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001562 XtCloseDisplay(gui.dpy);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001563 gui.dpy = NULL;
1564 vimShell = (Widget)0;
Bram Moolenaare4bfca82009-02-24 03:12:40 +00001565 vim_free(gui_argv);
1566 gui_argv = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001567}
1568
1569/*
1570 * Called when the foreground or background color has been changed.
1571 */
1572 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001573gui_mch_new_colors(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001574{
1575 long_u gc_mask;
1576 XGCValues gc_vals;
1577
1578 gc_mask = GCForeground | GCBackground;
1579 gc_vals.foreground = gui.norm_pixel;
1580 gc_vals.background = gui.back_pixel;
1581 if (gui.text_gc != NULL)
1582 XChangeGC(gui.dpy, gui.text_gc, gc_mask, &gc_vals);
1583
1584 gc_vals.foreground = gui.back_pixel;
1585 gc_vals.background = gui.norm_pixel;
1586 if (gui.back_gc != NULL)
1587 XChangeGC(gui.dpy, gui.back_gc, gc_mask, &gc_vals);
1588
1589 gc_mask |= GCFunction;
1590 gc_vals.foreground = gui.norm_pixel ^ gui.back_pixel;
1591 gc_vals.background = gui.norm_pixel ^ gui.back_pixel;
1592 gc_vals.function = GXxor;
1593 if (gui.invert_gc != NULL)
1594 XChangeGC(gui.dpy, gui.invert_gc, gc_mask, &gc_vals);
1595
1596 gui_x11_set_back_color();
1597}
1598
1599/*
1600 * Open the GUI window which was created by a call to gui_mch_init().
1601 */
1602 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001603gui_mch_open(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001604{
1605 /* Actually open the window */
Bram Moolenaara5792f52005-11-23 21:25:05 +00001606 XtRealizeWidget(vimShell);
1607 XtManageChild(XtNameToWidget(vimShell, "*vimForm"));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001608
1609 gui.wid = gui_x11_get_wid();
1610 gui.blank_pointer = gui_x11_create_blank_mouse();
1611
1612 /*
1613 * Add a callback for the Close item on the window managers menu, and the
1614 * save-yourself event.
1615 */
1616 wm_atoms[SAVE_YOURSELF_IDX] =
1617 XInternAtom(gui.dpy, "WM_SAVE_YOURSELF", False);
1618 wm_atoms[DELETE_WINDOW_IDX] =
1619 XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False);
1620 XSetWMProtocols(gui.dpy, XtWindow(vimShell), wm_atoms, 2);
1621 XtAddEventHandler(vimShell, NoEventMask, True, gui_x11_wm_protocol_handler,
1622 NULL);
1623#ifdef HAVE_X11_XMU_EDITRES_H
1624 /*
1625 * Enable editres protocol (see "man editres").
1626 * Usually will need to add -lXmu to the linker line as well.
1627 */
1628 XtAddEventHandler(vimShell, (EventMask)0, True, _XEditResCheckMessages,
1629 (XtPointer)NULL);
1630#endif
1631
1632#ifdef FEAT_CLIENTSERVER
1633 if (serverName == NULL && serverDelayedStartName != NULL)
1634 {
1635 /* This is a :gui command in a plain vim with no previous server */
1636 commWindow = XtWindow(vimShell);
1637 (void)serverRegisterName(gui.dpy, serverDelayedStartName);
1638 }
1639 else
1640 {
1641 /*
1642 * Cannot handle "widget-less" windows with XtProcessEvent() we'll
1643 * have to change the "server" registration to that of the main window
1644 * If we have not registered a name yet, remember the window
1645 */
1646 serverChangeRegisteredWindow(gui.dpy, XtWindow(vimShell));
1647 }
1648 XtAddEventHandler(vimShell, PropertyChangeMask, False,
1649 gui_x11_send_event_handler, NULL);
1650#endif
1651
1652
1653#if defined(FEAT_MENU) && defined(FEAT_GUI_ATHENA)
1654 /* The Athena GUI needs this again after opening the window */
1655 gui_position_menu();
1656# ifdef FEAT_TOOLBAR
1657 gui_mch_set_toolbar_pos(0, gui.menu_height, gui.menu_width,
1658 gui.toolbar_height);
1659# endif
1660#endif
1661
1662 /* Get the colors for the highlight groups (gui_check_colors() might have
1663 * changed them) */
1664 highlight_gui_started(); /* re-init colors and fonts */
1665
1666#ifdef FEAT_HANGULIN
1667 hangul_keyboard_set();
1668#endif
1669#ifdef FEAT_XIM
1670 xim_init();
1671#endif
1672#ifdef FEAT_SUN_WORKSHOP
1673 workshop_postinit();
1674#endif
1675
1676 return OK;
1677}
1678
1679#if defined(FEAT_BEVAL) || defined(PROTO)
1680/*
1681 * Convert the tooltip fontset name to an XFontSet.
1682 */
1683 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001684gui_init_tooltip_font(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001685{
1686 XrmValue from, to;
1687
1688 from.addr = (char *)gui.rsrc_tooltip_font_name;
1689 from.size = strlen(from.addr);
1690 to.addr = (XtPointer)&gui.tooltip_fontset;
1691 to.size = sizeof(XFontSet);
1692
1693 if (XtConvertAndStore(vimShell, XtRString, &from, XtRFontSet, &to) == False)
1694 {
1695 /* Failed. What to do? */
1696 }
1697}
1698#endif
1699
1700#if defined(FEAT_MENU) || defined(PROTO)
1701/* Convert the menu font/fontset name to an XFontStruct/XFontset */
1702 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001703gui_init_menu_font(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001704{
1705 XrmValue from, to;
1706
1707#ifdef FONTSET_ALWAYS
1708 from.addr = (char *)gui.rsrc_menu_font_name;
1709 from.size = strlen(from.addr);
1710 to.addr = (XtPointer)&gui.menu_fontset;
1711 to.size = sizeof(GuiFontset);
1712
1713 if (XtConvertAndStore(vimShell, XtRString, &from, XtRFontSet, &to) == False)
1714 {
1715 /* Failed. What to do? */
1716 }
1717#else
1718 from.addr = (char *)gui.rsrc_menu_font_name;
1719 from.size = strlen(from.addr);
1720 to.addr = (XtPointer)&gui.menu_font;
1721 to.size = sizeof(GuiFont);
1722
1723 if (XtConvertAndStore(vimShell, XtRString, &from, XtRFontStruct, &to) == False)
1724 {
1725 /* Failed. What to do? */
1726 }
1727#endif
1728}
1729#endif
1730
Bram Moolenaar071d4272004-06-13 20:20:40 +00001731 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001732gui_mch_exit(int rc UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001733{
1734#if 0
1735 /* Lesstif gives an error message here, and so does Solaris. The man page
1736 * says that this isn't needed when exiting, so just skip it. */
1737 XtCloseDisplay(gui.dpy);
1738#endif
Bram Moolenaare4bfca82009-02-24 03:12:40 +00001739 vim_free(gui_argv);
1740 gui_argv = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001741}
1742
1743/*
1744 * Get the position of the top left corner of the window.
1745 */
1746 int
Bram Moolenaar02fdaea2016-01-30 18:13:55 +01001747gui_mch_get_winpos(int *x, int *y)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748{
1749 Dimension xpos, ypos;
1750
1751 XtVaGetValues(vimShell,
1752 XtNx, &xpos,
1753 XtNy, &ypos,
1754 NULL);
1755 *x = xpos;
1756 *y = ypos;
1757 return OK;
1758}
1759
1760/*
1761 * Set the position of the top left corner of the window to the given
1762 * coordinates.
1763 */
1764 void
Bram Moolenaar02fdaea2016-01-30 18:13:55 +01001765gui_mch_set_winpos(int x, int y)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001766{
1767 XtVaSetValues(vimShell,
1768 XtNx, x,
1769 XtNy, y,
1770 NULL);
1771}
1772
1773 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001774gui_mch_set_shellsize(
1775 int width,
1776 int height,
1777 int min_width,
1778 int min_height,
1779 int base_width,
1780 int base_height,
1781 int direction UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782{
Bram Moolenaar1c2fda22005-01-02 11:43:19 +00001783#ifdef FEAT_XIM
1784 height += xim_get_status_area_height(),
1785#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001786 XtVaSetValues(vimShell,
1787 XtNwidthInc, gui.char_width,
1788 XtNheightInc, gui.char_height,
1789#if defined(XtSpecificationRelease) && XtSpecificationRelease >= 4
1790 XtNbaseWidth, base_width,
1791 XtNbaseHeight, base_height,
1792#endif
1793 XtNminWidth, min_width,
1794 XtNminHeight, min_height,
1795 XtNwidth, width,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001796 XtNheight, height,
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797 NULL);
1798}
1799
1800/*
Bram Moolenaar231334e2005-07-25 20:46:57 +00001801 * Allow 10 pixels for horizontal borders, 'guiheadroom' for vertical borders.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001802 * Is there no way in X to find out how wide the borders really are?
1803 */
1804 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001805gui_mch_get_screen_dimensions(
1806 int *screen_w,
1807 int *screen_h)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808{
1809 *screen_w = DisplayWidth(gui.dpy, DefaultScreen(gui.dpy)) - 10;
1810 *screen_h = DisplayHeight(gui.dpy, DefaultScreen(gui.dpy)) - p_ghr;
1811}
1812
1813/*
1814 * Initialise vim to use the font "font_name". If it's NULL, pick a default
1815 * font.
1816 * If "fontset" is TRUE, load the "font_name" as a fontset.
1817 * Return FAIL if the font could not be loaded, OK otherwise.
1818 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001819 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001820gui_mch_init_font(
1821 char_u *font_name,
1822 int do_fontset UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001823{
1824 XFontStruct *font = NULL;
1825
1826#ifdef FEAT_XFONTSET
1827 XFontSet fontset = NULL;
Bram Moolenaar567e4de2004-12-31 21:01:02 +00001828#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829
Bram Moolenaar567e4de2004-12-31 21:01:02 +00001830#ifdef FEAT_GUI_MOTIF
1831 /* A font name equal "*" is indicating, that we should activate the font
1832 * selection dialogue to get a new font name. So let us do it here. */
1833 if (font_name != NULL && STRCMP(font_name, "*") == 0)
1834 font_name = gui_xm_select_font(hl_get_font_name());
1835#endif
1836
1837#ifdef FEAT_XFONTSET
Bram Moolenaar071d4272004-06-13 20:20:40 +00001838 if (do_fontset)
1839 {
1840 /* If 'guifontset' is set, VIM treats all font specifications as if
1841 * they were fontsets, and 'guifontset' becomes the default. */
1842 if (font_name != NULL)
1843 {
1844 fontset = (XFontSet)gui_mch_get_fontset(font_name, FALSE, TRUE);
1845 if (fontset == NULL)
1846 return FAIL;
1847 }
1848 }
1849 else
1850#endif
1851 {
1852 if (font_name == NULL)
1853 {
1854 /*
1855 * If none of the fonts in 'font' could be loaded, try the one set
1856 * in the X resource, and finally just try using DFLT_FONT, which
1857 * will hopefully always be there.
1858 */
1859 font_name = gui.rsrc_font_name;
1860 font = (XFontStruct *)gui_mch_get_font(font_name, FALSE);
1861 if (font == NULL)
1862 font_name = (char_u *)DFLT_FONT;
1863 }
1864 if (font == NULL)
1865 font = (XFontStruct *)gui_mch_get_font(font_name, FALSE);
1866 if (font == NULL)
1867 return FAIL;
1868 }
1869
1870 gui_mch_free_font(gui.norm_font);
1871#ifdef FEAT_XFONTSET
1872 gui_mch_free_fontset(gui.fontset);
1873
1874 if (fontset != NULL)
1875 {
1876 gui.norm_font = NOFONT;
1877 gui.fontset = (GuiFontset)fontset;
1878 gui.char_width = fontset_width(fontset);
1879 gui.char_height = fontset_height(fontset) + p_linespace;
1880 gui.char_ascent = fontset_ascent(fontset) + p_linespace / 2;
1881 }
1882 else
1883#endif
1884 {
1885 gui.norm_font = (GuiFont)font;
1886#ifdef FEAT_XFONTSET
1887 gui.fontset = NOFONTSET;
1888#endif
1889 gui.char_width = font->max_bounds.width;
1890 gui.char_height = font->ascent + font->descent + p_linespace;
1891 gui.char_ascent = font->ascent + p_linespace / 2;
1892 }
1893
1894 hl_set_font_name(font_name);
1895
1896 /*
1897 * Try to load other fonts for bold, italic, and bold-italic.
1898 * We should also try to work out what font to use for these when they are
1899 * not specified by X resources, but we don't yet.
1900 */
1901 if (font_name == gui.rsrc_font_name)
1902 {
1903 if (gui.bold_font == NOFONT
1904 && gui.rsrc_bold_font_name != NULL
1905 && *gui.rsrc_bold_font_name != NUL)
1906 gui.bold_font = gui_mch_get_font(gui.rsrc_bold_font_name, FALSE);
1907 if (gui.ital_font == NOFONT
1908 && gui.rsrc_ital_font_name != NULL
1909 && *gui.rsrc_ital_font_name != NUL)
1910 gui.ital_font = gui_mch_get_font(gui.rsrc_ital_font_name, FALSE);
1911 if (gui.boldital_font == NOFONT
1912 && gui.rsrc_boldital_font_name != NULL
1913 && *gui.rsrc_boldital_font_name != NUL)
1914 gui.boldital_font = gui_mch_get_font(gui.rsrc_boldital_font_name,
1915 FALSE);
1916 }
1917 else
1918 {
1919 /* When not using the font specified by the resources, also don't use
1920 * the bold/italic fonts, otherwise setting 'guifont' will look very
1921 * strange. */
1922 if (gui.bold_font != NOFONT)
1923 {
1924 XFreeFont(gui.dpy, (XFontStruct *)gui.bold_font);
1925 gui.bold_font = NOFONT;
1926 }
1927 if (gui.ital_font != NOFONT)
1928 {
1929 XFreeFont(gui.dpy, (XFontStruct *)gui.ital_font);
1930 gui.ital_font = NOFONT;
1931 }
1932 if (gui.boldital_font != NOFONT)
1933 {
1934 XFreeFont(gui.dpy, (XFontStruct *)gui.boldital_font);
1935 gui.boldital_font = NOFONT;
1936 }
1937 }
1938
Bram Moolenaar567e4de2004-12-31 21:01:02 +00001939#ifdef FEAT_GUI_MOTIF
1940 gui_motif_synch_fonts();
1941#endif
1942
Bram Moolenaar071d4272004-06-13 20:20:40 +00001943 return OK;
1944}
1945
1946/*
1947 * Get a font structure for highlighting.
1948 */
1949 GuiFont
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001950gui_mch_get_font(char_u *name, int giveErrorIfMissing)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001951{
1952 XFontStruct *font;
1953
1954 if (!gui.in_use || name == NULL) /* can't do this when GUI not running */
1955 return NOFONT;
1956
1957 font = XLoadQueryFont(gui.dpy, (char *)name);
1958
1959 if (font == NULL)
1960 {
1961 if (giveErrorIfMissing)
1962 EMSG2(_(e_font), name);
1963 return NOFONT;
1964 }
1965
1966#ifdef DEBUG
1967 printf("Font Information for '%s':\n", name);
1968 printf(" w = %d, h = %d, ascent = %d, descent = %d\n",
1969 font->max_bounds.width, font->ascent + font->descent,
1970 font->ascent, font->descent);
1971 printf(" max ascent = %d, max descent = %d, max h = %d\n",
1972 font->max_bounds.ascent, font->max_bounds.descent,
1973 font->max_bounds.ascent + font->max_bounds.descent);
1974 printf(" min lbearing = %d, min rbearing = %d\n",
1975 font->min_bounds.lbearing, font->min_bounds.rbearing);
1976 printf(" max lbearing = %d, max rbearing = %d\n",
1977 font->max_bounds.lbearing, font->max_bounds.rbearing);
1978 printf(" leftink = %d, rightink = %d\n",
1979 (font->min_bounds.lbearing < 0),
1980 (font->max_bounds.rbearing > font->max_bounds.width));
1981 printf("\n");
1982#endif
1983
1984 if (font->max_bounds.width != font->min_bounds.width)
1985 {
1986 EMSG2(_(e_fontwidth), name);
1987 XFreeFont(gui.dpy, font);
1988 return NOFONT;
1989 }
1990 return (GuiFont)font;
1991}
1992
Bram Moolenaar567e4de2004-12-31 21:01:02 +00001993#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaar46c9c732004-12-12 11:37:09 +00001994/*
1995 * Return the name of font "font" in allocated memory.
1996 * Don't know how to get the actual name, thus use the provided name.
1997 */
1998 char_u *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01001999gui_mch_get_fontname(GuiFont font UNUSED, char_u *name)
Bram Moolenaar46c9c732004-12-12 11:37:09 +00002000{
2001 if (name == NULL)
2002 return NULL;
2003 return vim_strsave(name);
2004}
Bram Moolenaar567e4de2004-12-31 21:01:02 +00002005#endif
Bram Moolenaar46c9c732004-12-12 11:37:09 +00002006
Bram Moolenaar231334e2005-07-25 20:46:57 +00002007/*
2008 * Adjust gui.char_height (after 'linespace' was changed).
2009 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002010 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002011gui_mch_adjust_charheight(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002012{
2013#ifdef FEAT_XFONTSET
2014 if (gui.fontset != NOFONTSET)
2015 {
2016 gui.char_height = fontset_height((XFontSet)gui.fontset) + p_linespace;
2017 gui.char_ascent = fontset_ascent((XFontSet)gui.fontset)
2018 + p_linespace / 2;
2019 }
2020 else
2021#endif
2022 {
2023 XFontStruct *font = (XFontStruct *)gui.norm_font;
2024
2025 gui.char_height = font->ascent + font->descent + p_linespace;
2026 gui.char_ascent = font->ascent + p_linespace / 2;
2027 }
2028 return OK;
2029}
2030
2031/*
2032 * Set the current text font.
2033 */
2034 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002035gui_mch_set_font(GuiFont font)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036{
2037 static Font prev_font = (Font)-1;
2038 Font fid = ((XFontStruct *)font)->fid;
2039
2040 if (fid != prev_font)
2041 {
2042 XSetFont(gui.dpy, gui.text_gc, fid);
2043 XSetFont(gui.dpy, gui.back_gc, fid);
2044 prev_font = fid;
2045 gui.char_ascent = ((XFontStruct *)font)->ascent + p_linespace / 2;
2046 }
2047#ifdef FEAT_XFONTSET
2048 current_fontset = (XFontSet)NULL;
2049#endif
2050}
2051
2052#if defined(FEAT_XFONTSET) || defined(PROTO)
2053/*
2054 * Set the current text fontset.
2055 * Adjust the ascent, in case it's different.
2056 */
2057 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002058gui_mch_set_fontset(GuiFontset fontset)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002059{
2060 current_fontset = (XFontSet)fontset;
2061 gui.char_ascent = fontset_ascent(current_fontset) + p_linespace / 2;
2062}
2063#endif
2064
Bram Moolenaar071d4272004-06-13 20:20:40 +00002065/*
2066 * If a font is not going to be used, free its structure.
2067 */
2068 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002069gui_mch_free_font(GuiFont font)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002070{
2071 if (font != NOFONT)
2072 XFreeFont(gui.dpy, (XFontStruct *)font);
2073}
2074
2075#if defined(FEAT_XFONTSET) || defined(PROTO)
2076/*
2077 * If a fontset is not going to be used, free its structure.
2078 */
2079 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002080gui_mch_free_fontset(GuiFontset fontset)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002081{
2082 if (fontset != NOFONTSET)
2083 XFreeFontSet(gui.dpy, (XFontSet)fontset);
2084}
2085
2086/*
2087 * Load the fontset "name".
2088 * Return a reference to the fontset, or NOFONTSET when failing.
2089 */
2090 GuiFontset
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002091gui_mch_get_fontset(
2092 char_u *name,
2093 int giveErrorIfMissing,
2094 int fixed_width)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002095{
2096 XFontSet fontset;
2097 char **missing, *def_str;
2098 int num_missing;
2099
2100 if (!gui.in_use || name == NULL)
2101 return NOFONTSET;
2102
2103 fontset = XCreateFontSet(gui.dpy, (char *)name, &missing, &num_missing,
2104 &def_str);
2105 if (num_missing > 0)
2106 {
2107 int i;
2108
2109 if (giveErrorIfMissing)
2110 {
2111 EMSG2(_("E250: Fonts for the following charsets are missing in fontset %s:"), name);
2112 for (i = 0; i < num_missing; i++)
2113 EMSG2("%s", missing[i]);
2114 }
2115 XFreeStringList(missing);
2116 }
2117
2118 if (fontset == NULL)
2119 {
2120 if (giveErrorIfMissing)
2121 EMSG2(_(e_fontset), name);
2122 return NOFONTSET;
2123 }
2124
2125 if (fixed_width && check_fontset_sanity(fontset) == FAIL)
2126 {
2127 XFreeFontSet(gui.dpy, fontset);
2128 return NOFONTSET;
2129 }
2130 return (GuiFontset)fontset;
2131}
2132
2133/*
2134 * Check if fontset "fs" is fixed width.
2135 */
2136 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002137check_fontset_sanity(XFontSet fs)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002138{
2139 XFontStruct **xfs;
2140 char **font_name;
2141 int fn;
2142 char *base_name;
2143 int i;
2144 int min_width;
2145 int min_font_idx = 0;
2146
2147 base_name = XBaseFontNameListOfFontSet(fs);
2148 fn = XFontsOfFontSet(fs, &xfs, &font_name);
2149 for (i = 0; i < fn; i++)
2150 {
2151 if (xfs[i]->max_bounds.width != xfs[i]->min_bounds.width)
2152 {
2153 EMSG2(_("E252: Fontset name: %s"), base_name);
2154 EMSG2(_("Font '%s' is not fixed-width"), font_name[i]);
2155 return FAIL;
2156 }
2157 }
2158 /* scan base font width */
2159 min_width = 32767;
2160 for (i = 0; i < fn; i++)
2161 {
2162 if (xfs[i]->max_bounds.width<min_width)
2163 {
2164 min_width = xfs[i]->max_bounds.width;
2165 min_font_idx = i;
2166 }
2167 }
2168 for (i = 0; i < fn; i++)
2169 {
2170 if ( xfs[i]->max_bounds.width != 2 * min_width
2171 && xfs[i]->max_bounds.width != min_width)
2172 {
Bram Moolenaar9d438d32013-06-13 21:57:20 +02002173 EMSG2(_("E253: Fontset name: %s"), base_name);
2174 EMSG2(_("Font0: %s"), font_name[min_font_idx]);
2175 EMSG2(_("Font1: %s"), font_name[i]);
2176 EMSGN(_("Font%ld width is not twice that of font0"), i);
2177 EMSGN(_("Font0 width: %ld"), xfs[min_font_idx]->max_bounds.width);
2178 EMSGN(_("Font1 width: %ld"), xfs[i]->max_bounds.width);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002179 return FAIL;
2180 }
2181 }
2182 /* it seems ok. Good Luck!! */
2183 return OK;
2184}
2185
2186 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002187fontset_width(XFontSet fs)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002188{
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002189 return XmbTextEscapement(fs, "Vim", 3) / 3;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002190}
2191
2192 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002193fontset_height(
2194 XFontSet fs)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002195{
2196 XFontSetExtents *extents;
2197
2198 extents = XExtentsOfFontSet(fs);
2199 return extents->max_logical_extent.height;
2200}
2201
2202#if (defined(FONTSET_ALWAYS) && defined(FEAT_GUI_ATHENA) \
2203 && defined(FEAT_MENU)) || defined(PROTO)
2204/*
2205 * Returns the bounding box height around the actual glyph image of all
2206 * characters in all fonts of the fontset.
2207 */
2208 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002209fontset_height2(XFontSet fs)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002210{
2211 XFontSetExtents *extents;
2212
2213 extents = XExtentsOfFontSet(fs);
2214 return extents->max_ink_extent.height;
2215}
2216#endif
2217
2218/* NOT USED YET
2219 static int
Bram Moolenaard14e00e2016-01-31 17:30:51 +01002220fontset_descent(XFontSet fs)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002221{
2222 XFontSetExtents *extents;
2223
2224 extents = XExtentsOfFontSet (fs);
2225 return extents->max_logical_extent.height + extents->max_logical_extent.y;
2226}
2227*/
2228
2229 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002230fontset_ascent(XFontSet fs)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002231{
2232 XFontSetExtents *extents;
2233
2234 extents = XExtentsOfFontSet(fs);
2235 return -extents->max_logical_extent.y;
2236}
2237
2238#endif /* FEAT_XFONTSET */
2239
2240/*
2241 * Return the Pixel value (color) for the given color name.
2242 * Return INVALCOLOR for error.
2243 */
2244 guicolor_T
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002245gui_mch_get_color(char_u *reqname)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002246{
2247 int i;
2248 char_u *name = reqname;
2249 Colormap colormap;
2250 XColor color;
2251 static char *(vimnames[][2]) =
2252 {
2253 /* A number of colors that some X11 systems don't have */
2254 {"LightRed", "#FFBBBB"},
2255 {"LightGreen", "#88FF88"},
2256 {"LightMagenta","#FFBBFF"},
2257 {"DarkCyan", "#008888"},
2258 {"DarkBlue", "#0000BB"},
2259 {"DarkRed", "#BB0000"},
2260 {"DarkMagenta", "#BB00BB"},
2261 {"DarkGrey", "#BBBBBB"},
2262 {"DarkYellow", "#BBBB00"},
Bram Moolenaarb21e5842006-04-16 18:30:08 +00002263 {"Gray10", "#1A1A1A"},
2264 {"Grey10", "#1A1A1A"},
2265 {"Gray20", "#333333"},
2266 {"Grey20", "#333333"},
2267 {"Gray30", "#4D4D4D"},
2268 {"Grey30", "#4D4D4D"},
2269 {"Gray40", "#666666"},
2270 {"Grey40", "#666666"},
2271 {"Gray50", "#7F7F7F"},
2272 {"Grey50", "#7F7F7F"},
2273 {"Gray60", "#999999"},
2274 {"Grey60", "#999999"},
2275 {"Gray70", "#B3B3B3"},
2276 {"Grey70", "#B3B3B3"},
2277 {"Gray80", "#CCCCCC"},
2278 {"Grey80", "#CCCCCC"},
Bram Moolenaare2f98b92006-03-29 21:18:24 +00002279 {"Gray90", "#E5E5E5"},
2280 {"Grey90", "#E5E5E5"},
Bram Moolenaar071d4272004-06-13 20:20:40 +00002281 {NULL, NULL}
2282 };
2283
2284 /* can't do this when GUI not running */
2285 if (!gui.in_use || *reqname == NUL)
2286 return INVALCOLOR;
2287
2288 colormap = DefaultColormap(gui.dpy, XDefaultScreen(gui.dpy));
2289
2290 /* Do this twice if the name isn't recognized. */
2291 while (name != NULL)
2292 {
2293 i = XParseColor(gui.dpy, colormap, (char *)name, &color);
2294
2295#if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
2296 if (i == 0)
2297 {
2298 char *old;
2299
2300 /* The X11 system is trying to resolve named colors only by names
2301 * corresponding to the current locale language. But Vim scripts
2302 * usually contain the English color names. Therefore we have to
2303 * try a second time here with the native "C" locale set.
2304 * Hopefully, restoring the old locale this way works on all
2305 * systems...
2306 */
2307 old = setlocale(LC_ALL, NULL);
2308 if (old != NULL && STRCMP(old, "C") != 0)
2309 {
2310 old = (char *)vim_strsave((char_u *)old);
2311 setlocale(LC_ALL, "C");
2312 i = XParseColor(gui.dpy, colormap, (char *)name, &color);
2313 setlocale(LC_ALL, old);
2314 vim_free(old);
2315 }
2316 }
2317#endif
2318 if (i != 0 && (XAllocColor(gui.dpy, colormap, &color) != 0
2319 || find_closest_color(colormap, &color) == OK))
2320 return (guicolor_T)color.pixel;
2321
2322 /* check for a few builtin names */
2323 for (i = 0; ; ++i)
2324 {
2325 if (vimnames[i][0] == NULL)
2326 {
2327 name = NULL;
2328 break;
2329 }
2330 if (STRICMP(name, vimnames[i][0]) == 0)
2331 {
2332 name = (char_u *)vimnames[i][1];
2333 break;
2334 }
2335 }
2336 }
2337
2338 return INVALCOLOR;
2339}
2340
2341/*
2342 * Find closest color for "colorPtr" in "colormap". set "colorPtr" to the
2343 * resulting color.
2344 * Based on a similar function in TCL.
2345 * Return FAIL if not able to find or allocate a color.
2346 */
2347 static int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002348find_closest_color(Colormap colormap, XColor *colorPtr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002349{
2350 double tmp, distance, closestDistance;
2351 int i, closest, numFound, cmap_size;
2352 XColor *colortable;
2353 XVisualInfo template, *visInfoPtr;
2354
2355 template.visualid = XVisualIDFromVisual(DefaultVisual(gui.dpy,
2356 XDefaultScreen(gui.dpy)));
2357 visInfoPtr = XGetVisualInfo(gui.dpy, (long)VisualIDMask,
2358 &template, &numFound);
2359 if (numFound < 1)
2360 /* FindClosestColor couldn't lookup visual */
2361 return FAIL;
2362
2363 cmap_size = visInfoPtr->colormap_size;
2364 XFree((char *)visInfoPtr);
2365 colortable = (XColor *)alloc((unsigned)(cmap_size * sizeof(XColor)));
2366 if (!colortable)
2367 return FAIL; /* out of memory */
2368
2369 for (i = 0; i < cmap_size; i++)
2370 colortable[i].pixel = (unsigned long)i;
2371 XQueryColors (gui.dpy, colormap, colortable, cmap_size);
2372
2373 /*
2374 * Find the color that best approximates the desired one, then
2375 * try to allocate that color. If that fails, it must mean that
2376 * the color was read-write (so we can't use it, since it's owner
2377 * might change it) or else it was already freed. Try again,
2378 * over and over again, until something succeeds.
2379 */
2380 closestDistance = 1e30;
2381 closest = 0;
2382 for (i = 0; i < cmap_size; i++)
2383 {
2384 /*
2385 * Use Euclidean distance in RGB space, weighted by Y (of YIQ)
2386 * as the objective function; this accounts for differences
2387 * in the color sensitivity of the eye.
2388 */
2389 tmp = .30 * (((int)colorPtr->red) - (int)colortable[i].red);
2390 distance = tmp * tmp;
2391 tmp = .61 * (((int)colorPtr->green) - (int)colortable[i].green);
2392 distance += tmp * tmp;
2393 tmp = .11 * (((int)colorPtr->blue) - (int)colortable[i].blue);
2394 distance += tmp * tmp;
2395 if (distance < closestDistance)
2396 {
2397 closest = i;
2398 closestDistance = distance;
2399 }
2400 }
2401
2402 if (XAllocColor(gui.dpy, colormap, &colortable[closest]) != 0)
2403 {
2404 gui.color_approx = TRUE;
2405 *colorPtr = colortable[closest];
2406 }
2407
Bram Moolenaar5a221812008-11-12 12:08:45 +00002408 vim_free(colortable);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002409 return OK;
2410}
2411
Bram Moolenaarfb269802005-03-15 22:46:30 +00002412/*
2413 * Set the current text foreground color.
2414 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002415 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002416gui_mch_set_fg_color(guicolor_T color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002417{
2418 if (color != prev_fg_color)
2419 {
2420 XSetForeground(gui.dpy, gui.text_gc, (Pixel)color);
2421 prev_fg_color = color;
2422 }
2423}
2424
2425/*
2426 * Set the current text background color.
2427 */
2428 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002429gui_mch_set_bg_color(guicolor_T color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002430{
2431 if (color != prev_bg_color)
2432 {
2433 XSetBackground(gui.dpy, gui.text_gc, (Pixel)color);
2434 prev_bg_color = color;
2435 }
2436}
2437
2438/*
Bram Moolenaarfb269802005-03-15 22:46:30 +00002439 * Set the current text special color.
2440 */
2441 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002442gui_mch_set_sp_color(guicolor_T color)
Bram Moolenaarfb269802005-03-15 22:46:30 +00002443{
2444 prev_sp_color = color;
2445}
2446
2447/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448 * create a mouse pointer that is blank
2449 */
2450 static Cursor
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002451gui_x11_create_blank_mouse(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002452{
2453 Pixmap blank_pixmap = XCreatePixmap(gui.dpy, gui.wid, 1, 1, 1);
2454 GC gc = XCreateGC(gui.dpy, blank_pixmap, (unsigned long)0, (XGCValues*)0);
2455 XDrawPoint(gui.dpy, blank_pixmap, gc, 0, 0);
2456 XFreeGC(gui.dpy, gc);
2457 return XCreatePixmapCursor(gui.dpy, blank_pixmap, blank_pixmap,
2458 (XColor*)&gui.norm_pixel, (XColor*)&gui.norm_pixel, 0, 0);
2459}
2460
Bram Moolenaarfb269802005-03-15 22:46:30 +00002461/*
2462 * Draw a curled line at the bottom of the character cell.
2463 */
2464 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002465draw_curl(int row, int col, int cells)
Bram Moolenaarfb269802005-03-15 22:46:30 +00002466{
2467 int i;
2468 int offset;
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00002469 static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
Bram Moolenaarfb269802005-03-15 22:46:30 +00002470
2471 XSetForeground(gui.dpy, gui.text_gc, prev_sp_color);
2472 for (i = FILL_X(col); i < FILL_X(col + cells); ++i)
2473 {
2474 offset = val[i % 8];
2475 XDrawPoint(gui.dpy, gui.wid, gui.text_gc, i,
2476 FILL_Y(row + 1) - 1 - offset);
2477 }
2478 XSetForeground(gui.dpy, gui.text_gc, prev_fg_color);
2479}
2480
Bram Moolenaar071d4272004-06-13 20:20:40 +00002481 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002482gui_mch_draw_string(
2483 int row,
2484 int col,
2485 char_u *s,
2486 int len,
2487 int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002488{
2489 int cells = len;
2490#ifdef FEAT_MBYTE
Bram Moolenaara3227e22006-03-08 21:32:40 +00002491 static void *buf = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002492 static int buflen = 0;
2493 char_u *p;
2494 int wlen = 0;
2495 int c;
2496
2497 if (enc_utf8)
2498 {
2499 /* Convert UTF-8 byte sequence to 16 bit characters for the X
2500 * functions. Need a buffer for the 16 bit characters. Keep it
2501 * between calls, because allocating it each time is slow. */
2502 if (buflen < len)
2503 {
2504 XtFree((char *)buf);
Bram Moolenaara3227e22006-03-08 21:32:40 +00002505 buf = (void *)XtMalloc(len * (sizeof(XChar2b) < sizeof(wchar_t)
2506 ? sizeof(wchar_t) : sizeof(XChar2b)));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002507 buflen = len;
2508 }
2509 p = s;
2510 cells = 0;
2511 while (p < s + len)
2512 {
2513 c = utf_ptr2char(p);
Bram Moolenaara3227e22006-03-08 21:32:40 +00002514# ifdef FEAT_XFONTSET
2515 if (current_fontset != NULL)
2516 {
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00002517# ifdef SMALL_WCHAR_T
2518 if (c >= 0x10000)
Bram Moolenaara3227e22006-03-08 21:32:40 +00002519 c = 0xbf; /* show chars > 0xffff as ? */
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00002520# endif
Bram Moolenaara3227e22006-03-08 21:32:40 +00002521 ((wchar_t *)buf)[wlen] = c;
2522 }
2523 else
2524# endif
2525 {
2526 if (c >= 0x10000)
2527 c = 0xbf; /* show chars > 0xffff as ? */
2528 ((XChar2b *)buf)[wlen].byte1 = (unsigned)c >> 8;
2529 ((XChar2b *)buf)[wlen].byte2 = c;
2530 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002531 ++wlen;
2532 cells += utf_char2cells(c);
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002533 p += utf_ptr2len(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002534 }
2535 }
2536 else if (has_mbyte)
2537 {
2538 cells = 0;
2539 for (p = s; p < s + len; )
2540 {
2541 cells += ptr2cells(p);
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00002542 p += (*mb_ptr2len)(p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002543 }
2544 }
2545
2546#endif
2547
2548#ifdef FEAT_XFONTSET
2549 if (current_fontset != NULL)
2550 {
2551 /* Setup a clip rectangle to avoid spilling over in the next or
2552 * previous line. This is apparently needed for some fonts which are
2553 * used in a fontset. */
2554 XRectangle clip;
2555
2556 clip.x = 0;
2557 clip.y = 0;
2558 clip.height = gui.char_height;
2559 clip.width = gui.char_width * cells + 1;
2560 XSetClipRectangles(gui.dpy, gui.text_gc, FILL_X(col), FILL_Y(row),
2561 &clip, 1, Unsorted);
2562 }
2563#endif
2564
2565 if (flags & DRAW_TRANSP)
2566 {
2567#ifdef FEAT_MBYTE
2568 if (enc_utf8)
2569 XDrawString16(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col),
2570 TEXT_Y(row), buf, wlen);
2571 else
2572#endif
2573 XDrawString(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col),
2574 TEXT_Y(row), (char *)s, len);
2575 }
2576 else if (p_linespace != 0
2577#ifdef FEAT_XFONTSET
2578 || current_fontset != NULL
2579#endif
2580 )
2581 {
2582 XSetForeground(gui.dpy, gui.text_gc, prev_bg_color);
2583 XFillRectangle(gui.dpy, gui.wid, gui.text_gc, FILL_X(col),
2584 FILL_Y(row), gui.char_width * cells, gui.char_height);
2585 XSetForeground(gui.dpy, gui.text_gc, prev_fg_color);
Bram Moolenaarfb269802005-03-15 22:46:30 +00002586
Bram Moolenaar071d4272004-06-13 20:20:40 +00002587#ifdef FEAT_MBYTE
2588 if (enc_utf8)
2589 XDrawString16(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col),
2590 TEXT_Y(row), buf, wlen);
2591 else
2592#endif
2593 XDrawString(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col),
2594 TEXT_Y(row), (char *)s, len);
2595 }
2596 else
2597 {
2598 /* XmbDrawImageString has bug, don't use it for fontset. */
2599#ifdef FEAT_MBYTE
2600 if (enc_utf8)
2601 XDrawImageString16(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col),
2602 TEXT_Y(row), buf, wlen);
2603 else
2604#endif
2605 XDrawImageString(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col),
2606 TEXT_Y(row), (char *)s, len);
2607 }
2608
2609 /* Bold trick: draw the text again with a one-pixel offset. */
2610 if (flags & DRAW_BOLD)
2611 {
2612#ifdef FEAT_MBYTE
2613 if (enc_utf8)
2614 XDrawString16(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col) + 1,
2615 TEXT_Y(row), buf, wlen);
2616 else
2617#endif
2618 XDrawString(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col) + 1,
2619 TEXT_Y(row), (char *)s, len);
2620 }
2621
Bram Moolenaarfb269802005-03-15 22:46:30 +00002622 /* Undercurl: draw curl at the bottom of the character cell. */
2623 if (flags & DRAW_UNDERC)
2624 draw_curl(row, col, cells);
2625
Bram Moolenaar071d4272004-06-13 20:20:40 +00002626 /* Underline: draw a line at the bottom of the character cell. */
2627 if (flags & DRAW_UNDERL)
Bram Moolenaarfb269802005-03-15 22:46:30 +00002628 {
2629 int y = FILL_Y(row + 1) - 1;
2630
2631 /* When p_linespace is 0, overwrite the bottom row of pixels.
2632 * Otherwise put the line just below the character. */
2633 if (p_linespace > 1)
2634 y -= p_linespace - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002635 XDrawLine(gui.dpy, gui.wid, gui.text_gc, FILL_X(col),
Bram Moolenaarfb269802005-03-15 22:46:30 +00002636 y, FILL_X(col + cells) - 1, y);
2637 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002638
2639#ifdef FEAT_XFONTSET
2640 if (current_fontset != NULL)
2641 XSetClipMask(gui.dpy, gui.text_gc, None);
2642#endif
2643}
2644
2645/*
2646 * Return OK if the key with the termcap name "name" is supported.
2647 */
2648 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002649gui_mch_haskey(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650{
2651 int i;
2652
2653 for (i = 0; special_keys[i].key_sym != (KeySym)0; i++)
2654 if (name[0] == special_keys[i].vim_code0 &&
2655 name[1] == special_keys[i].vim_code1)
2656 return OK;
2657 return FAIL;
2658}
2659
2660/*
2661 * Return the text window-id and display. Only required for X-based GUI's
2662 */
2663 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002664gui_get_x11_windis(Window *win, Display **dis)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002665{
2666 *win = XtWindow(vimShell);
2667 *dis = gui.dpy;
2668 return OK;
2669}
2670
2671 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002672gui_mch_beep(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002673{
2674 XBell(gui.dpy, 0);
2675}
2676
2677 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002678gui_mch_flash(int msec)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002679{
2680 /* Do a visual beep by reversing the foreground and background colors */
2681 XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0,
2682 FILL_X((int)Columns) + gui.border_offset,
2683 FILL_Y((int)Rows) + gui.border_offset);
2684 XSync(gui.dpy, False);
2685 ui_delay((long)msec, TRUE); /* wait for a few msec */
2686 XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0,
2687 FILL_X((int)Columns) + gui.border_offset,
2688 FILL_Y((int)Rows) + gui.border_offset);
2689}
2690
2691/*
2692 * Invert a rectangle from row r, column c, for nr rows and nc columns.
2693 */
2694 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002695gui_mch_invert_rectangle(
2696 int r,
2697 int c,
2698 int nr,
2699 int nc)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700{
2701 XFillRectangle(gui.dpy, gui.wid, gui.invert_gc,
2702 FILL_X(c), FILL_Y(r), (nc) * gui.char_width, (nr) * gui.char_height);
2703}
2704
2705/*
2706 * Iconify the GUI window.
2707 */
2708 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002709gui_mch_iconify(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002710{
2711 XIconifyWindow(gui.dpy, XtWindow(vimShell), DefaultScreen(gui.dpy));
2712}
2713
2714#if defined(FEAT_EVAL) || defined(PROTO)
2715/*
2716 * Bring the Vim window to the foreground.
2717 */
2718 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002719gui_mch_set_foreground(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002720{
2721 XMapRaised(gui.dpy, XtWindow(vimShell));
2722}
2723#endif
2724
2725/*
2726 * Draw a cursor without focus.
2727 */
2728 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002729gui_mch_draw_hollow_cursor(guicolor_T color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002730{
2731 int w = 1;
2732
2733#ifdef FEAT_MBYTE
2734 if (mb_lefthalve(gui.row, gui.col))
2735 w = 2;
2736#endif
2737 gui_mch_set_fg_color(color);
2738 XDrawRectangle(gui.dpy, gui.wid, gui.text_gc, FILL_X(gui.col),
2739 FILL_Y(gui.row), w * gui.char_width - 1, gui.char_height - 1);
2740}
2741
2742/*
2743 * Draw part of a cursor, "w" pixels wide, and "h" pixels high, using
2744 * color "color".
2745 */
2746 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002747gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002748{
2749 gui_mch_set_fg_color(color);
2750
2751 XFillRectangle(gui.dpy, gui.wid, gui.text_gc,
2752#ifdef FEAT_RIGHTLEFT
2753 /* vertical line should be on the right of current point */
2754 CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w :
2755#endif
2756 FILL_X(gui.col),
2757 FILL_Y(gui.row) + gui.char_height - h,
2758 w, h);
2759}
2760
2761/*
2762 * Catch up with any queued X events. This may put keyboard input into the
2763 * input buffer, call resize call-backs, trigger timers etc. If there is
2764 * nothing in the X event queue (& no timers pending), then we return
2765 * immediately.
2766 */
2767 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002768gui_mch_update(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002769{
2770 XtInputMask mask, desired;
2771
2772#ifdef ALT_X_INPUT
2773 if (suppress_alternate_input)
2774 desired = (XtIMXEvent | XtIMTimer);
2775 else
2776#endif
2777 desired = (XtIMAll);
2778 while ((mask = XtAppPending(app_context)) && (mask & desired)
2779 && !vim_is_input_buf_full())
2780 XtAppProcessEvent(app_context, desired);
2781}
2782
2783/*
2784 * GUI input routine called by gui_wait_for_chars(). Waits for a character
2785 * from the keyboard.
2786 * wtime == -1 Wait forever.
2787 * wtime == 0 This should never happen.
2788 * wtime > 0 Wait wtime milliseconds for a character.
2789 * Returns OK if a character was found to be available within the given time,
2790 * or FAIL otherwise.
2791 */
2792 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002793gui_mch_wait_for_chars(long wtime)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002794{
2795 int focus;
2796
2797 /*
2798 * Make this static, in case gui_x11_timer_cb is called after leaving
2799 * this function (otherwise a random value on the stack may be changed).
2800 */
2801 static int timed_out;
2802 XtIntervalId timer = (XtIntervalId)0;
2803 XtInputMask desired;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002804
2805 timed_out = FALSE;
2806
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807 if (wtime > 0)
2808 timer = XtAppAddTimeOut(app_context, (long_u)wtime, gui_x11_timer_cb,
2809 &timed_out);
2810
2811 focus = gui.in_focus;
2812#ifdef ALT_X_INPUT
2813 if (suppress_alternate_input)
2814 desired = (XtIMXEvent | XtIMTimer);
2815 else
2816#endif
2817 desired = (XtIMAll);
2818 while (!timed_out)
2819 {
2820 /* Stop or start blinking when focus changes */
2821 if (gui.in_focus != focus)
2822 {
2823 if (gui.in_focus)
2824 gui_mch_start_blink();
2825 else
2826 gui_mch_stop_blink();
2827 focus = gui.in_focus;
2828 }
2829
Bram Moolenaar93c88e02015-09-15 14:12:05 +02002830#ifdef MESSAGE_QUEUE
2831 parse_queued_messages();
Bram Moolenaar03531f72010-11-16 15:04:57 +01002832#endif
2833
Bram Moolenaar071d4272004-06-13 20:20:40 +00002834 /*
2835 * Don't use gui_mch_update() because then we will spin-lock until a
2836 * char arrives, instead we use XtAppProcessEvent() to hang until an
2837 * event arrives. No need to check for input_buf_full because we are
2838 * returning as soon as it contains a single char. Note that
2839 * XtAppNextEvent() may not be used because it will not return after a
2840 * timer event has arrived -- webb
2841 */
2842 XtAppProcessEvent(app_context, desired);
2843
2844 if (input_available())
2845 {
2846 if (timer != (XtIntervalId)0 && !timed_out)
2847 XtRemoveTimeOut(timer);
2848 return OK;
2849 }
2850 }
2851 return FAIL;
2852}
2853
2854/*
2855 * Output routines.
2856 */
2857
2858/* Flush any output to the screen */
2859 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002860gui_mch_flush(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002861{
2862 XFlush(gui.dpy);
2863}
2864
2865/*
2866 * Clear a rectangular region of the screen from text pos (row1, col1) to
2867 * (row2, col2) inclusive.
2868 */
2869 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002870gui_mch_clear_block(
2871 int row1,
2872 int col1,
2873 int row2,
2874 int col2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002875{
2876 int x;
2877
2878 x = FILL_X(col1);
2879
2880 /* Clear one extra pixel at the far right, for when bold characters have
2881 * spilled over to the next column. */
2882 XFillRectangle(gui.dpy, gui.wid, gui.back_gc, x, FILL_Y(row1),
2883 (col2 - col1 + 1) * gui.char_width + (col2 == Columns - 1),
2884 (row2 - row1 + 1) * gui.char_height);
2885}
2886
2887 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002888gui_mch_clear_all(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002889{
2890 XClearArea(gui.dpy, gui.wid, 0, 0, 0, 0, False);
2891}
2892
2893/*
2894 * Delete the given number of lines from the given row, scrolling up any
2895 * text further down within the scroll region.
2896 */
2897 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002898gui_mch_delete_lines(int row, int num_lines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002899{
2900 if (gui.visibility == VisibilityFullyObscured)
2901 return; /* Can't see the window */
2902
2903 /* copy one extra pixel at the far right, for when bold has spilled
2904 * over */
2905 XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc,
2906 FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines),
2907 gui.char_width * (gui.scroll_region_right - gui.scroll_region_left + 1)
2908 + (gui.scroll_region_right == Columns - 1),
2909 gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
2910 FILL_X(gui.scroll_region_left), FILL_Y(row));
2911
2912 gui_clear_block(gui.scroll_region_bot - num_lines + 1,
2913 gui.scroll_region_left,
2914 gui.scroll_region_bot, gui.scroll_region_right);
2915 gui_x11_check_copy_area();
2916}
2917
2918/*
2919 * Insert the given number of lines before the given row, scrolling down any
2920 * following text within the scroll region.
2921 */
2922 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002923gui_mch_insert_lines(int row, int num_lines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002924{
2925 if (gui.visibility == VisibilityFullyObscured)
2926 return; /* Can't see the window */
2927
2928 /* copy one extra pixel at the far right, for when bold has spilled
2929 * over */
2930 XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc,
2931 FILL_X(gui.scroll_region_left), FILL_Y(row),
2932 gui.char_width * (gui.scroll_region_right - gui.scroll_region_left + 1)
2933 + (gui.scroll_region_right == Columns - 1),
2934 gui.char_height * (gui.scroll_region_bot - row - num_lines + 1),
2935 FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines));
2936
2937 gui_clear_block(row, gui.scroll_region_left,
2938 row + num_lines - 1, gui.scroll_region_right);
2939 gui_x11_check_copy_area();
2940}
2941
2942/*
2943 * Update the region revealed by scrolling up/down.
2944 */
2945 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002946gui_x11_check_copy_area(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002947{
2948 XEvent event;
2949 XGraphicsExposeEvent *gevent;
2950
2951 if (gui.visibility != VisibilityPartiallyObscured)
2952 return;
2953
2954 XFlush(gui.dpy);
2955
2956 /* Wait to check whether the scroll worked or not */
2957 for (;;)
2958 {
2959 if (XCheckTypedEvent(gui.dpy, NoExpose, &event))
2960 return; /* The scroll worked. */
2961
2962 if (XCheckTypedEvent(gui.dpy, GraphicsExpose, &event))
2963 {
2964 gevent = (XGraphicsExposeEvent *)&event;
2965 gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height);
2966 if (gevent->count == 0)
2967 return; /* This was the last expose event */
2968 }
2969 XSync(gui.dpy, False);
2970 }
2971}
2972
2973/*
2974 * X Selection stuff, for cutting and pasting text to other windows.
2975 */
2976
2977 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002978clip_mch_lose_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002979{
2980 clip_x11_lose_selection(vimShell, cbd);
2981}
2982
2983 int
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002984clip_mch_own_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002985{
2986 return clip_x11_own_selection(vimShell, cbd);
2987}
2988
2989 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002990clip_mch_request_selection(VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002991{
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002992 clip_x11_request_selection(vimShell, gui.dpy, cbd);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002993}
2994
2995 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01002996clip_mch_set_selection(
2997 VimClipboard *cbd)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002998{
2999 clip_x11_set_selection(cbd);
3000}
3001
3002#if defined(FEAT_MENU) || defined(PROTO)
3003/*
3004 * Menu stuff.
3005 */
3006
3007/*
3008 * Make a menu either grey or not grey.
3009 */
3010 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003011gui_mch_menu_grey(vimmenu_T *menu, int grey)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003012{
3013 if (menu->id != (Widget)0)
3014 {
3015 gui_mch_menu_hidden(menu, False);
3016 if (grey
3017#ifdef FEAT_GUI_MOTIF
3018 || !menu->sensitive
3019#endif
3020 )
3021 XtSetSensitive(menu->id, False);
3022 else
3023 XtSetSensitive(menu->id, True);
3024 }
3025}
3026
3027/*
3028 * Make menu item hidden or not hidden
3029 */
3030 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003031gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003032{
3033 if (menu->id != (Widget)0)
3034 {
3035 if (hidden)
3036 XtUnmanageChild(menu->id);
3037 else
3038 XtManageChild(menu->id);
3039 }
3040}
3041
3042/*
3043 * This is called after setting all the menus to grey/hidden or not.
3044 */
3045 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003046gui_mch_draw_menubar(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047{
3048 /* Nothing to do in X */
3049}
3050
Bram Moolenaar071d4272004-06-13 20:20:40 +00003051 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003052gui_x11_menu_cb(
3053 Widget w UNUSED,
3054 XtPointer client_data,
3055 XtPointer call_data UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056{
3057 gui_menu_cb((vimmenu_T *)client_data);
3058}
3059
3060#endif /* FEAT_MENU */
3061
3062
3063
3064/*
3065 * Function called when window closed. Works like ":qa".
3066 * Should put up a requester!
3067 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003068 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003069gui_x11_wm_protocol_handler(
3070 Widget w UNUSED,
3071 XtPointer client_data UNUSED,
3072 XEvent *event,
3073 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003074{
3075 /*
3076 * Only deal with Client messages.
3077 */
3078 if (event->type != ClientMessage)
3079 return;
3080
3081 /*
3082 * The WM_SAVE_YOURSELF event arrives when the window manager wants to
3083 * exit. That can be cancelled though, thus Vim shouldn't exit here.
3084 * Just sync our swap files.
3085 */
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00003086 if ((Atom)((XClientMessageEvent *)event)->data.l[0] ==
Bram Moolenaar071d4272004-06-13 20:20:40 +00003087 wm_atoms[SAVE_YOURSELF_IDX])
3088 {
3089 out_flush();
3090 ml_sync_all(FALSE, FALSE); /* preserve all swap files */
3091
3092 /* Set the window's WM_COMMAND property, to let the window manager
3093 * know we are done saving ourselves. We don't want to be restarted,
3094 * thus set argv to NULL. */
3095 XSetCommand(gui.dpy, XtWindow(vimShell), NULL, 0);
3096 return;
3097 }
3098
Bram Moolenaar4bdbbf72009-05-21 21:27:43 +00003099 if ((Atom)((XClientMessageEvent *)event)->data.l[0] !=
Bram Moolenaar071d4272004-06-13 20:20:40 +00003100 wm_atoms[DELETE_WINDOW_IDX])
3101 return;
3102
3103 gui_shell_closed();
3104}
3105
3106#ifdef FEAT_CLIENTSERVER
3107/*
3108 * Function called when property changed. Check for incoming commands
3109 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003110 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003111gui_x11_send_event_handler(
3112 Widget w UNUSED,
3113 XtPointer client_data UNUSED,
3114 XEvent *event,
3115 Boolean *dum UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003116{
3117 XPropertyEvent *e = (XPropertyEvent *) event;
3118
3119 if (e->type == PropertyNotify && e->window == commWindow
3120 && e->atom == commProperty && e->state == PropertyNewValue)
3121 {
Bram Moolenaar93c88e02015-09-15 14:12:05 +02003122 serverEventProc(gui.dpy, event, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003123 }
3124}
3125#endif
3126
3127/*
3128 * Cursor blink functions.
3129 *
3130 * This is a simple state machine:
3131 * BLINK_NONE not blinking at all
3132 * BLINK_OFF blinking, cursor is not shown
3133 * BLINK_ON blinking, cursor is shown
3134 */
3135
3136#define BLINK_NONE 0
3137#define BLINK_OFF 1
3138#define BLINK_ON 2
3139
3140static int blink_state = BLINK_NONE;
3141static long_u blink_waittime = 700;
3142static long_u blink_ontime = 400;
3143static long_u blink_offtime = 250;
3144static XtIntervalId blink_timer = (XtIntervalId)0;
3145
3146 void
Bram Moolenaar02fdaea2016-01-30 18:13:55 +01003147gui_mch_set_blinking(long waittime, long on, long off)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003148{
3149 blink_waittime = waittime;
3150 blink_ontime = on;
3151 blink_offtime = off;
3152}
3153
3154/*
3155 * Stop the cursor blinking. Show the cursor if it wasn't shown.
3156 */
3157 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003158gui_mch_stop_blink(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003159{
3160 if (blink_timer != (XtIntervalId)0)
3161 {
3162 XtRemoveTimeOut(blink_timer);
3163 blink_timer = (XtIntervalId)0;
3164 }
3165 if (blink_state == BLINK_OFF)
3166 gui_update_cursor(TRUE, FALSE);
3167 blink_state = BLINK_NONE;
3168}
3169
3170/*
3171 * Start the cursor blinking. If it was already blinking, this restarts the
3172 * waiting time and shows the cursor.
3173 */
3174 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003175gui_mch_start_blink(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003176{
3177 if (blink_timer != (XtIntervalId)0)
3178 XtRemoveTimeOut(blink_timer);
3179 /* Only switch blinking on if none of the times is zero */
3180 if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus)
3181 {
3182 blink_timer = XtAppAddTimeOut(app_context, blink_waittime,
3183 gui_x11_blink_cb, NULL);
3184 blink_state = BLINK_ON;
3185 gui_update_cursor(TRUE, FALSE);
3186 }
3187}
3188
Bram Moolenaar071d4272004-06-13 20:20:40 +00003189 static void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003190gui_x11_blink_cb(
3191 XtPointer timed_out UNUSED,
3192 XtIntervalId *interval_id UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003193{
3194 if (blink_state == BLINK_ON)
3195 {
3196 gui_undraw_cursor();
3197 blink_state = BLINK_OFF;
3198 blink_timer = XtAppAddTimeOut(app_context, blink_offtime,
3199 gui_x11_blink_cb, NULL);
3200 }
3201 else
3202 {
3203 gui_update_cursor(TRUE, FALSE);
3204 blink_state = BLINK_ON;
3205 blink_timer = XtAppAddTimeOut(app_context, blink_ontime,
3206 gui_x11_blink_cb, NULL);
3207 }
3208}
3209
3210/*
3211 * Return the RGB value of a pixel as a long.
3212 */
3213 long_u
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003214gui_mch_get_rgb(guicolor_T pixel)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003215{
3216 XColor xc;
3217 Colormap colormap;
3218
3219 colormap = DefaultColormap(gui.dpy, XDefaultScreen(gui.dpy));
3220 xc.pixel = pixel;
3221 XQueryColor(gui.dpy, colormap, &xc);
3222
3223 return ((xc.red & 0xff00) << 8) + (xc.green & 0xff00)
3224 + ((unsigned)xc.blue >> 8);
3225}
3226
3227/*
3228 * Add the callback functions.
3229 */
3230 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003231gui_x11_callbacks(Widget textArea, Widget vimForm)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232{
3233 XtAddEventHandler(textArea, VisibilityChangeMask, FALSE,
3234 gui_x11_visibility_cb, (XtPointer)0);
3235
3236 XtAddEventHandler(textArea, ExposureMask, FALSE, gui_x11_expose_cb,
3237 (XtPointer)0);
3238
3239 XtAddEventHandler(vimShell, StructureNotifyMask, FALSE,
3240 gui_x11_resize_window_cb, (XtPointer)0);
3241
3242 XtAddEventHandler(vimShell, FocusChangeMask, FALSE, gui_x11_focus_change_cb,
3243 (XtPointer)0);
3244 /*
3245 * Only install these enter/leave callbacks when 'p' in 'guioptions'.
3246 * Only needed for some window managers.
3247 */
3248 if (vim_strchr(p_go, GO_POINTER) != NULL)
3249 {
3250 XtAddEventHandler(vimShell, LeaveWindowMask, FALSE, gui_x11_leave_cb,
3251 (XtPointer)0);
3252 XtAddEventHandler(textArea, LeaveWindowMask, FALSE, gui_x11_leave_cb,
3253 (XtPointer)0);
3254 XtAddEventHandler(textArea, EnterWindowMask, FALSE, gui_x11_enter_cb,
3255 (XtPointer)0);
3256 XtAddEventHandler(vimShell, EnterWindowMask, FALSE, gui_x11_enter_cb,
3257 (XtPointer)0);
3258 }
3259
3260 XtAddEventHandler(vimForm, KeyPressMask, FALSE, gui_x11_key_hit_cb,
3261 (XtPointer)0);
3262 XtAddEventHandler(textArea, KeyPressMask, FALSE, gui_x11_key_hit_cb,
3263 (XtPointer)0);
3264
3265 /* get pointer moved events from scrollbar, needed for 'mousefocus' */
3266 XtAddEventHandler(vimForm, PointerMotionMask,
3267 FALSE, gui_x11_mouse_cb, (XtPointer)1);
3268 XtAddEventHandler(textArea, ButtonPressMask | ButtonReleaseMask |
3269 ButtonMotionMask | PointerMotionMask,
3270 FALSE, gui_x11_mouse_cb, (XtPointer)0);
3271}
3272
3273/*
Bram Moolenaar6cc16192005-01-08 21:49:45 +00003274 * Get current mouse coordinates in text window.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003275 */
Bram Moolenaar6cc16192005-01-08 21:49:45 +00003276 void
3277gui_mch_getmouse(int *x, int *y)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278{
3279 int rootx, rooty, winx, winy;
3280 Window root, child;
3281 unsigned int mask;
3282
3283 if (gui.wid && XQueryPointer(gui.dpy, gui.wid, &root, &child,
Bram Moolenaar6cc16192005-01-08 21:49:45 +00003284 &rootx, &rooty, &winx, &winy, &mask)) {
3285 *x = winx;
3286 *y = winy;
3287 } else {
3288 *x = -1;
3289 *y = -1;
3290 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003291}
3292
3293 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003294gui_mch_setmouse(int x, int y)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003295{
3296 if (gui.wid)
3297 XWarpPointer(gui.dpy, (Window)0, gui.wid, 0, 0, 0, 0, x, y);
3298}
3299
3300#if (defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)) || defined(PROTO)
3301 XButtonPressedEvent *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003302gui_x11_get_last_mouse_event(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003303{
3304 return &last_mouse_event;
3305}
3306#endif
3307
3308#if defined(FEAT_SIGN_ICONS) || defined(PROTO)
3309
3310/* Signs are currently always 2 chars wide. Hopefully the font is big enough
3311 * to provide room for the bitmap! */
3312# define SIGN_WIDTH (gui.char_width * 2)
3313
Bram Moolenaar071d4272004-06-13 20:20:40 +00003314 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003315gui_mch_drawsign(int row, int col, int typenr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003316{
3317 XImage *sign;
3318
3319 if (gui.in_use && (sign = (XImage *)sign_get_image(typenr)) != NULL)
3320 {
3321 XClearArea(gui.dpy, gui.wid, TEXT_X(col), TEXT_Y(row) - sign->height,
3322 SIGN_WIDTH, gui.char_height, FALSE);
3323 XPutImage(gui.dpy, gui.wid, gui.text_gc, sign, 0, 0,
3324 TEXT_X(col) + (SIGN_WIDTH - sign->width) / 2,
3325 TEXT_Y(row) - sign->height,
3326 sign->width, sign->height);
3327 }
3328}
3329
3330 void *
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003331gui_mch_register_sign(char_u *signfile)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003332{
3333 XpmAttributes attrs;
Bram Moolenaare4bfca82009-02-24 03:12:40 +00003334 XImage *sign = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335 int status;
3336
3337 /*
3338 * Setup the color substitution table.
3339 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003340 if (signfile[0] != NUL && signfile[0] != '-')
3341 {
Bram Moolenaare4bfca82009-02-24 03:12:40 +00003342 XpmColorSymbol color[5] =
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343 {
Bram Moolenaare4bfca82009-02-24 03:12:40 +00003344 {"none", NULL, 0},
3345 {"iconColor1", NULL, 0},
3346 {"bottomShadowColor", NULL, 0},
3347 {"topShadowColor", NULL, 0},
3348 {"selectColor", NULL, 0}
3349 };
3350 attrs.valuemask = XpmColorSymbols;
3351 attrs.numsymbols = 2;
3352 attrs.colorsymbols = color;
3353 attrs.colorsymbols[0].pixel = gui.back_pixel;
3354 attrs.colorsymbols[1].pixel = gui.norm_pixel;
3355 status = XpmReadFileToImage(gui.dpy, (char *)signfile,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003356 &sign, NULL, &attrs);
Bram Moolenaare4bfca82009-02-24 03:12:40 +00003357 if (status == 0)
3358 {
3359 /* Sign width is fixed at two columns now.
3360 if (sign->width > gui.sign_width)
Bram Moolenaarcc448b32010-07-14 16:52:17 +02003361 gui.sign_width = sign->width + 8; */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003362 }
Bram Moolenaare4bfca82009-02-24 03:12:40 +00003363 else
3364 EMSG(_(e_signdata));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003365 }
3366
3367 return (void *)sign;
3368}
3369
3370 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003371gui_mch_destroy_sign(void *sign)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003372{
Bram Moolenaare4bfca82009-02-24 03:12:40 +00003373 XDestroyImage((XImage*)sign);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374}
3375#endif
3376
3377
3378#ifdef FEAT_MOUSESHAPE
3379/* The last set mouse pointer shape is remembered, to be used when it goes
3380 * from hidden to not hidden. */
3381static int last_shape = 0;
3382#endif
3383
3384/*
3385 * Use the blank mouse pointer or not.
3386 */
3387 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003388gui_mch_mousehide(
3389 int hide) /* TRUE = use blank ptr, FALSE = use parent ptr */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003390{
3391 if (gui.pointer_hidden != hide)
3392 {
3393 gui.pointer_hidden = hide;
3394 if (hide)
3395 XDefineCursor(gui.dpy, gui.wid, gui.blank_pointer);
3396 else
3397#ifdef FEAT_MOUSESHAPE
3398 mch_set_mouse_shape(last_shape);
3399#else
3400 XUndefineCursor(gui.dpy, gui.wid);
3401#endif
3402 }
3403}
3404
3405#if defined(FEAT_MOUSESHAPE) || defined(PROTO)
3406
3407/* Table for shape IDs. Keep in sync with the mshape_names[] table in
3408 * misc2.c! */
3409static int mshape_ids[] =
3410{
3411 XC_left_ptr, /* arrow */
3412 0, /* blank */
3413 XC_xterm, /* beam */
3414 XC_sb_v_double_arrow, /* updown */
3415 XC_sizing, /* udsizing */
3416 XC_sb_h_double_arrow, /* leftright */
3417 XC_sizing, /* lrsizing */
3418 XC_watch, /* busy */
3419 XC_X_cursor, /* no */
3420 XC_crosshair, /* crosshair */
3421 XC_hand1, /* hand1 */
3422 XC_hand2, /* hand2 */
3423 XC_pencil, /* pencil */
3424 XC_question_arrow, /* question */
3425 XC_right_ptr, /* right-arrow */
3426 XC_center_ptr, /* up-arrow */
3427 XC_left_ptr /* last one */
3428};
3429
3430 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003431mch_set_mouse_shape(int shape)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432{
3433 int id;
3434
3435 if (!gui.in_use)
3436 return;
3437
3438 if (shape == MSHAPE_HIDE || gui.pointer_hidden)
3439 XDefineCursor(gui.dpy, gui.wid, gui.blank_pointer);
3440 else
3441 {
3442 if (shape >= MSHAPE_NUMBERED)
3443 {
3444 id = shape - MSHAPE_NUMBERED;
3445 if (id >= XC_num_glyphs)
3446 id = XC_left_ptr;
3447 else
3448 id &= ~1; /* they are always even (why?) */
3449 }
3450 else
3451 id = mshape_ids[shape];
3452
3453 XDefineCursor(gui.dpy, gui.wid, XCreateFontCursor(gui.dpy, id));
3454 }
3455 if (shape != MSHAPE_HIDE)
3456 last_shape = shape;
3457}
3458#endif
3459
Bram Moolenaar071d4272004-06-13 20:20:40 +00003460#if (defined(FEAT_TOOLBAR) && defined(FEAT_BEVAL)) || defined(PROTO)
3461/*
3462 * Set the balloon-eval used for the tooltip of a toolbar menu item.
3463 * The check for a non-toolbar item was added, because there is a crash when
3464 * passing a normal menu item here. Can't explain that, but better avoid it.
3465 */
3466 void
Bram Moolenaar68c2f632016-01-30 17:24:07 +01003467gui_mch_menu_set_tip(vimmenu_T *menu)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003468{
3469 if (menu->id != NULL && menu->parent != NULL
3470 && menu_is_toolbar(menu->parent->name))
3471 {
3472 /* Always destroy and create the balloon, in case the string was
3473 * changed. */
3474 if (menu->tip != NULL)
3475 {
3476 gui_mch_destroy_beval_area(menu->tip);
3477 menu->tip = NULL;
3478 }
3479 if (menu->strings[MENU_INDEX_TIP] != NULL)
3480 menu->tip = gui_mch_create_beval_area(
3481 menu->id,
3482 menu->strings[MENU_INDEX_TIP],
3483 NULL,
3484 NULL);
3485 }
3486}
3487#endif