This is gui_x11.c in view mode; [Download] [Up]
/* vi:set ts=4 sw=4: * * VIM - Vi IMproved by Bram Moolenaar * GUI/Motif support by Robert Webb * * Do ":help uganda" in Vim to read copying and usage conditions. * Do ":help credits" in Vim to see a list of people who contributed. */ #include <X11/keysym.h> #include <X11/Xatom.h> #include <X11/StringDefs.h> #include <X11/Intrinsic.h> #include <X11/Shell.h> #include "vim.h" #include "globals.h" #include "proto.h" #include "option.h" #define VIM_NAME "vim" #define VIM_CLASS "Vim" /* Default resource values */ #define DFLT_FONT "7x13" #define DFLT_MENU_BG_COLOR "gray77" #define DFLT_MENU_FG_COLOR "black" #define DFLT_SCROLL_BG_COLOR "gray60" #define DFLT_SCROLL_FG_COLOR "gray77" Widget vimShell = (Widget)NULL; static XtAppContext app_context; static Atom Atom_WM_DELETE_WINDOW; static void gui_x11_check_copy_area __ARGS((void)); static void clip_x11_request_selection_cb __ARGS((Widget, XtPointer, Atom *, Atom *, XtPointer, long_u *, int *)); static Boolean clip_x11_convert_selection_cb __ARGS((Widget, Atom *, Atom *, Atom *, XtPointer *, long_u *, int *)); static void clip_x11_lose_ownership_cb __ARGS((Widget, Atom *)); static void gui_x11_wm_protocol_handler __ARGS((Widget, XtPointer, XEvent *, Boolean *)); static struct { KeySym key_sym; char_u vim_code0; char_u vim_code1; } special_keys[] = { {XK_Up, 'k', 'u'}, {XK_Down, 'k', 'd'}, {XK_Left, 'k', 'l'}, {XK_Right, 'k', 'r'}, {XK_F1, 'k', '1'}, {XK_F2, 'k', '2'}, {XK_F3, 'k', '3'}, {XK_F4, 'k', '4'}, {XK_F5, 'k', '5'}, {XK_F6, 'k', '6'}, {XK_F7, 'k', '7'}, {XK_F8, 'k', '8'}, {XK_F9, 'k', '9'}, {XK_F10, 'k', ';'}, {XK_F11, 'F', '1'}, {XK_F12, 'F', '2'}, {XK_F13, 'F', '3'}, {XK_F14, 'F', '4'}, {XK_F15, 'F', '5'}, {XK_F16, 'F', '6'}, {XK_F17, 'F', '7'}, {XK_F18, 'F', '8'}, {XK_F19, 'F', '9'}, {XK_F20, 'F', 'A'}, {XK_F21, 'F', 'B'}, {XK_F22, 'F', 'C'}, {XK_F23, 'F', 'D'}, {XK_F24, 'F', 'E'}, {XK_F25, 'F', 'F'}, {XK_F26, 'F', 'G'}, {XK_F27, 'F', 'H'}, {XK_F28, 'F', 'I'}, {XK_F29, 'F', 'J'}, {XK_F30, 'F', 'K'}, {XK_F31, 'F', 'L'}, {XK_F32, 'F', 'M'}, {XK_F33, 'F', 'N'}, {XK_F34, 'F', 'O'}, {XK_F35, 'F', 'P'}, /* keysymdef.h defines up to F35 */ {XK_Help, '%', '1'}, {XK_Undo, '&', '8'}, {XK_BackSpace, 'k', 'b'}, {XK_Insert, 'k', 'I'}, {XK_Delete, 'k', 'D'}, {XK_Home, 'k', 'h'}, {XK_End, '@', '7'}, {XK_Prior, 'k', 'P'}, {XK_Next, 'k', 'N'}, {XK_Print, '%', '9'}, /* Keypad keys: */ #ifdef XK_KP_Left {XK_KP_Left, 'k', 'l'}, {XK_KP_Right, 'k', 'r'}, {XK_KP_Up, 'k', 'u'}, {XK_KP_Down, 'k', 'd'}, {XK_KP_Insert, 'k', 'I'}, {XK_KP_Delete, 'k', 'D'}, {XK_KP_Home, 'k', 'h'}, {XK_KP_End, '@', '7'}, {XK_KP_Prior, 'k', 'P'}, {XK_KP_Next, 'k', 'N'}, #endif /* End of list marker: */ {(KeySym)0, 0, 0} }; #define XtNboldColor "boldColor" #define XtCBoldColor "BoldColor" #define XtNitalicColor "italicColor" #define XtCItalicColor "ItalicColor" #define XtNunderlineColor "underlineColor" #define XtCUnderlineColor "UnderlineColor" #define XtNcursorColor "cursorColor" #define XtCCursorColor "CursorColor" #define XtNboldFont "boldFont" #define XtCBoldFont "BoldFont" #define XtNitalicFont "italicFont" #define XtCItalicFont "ItalicFont" #define XtNboldItalicFont "boldItalicFont" #define XtCBoldItalicFont "BoldItalicFont" #define XtNscrollbarWidth "scrollbarWidth" #define XtCScrollbarWidth "ScrollbarWidth" #define XtNmenuHeight "menuHeight" #define XtCMenuHeight "MenuHeight" /* Resources for setting the foreground and background colors of menus */ #define XtNmenuBackground "menuBackground" #define XtCMenuBackground "MenuBackground" #define XtNmenuForeground "menuForeground" #define XtCMenuForeground "MenuForeground" /* Resources for setting the foreground and background colors of scrollbars */ #define XtNscrollBackground "scrollBackground" #define XtCScrollBackground "ScrollBackground" #define XtNscrollForeground "scrollForeground" #define XtCScrollForeground "ScrollForeground" /* * X Resources: */ static XtResource vim_resources[] = { { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, norm_pixel), XtRString, XtDefaultForeground }, { XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, back_pixel), XtRString, XtDefaultBackground }, { XtNboldColor, XtCBoldColor, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, bold_pixel), XtRString, XtDefaultForeground }, { XtNitalicColor, XtCItalicColor, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, ital_pixel), XtRString, XtDefaultForeground }, { XtNunderlineColor, XtCUnderlineColor, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, underline_pixel), XtRString, XtDefaultForeground }, { XtNcursorColor, XtCCursorColor, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, cursor_pixel), XtRString, XtDefaultForeground }, { XtNfont, XtCFont, XtRString, sizeof(String *), XtOffsetOf(Gui, dflt_font), XtRImmediate, XtDefaultFont }, { XtNboldFont, XtCBoldFont, XtRString, sizeof(String *), XtOffsetOf(Gui, dflt_bold_fn), XtRImmediate, "" }, { XtNitalicFont, XtCItalicFont, XtRString, sizeof(String *), XtOffsetOf(Gui, dflt_ital_fn), XtRImmediate, "" }, { XtNboldItalicFont, XtCBoldItalicFont, XtRString, sizeof(String *), XtOffsetOf(Gui, dflt_boldital_fn), XtRImmediate, "" }, { XtNgeometry, XtCGeometry, XtRString, sizeof(String *), XtOffsetOf(Gui, geom), XtRImmediate, "" }, { XtNreverseVideo, XtCReverseVideo, XtRBool, sizeof(Bool), XtOffsetOf(Gui, rev_video), XtRImmediate, (XtPointer) False }, { XtNborderWidth, XtCBorderWidth, XtRInt, sizeof(int), XtOffsetOf(Gui, border_width), XtRImmediate, (XtPointer) 2 }, { XtNscrollbarWidth, XtCScrollbarWidth, XtRInt, sizeof(int), XtOffsetOf(Gui, scrollbar_width), XtRImmediate, (XtPointer) SB_DEFAULT_WIDTH }, { XtNmenuHeight, XtCMenuHeight, XtRInt, sizeof(int), XtOffsetOf(Gui, menu_height), XtRImmediate, (XtPointer) MENU_DEFAULT_HEIGHT /* Should figure out at run time */ }, { XtNmenuForeground, XtCMenuForeground, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, menu_fg_pixel), XtRString, DFLT_MENU_FG_COLOR }, { XtNmenuBackground, XtCMenuBackground, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, menu_bg_pixel), XtRString, DFLT_MENU_BG_COLOR }, { XtNscrollForeground, XtCScrollForeground, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, scroll_fg_pixel), XtRString, DFLT_SCROLL_FG_COLOR }, { XtNscrollBackground, XtCScrollBackground, XtRPixel, sizeof(Pixel), XtOffsetOf(Gui, scroll_bg_pixel), XtRString, DFLT_SCROLL_BG_COLOR }, }; /* * This table holds all the X GUI command line options allowed. This includes * the standard ones so that we can skip them when vim is started without the * GUI (but the GUI might start up later). * When changing this, also update doc/vim_gui.txt and the usage message!!! */ static XrmOptionDescRec cmdline_options[] = { /* We handle these options ourselves */ {"-bg", ".background", XrmoptionSepArg, NULL}, {"-background", ".background", XrmoptionSepArg, NULL}, {"-fg", ".foreground", XrmoptionSepArg, NULL}, {"-foreground", ".foreground", XrmoptionSepArg, NULL}, {"-bold", ".boldColor", XrmoptionSepArg, NULL}, {"-italic", ".italicColor", XrmoptionSepArg, NULL}, {"-ul", ".underlineColor", XrmoptionSepArg, NULL}, {"-underline", ".underlineColor", XrmoptionSepArg, NULL}, {"-cursor", ".cursorColor", XrmoptionSepArg, NULL}, {"-fn", ".font", XrmoptionSepArg, NULL}, {"-font", ".font", XrmoptionSepArg, NULL}, {"-boldfont", ".boldFont", XrmoptionSepArg, NULL}, {"-italicfont", ".italicFont", XrmoptionSepArg, NULL}, {"-geom", ".geometry", XrmoptionSepArg, NULL}, {"-geometry", ".geometry", XrmoptionSepArg, NULL}, {"-reverse", "*reverseVideo", XrmoptionNoArg, "True"}, {"-rv", "*reverseVideo", XrmoptionNoArg, "True"}, {"+reverse", "*reverseVideo", XrmoptionNoArg, "False"}, {"+rv", "*reverseVideo", XrmoptionNoArg, "False"}, {"-display", ".display", XrmoptionSepArg, NULL}, {"-iconic", "*iconic", XrmoptionNoArg, "True"}, {"-name", ".name", XrmoptionSepArg, NULL}, {"-bw", ".borderWidth", XrmoptionSepArg, NULL}, {"-borderwidth", ".borderWidth", XrmoptionSepArg, NULL}, {"-sw", ".scrollbarWidth", XrmoptionSepArg, NULL}, {"-scrollbarwidth", ".scrollbarWidth", XrmoptionSepArg, NULL}, {"-mh", ".menuHeight", XrmoptionSepArg, NULL}, {"-menuheight", ".menuHeight", XrmoptionSepArg, NULL}, {"-xrm", NULL, XrmoptionResArg, NULL} }; static int gui_argc = 0; static char **gui_argv = NULL; /* * Call-back routines. */ void gui_x11_timer_cb(timed_out, interval_id) XtPointer timed_out; XtIntervalId *interval_id; { *((int *)timed_out) = TRUE; } void gui_x11_visibility_cb(w, dud, event, dum) Widget w; XtPointer dud; XEvent *event; Boolean *dum; { if (event->type != VisibilityNotify) return; gui.visibility = event->xvisibility.state; /* * When we do an XCopyArea(), and the window is partially obscured, we want * to receive an event to tell us whether it worked or not. */ XSetGraphicsExposures(gui.dpy, gui.text_gc, gui.visibility != VisibilityUnobscured); } void gui_x11_expose_cb(w, dud, event, dum) Widget w; XtPointer dud; XEvent *event; Boolean *dum; { XExposeEvent *gevent; if (event->type != Expose) return; gevent = (XExposeEvent *)event; gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height); /* Clear the border areas if needed */ if (gevent->x < FILL_X(0)) XClearArea(gui.dpy, gui.wid, 0, 0, FILL_X(0), 0, False); if (gevent->y < FILL_Y(0)) XClearArea(gui.dpy, gui.wid, 0, 0, 0, FILL_Y(0), False); if (gevent->x > FILL_X(Columns)) XClearArea(gui.dpy, gui.wid, FILL_X(Columns), 0, 0, 0, False); if (gevent->y > FILL_Y(Rows)) XClearArea(gui.dpy, gui.wid, 0, FILL_Y(Rows), 0, 0, False); } void gui_x11_resize_window_cb(w, dud, event, dum) Widget w; XtPointer dud; XEvent *event; Boolean *dum; { if (event->type != ConfigureNotify) return; gui_resize_window(event->xconfigure.width, event->xconfigure.height); /* Make sure the border strips on the right and bottom get cleared. */ /* * Now this callback is on vimForm instead of textArea, so this is probably * no longer valid: */ /* XClearArea(gui.dpy, gui.wid, FILL_X(Columns), 0, 0, 0, False); XClearArea(gui.dpy, gui.wid, 0, FILL_Y(Rows), 0, 0, False); */ } void gui_x11_focus_change_cb(w, data, event, dum) Widget w; XtPointer data; XEvent *event; Boolean *dum; { if (event->type == FocusIn) gui.in_focus = TRUE; else gui.in_focus = FALSE; gui_update_cursor(TRUE); } void gui_x11_key_hit_cb(w, dud, event, dum) Widget w; XtPointer dud; XEvent *event; Boolean *dum; { XKeyPressedEvent *ev_press; char_u string[3], string2[3]; KeySym key_sym; int num, i; ev_press = (XKeyPressedEvent *)event; num = XLookupString(ev_press, (char *)string, sizeof(string), &key_sym, NULL); if (key_sym == XK_space) string[0] = ' '; /* Otherwise Ctrl-Space doesn't work */ /* Check for Alt/Meta key (Mod1Mask) */ if (num == 1 && (ev_press->state & Mod1Mask)) { /* * Before we set the 8th bit, check to make sure the user doesn't * already have a mapping defined for this sequence. We determine this * by checking to see if the input would be the same without the * Alt/Meta key. */ ev_press->state &= ~Mod1Mask; if (XLookupString(ev_press, (char *)string2, sizeof(string2), &key_sym, NULL) == 1 && string[0] == string2[0]) string[0] |= 0x80; ev_press->state |= Mod1Mask; } #if 0 if (num == 1 && string[0] == CSI) /* this doesn't work yet */ { string[1] = CSI; string[2] = CSI; num = 3; } #endif /* Check for special keys, making sure BS and DEL are recognised. */ if (num == 0 || key_sym == XK_BackSpace || key_sym == XK_Delete) { for (i = 0; special_keys[i].key_sym != (KeySym)0; i++) { if (special_keys[i].key_sym == key_sym) { string[0] = CSI; string[1] = special_keys[i].vim_code0; string[2] = special_keys[i].vim_code1; num = 3; } } } /* Unrecognised key */ if (num == 0) return; /* Special keys (and a few others) may have modifiers */ if (num == 3 || key_sym == XK_space || key_sym == XK_Tab || key_sym == XK_Return || key_sym == XK_Linefeed || key_sym == XK_Escape) { string2[0] = CSI; string2[1] = KS_MODIFIER; string2[2] = 0; if (ev_press->state & ShiftMask) string2[2] |= MOD_MASK_SHIFT; if (ev_press->state & ControlMask) string2[2] |= MOD_MASK_CTRL; if (ev_press->state & Mod1Mask) string2[2] |= MOD_MASK_ALT; if (string2[2] != 0) add_to_input_buf(string2, 3); } if (num == 1 && string[0] == Ctrl('C')) { trash_input_buf(); got_int = TRUE; } add_to_input_buf(string, num); } void gui_x11_mouse_cb(w, dud, event, dum) Widget w; XtPointer dud; XEvent *event; Boolean *dum; { static XtIntervalId timer = (XtIntervalId)0; static int timed_out = TRUE; int button; int repeated_click = FALSE; int x, y; int_u x_modifiers; int_u vim_modifiers; if (event->type == MotionNotify) { x = event->xmotion.x; y = event->xmotion.y; button = MOUSE_DRAG; x_modifiers = event->xmotion.state; } else { x = event->xbutton.x; y = event->xbutton.y; if (event->type == ButtonPress) { /* Handle multiple clicks */ if (!timed_out) { XtRemoveTimeOut(timer); repeated_click = TRUE; } timed_out = FALSE; timer = XtAppAddTimeOut(app_context, (long_u)p_mouset, gui_x11_timer_cb, &timed_out); switch (event->xbutton.button) { case Button1: button = MOUSE_LEFT; break; case Button2: button = MOUSE_MIDDLE; break; case Button3: button = MOUSE_RIGHT; break; default: return; /* Unknown button */ } } else if (event->type == ButtonRelease) button = MOUSE_RELEASE; else return; /* Unknown mouse event type */ x_modifiers = event->xbutton.state; } vim_modifiers = 0x0; if (x_modifiers & ShiftMask) vim_modifiers |= MOUSE_SHIFT; if (x_modifiers & ControlMask) vim_modifiers |= MOUSE_CTRL; if (x_modifiers & Mod1Mask) /* Alt or Meta key */ vim_modifiers |= MOUSE_ALT; gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers); } /* * End of call-back routines */ /* * Parse the GUI related command-line arguments. Any arguments used are * deleted from argv, and *argc is decremented accordingly. This is called * when vim is started, whether or not the GUI has been started. */ void gui_mch_prepare(argc, argv) int *argc; char **argv; { int arg; int i; /* * Move all the entries in argv which are relevant to X into gui_argv. */ gui_argc = 0; gui_argv = (char **)lalloc(*argc * sizeof(char *), FALSE); if (gui_argv == NULL) return; gui_argv[gui_argc++] = argv[0]; arg = 1; while (arg < *argc) { /* Look for argv[arg] in cmdline_options[] table */ for (i = 0; i < XtNumber(cmdline_options); i++) if (strcmp(argv[arg], cmdline_options[i].option) == 0) break; if (i < XtNumber(cmdline_options)) { /* Found match in table, so move it into gui_argv */ gui_argv[gui_argc++] = argv[arg]; if (--*argc > arg) { vim_memmove(&argv[arg], &argv[arg + 1], (*argc - arg) * sizeof(char *)); if (cmdline_options[i].argKind != XrmoptionNoArg) { /* Move the options argument as well */ gui_argv[gui_argc++] = argv[arg]; if (--*argc > arg) vim_memmove(&argv[arg], &argv[arg + 1], (*argc - arg) * sizeof(char *)); } } } else arg++; } } /* * Initialise the X GUI. Create all the windows, set up all the call-backs * etc. */ int gui_mch_init() { Widget AppShell; long_u gc_mask; XGCValues gc_vals; Pixel tmp_pixel; int x, y, mask; unsigned w, h; XtToolkitInitialize(); app_context = XtCreateApplicationContext(); gui.dpy = XtOpenDisplay(app_context, 0, VIM_NAME, VIM_CLASS, cmdline_options, XtNumber(cmdline_options), #ifndef XtSpecificationRelease (Cardinal*)&gui_argc, gui_argv); #else #if XtSpecificationRelease == 4 (Cardinal*)&gui_argc, gui_argv); #else &gui_argc, gui_argv); #endif #endif vim_free(gui_argv); if (gui.dpy == NULL) { EMSG("cannot open display"); return FAIL; } /* Uncomment this to enable synchronous mode for debugging */ /* XSynchronize(gui.dpy, True); */ /* * So converters work. */ XtInitializeWidgetClass(applicationShellWidgetClass); XtInitializeWidgetClass(topLevelShellWidgetClass); /* * The applicationShell is created as an unrealized * parent for multiple topLevelShells. The topLevelShells * are created as popup children of the applicationShell. * This is a recommendation of Paul Asente & Ralph Swick in * _X_Window_System_Toolkit_ p. 677. */ AppShell = XtVaAppCreateShell(VIM_NAME, VIM_CLASS, applicationShellWidgetClass, gui.dpy, NULL); /* * Get the application resources */ XtVaGetApplicationResources(AppShell, &gui, vim_resources, XtNumber(vim_resources), NULL); gui.scrollbar_height = gui.scrollbar_width; /* For reverse video, swap foreground and background colours */ if (gui.rev_video) { tmp_pixel = gui.norm_pixel; gui.norm_pixel = gui.back_pixel; gui.back_pixel = tmp_pixel; } /* Create shell widget to put vim in */ vimShell = XtVaCreatePopupShell("VIM", topLevelShellWidgetClass, AppShell, XtNborderWidth, 0, NULL); /* * Check that none of the colors are the same as the background color */ if (gui.norm_pixel == gui.back_pixel) { gui.norm_pixel = gui_mch_get_color((char_u *)"White"); if (gui.norm_pixel == gui.back_pixel) gui.norm_pixel = gui_mch_get_color((char_u *)"Black"); } if (gui.bold_pixel == gui.back_pixel) gui.bold_pixel = gui.norm_pixel; if (gui.ital_pixel == gui.back_pixel) gui.ital_pixel = gui.norm_pixel; if (gui.underline_pixel == gui.back_pixel) gui.underline_pixel = gui.norm_pixel; if (gui.cursor_pixel == gui.back_pixel) gui.cursor_pixel = gui.norm_pixel; /* * Set up the GCs. The font attributes will be set in gui_init_font(). */ gc_mask = GCForeground | GCBackground; gc_vals.foreground = gui.norm_pixel; gc_vals.background = gui.back_pixel; gui.text_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask, &gc_vals); gc_vals.foreground = gui.back_pixel; gc_vals.background = gui.norm_pixel; gui.back_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask, &gc_vals); gc_mask |= GCFunction; gc_vals.foreground = gui.norm_pixel ^ gui.back_pixel; gc_vals.background = gui.norm_pixel ^ gui.back_pixel; gc_vals.function = GXxor; gui.invert_gc = XCreateGC(gui.dpy, DefaultRootWindow(gui.dpy), gc_mask, &gc_vals); gui.visibility = VisibilityUnobscured; clipboard.atom = XInternAtom(gui.dpy, "VIM_SELECTION", False); /* Now adapt the supplied(?) geometry-settings */ /* Added by Kjetil Jacobsen <kjetilja@stud.cs.uit.no> */ if (gui.geom != NULL && *gui.geom != NUL) { mask = XParseGeometry((char *)gui.geom, &x, &y, &w, &h); if (mask & WidthValue) Columns = w; if (mask & HeightValue) Rows = h; /* * Set the (x,y) position of the main window only if specified in the * users geometry, so we get good defaults when they don't. This needs * to be done before the shell is popped up. */ if (mask & (XValue|YValue)) XtVaSetValues(vimShell, XtNgeometry, gui.geom, NULL); } gui_x11_create_widgets(); return OK; } /* * Open the GUI window which was created by a call to gui_mch_init(). */ int gui_mch_open() { /* Actually open the window */ XtPopup(vimShell, XtGrabNone); gui.wid = gui_x11_get_wid(); /* Add a callback for the Close item on the window managers menu */ Atom_WM_DELETE_WINDOW = XInternAtom(gui.dpy, "WM_DELETE_WINDOW", False); XSetWMProtocols(gui.dpy, XtWindow(vimShell), &Atom_WM_DELETE_WINDOW, 1); XtAddEventHandler(vimShell, NoEventMask, True, gui_x11_wm_protocol_handler, NULL); #ifdef ENABLE_EDITRES /* * Enable editres protocol (see editres(1)) * Usually will need to add -lXmu to the linker line as well. */ { extern void _XEditResCheckMessages(); XtAddEventHandler(vimShell, 0, True, _XEditResCheckMessages, (XtPointer)NULL); } #endif #ifdef USE_GUI_ATHENA /* The Athena GUI needs this again after opening the window */ gui_position_menu(); #endif return OK; } void gui_mch_exit() { XtCloseDisplay(gui.dpy); } void gui_mch_set_winsize(width, height, min_width, min_height, base_width, base_height) int width; int height; int min_width; int min_height; int base_width; int base_height; { XtVaSetValues(vimShell, XtNwidthInc, gui.char_width, XtNheightInc, gui.char_height, #if defined(XtSpecificationRelease) && XtSpecificationRelease >= 4 XtNbaseWidth, base_width, XtNbaseHeight, base_height, #endif XtNminWidth, min_width, XtNminHeight, min_height, XtNwidth, width, XtNheight, height, NULL); } /* * Allow 10 pixels for horizontal borders, 30 for vertical borders. * Is there no way in X to find out how wide the borders really are? */ void gui_mch_get_screen_dimensions(screen_w, screen_h) int *screen_w; int *screen_h; { *screen_w = DisplayWidth(gui.dpy, DefaultScreen(gui.dpy)) - 10; *screen_h = DisplayHeight(gui.dpy, DefaultScreen(gui.dpy)) - 30; } /* * Initialise vim to use the font with the given name. Return FAIL if the font * could not be loaded, OK otherwise. */ int gui_mch_init_font(font_name) char_u *font_name; { XFontStruct *font = NULL; if (font_name == NULL) { /* * If none of the fonts in 'font' could be loaded, try the one set in * the X resource, and finally just try using DFLT_FONT, which will * hopefully always be there. */ font_name = gui.dflt_font; font = (XFontStruct *)gui_mch_get_font(font_name, FALSE); if (font == NULL) font_name = (char_u *)DFLT_FONT; } if (font == NULL) font = (XFontStruct *)gui_mch_get_font(font_name, FALSE); if (font == NULL) return FAIL; if (gui.norm_font != 0) XFreeFont(gui.dpy, (XFontStruct *)gui.norm_font); gui.norm_font = (GuiFont)font; gui.char_width = font->max_bounds.width; gui.char_height = font->ascent + font->descent; gui.char_ascent = font->ascent; /* * Try to load other fonts for bold, italic, and bold-italic. * We should also try to work out what font to use for these when they are * not specified by X resources, but we don't yet. */ if (gui.bold_font == 0 && gui.dflt_bold_fn != NULL && *gui.dflt_bold_fn != NUL) gui.bold_font = gui_mch_get_font(gui.dflt_bold_fn, FALSE); if (gui.ital_font == 0 && gui.dflt_ital_fn != NULL && *gui.dflt_ital_fn != NUL) gui.ital_font = gui_mch_get_font(gui.dflt_ital_fn, FALSE); if (gui.boldital_font == 0 && gui.dflt_boldital_fn != NULL && *gui.dflt_boldital_fn != NUL) gui.boldital_font = gui_mch_get_font(gui.dflt_boldital_fn, FALSE); /* TODO: if (vimShell != (Widget)NULL && XtIsRealized(vimShell)) */ return OK; } /* * Get a font structure for highlighting. */ GuiFont gui_mch_get_font(name, giveErrorIfMissing) char_u *name; int giveErrorIfMissing; { XFontStruct *font; if (!gui.in_use) /* can't do this when GUI not running */ return (GuiFont)0; font = XLoadQueryFont(gui.dpy, (char *)name); if (font == NULL) { if (giveErrorIfMissing) EMSG2("Unknown font: %s", name); return (GuiFont)0; } #ifdef DEBUG printf("Font Information for '%s':\n", name); printf(" w = %d, h = %d, ascent = %d, descent = %d\n", font->max_bounds.width, font->ascent + font->descent, font->ascent, font->descent); printf(" max ascent = %d, max descent = %d, max h = %d\n", font->max_bounds.ascent, font->max_bounds.descent, font->max_bounds.ascent + font->max_bounds.descent); printf(" min lbearing = %d, min rbearing = %d\n", font->min_bounds.lbearing, font->min_bounds.rbearing); printf(" max lbearing = %d, max rbearing = %d\n", font->max_bounds.lbearing, font->max_bounds.rbearing); printf(" leftink = %d, rightink = %d\n", (font->min_bounds.lbearing < 0), (font->max_bounds.rbearing > font->max_bounds.width)); printf("\n"); #endif if (font->max_bounds.width != font->min_bounds.width) { EMSG2("Font \"%s\" is not fixed-width", name); XFreeFont(gui.dpy, font); return (GuiFont)0; } return (GuiFont)font; } /* * Set the current text font. */ void gui_mch_set_font(font) GuiFont font; { static Font prev_font = (Font) -1; Font fid = ((XFontStruct *)font)->fid; if (fid != prev_font) { XSetFont(gui.dpy, gui.text_gc, fid); XSetFont(gui.dpy, gui.back_gc, fid); prev_font = fid; } } /* * Return TRUE if the two fonts given are equivalent. */ int gui_mch_same_font(f1, f2) GuiFont f1; GuiFont f2; { return ((XFontStruct *)f1)->fid == ((XFontStruct *)f2)->fid; } /* * If a font is not going to be used, free its structure. */ void gui_mch_free_font(font) GuiFont font; { XFreeFont(gui.dpy, (XFontStruct *)font); } /* * Return the Pixel value (color) for the given color name. This routine was * pretty much taken from example code in the Silicon Graphics OSF/Motif * Programmer's Guide. */ GuiColor gui_mch_get_color(name) char_u *name; { XrmValue from, to; if (!gui.in_use) /* can't do this when GUI not running */ return (GuiColor)NULL; from.size = STRLEN(name) + 1; if (from.size < sizeof(String)) from.size = sizeof(String); from.addr = (char *)name; to.addr = NULL; XtConvert(vimShell, XtRString, &from, XtRPixel, &to); if (to.addr != NULL) return (GuiColor)*((Pixel *)to.addr); else return (GuiColor)NULL; } /* * Set the current text foreground color. */ void gui_mch_set_fg_color(color) GuiColor color; { static GuiColor prev_color = (GuiColor) -1; if (color != prev_color) { XSetForeground(gui.dpy, gui.text_gc, (Pixel)color); prev_color = color; } } /* * Set the current text background color. */ void gui_mch_set_bg_color(color) GuiColor color; { static GuiColor prev_color = (GuiColor) -1; if (color != prev_color) { XSetBackground(gui.dpy, gui.text_gc, (Pixel)color); prev_color = color; } } void gui_mch_draw_string(row, col, s, len, fake_bold, underline) int row; int col; char_u *s; int len; int fake_bold; int underline; { XDrawImageString(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col), TEXT_Y(row), (char *)s, len); if (fake_bold) XDrawString(gui.dpy, gui.wid, gui.text_gc, TEXT_X(col) + 1, TEXT_Y(row), (char *)s, len); if (underline) XDrawLine(gui.dpy, gui.wid, gui.text_gc, FILL_X(col), FILL_Y(row + 1) - 1, FILL_X(col + len) - 1, FILL_Y(row + 1) - 1); } /* * Return OK if the key with the termcap name "name" is supported. */ int gui_mch_haskey(name) char_u *name; { int i; for (i = 0; special_keys[i].key_sym != (KeySym)0; i++) if (name[0] == special_keys[i].vim_code0 && name[1] == special_keys[i].vim_code1) return OK; return FAIL; } /* * Return the text window-id and display. Only required for X-based GUI's */ int gui_get_x11_windis(win, dis) Window *win; Display **dis; { *win = XtWindow(vimShell); *dis = gui.dpy; return OK; } void gui_mch_beep() { XBell(gui.dpy, 0); } void gui_mch_flash() { /* Do a visual beep by reversing the foreground and background colors */ XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0, FILL_X(Columns) + gui.border_offset, FILL_Y(Rows) + gui.border_offset); XSync(gui.dpy, False); ui_delay(20L, TRUE); /* wait 1/50 of a second */ XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, 0, 0, FILL_X(Columns) + gui.border_offset, FILL_Y(Rows) + gui.border_offset); } /* * Invert a rectangle from row r, column c, for nr rows and nc columns. */ void gui_mch_invert_rectangle(r, c, nr, nc) int r; int c; int nr; int nc; { XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, FILL_X(c), FILL_Y(r), (nc) * gui.char_width, (nr) * gui.char_height); } /* * Iconify the GUI window. */ void gui_mch_iconify() { XIconifyWindow(gui.dpy, XtWindow(vimShell), DefaultScreen(gui.dpy)); } /* * Draw a cursor without focus. */ void gui_mch_draw_hollow_cursor() { gui_mch_set_fg_color(gui.cursor_pixel); XDrawRectangle(gui.dpy, gui.wid, gui.text_gc, FILL_X(gui.col), FILL_Y(gui.row), gui.char_width - 1, gui.char_height - 1); } /* * Draw part of a cursor, only w pixels wide, and h pixels high. */ void gui_mch_draw_part_cursor(w, h) int w; int h; { gui_mch_set_fg_color(gui.cursor_pixel); XFillRectangle(gui.dpy, gui.wid, gui.text_gc, FILL_X(gui.col), FILL_Y(gui.row) + gui.char_height - h, w, h); } /* * Catch up with any queued X events. This may put keyboard input into the * input buffer, call resize call-backs, trigger timers etc. If there is * nothing in the X event queue (& no timers pending), then we return * immediately. */ void gui_mch_update() { while(XtAppPending(app_context) && !is_input_buf_full()) XtAppProcessEvent(app_context, XtIMAll); } /* * GUI input routine called by gui_wait_for_chars(). Waits for a character * from the keyboard. * wtime == -1 Wait forever. * wtime == 0 This should never happen. * wtime > 0 Wait wtime milliseconds for a character. * Returns OK if a character was found to be available within the given time, * or FAIL otherwise. */ int gui_mch_wait_for_chars(wtime) int wtime; { /* * Make this static, in case gui_x11_timer_cb is called after leaving * this function (otherwise a random value on the stack may be changed). */ static int timed_out; XtIntervalId timer = (XtIntervalId)0; timed_out = FALSE; if (wtime > 0) { timer = XtAppAddTimeOut(app_context, (long_u)wtime, gui_x11_timer_cb, &timed_out); } while (!timed_out) { /* * Don't use gui_mch_update() because then we will spin-lock until a * char arrives, instead we use XtAppProcessEvent() to hang until an * event arrives. No need to check for input_buf_full because we are * returning as soon as it contains a single char. Note that * XtAppNextEvent() may not be used because it will not return after a * timer event has arrived -- webb */ XtAppProcessEvent(app_context, XtIMAll); if (!is_input_buf_empty()) { if (timer != (XtIntervalId)0 && !timed_out) XtRemoveTimeOut(timer); return OK; } } return FAIL; } /* * Output routines. */ /* Flush any output to the screen */ void gui_mch_flush() { XFlush(gui.dpy); } /* * Clear a rectangular region of the screen from text pos (row1, col1) to * (row2, col2) inclusive. */ void gui_mch_clear_block(row1, col1, row2, col2) int row1; int col1; int row2; int col2; { /* * Clear one extra pixel at the right, for when bold characters have * spilled over to the next column. * Can this ever erase part of the next character? - webb */ XFillRectangle(gui.dpy, gui.wid, gui.back_gc, FILL_X(col1), FILL_Y(row1), (col2 - col1 + 1) * gui.char_width + 1, (row2 - row1 + 1) * gui.char_height); } /* * Delete the given number of lines from the given row, scrolling up any * text further down within the scroll region. */ void gui_mch_delete_lines(row, num_lines) int row; int num_lines; { if (gui.visibility == VisibilityFullyObscured) return; /* Can't see the window */ if (num_lines <= 0) return; if (row + num_lines > gui.scroll_region_bot) { /* Scrolled out of region, just blank the lines out */ gui_clear_block(row, 0, gui.scroll_region_bot, Columns - 1); } else { XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc, FILL_X(0), FILL_Y(row + num_lines), gui.char_width * Columns, gui.char_height * (gui.scroll_region_bot - row - num_lines + 1), FILL_X(0), FILL_Y(row)); /* Update gui.cursor_row if the cursor scrolled or copied over */ if (gui.cursor_row >= row) { if (gui.cursor_row < row + num_lines) INVALIDATE_CURSOR(); else if (gui.cursor_row <= gui.scroll_region_bot) gui.cursor_row -= num_lines; } gui_clear_block(gui.scroll_region_bot - num_lines + 1, 0, gui.scroll_region_bot, Columns - 1); gui_x11_check_copy_area(); } } /* * Insert the given number of lines before the given row, scrolling down any * following text within the scroll region. */ void gui_mch_insert_lines(row, num_lines) int row; int num_lines; { if (gui.visibility == VisibilityFullyObscured) return; /* Can't see the window */ if (num_lines <= 0) return; if (row + num_lines > gui.scroll_region_bot) { /* Scrolled out of region, just blank the lines out */ gui_clear_block(row, 0, gui.scroll_region_bot, Columns - 1); } else { XCopyArea(gui.dpy, gui.wid, gui.wid, gui.text_gc, FILL_X(0), FILL_Y(row), gui.char_width * Columns, gui.char_height * (gui.scroll_region_bot - row - num_lines + 1), FILL_X(0), FILL_Y(row + num_lines)); /* Update gui.cursor_row if the cursor scrolled or copied over */ if (gui.cursor_row >= gui.row) { if (gui.cursor_row <= gui.scroll_region_bot - num_lines) gui.cursor_row += num_lines; else if (gui.cursor_row <= gui.scroll_region_bot) INVALIDATE_CURSOR(); } gui_clear_block(row, 0, row + num_lines - 1, Columns - 1); gui_x11_check_copy_area(); } } /* * Scroll the text between gui.scroll_region_top & gui.scroll_region_bot by the * number of lines given. Positive scrolls down (text goes up) and negative * scrolls up (text goes down). */ static void gui_x11_check_copy_area() { XEvent event; XGraphicsExposeEvent *gevent; if (gui.visibility != VisibilityPartiallyObscured) return; XFlush(gui.dpy); /* Wait to check whether the scroll worked or not */ for (;;) { if (XCheckTypedEvent(gui.dpy, NoExpose, &event)) return; /* The scroll worked. */ if (XCheckTypedEvent(gui.dpy, GraphicsExpose, &event)) { gevent = (XGraphicsExposeEvent *)&event; gui_redraw(gevent->x, gevent->y, gevent->width, gevent->height); if (gevent->count == 0) return; /* This was the last expose event */ } XSync(gui.dpy, False); } } /* * X Selection stuff, for cutting and pasting text to other windows. */ static void clip_x11_request_selection_cb(w, success, selection, type, value, length, format) Widget w; XtPointer success; Atom *selection; Atom *type; XtPointer value; long_u *length; int *format; { int motion_type; long_u len; char_u *p; if (value == NULL || *length == 0) { clip_free_selection(); /* ??? */ *(int *)success = FALSE; return; } motion_type = MCHAR; p = (char_u *)value; len = *length; if (*type == clipboard.atom) { motion_type = *p++; len--; } clip_yank_selection(motion_type, p, len); XtFree((char *)value); *(int *)success = TRUE; } void clip_mch_request_selection() { XEvent event; Atom type = clipboard.atom; int success; int i; for (i = 0; i < 2; i++) { XtGetSelectionValue(vimShell, XA_PRIMARY, type, clip_x11_request_selection_cb, (XtPointer)&success, CurrentTime); /* Do we need this?: */ XFlush(gui.dpy); /* * Wait for result of selection request, otherwise if we type more * characters, then they will appear before the one that requested the * paste! Don't worry, we will catch up with any other events later. */ for (;;) { if (XCheckTypedEvent(gui.dpy, SelectionNotify, &event)) break; /* Do we need this?: */ XSync(gui.dpy, False); } XtDispatchEvent(&event); if (success) return; type = XA_STRING; } } static Boolean clip_x11_convert_selection_cb(w, selection, target, type, value, length, format) Widget w; Atom *selection; Atom *target; Atom *type; XtPointer *value; long_u *length; int *format; { char_u *string; char_u *result; int motion_type; if (!clipboard.owned) return False; /* Shouldn't ever happen */ if (*target == clipboard.atom) { clip_get_selection(); motion_type = clip_convert_selection(&string, length); if (motion_type < 0) return False; (*length)++; *value = XtMalloc(*length); result = (char_u *)*value; if (result == NULL) return False; result[0] = motion_type; vim_memmove(result + 1, string, (size_t)(*length - 1)); *type = *target; *format = 8; /* 8 bits per char */ return True; } else if (*target == XA_STRING) { clip_get_selection(); motion_type = clip_convert_selection(&string, length); if (motion_type < 0) return False; *value = XtMalloc(*length); result = (char_u *)*value; if (result == NULL) return False; vim_memmove(result, string, (size_t)(*length)); *type = *target; *format = 8; /* 8 bits per char */ return True; } else return False; } static void clip_x11_lose_ownership_cb(w, selection) Widget w; Atom *selection; { clip_lose_selection(); } void clip_mch_lose_selection() { XtDisownSelection(vimShell, XA_PRIMARY, CurrentTime); } int clip_mch_own_selection() { if (XtOwnSelection(vimShell, XA_PRIMARY, CurrentTime, clip_x11_convert_selection_cb, clip_x11_lose_ownership_cb, NULL) == False) return FAIL; return OK; } /* * Menu stuff. */ /* * Make a menu either grey or not grey. */ void gui_mch_menu_grey(menu, grey) GuiMenu *menu; int grey; { gui_mch_menu_hidden(menu, False); if (grey) XtSetSensitive(menu->id, False); else XtSetSensitive(menu->id, True); } /* * Make menu item hidden or not hidden */ void gui_mch_menu_hidden(menu, hidden) GuiMenu *menu; int hidden; { if (hidden) XtUnmanageChild(menu->id); else XtManageChild(menu->id); } /* * This is called after setting all the menus to grey/hidden or not. */ void gui_mch_draw_menubar() { /* Nothing to do in X */ } void gui_x11_menu_cb(w, client_data, call_data) Widget w; XtPointer client_data, call_data; { gui_menu_cb((GuiMenu *)client_data); } /* * Scrollbar stuff. */ void gui_mch_enable_scrollbar(sb, flag) GuiScrollbar *sb; int flag; { if (flag) XtManageChild(sb->id); else XtUnmanageChild(sb->id); } /* * Function called when window closed. Preserve files and exit. * Should put up a requester! */ /*ARGSUSED*/ static void gui_x11_wm_protocol_handler(w, client_data, event, dum) Widget w; XtPointer client_data; XEvent *event; Boolean *dum; { /* * On some HPUX system with Motif 1.2 this function is somehow called when * starting up. This if () avoids an unexpected exit. */ if (event->type != ClientMessage || ((XClientMessageEvent *)event)->data.l[0] != Atom_WM_DELETE_WINDOW) return; STRCPY(IObuff, "Vim: Window closed\n"); preserve_exit(); /* preserve files and exit */ }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.