File: [local] / src / usr.bin / vim / Attic / gui_x11.c (download)
Revision 1.1.1.1 (vendor branch), Sat Sep 7 21:40:28 1996 UTC (27 years, 9 months ago) by downsj
Branch: VIM
CVS Tags: VIM42, OPENBSD_2_2_BASE, OPENBSD_2_2, OPENBSD_2_1_BASE, OPENBSD_2_1, OPENBSD_2_0_BASE, OPENBSD_2_0 Changes since 1.1: +0 -0 lines
Initial import of vim 4.2.
This is meant to replace nvi in the tree. Vim, in general, works better,
provides more features, and does not suffer from the license problems
being imposed upon nvi.
On the other hand, vim lacks a non-visual ex mode, in addition to open mode.
This includes the GUI (X11) code, but doesn't try to compile it.
|
/* $OpenBSD: gui_x11.c,v 1.1.1.1 1996/09/07 21:40:28 downsj Exp $ */
/* 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"
#include "ops.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 Pixel gui_x11_get_color __ARGS((char_u *));
static void gui_x11_set_color __ARGS((GC, Pixel));
static void gui_x11_set_font __ARGS((GC, Font));
static void gui_x11_check_copy_area __ARGS((void));
static void gui_x11_update_menus_recurse __ARGS((GuiMenu *, int));
static void gui_x11_request_selection_cb __ARGS((Widget, XtPointer,
Atom *, Atom *, XtPointer,
long_u *, int *));
static Boolean gui_x11_convert_selection_cb __ARGS((Widget, Atom *, Atom *,
Atom *, XtPointer *,
long_u *, int *));
static void gui_x11_lose_ownership_cb __ARGS((Widget, Atom *));
static void gui_x11_wm_protocol_handler __ARGS((Widget, XtPointer,
XEvent *, Boolean *));
static void gui_x11_invert_area __ARGS((int, int, int, int));
static void gui_x11_yank_selection __ARGS((int, int, int, int));
static void gui_x11_get_word_boundaries __ARGS((GuiSelection *, int, int));
static int gui_x11_get_line_end __ARGS((int));
static void gui_x11_update_selection __ARGS((GuiSelection *, int, int, int,
int));
#define char_class(c) (c <= ' ' ? ' ' : iswordchar(c))
#define invert_rectangle(r, c, nr, nc) \
XFillRectangle(gui.dpy, gui.wid, gui.invert_gc, \
FILL_X(c), FILL_Y(r), (nc) * gui.char_width, \
(nr) * gui.char_height)
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
},
{
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, bool)
Widget w;
XtPointer dud;
XEvent *event;
Boolean *bool;
{
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, bool)
Widget w;
XtPointer dud;
XEvent *event;
Boolean *bool;
{
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);
if (gui.selection.state != SELECT_CLEARED)
gui_x11_redraw_selection(gevent->x, gevent->y, gevent->width,
gevent->height);
}
void
gui_x11_resize_window_cb(w, dud, event, bool)
Widget w;
XtPointer dud;
XEvent *event;
Boolean *bool;
{
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. */
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, bool)
Widget w;
XtPointer data;
XEvent *event;
Boolean *bool;
{
if (event->type == FocusIn)
gui.in_focus = TRUE;
else
gui.in_focus = FALSE;
if (gui.row == gui.cursor_row && gui.col == gui.cursor_col)
INVALIDATE_CURSOR();
gui_update_cursor();
}
void
gui_x11_key_hit_cb(w, dud, event, bool)
Widget w;
XtPointer dud;
XEvent *event;
Boolean *bool;
{
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, bool)
Widget w;
XtPointer dud;
XEvent *event;
Boolean *bool;
{
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;
int checkfor;
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;
/* If an x11 selection is in progress, finish it */
if (gui.selection.state == SELECT_IN_PROGRESS)
{
gui_x11_process_selection(button, x, y, repeated_click, vim_modifiers);
return;
}
/* Determine which mouse settings to look for based on the current mode */
switch (State)
{
case NORMAL_BUSY:
case NORMAL: checkfor = MOUSE_NORMAL; break;
case VISUAL: checkfor = MOUSE_VISUAL; break;
case REPLACE:
case INSERT: checkfor = MOUSE_INSERT; break;
case HITRETURN: checkfor = MOUSE_RETURN; break;
/*
* On the command line, use the X11 selection on all lines but the
* command line.
*/
case CMDLINE:
if (Y_2_ROW(y) < cmdline_row)
checkfor = ' ';
else
checkfor = MOUSE_COMMAND;
break;
default:
checkfor = ' ';
break;
};
/*
* Allow selection of text in the command line in "normal" modes.
*/
if ((State == NORMAL || State == NORMAL_BUSY ||
State == INSERT || State == REPLACE) &&
Y_2_ROW(y) >= gui.num_rows - p_ch)
checkfor = ' ';
/*
* If the mouse settings say to not use the mouse, use the X11 selection.
* But if Visual is active, assume that only the Visual area will be
* selected.
*/
if (!mouse_has(checkfor) && !VIsual_active)
{
/* If the selection is done, allow the right button to extend it */
if (gui.selection.state == SELECT_DONE && button == MOUSE_RIGHT)
{
gui_x11_process_selection(button, x, y, repeated_click,
vim_modifiers);
return;
}
/* Allow the left button to start the selection */
else if (button == MOUSE_LEFT)
{
gui_x11_start_selection(button, x, y, repeated_click,
vim_modifiers);
return;
}
}
if (gui.selection.state != SELECT_CLEARED)
gui_mch_clear_selection();
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)
{
x = full_screen;
full_screen = FALSE; /* use fprintf() */
EMSG("cannot open display");
full_screen = x;
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);
/*
* Check validity of resources
*/
if (gui.border_width < 0)
gui.border_width = 0;
/* 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 colours are the same as the background color
*/
if (gui.norm_pixel == gui.back_pixel)
{
gui.norm_pixel = gui_x11_get_color((char_u *)"White");
if (gui.norm_pixel == gui.back_pixel)
gui.norm_pixel = gui_x11_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;
gui.selection.atom = XInternAtom(gui.dpy, "VIM_SELECTION", False);
/*
* Set up the fonts. This call will also set the window to the correct
* size for the used font.
*/
gui.norm_font = NULL;
gui.bold_font = NULL;
gui.ital_font = NULL;
gui.boldital_font = NULL;
if (gui_init_font() == FAIL)
return FAIL;
/* 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.num_cols = Columns;
gui.num_rows = Rows;
gui_reset_scroll_region();
gui_mch_create_widgets();
/* Configure the desired scrollbars */
gui_init_which_components(NULL);
/* Actually open the window */
XtPopup(vimShell, XtGrabNone);
gui_mch_set_winsize();
gui.wid = gui_mch_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
return OK;
}
void
gui_mch_exit()
{
XtCloseDisplay(gui.dpy);
}
/*
* While unmanaging and re-managing scrollbars etc, we don't want the resize
* callback to be called, so this function is used to disable this callback,
* and then re-enable it afterwards. XSync() makes sure we don't register the
* callback before the server has finished processing any resize events we have
* created, otherwise we can get in a loop where the window flashes between two
* sizes, since resize_window_cb() calls set_winsize().
*/
void
gui_x11_use_resize_callback(widget, enabled)
Widget widget;
int enabled;
{
if (enabled)
{
XSync(gui.dpy, False);
XtAddEventHandler(widget, StructureNotifyMask, FALSE,
gui_x11_resize_window_cb, (XtPointer)0);
XtAddEventHandler(widget, ExposureMask, FALSE, gui_x11_expose_cb,
(XtPointer)0);
}
else
{
XtRemoveEventHandler(widget, StructureNotifyMask, FALSE,
gui_x11_resize_window_cb, (XtPointer)0);
XtRemoveEventHandler(widget, ExposureMask, FALSE, gui_x11_expose_cb,
(XtPointer)0);
XSync(gui.dpy, False);
}
}
/*
* 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 = XLoadQueryFont(gui.dpy, (char *)gui.dflt_font);
if (font == NULL)
font_name = (char_u *)DFLT_FONT;
font_name = (char_u *)DFLT_FONT;
}
if (font == NULL)
font = XLoadQueryFont(gui.dpy, (char *)font_name);
if (font == NULL)
return FAIL;
if (font->max_bounds.width != font->min_bounds.width)
{
sprintf((char *)IObuff, "Font \"%s\" is not fixed-width",
(char *)font_name);
emsg(IObuff);
XFreeFont(gui.dpy, font);
return FAIL;
}
if (gui.norm_font != NULL)
XFreeFont(gui.dpy, gui.norm_font);
gui.norm_font = font;
gui.char_width = font->max_bounds.width;
gui.char_height = font->ascent + font->descent;
gui.char_ascent = font->ascent;
#ifdef DEBUG
printf("Font Information for '%s':\n", font_name);
printf(" w = %d, h = %d, ascent = %d, descent = %d\n", gui.char_width,
gui.char_height, gui.char_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 > gui.char_width));
printf("\n");
#endif
/*
* 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.dflt_bold_fn != NULL && *gui.dflt_bold_fn != NUL)
gui.bold_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_bold_fn);
if (gui.dflt_ital_fn != NULL && *gui.dflt_ital_fn != NUL)
gui.ital_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_ital_fn);
if (gui.dflt_boldital_fn != NULL && *gui.dflt_boldital_fn != NUL)
gui.boldital_font = XLoadQueryFont(gui.dpy, (char *)gui.dflt_boldital_fn);
/* Must set the appropriate fonts for all the GCs */
gui_x11_set_font(gui.text_gc, gui.norm_font->fid);
gui_x11_set_font(gui.back_gc, gui.norm_font->fid);
/*
* Set the window sizes if the window is already visible.
*/
if (vimShell != (Widget)NULL && XtIsRealized(vimShell))
{
WIN *wp;
gui_mch_set_winsize();
/* Force the scrollbar heights to get updated when a font is changed */
if (gui.which_scrollbars[SB_LEFT])
{
for (wp = firstwin; wp; wp = wp->w_next)
wp->w_scrollbar.update[SB_LEFT] = SB_UPDATE_HEIGHT;
gui_mch_update_scrollbars(SB_UPDATE_HEIGHT, SB_LEFT);
}
if (gui.which_scrollbars[SB_RIGHT])
{
for (wp = firstwin; wp; wp = wp->w_next)
wp->w_scrollbar.update[SB_RIGHT] = SB_UPDATE_HEIGHT;
gui_mch_update_scrollbars(SB_UPDATE_HEIGHT, SB_RIGHT);
}
}
return OK;
}
/*
* 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);
mch_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);
}
/*
* Iconify the GUI window.
*/
void
gui_mch_iconify()
{
XIconifyWindow(gui.dpy, XtWindow(vimShell), DefaultScreen(gui.dpy));
}
/*
* 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.
*/
static Pixel
gui_x11_get_color(name)
char_u *name;
{
XrmValue from, to;
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 (Pixel) *((Pixel *)to.addr);
else
return (Pixel)NULL;
}
/*
* Set the current text font (gc should be either gui.text_gc or gui.back_gc)
*/
static void
gui_x11_set_font(gc, fid)
GC gc;
Font fid;
{
static Font prev_text_fid = (Font) -1;
static Font prev_back_fid = (Font) -1;
if (gc == gui.text_gc && fid != prev_text_fid)
{
XSetFont(gui.dpy, gc, fid);
prev_text_fid = fid;
}
else if (gc == gui.back_gc && fid != prev_back_fid)
{
XSetFont(gui.dpy, gc, fid);
prev_back_fid = fid;
}
}
/*
* Set the current text color (gc should be either gui.text_gc or gui.back_gc)
*/
static void
gui_x11_set_color(gc, pixel)
GC gc;
Pixel pixel;
{
static Pixel prev_text_pixel = (Pixel) -1;
static Pixel prev_back_pixel = (Pixel) -1;
if (gc == gui.text_gc && pixel != prev_text_pixel)
{
XSetForeground(gui.dpy, gc, pixel);
prev_text_pixel = pixel;
}
else if (gc == gui.back_gc && pixel != prev_back_pixel)
{
XSetBackground(gui.dpy, gc, pixel);
prev_back_pixel = pixel;
}
}
/*
* Draw the cursor.
*/
void
gui_mch_draw_cursor()
{
/* Only write to the screen after LinePointers[] has been initialized */
if (screen_cleared && NextScreen != NULL)
{
/* Clear the selection if we are about to write over it */
if (gui.selection.state == SELECT_DONE
&& gui.row >= gui.selection.start.lnum
&& gui.row <= gui.selection.end.lnum)
gui_mch_clear_selection();
if (gui.row < screen_Rows && gui.col < screen_Columns)
gui_mch_outstr_nowrap(LinePointers[gui.row] + gui.col,
1, FALSE, TRUE);
if (!gui.in_focus)
{
gui_x11_set_color(gui.text_gc, 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);
}
}
}
/*
* 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);
}
/*
* The main GUI input routine. Waits for a character from the keyboard.
* wtime == -1 Wait forever.
* wtime == 0 Don't wait.
* 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;
{
XtIntervalId timer = (XtIntervalId)0;
XtIntervalId updatescript_timer = (XtIntervalId)0;
/*
* Make these 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;
static int do_updatescript;
timed_out = FALSE;
do_updatescript = FALSE;
/*
* If we're going to wait a bit, update the menus for the current State.
*/
if (wtime != 0)
gui_x11_update_menus(0);
gui_mch_update();
if (!is_input_buf_empty()) /* Got char, return immediately */
return OK;
else if (wtime == 0) /* Don't wait for char */
return FAIL;
else if (wtime > 0)
{
timer = XtAppAddTimeOut(app_context, (long_u)wtime, gui_x11_timer_cb,
&timed_out);
}
else
{
/* We are waiting for ever, so update swap files after p_ut msec's */
updatescript_timer = XtAppAddTimeOut(app_context, (long_u)p_ut,
gui_x11_timer_cb, &do_updatescript);
}
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 no characters arrive within 'updatetime' milli-seconds, flush all
* the swap files to disk.
*/
if (do_updatescript)
{
updatescript(0);
do_updatescript = FALSE;
updatescript_timer = (XtIntervalId)0;
}
if (!is_input_buf_empty())
{
if (timer != (XtIntervalId)0 && !timed_out)
XtRemoveTimeOut(timer);
if (updatescript_timer != (XtIntervalId)0 && !do_updatescript)
XtRemoveTimeOut(updatescript_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 the selection if we are about to write over it */
if (gui.selection.state == SELECT_DONE
&& row2 >= gui.selection.start.lnum
&& row1 <= gui.selection.end.lnum)
gui_mch_clear_selection();
XFillRectangle(gui.dpy, gui.wid, gui.back_gc, FILL_X(col1),
FILL_Y(row1), (col2 - col1 + 1) * gui.char_width,
(row2 - row1 + 1) * gui.char_height);
/* Invalidate cursor if it was in this block */
if (gui.cursor_row >= row1 && gui.cursor_row <= row2
&& gui.cursor_col >= col1 && gui.cursor_col <= col2)
INVALIDATE_CURSOR();
}
/*
* Output the given string at the current cursor position. If the string is
* too long to fit on the line, then it is truncated. wrap_cursor may be
* TRUE if the cursor position should be wrapped when the end of the line is
* reached, however the string will still be truncated and not continue on the
* next line. is_cursor should only be TRUE when this function is being called
* to actually draw the cursor.
*/
void
gui_mch_outstr_nowrap(s, len, wrap_cursor, is_cursor)
char_u *s;
int len;
int wrap_cursor;
int is_cursor;
{
GC text_gc;
long_u highlight_mask;
if (len == 0)
return;
if (len < 0)
len = STRLEN(s);
if (is_cursor && gui.in_focus)
highlight_mask = gui.highlight_mask | HL_INVERSE;
else
highlight_mask = gui.highlight_mask;
if (highlight_mask & (HL_INVERSE | HL_STANDOUT))
text_gc = gui.back_gc;
else
text_gc = gui.text_gc;
/* Set the font */
if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && gui.bold_font != NULL)
if ((highlight_mask & HL_ITAL) && gui.boldital_font != NULL)
gui_x11_set_font(text_gc, gui.boldital_font->fid);
else
gui_x11_set_font(text_gc, gui.bold_font->fid);
else if ((highlight_mask & HL_ITAL) && gui.ital_font != NULL)
gui_x11_set_font(text_gc, gui.ital_font->fid);
else
gui_x11_set_font(text_gc, gui.norm_font->fid);
/* Set the color */
if (is_cursor && gui.in_focus)
gui_x11_set_color(text_gc, gui.cursor_pixel);
else if (highlight_mask & (HL_BOLD | HL_STANDOUT))
gui_x11_set_color(text_gc, gui.bold_pixel);
else if (highlight_mask & HL_ITAL)
gui_x11_set_color(text_gc, gui.ital_pixel);
else if (highlight_mask & HL_UNDERLINE)
gui_x11_set_color(text_gc, gui.underline_pixel);
else
gui_x11_set_color(text_gc, gui.norm_pixel);
/* Clear the selection if we are about to write over it */
if (gui.selection.state == SELECT_DONE
&& gui.row >= gui.selection.start.lnum
&& gui.row <= gui.selection.end.lnum)
gui_mch_clear_selection();
/* Draw the text */
XDrawImageString(gui.dpy, gui.wid, text_gc,
TEXT_X(gui.col), TEXT_Y(gui.row), (char *)s, len);
/* No bold font, so fake it */
if ((highlight_mask & (HL_BOLD | HL_STANDOUT)) && gui.bold_font == NULL)
XDrawString(gui.dpy, gui.wid, text_gc,
TEXT_X(gui.col) + 1, TEXT_Y(gui.row), (char *)s, len);
/* Underline the text */
if ((highlight_mask & HL_UNDERLINE)
|| ((highlight_mask & HL_ITAL) && gui.ital_font == NULL))
XDrawLine(gui.dpy, gui.wid, text_gc, FILL_X(gui.col),
FILL_Y(gui.row + 1) - 1, FILL_X(gui.col + len) - 1,
FILL_Y(gui.row + 1) - 1);
if (!is_cursor)
{
/* Invalidate the old physical cursor position if we wrote over it */
if (gui.cursor_row == gui.row && gui.cursor_col >= gui.col
&& gui.cursor_col < gui.col + len)
INVALIDATE_CURSOR();
/* Update the cursor position */
gui.col += len;
if (wrap_cursor && gui.col >= Columns)
{
gui.col = 0;
gui.row++;
}
}
}
/*
* 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_mch_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_mch_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_mch_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_mch_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
gui_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)
{
gui_free_selection(); /* ??? */
*(int *)success = FALSE;
return;
}
motion_type = MCHAR;
p = (char_u *)value;
len = *length;
if (*type == gui.selection.atom)
{
motion_type = *p++;
len--;
}
gui_yank_selection(motion_type, p, len);
XtFree((char *)value);
*(int *)success = TRUE;
}
void
gui_request_selection()
{
XEvent event;
Atom type = gui.selection.atom;
int success;
int i;
for (i = 0; i < 2; i++)
{
XtGetSelectionValue(vimShell, XA_PRIMARY, type,
gui_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
gui_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 (!gui.selection.owned)
return False; /* Shouldn't ever happen */
if (*target == gui.selection.atom)
{
gui_get_selection();
motion_type = gui_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)
{
gui_get_selection();
motion_type = gui_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
gui_x11_lose_ownership_cb(w, selection)
Widget w;
Atom *selection;
{
gui_lose_selection();
}
void
gui_mch_lose_selection()
{
XtDisownSelection(vimShell, XA_PRIMARY, CurrentTime);
gui_mch_clear_selection();
}
int
gui_mch_own_selection()
{
if (XtOwnSelection(vimShell, XA_PRIMARY, CurrentTime,
gui_x11_convert_selection_cb, gui_x11_lose_ownership_cb,
NULL) == False)
return FAIL;
return OK;
}
/*
* Menu stuff.
*/
void
gui_x11_menu_cb(w, client_data, call_data)
Widget w;
XtPointer client_data, call_data;
{
gui_menu_cb((GuiMenu *)client_data);
}
/*
* Used recursively by gui_x11_update_menus (see below)
*/
static void
gui_x11_update_menus_recurse(menu, mode)
GuiMenu *menu;
int mode;
{
while (menu)
{
if (menu->modes & mode)
{
if (vim_strchr(p_guioptions, GO_GREY) != NULL)
XtSetSensitive(menu->id, True);
else
XtManageChild(menu->id);
gui_x11_update_menus_recurse(menu->children, mode);
}
else
{
if (vim_strchr(p_guioptions, GO_GREY) != NULL)
XtSetSensitive(menu->id, False);
else
XtUnmanageChild(menu->id);
}
menu = menu->next;
}
}
/*
* Make sure only the valid menu items appear for this mode. If
* force_menu_update is not TRUE, then we only do this if the mode has changed
* since last time. If "modes" is not 0, then we use these modes instead.
*/
void
gui_x11_update_menus(modes)
int modes;
{
static int prev_mode = -1;
int mode = 0;
if (modes != 0x0)
mode = modes;
else if (VIsual_active)
mode = MENU_VISUAL_MODE;
else if (State & NORMAL)
mode = MENU_NORMAL_MODE;
else if (State & INSERT)
mode = MENU_INSERT_MODE;
else if (State & CMDLINE)
mode = MENU_CMDLINE_MODE;
if (force_menu_update || mode != prev_mode)
{
gui_x11_update_menus_recurse(gui.root_menu, mode);
prev_mode = mode;
force_menu_update = FALSE;
}
}
/*
* 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, bool)
Widget w;
XtPointer client_data;
XEvent *event;
Boolean *bool;
{
/*
* 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 */
}
/*
* Compare two screen positions ala strcmp()
*/
static int
gui_x11_compare_pos(row1, col1, row2, col2)
short_u row1;
short_u col1;
short_u row2;
short_u col2;
{
if (row1 > row2) return( 1);
if (row1 < row2) return(-1);
if (col1 > col2) return( 1);
if (col1 < col2) return(-1);
return( 0);
}
/*
* Start out the selection
*/
void
gui_x11_start_selection(button, x, y, repeated_click, modifiers)
int button;
int x;
int y;
int repeated_click;
int_u modifiers;
{
GuiSelection *gs = &gui.selection;
if (gs->state == SELECT_DONE)
gui_mch_clear_selection();
gs->start.lnum = Y_2_ROW(y);
gs->start.col = X_2_COL(x);
gs->end = gs->start;
gs->origin_row = gs->start.lnum;
gs->state = SELECT_IN_PROGRESS;
if (repeated_click)
{
if (++(gs->mode) > SELECT_MODE_LINE)
gs->mode = SELECT_MODE_CHAR;
}
else
gs->mode = SELECT_MODE_CHAR;
/* clear the cursor until the selection is made */
gui_undraw_cursor();
switch (gs->mode)
{
case SELECT_MODE_CHAR:
gs->origin_start_col = gs->start.col;
gs->word_end_col = gui_x11_get_line_end(gs->start.lnum);
break;
case SELECT_MODE_WORD:
gui_x11_get_word_boundaries(gs, gs->start.lnum, gs->start.col);
gs->origin_start_col = gs->word_start_col;
gs->origin_end_col = gs->word_end_col;
gui_x11_invert_area(gs->start.lnum, gs->word_start_col,
gs->end.lnum, gs->word_end_col);
gs->start.col = gs->word_start_col;
gs->end.col = gs->word_end_col;
break;
case SELECT_MODE_LINE:
gui_x11_invert_area(gs->start.lnum, 0, gs->start.lnum,
gui.num_cols);
gs->start.col = 0;
gs->end.col = gui.num_cols;
break;
}
gs->prev = gs->start;
#ifdef DEBUG_SELECTION
printf("Selection started at (%u,%u)\n", gs->start.lnum, gs->start.col);
#endif
}
/*
* Continue processing the selection
*/
void
gui_x11_process_selection(button, x, y, repeated_click, modifiers)
int button;
int x;
int y;
int repeated_click;
int_u modifiers;
{
GuiSelection *gs = &gui.selection;
int row, col;
int diff;
if (button == MOUSE_RELEASE)
{
/* Check to make sure we have something selected */
if (gs->start.lnum == gs->end.lnum && gs->start.col == gs->end.col)
{
gui_update_cursor();
gs->state = SELECT_CLEARED;
return;
}
#ifdef DEBUG_SELECTION
printf("Selection ended: (%u,%u) to (%u,%u)\n", gs->start.lnum,
gs->start.col, gs->end.lnum, gs->end.col);
#endif
gui_free_selection();
gui_own_selection();
gui_x11_yank_selection(gs->start.lnum, gs->start.col, gs->end.lnum,
gs->end.col);
gui_update_cursor();
gs->state = SELECT_DONE;
return;
}
row = Y_2_ROW(y);
col = X_2_COL(x);
row = check_row(row);
col = check_col(col);
if (col == gs->prev.col && row == gs->prev.lnum)
return;
/*
* When extending the selection with the right mouse button, swap the
* start and end if the position is before half the selection
*/
if (gs->state == SELECT_DONE && button == MOUSE_RIGHT)
{
/*
* If the click is before the start, or the click is inside the
* selection and the start is the closest side, set the origin to the
* end of the selection.
*/
if (gui_x11_compare_pos(row, col, gs->start.lnum, gs->start.col) < 0 ||
(gui_x11_compare_pos(row, col, gs->end.lnum, gs->end.col) < 0 &&
(((gs->start.lnum == gs->end.lnum &&
gs->end.col - col > col - gs->start.col)) ||
((diff = (gs->end.lnum - row) - (row - gs->start.lnum)) > 0 ||
(diff == 0 && col < (gs->start.col + gs->end.col) / 2)))))
{
gs->origin_row = gs->end.lnum;
gs->origin_start_col = gs->end.col - 1;
gs->origin_end_col = gs->end.col;
}
else
{
gs->origin_row = gs->start.lnum;
gs->origin_start_col = gs->start.col;
gs->origin_end_col = gs->start.col;
}
if (gs->mode == SELECT_MODE_WORD)
{
gui_x11_get_word_boundaries(gs, gs->origin_row,
gs->origin_start_col);
gs->origin_start_col = gs->word_start_col;
gs->origin_end_col = gs->word_end_col;
}
}
/* set state, for when using the right mouse button */
gs->state = SELECT_IN_PROGRESS;
#ifdef DEBUG_SELECTION
printf("Selection extending to (%d,%d)\n", row, col);
#endif
switch (gs->mode)
{
case SELECT_MODE_CHAR:
/* If we're on a different line, find where the line ends */
if (row != gs->prev.lnum)
gs->word_end_col = gui_x11_get_line_end(row);
/* See if we are before or after the origin of the selection */
if (gui_x11_compare_pos(row, col, gs->origin_row,
gs->origin_start_col) >= 0)
{
if (col >= (int)gs->word_end_col)
gui_x11_update_selection(gs, gs->origin_row,
gs->origin_start_col, row, gui.num_cols);
else
gui_x11_update_selection(gs, gs->origin_row,
gs->origin_start_col, row, col + 1);
}
else
{
if (col >= (int)gs->word_end_col)
gui_x11_update_selection(gs, row, gs->word_end_col,
gs->origin_row, gs->origin_start_col + 1);
else
gui_x11_update_selection(gs, row, col, gs->origin_row,
gs->origin_start_col + 1);
}
break;
case SELECT_MODE_WORD:
/* If we are still within the same word, do nothing */
if (row == gs->prev.lnum && col >= (int)gs->word_start_col
&& col < (int)gs->word_end_col)
return;
/* Get new word boundaries */
gui_x11_get_word_boundaries(gs, row, col);
/* Handle being after the origin point of selection */
if (gui_x11_compare_pos(row, col, gs->origin_row,
gs->origin_start_col) >= 0)
gui_x11_update_selection(gs, gs->origin_row,
gs->origin_start_col, row, gs->word_end_col);
else
gui_x11_update_selection(gs, row, gs->word_start_col,
gs->origin_row, gs->origin_end_col);
break;
case SELECT_MODE_LINE:
if (row == gs->prev.lnum)
return;
if (gui_x11_compare_pos(row, col, gs->origin_row,
gs->origin_start_col) >= 0)
gui_x11_update_selection(gs, gs->origin_row, 0, row,
gui.num_cols);
else
gui_x11_update_selection(gs, row, 0, gs->origin_row,
gui.num_cols);
break;
}
gs->prev.lnum = row;
gs->prev.col = col;
#ifdef DEBUG_SELECTION
printf("Selection is: (%u,%u) to (%u,%u)\n", gs->start.lnum,
gs->start.col, gs->end.lnum, gs->end.col);
#endif
}
/*
* Called after an Expose event to redraw the selection
*/
void
gui_x11_redraw_selection(x, y, w, h)
int x;
int y;
int w;
int h;
{
GuiSelection *gs = &gui.selection;
int row1, col1, row2, col2;
int row;
int start;
int end;
if (gs->state == SELECT_CLEARED)
return;
row1 = Y_2_ROW(y);
col1 = X_2_COL(x);
row2 = Y_2_ROW(y + h - 1);
col2 = X_2_COL(x + w - 1);
/* Limit the rows that need to be re-drawn */
if (gs->start.lnum > row1)
row1 = gs->start.lnum;
if (gs->end.lnum < row2)
row2 = gs->end.lnum;
/* Look at each row that might need to be re-drawn */
for (row = row1; row <= row2; row++)
{
/* For the first selection row, use the starting selection column */
if (row == gs->start.lnum)
start = gs->start.col;
else
start = 0;
/* For the last selection row, use the ending selection column */
if (row == gs->end.lnum)
end = gs->end.col;
else
end = gui.num_cols;
if (col1 > start)
start = col1;
if (col2 < end)
end = col2 + 1;
if (end > start)
invert_rectangle(row, start, 1, end - start);
}
}
/*
* Called from outside to clear selected region from the display
*/
void
gui_mch_clear_selection()
{
GuiSelection *gs = &gui.selection;
if (gs->state == SELECT_CLEARED)
return;
gui_x11_invert_area(gs->start.lnum, gs->start.col, gs->end.lnum,
gs->end.col);
gs->state = SELECT_CLEARED;
}
/*
* Invert a region of the display between a starting and ending row and column
*/
static void
gui_x11_invert_area(row1, col1, row2, col2)
int row1;
int col1;
int row2;
int col2;
{
/* Swap the from and to positions so the from is always before */
if (gui_x11_compare_pos(row1, col1, row2, col2) > 0)
{
int tmp_row, tmp_col;
tmp_row = row1;
tmp_col = col1;
row1 = row2;
col1 = col2;
row2 = tmp_row;
col2 = tmp_col;
}
/* If all on the same line, do it the easy way */
if (row1 == row2)
{
invert_rectangle(row1, col1, 1, col2 - col1);
return;
}
/* Handle a piece of the first line */
if (col1 > 0)
{
invert_rectangle(row1, col1, 1, gui.num_cols - col1);
row1++;
}
/* Handle a piece of the last line */
if (col2 < gui.num_cols - 1)
{
invert_rectangle(row2, 0, 1, col2);
row2--;
}
/* Handle the rectangle thats left */
if (row2 >= row1)
invert_rectangle(row1, 0, row2 - row1 + 1, gui.num_cols);
}
/*
* Yank the currently selected area into the special selection buffer so it
* will be available for pasting.
*/
static void
gui_x11_yank_selection(row1, col1, row2, col2)
int row1;
int col1;
int row2;
int col2;
{
char_u *buffer;
char_u *bufp;
int row;
int start_col;
int end_col;
int line_end_col;
int add_newline_flag = FALSE;
/* Create a temporary buffer for storing the text */
buffer = lalloc((row2 - row1 + 1) * gui.num_cols + 1, TRUE);
if (buffer == NULL)
return; /* Should there be an error message here? */
/* Process each row in the selection */
for (bufp = buffer, row = row1; row <= row2; row++)
{
if (row == row1)
start_col = col1;
else
start_col = 0;
if (row == row2)
end_col = col2;
else
end_col = gui.num_cols;
line_end_col = gui_x11_get_line_end(row);
/* See if we need to nuke some trailing whitespace */
if (end_col >= gui.num_cols && (row < row2 || end_col > line_end_col))
{
/* Get rid of trailing whitespace */
end_col = line_end_col;
if (end_col < start_col)
end_col = start_col;
/* If the last line extended to the end, add an extra newline */
if (row == row2)
add_newline_flag = TRUE;
}
/* If after the first row, we need to always add a newline */
if (row > row1)
*bufp++ = NL;
if (gui.row < screen_Rows && end_col <= screen_Columns)
{
STRNCPY(bufp, &LinePointers[row][start_col], end_col - start_col);
bufp += end_col - start_col;
}
}
/* Add a newline at the end if the selection ended there */
if (add_newline_flag)
*bufp++ = NL;
gui_yank_selection(MCHAR, buffer, bufp - buffer);
vim_free(buffer);
}
/*
* Find the starting and ending positions of the word at the given row and
* column.
*/
static void
gui_x11_get_word_boundaries(gs, row, col)
GuiSelection *gs;
int row;
int col;
{
char start_class;
int temp_col;
if (row >= screen_Rows || col >= screen_Columns)
return;
start_class = char_class(LinePointers[row][col]);
temp_col = col;
for ( ; temp_col > 0; temp_col--)
if (char_class(LinePointers[row][temp_col - 1]) != start_class)
break;
gs->word_start_col = temp_col;
temp_col = col;
for ( ; temp_col < screen_Columns; temp_col++)
if (char_class(LinePointers[row][temp_col]) != start_class)
break;
gs->word_end_col = temp_col;
#ifdef DEBUG_SELECTION
printf("Current word: col %u to %u\n", gs->word_start_col,
gs->word_end_col);
#endif
}
/*
* Find the column position for the last non-whitespace character on the given
* line.
*/
static int
gui_x11_get_line_end(row)
int row;
{
int i;
if (row >= screen_Rows)
return 0;
for (i = screen_Columns; i > 0; i--)
if (LinePointers[row][i - 1] != ' ')
break;
return i;
}
/*
* Update the currently selected region by adding and/or subtracting from the
* beginning or end and inverting the changed area(s).
*/
static void
gui_x11_update_selection(gs, row1, col1, row2, col2)
GuiSelection *gs;
int row1;
int col1;
int row2;
int col2;
{
/* See if we changed at the beginning of the selection */
if (row1 != gs->start.lnum || col1 != gs->start.col)
{
gui_x11_invert_area(row1, col1, gs->start.lnum, gs->start.col);
gs->start.lnum = row1;
gs->start.col = col1;
}
/* See if we changed at the end of the selection */
if (row2 != gs->end.lnum || col2 != gs->end.col)
{
gui_x11_invert_area(row2, col2, gs->end.lnum, gs->end.col);
gs->end.lnum = row2;
gs->end.col = col2;
}
}