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