1 /*
2  *	X11 support, Dave Lemke, 11/91
3  *	X Toolkit support, Kevin Buettner, 2/94
4  *
5  * $Id: x11.c,v 1.433 2021/11/30 23:48:34 tom Exp $
6  */
7 
8 /*
9  * Widget set selection.
10  *
11  * You must have exactly one of the following defined
12  *
13  *    NO_WIDGETS	-- Use only Xlib and X toolkit (Xt)
14  *    ATHENA_WIDGETS	-- Use Xlib, Xt, and Xaw widget set
15  *    MOTIF_WIDGETS	-- Use Xlib, Xt, and Motif widget set
16  *
17  * We derive/set from the configure script some flags that allow intermediate
18  * configurations between NO_WIDGETS and ATHENA_WIDGETS
19  *
20  *    KEV_WIDGETS
21  *    OPT_KEV_DRAGGING
22  *    OPT_KEV_SCROLLBARS
23  */
24 
25 #include <x11vile.h>
26 
27 #ifndef GCC_NORETURN
28 #ifdef _X_NORETURN
29 #define GCC_NORETURN		_X_NORETURN
30 #else
31 #define GCC_NORETURN		/* nothing */
32 #endif
33 #endif
34 
35 #if DISP_X11 && XTOOLKIT
36 
37 static Display *dpy;
38 static ENC_CHOICES term_encoding = enc_DEFAULT;
39 
40 #if OPT_MULTIBYTE
41 static ENC_CHOICES font_encoding = enc_DEFAULT;
42 #endif
43 
44 static TextWindowRec cur_win_rec;
45 static TextWindow cur_win = &cur_win_rec;
46 static TBUFF *PasteBuf;
47 
48 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
49 static Cursor curs_sb_v_double_arrow;
50 static Cursor curs_sb_up_arrow;
51 static Cursor curs_sb_down_arrow;
52 static Cursor curs_sb_left_arrow;
53 static Cursor curs_sb_right_arrow;
54 static Cursor curs_double_arrow;
55 #endif
56 
57 #if MOTIF_WIDGETS
58 static Bool lookfor_sb_resize = FALSE;
59 #endif
60 
61 struct eventqueue {
62     XEvent event;
63     struct eventqueue *next;
64 };
65 
66 static struct eventqueue *evqhead = NULL;
67 static struct eventqueue *evqtail = NULL;
68 
69 static void x_flush(void);
70 static void x_beep(void);
71 
72 #if OPT_COLOR
73 static void x_set_cursor_color(int);
74 #else
75 #define	x_set_foreground	nullterm_setfore
76 #define	x_set_background	nullterm_setback
77 #define	x_set_cursor_color	nullterm_setccol
78 #endif
79 
80 static int set_character_class(const char *s);
81 static void x_touch(TextWindow tw, int sc, int sr, UINT ec, UINT er);
82 static void x_own_selection(Atom selection);
83 static Boolean x_get_selected_text(UCHAR ** datp, size_t *lenp);
84 static void extend_selection(TextWindow tw, int nr, int nc, Bool wipe);
85 static void x_process_event(Widget w, XtPointer unused, XEvent *ev,
86 			    Boolean *continue_to_dispatch);
87 static void x_configure_window(Widget w, XtPointer unused, XEvent *ev,
88 			       Boolean *continue_to_dispatch);
89 static void x_change_focus(Widget w, XtPointer unused, XEvent *ev,
90 			   Boolean *continue_to_dispatch);
91 static void x_typahead_timeout(XtPointer flagp, XtIntervalId * id);
92 static void x_key_press(Widget w, XtPointer unused, XEvent *ev,
93 			Boolean *continue_to_dispatch);
94 static void x_wm_delwin(Widget w, XtPointer unused, XEvent *ev,
95 			Boolean *continue_to_dispatch);
96 static void x_start_autocolor_timer(void);
97 static void x_autocolor_timeout(XtPointer flagp, XtIntervalId * id);
98 static void x_stop_autocolor_timer(void);
99 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
100 static Boolean too_light_or_too_dark(Pixel pixel);
101 #endif
102 #if OPT_KEV_SCROLLBARS
103 static Boolean alloc_shadows(Pixel pixel, Pixel *light, Pixel *dark);
104 #endif
105 static void configure_bar(Widget w, XEvent *event, String *params,
106 			  Cardinal *num_params);
107 static int check_scrollbar_allocs(void);
108 static void kqinit(TextWindow tw);
109 static int kqempty(TextWindow tw);
110 static int kqfull(TextWindow tw);
111 static void kqadd(TextWindow tw, int c);
112 static int kqpop(TextWindow tw);
113 static void display_cursor(XtPointer client_data, XtIntervalId * idp);
114 #if MOTIF_WIDGETS
115 static void pane_button(Widget w, XtPointer unused, XEvent *ev,
116 			Boolean *continue_to_dispatch);
117 #endif /* MOTIF_WIDGETS */
118 #if OPT_KEV_SCROLLBARS
119 static void x_expose_scrollbar(Widget w, XtPointer unused, XEvent *ev,
120 			       Boolean *continue_to_dispatch);
121 #endif /* OPT_KEV_SCROLLBARS */
122 #if OPT_KEV_DRAGGING
123 static void repeat_scroll(XtPointer count, XtIntervalId * id);
124 #endif
125 #if OPT_INPUT_METHOD
126 static void init_xlocale(void);
127 #endif
128 #if OPT_WORKING
129 static void x_set_watch_cursor(int onflag);
130 static int x_has_events(void);
131 #else
132 #define x_has_events() (XtAppPending(cur_win->app_context) & XtIMXEvent)
133 #endif /* OPT_WORKING */
134 static void evqadd(const XEvent *evp);
135 
136 #define	x_width(tw)		((tw)->cols * (UINT) (tw)->char_width)
137 #define	x_height(tw)		((tw)->rows * (UINT) (tw)->char_height)
138 #define	x_pos(tw, c)		((c) * (tw)->char_width)
139 #define	y_pos(tw, r)		((r) * (tw)->char_height)
140 #define	text_y_pos(tw, r)	(y_pos((tw), (r)) + (tw)->char_ascent)
141 
142 #undef min
143 #define min(a,b)		((a) < (b) ? (a) : (b))
144 
145 #undef max
146 #define max(a,b)		((a) > (b) ? (a) : (b))
147 
148 #if KEV_WIDGETS
149 /* We define our own little bulletin board widget here...if this gets
150  * too unwieldy, we should move it to another file.
151  */
152 
153 #include	<X11/IntrinsicP.h>
154 
155 /* New fields for the Bb widget class record */
156 typedef struct {
157     int empty;
158 } BbClassPart;
159 
160 /* Class record declaration */
161 typedef struct _BbClassRec {
162     CoreClassPart core_class;
163     CompositeClassPart composite_class;
164     BbClassPart bb_class;
165 } BbClassRec;
166 
167 extern BbClassRec bbClassRec;
168 
169 /* Instance declaration */
170 typedef struct _BbRec {
171     CorePart core;
172     CompositePart composite;
173 } BbRec;
174 
175 static XtGeometryResult bbGeometryManager(Widget w, XtWidgetGeometry *
176 					  request, XtWidgetGeometry * reply);
177 static XtGeometryResult bbPreferredSize(Widget widget, XtWidgetGeometry *
178 					constraint, XtWidgetGeometry * preferred);
179 
180 BbClassRec bbClassRec =
181 {
182     {
183 /* core_class fields      */
184     /* superclass         */ (WidgetClass) & compositeClassRec,
185     /* class_name         */ "Bb",
186     /* widget_size        */ sizeof(BbRec),
187     /* class_initialize   */ NULL,
188     /* class_part_init    */ NULL,
189     /* class_inited       */ FALSE,
190     /* initialize         */ NULL,
191     /* initialize_hook    */ NULL,
192     /* realize            */ XtInheritRealize,
193     /* actions            */ NULL,
194     /* num_actions        */ 0,
195     /* resources          */ NULL,
196     /* num_resources      */ 0,
197     /* xrm_class          */ NULLQUARK,
198     /* compress_motion    */ TRUE,
199     /* compress_exposure  */ TRUE,
200     /* compress_enterleave */ TRUE,
201     /* visible_interest   */ FALSE,
202     /* destroy            */ NULL,
203     /* resize             */ XtInheritResize,
204     /* expose             */ NULL,
205     /* set_values         */ NULL,
206     /* set_values_hook    */ NULL,
207     /* set_values_almost  */ XtInheritSetValuesAlmost,
208     /* get_values_hook    */ NULL,
209     /* accept_focus       */ NULL,
210     /* version            */ XtVersion,
211     /* callback_private   */ NULL,
212     /* tm_table           */ NULL,
213 							/* query_geometry     */ bbPreferredSize,
214 							/*XtInheritQueryGeometry, */
215     /* display_accelerator */ XtInheritDisplayAccelerator,
216     /* extension          */ NULL
217     },
218     {
219 /* composite_class fields */
220     /* geometry_manager   */ bbGeometryManager,
221     /* change_managed     */ XtInheritChangeManaged,
222     /* insert_child       */ XtInheritInsertChild,
223     /* delete_child       */ XtInheritDeleteChild,
224     /* extension          */ NULL
225     },
226     {
227 /* Bb class fields */
228     /* empty              */ 0,
229     }
230 };
231 
232 static WidgetClass bbWidgetClass = (WidgetClass) & bbClassRec;
233 
234 /*ARGSUSED*/
235 static XtGeometryResult
bbPreferredSize(Widget widget GCC_UNUSED,XtWidgetGeometry * constraint GCC_UNUSED,XtWidgetGeometry * preferred GCC_UNUSED)236 bbPreferredSize(Widget widget GCC_UNUSED,
237 		XtWidgetGeometry * constraint GCC_UNUSED,
238 		XtWidgetGeometry * preferred GCC_UNUSED)
239 {
240     return XtGeometryYes;
241 }
242 
243 /*ARGSUSED*/
244 static XtGeometryResult
bbGeometryManager(Widget w,XtWidgetGeometry * request,XtWidgetGeometry * reply GCC_UNUSED)245 bbGeometryManager(Widget w,
246 		  XtWidgetGeometry * request,
247 		  XtWidgetGeometry * reply GCC_UNUSED)
248 {
249     /* Allow any and all changes to the geometry */
250     if (request->request_mode & CWWidth)
251 	w->core.width = request->width;
252     if (request->request_mode & CWHeight)
253 	w->core.height = request->height;
254     if (request->request_mode & CWBorderWidth)
255 	w->core.border_width = request->border_width;
256     if (request->request_mode & CWX)
257 	w->core.x = request->x;
258     if (request->request_mode & CWY)
259 	w->core.y = request->y;
260 
261     return XtGeometryYes;
262 }
263 
264 #endif /* KEV_WIDGETS */
265 
266 static void
set_pointer(Window win,Cursor cursor)267 set_pointer(Window win, Cursor cursor)
268 {
269     XColor colordefs[2];	/* 0 is foreground, 1 is background */
270 
271     XDefineCursor(dpy, win, cursor);
272 
273     colordefs[0].pixel = cur_win->pointer_fg;
274     colordefs[1].pixel = cur_win->pointer_bg;
275     XQueryColors(dpy, DefaultColormap(dpy, DefaultScreen(dpy)),
276 		 colordefs, 2);
277     XRecolorCursor(dpy, cursor, colordefs, colordefs + 1);
278 }
279 
280 #if !OPT_KEV_SCROLLBARS && !OPT_XAW_SCROLLBARS
281 static int dont_update_sb = FALSE;
282 #endif
283 
284 #if !OPT_KEV_DRAGGING
285 static void
set_scroll_window(long n)286 set_scroll_window(long n)
287 {
288     WINDOW *wp;
289     for_each_visible_window(wp) {
290 	if (n-- == 0)
291 	    break;
292     }
293     if (n < 0)
294 	set_curwp(wp);
295 }
296 #endif /* !OPT_KEV_DRAGGING */
297 
298 #if MOTIF_WIDGETS
299 
300 static void
JumpProc(Widget scrollbar GCC_UNUSED,XtPointer closure,XtPointer call_data)301 JumpProc(Widget scrollbar GCC_UNUSED,
302 	 XtPointer closure,
303 	 XtPointer call_data)
304 {
305     long value = (long) closure;
306     int lcur;
307 
308     lookfor_sb_resize = FALSE;
309     if (value >= cur_win->nscrollbars)
310 	return;
311     set_scroll_window(value);
312     lcur = line_no(curwp->w_bufp, curwp->w_line.l);
313     mvupwind(TRUE, lcur - ((XmScrollBarCallbackStruct *) call_data)->value);
314     dont_update_sb = TRUE;
315     (void) update(TRUE);
316     dont_update_sb = FALSE;
317 }
318 
319 static void
grip_moved(Widget w GCC_UNUSED,XtPointer unused GCC_UNUSED,XEvent * ev GCC_UNUSED,Boolean * continue_to_dispatch GCC_UNUSED)320 grip_moved(Widget w GCC_UNUSED,
321 	   XtPointer unused GCC_UNUSED,
322 	   XEvent *ev GCC_UNUSED,
323 	   Boolean *continue_to_dispatch GCC_UNUSED)
324 {
325     int i;
326     WINDOW *wp, *saved_curwp;
327     Dimension height;
328     int lines;
329 
330     if (!lookfor_sb_resize)
331 	return;
332     lookfor_sb_resize = FALSE;
333     saved_curwp = curwp;
334 
335     i = 0;
336     for_each_visible_window(wp) {
337 	XtVaGetValues(cur_win->scrollbars[i],
338 		      XtNheight, &height,
339 		      NULL);
340 	lines = (height + (cur_win->char_height / 2)) / cur_win->char_height;
341 	if (lines <= 0) {
342 	    lines = 1;
343 	}
344 	curwp = wp;
345 	resize(TRUE, lines);
346 	i++;
347     }
348     set_curwp(saved_curwp);
349     (void) update(TRUE);
350 }
351 
352 static void
update_scrollbar_sizes(void)353 update_scrollbar_sizes(void)
354 {
355     WINDOW *wp;
356     int newsbcnt;
357     Dimension new_height;
358     Widget *children;
359     int num_children;
360     long i = 0;
361 
362     for_each_visible_window(wp)
363 	i++;
364     newsbcnt = (int) i;
365 
366     /* Remove event handlers on sashes */
367     XtVaGetValues(cur_win->pane,
368 		  XmNchildren, &children,
369 		  XmNnumChildren, &num_children,
370 		  NULL);
371     while (num_children-- > 0)
372 	if (XmIsSash(children[num_children]))
373 	    XtRemoveEventHandler(children[num_children],
374 				 ButtonReleaseMask,
375 				 FALSE,
376 				 pane_button,
377 				 NULL);
378 
379     /* Create any needed new scrollbars */
380     for (i = cur_win->nscrollbars + 1; i <= newsbcnt; i++)
381 	if (cur_win->scrollbars[i] == NULL) {
382 	    cur_win->scrollbars[i] =
383 		XtVaCreateWidget("scrollbar",
384 				 xmScrollBarWidgetClass,
385 				 cur_win->pane,
386 				 Nval(XmNsliderSize, 1),
387 				 Nval(XmNvalue, 1),
388 				 Nval(XmNminimum, 1),
389 				 Nval(XmNmaximum, 2),	/* so we don't get warning */
390 				 Nval(XmNorientation, XmVERTICAL),
391 				 Nval(XmNtranslations, cur_win->my_scrollbars_trans),
392 				 NULL);
393 	    XtAddCallback(cur_win->scrollbars[i],
394 			  XmNvalueChangedCallback, JumpProc, (XtPointer) i);
395 	    XtAddCallback(cur_win->scrollbars[i],
396 			  XmNdragCallback, JumpProc, (XtPointer) i);
397 	    XtAddEventHandler(cur_win->scrollbars[i],
398 			      StructureNotifyMask,
399 			      FALSE,
400 			      grip_moved,
401 			      (XtPointer) i);
402 	}
403 
404     /* Unmanage current set of scrollbars */
405     if (cur_win->nscrollbars >= 0)
406 	XtUnmanageChildren(cur_win->scrollbars,
407 			   (Cardinal) (cur_win->nscrollbars + 1));
408 
409     /* Set sizes on scrollbars */
410     cur_win->nscrollbars = newsbcnt;
411     i = 0;
412     for_each_visible_window(wp) {
413 	new_height = (Dimension) (wp->w_ntrows * cur_win->char_height);
414 	XtVaSetValues(cur_win->scrollbars[i],
415 		      Nval(XmNallowResize, TRUE),
416 		      Nval(XmNheight, new_height),
417 		      Nval(XmNpaneMinimum, 1),
418 		      Nval(XmNpaneMaximum, DisplayHeight(dpy, DefaultScreen(dpy))),
419 		      Nval(XmNshowArrows, wp->w_ntrows > 3 ? TRUE : FALSE),
420 		      NULL);
421 	wp->w_flag &= (USHORT) (~WFSBAR);
422 	gui_update_scrollbar(wp);
423 	i++;
424     }
425     XtVaSetValues(cur_win->scrollbars[i],
426 		  Nval(XmNheight, cur_win->char_height - 1),
427 		  Nval(XmNallowResize, FALSE),
428 		  Nval(XmNpaneMinimum, cur_win->char_height - 1),
429 		  Nval(XmNpaneMaximum, cur_win->char_height - 1),
430 		  Nval(XmNshowArrows, FALSE),
431 		  NULL);
432 
433     /* Manage the current set of scrollbars */
434     XtManageChildren(cur_win->scrollbars,
435 		     (Cardinal) (cur_win->nscrollbars + 1));
436 
437     /* Add event handlers for sashes */
438     XtVaGetValues(cur_win->pane,
439 		  XmNchildren, &children,
440 		  XmNnumChildren, &num_children,
441 		  NULL);
442     while (num_children-- > 0)
443 	if (XmIsSash(children[num_children]))
444 	    XtAddEventHandler(children[num_children],
445 			      ButtonReleaseMask,
446 			      FALSE,
447 			      pane_button,
448 			      NULL);
449 }
450 
451 #else
452 #if OPT_XAW_SCROLLBARS
453 
454 #if !OPT_KEV_DRAGGING
455 static void
JumpProc(Widget scrollbar GCC_UNUSED,XtPointer closure,XtPointer call_data)456 JumpProc(Widget scrollbar GCC_UNUSED,
457 	 XtPointer closure,
458 	 XtPointer call_data)
459 {
460     L_NUM lcur, lmax;
461     long value = (long) closure;
462     float *percent = (float *) call_data;
463 
464     if (value < cur_win->nscrollbars) {
465 	set_scroll_window(value);
466 	lcur = line_no(curwp->w_bufp, curwp->w_line.l);
467 	lmax = vl_line_count(curwp->w_bufp);
468 	mvupwind(TRUE, (int) ((float) lcur - (float) lmax * (*percent)));
469 	(void) update(TRUE);
470     }
471 }
472 
473 static void
ScrollProc(Widget scrollbar GCC_UNUSED,XtPointer closure,XtPointer call_data)474 ScrollProc(Widget scrollbar GCC_UNUSED,
475 	   XtPointer closure,
476 	   XtPointer call_data)
477 {
478     long value = (long) closure;
479     long position = (long) call_data;
480 
481     if (value < cur_win->nscrollbars) {
482 	set_scroll_window(value);
483 	mvupwind(TRUE, -(position / cur_win->char_height));
484 	(void) update(TRUE);
485     }
486 }
487 #endif
488 
489 static void
update_scrollbar_sizes(void)490 update_scrollbar_sizes(void)
491 {
492     WINDOW *wp;
493     int i, newsbcnt;
494     Dimension new_height;
495 
496     i = 0;
497     for_each_visible_window(wp)
498 	i++;
499     newsbcnt = i;
500 
501     /* Create any needed new scrollbars and grips */
502     for (i = cur_win->nscrollbars + 1; i <= newsbcnt; i++) {
503 	if (cur_win->scrollbars[i] == NULL) {
504 	    cur_win->scrollbars[i] =
505 		XtVaCreateWidget("scrollbar",
506 				 scrollbarWidgetClass,
507 				 cur_win->pane,
508 				 Nval(XtNforeground, cur_win->scrollbar_fg),
509 				 Nval(XtNbackground, cur_win->scrollbar_bg),
510 				 Nval(XtNthumb, cur_win->thumb_bm),
511 				 Nval(XtNtranslations, cur_win->my_scrollbars_trans),
512 				 NULL);
513 #if !OPT_KEV_DRAGGING
514 	    XtAddCallback(cur_win->scrollbars[i],
515 			  XtNjumpProc, JumpProc, (XtPointer) (intptr_t) i);
516 	    XtAddCallback(cur_win->scrollbars[i],
517 			  XtNscrollProc, ScrollProc, (XtPointer) (intptr_t) i);
518 #endif
519 	}
520     }
521     for (i = newsbcnt - 2; i >= 0 && cur_win->grips[i] == NULL; i--) {
522 	cur_win->grips[i] =
523 	    XtVaCreateWidget("resizeGrip",
524 			     gripWidgetClass,
525 			     cur_win->pane,
526 			     Nval(XtNbackground, cur_win->modeline_bg),
527 			     Nval(XtNborderWidth, 0),
528 			     Nval(XtNheight, 1),
529 			     Nval(XtNwidth, 1),
530 			     Sval(XtNleft, ((cur_win->scrollbar_on_left)
531 					    ? XtChainLeft
532 					    : XtChainRight)),
533 			     Sval(XtNright, ((cur_win->scrollbar_on_left)
534 					     ? XtChainLeft
535 					     : XtChainRight)),
536 			     Nval(XtNtranslations, cur_win->my_resizeGrip_trans),
537 			     NULL);
538     }
539 
540     /* Unmanage current set of scrollbars */
541     if (cur_win->nscrollbars > 0)
542 	XtUnmanageChildren(cur_win->scrollbars,
543 			   (Cardinal) (cur_win->nscrollbars));
544 
545     /* Set sizes and positions on scrollbars and grips */
546     cur_win->nscrollbars = newsbcnt;
547     i = 0;
548     for_each_visible_window(wp) {
549 	L_NUM total = vl_line_count(curwp->w_bufp);
550 	L_NUM thumb = line_no(curwp->w_bufp, curwp->w_line.l);
551 
552 	new_height = (Dimension) (wp->w_ntrows * cur_win->char_height);
553 	cur_win->scrollinfo[i].totlen = new_height;
554 	XtVaSetValues(cur_win->scrollbars[i],
555 		      Nval(XtNy, wp->w_toprow * cur_win->char_height),
556 		      Nval(XtNheight, new_height),
557 		      Nval(XtNwidth, cur_win->pane_width - 1),
558 		      Nval(XtNorientation, XtorientVertical),
559 		      Nval(XtNvertDistance, wp->w_toprow * cur_win->char_height),
560 		      Nval(XtNhorizDistance, 1),
561 		      NULL);
562 	XawScrollbarSetThumb(cur_win->scrollbars[i],
563 			     ((float) (thumb - 1)) / (float) max(total, 1),
564 			     ((float) wp->w_ntrows) / (float) max(total, wp->w_ntrows));
565 	clr_typed_flags(wp->w_flag, USHORT, WFSBAR);
566 	gui_update_scrollbar(wp);
567 	if (wp->w_wndp) {
568 	    XtVaSetValues(cur_win->grips[i],
569 			  Nval(XtNx, 1),
570 			  Nval(XtNy, ((wp->w_wndp->w_toprow - 1)
571 				      * cur_win->char_height) + 2),
572 			  Nval(XtNheight, cur_win->char_height - 3),
573 			  Nval(XtNwidth, cur_win->pane_width - 1),
574 			  Nval(XtNvertDistance, ((wp->w_wndp->w_toprow - 1)
575 						 * cur_win->char_height) + 2),
576 			  Nval(XtNhorizDistance, 1),
577 			  NULL);
578 	}
579 	i++;
580     }
581 
582     /* Manage the current set of scrollbars */
583     XtManageChildren(cur_win->scrollbars,
584 		     (Cardinal) (cur_win->nscrollbars));
585 
586     XtManageChildren(cur_win->grips,
587 		     (Cardinal) (cur_win->nscrollbars - 1));
588 
589     for (i = 0; i < cur_win->nscrollbars; i++)
590 	XRaiseWindow(dpy, XtWindow(cur_win->scrollbars[i]));
591 
592     for (i = 1; i < cur_win->nscrollbars; i++)
593 	XRaiseWindow(dpy, XtWindow(cur_win->grips[i - 1]));
594 }
595 
596 #else
597 #if OPT_KEV_SCROLLBARS
598 static void
update_scrollbar_sizes(void)599 update_scrollbar_sizes(void)
600 {
601     WINDOW *wp;
602     int i, newsbcnt;
603     Dimension new_height;
604     Cardinal nchildren;
605 
606     i = 0;
607     for_each_visible_window(wp)
608 	i++;
609     newsbcnt = i;
610 
611     /* Create any needed new scrollbars and grips */
612     for (i = newsbcnt - 1; i >= 0 && cur_win->scrollbars[i] == NULL; i--) {
613 	if (cur_win->slider_is_3D) {
614 	    cur_win->scrollbars[i] =
615 		XtVaCreateWidget("scrollbar",
616 				 coreWidgetClass,
617 				 cur_win->pane,
618 				 Nval(XtNbackgroundPixmap, cur_win->trough_pixmap),
619 				 Nval(XtNborderWidth, 0),
620 				 Nval(XtNheight, 1),
621 				 Nval(XtNwidth, 1),
622 				 Nval(XtNtranslations, cur_win->my_scrollbars_trans),
623 				 NULL);
624 	} else {
625 	    cur_win->scrollbars[i] =
626 		XtVaCreateWidget("scrollbar",
627 				 coreWidgetClass,
628 				 cur_win->pane,
629 				 Nval(XtNbackground, cur_win->scrollbar_bg),
630 				 Nval(XtNborderWidth, 0),
631 				 Nval(XtNheight, 1),
632 				 Nval(XtNwidth, 1),
633 				 Nval(XtNtranslations, cur_win->my_scrollbars_trans),
634 				 NULL);
635 	}
636 
637 	XtAddEventHandler(cur_win->scrollbars[i],
638 			  ExposureMask,
639 			  FALSE,
640 			  x_expose_scrollbar,
641 			  (XtPointer) 0);
642 	cur_win->scrollinfo[i].exposed = False;
643     }
644     for (i = newsbcnt - 2; i >= 0 && cur_win->grips[i] == NULL; i--)
645 	cur_win->grips[i] =
646 	    XtVaCreateWidget("resizeGrip",
647 			     coreWidgetClass,
648 			     cur_win->pane,
649 			     Nval(XtNbackground, cur_win->modeline_bg),
650 			     Nval(XtNborderWidth, 0),
651 			     Nval(XtNheight, 1),
652 			     Nval(XtNwidth, 1),
653 			     Nval(XtNtranslations, cur_win->my_resizeGrip_trans),
654 			     NULL);
655 
656     /* Set sizes and positions on scrollbars and grips */
657     i = 0;
658     for_each_visible_window(wp) {
659 	new_height = (Dimension) (wp->w_ntrows * cur_win->char_height);
660 	XtVaSetValues(cur_win->scrollbars[i],
661 		      Nval(XtNx, cur_win->slider_is_3D ? 0 : 1),
662 		      Nval(XtNy, wp->w_toprow * cur_win->char_height),
663 		      Nval(XtNheight, new_height),
664 		      Nval(XtNwidth, (int) cur_win->pane_width
665 			   + (cur_win->slider_is_3D ? 2 : -1)),
666 		      NULL);
667 	cur_win->scrollinfo[i].totlen = new_height;
668 	if (wp->w_wndp) {
669 	    XtVaSetValues(cur_win->grips[i],
670 			  Nval(XtNx, 1),
671 			  Nval(XtNy, ((wp->w_wndp->w_toprow - 1)
672 				      * cur_win->char_height) + 2),
673 			  Nval(XtNheight, cur_win->char_height - 3),
674 			  Nval(XtNwidth, cur_win->pane_width - 1),
675 			  NULL);
676 	}
677 	i++;
678     }
679 
680     if (cur_win->nscrollbars > newsbcnt) {
681 	nchildren = (Cardinal) (cur_win->nscrollbars - newsbcnt);
682 	XtUnmanageChildren(cur_win->scrollbars + newsbcnt, nchildren);
683 	XtUnmanageChildren(cur_win->grips + newsbcnt - 1, nchildren);
684 	for (i = cur_win->nscrollbars; i > newsbcnt;) {
685 	    cur_win->scrollinfo[--i].exposed = False;
686 	}
687     } else if (cur_win->nscrollbars < newsbcnt) {
688 	nchildren = (Cardinal) (newsbcnt - cur_win->nscrollbars);
689 	XtManageChildren(cur_win->scrollbars + cur_win->nscrollbars, nchildren);
690 	if (cur_win->nscrollbars > 0)
691 	    XtManageChildren(cur_win->grips + cur_win->nscrollbars - 1, nchildren);
692 	else if (cur_win->nscrollbars == 0 && nchildren > 1)
693 	    XtManageChildren(cur_win->grips, nchildren - 1);
694     }
695     cur_win->nscrollbars = newsbcnt;
696 
697     i = 0;
698     for_each_visible_window(wp) {
699 	wp->w_flag = (USHORT) (wp->w_flag & ~WFSBAR);
700 	gui_update_scrollbar(wp);
701 	i++;
702     }
703 
704     /*
705      * Set the cursors... It would be nice if we could do this in the
706      * initialization above, but the widget needs to be realized for this
707      * to work
708      */
709     for (i = 0; i < cur_win->nscrollbars; i++) {
710 	if (XtIsRealized(cur_win->scrollbars[i]))
711 	    set_pointer(XtWindow(cur_win->scrollbars[i]),
712 			curs_sb_v_double_arrow);
713 	if (i < cur_win->nscrollbars - 1 && XtIsRealized(cur_win->grips[i]))
714 	    set_pointer(XtWindow(cur_win->grips[i]),
715 			curs_double_arrow);
716     }
717 }
718 
719 /*
720  * The X11R5 Athena scrollbar code was used as a reference for writing
721  * draw_thumb and parts of update_thumb.
722  */
723 
724 #define FILL_TOP 0x01
725 #define FILL_BOT 0x02
726 
727 #define SP_HT 16		/* slider pixmap height */
728 
729 static void
draw_thumb(Widget w,int top,int bot,int dofill)730 draw_thumb(Widget w, int top, int bot, int dofill)
731 {
732     UINT length = (UINT) (bot - top);
733     if (bot < 0 || top < 0 || bot <= top)
734 	return;
735 
736     if (!dofill)
737 	XClearArea(XtDisplay(w), XtWindow(w), cur_win->slider_is_3D ? 2 : 1,
738 		   top, cur_win->pane_width - 2, length, FALSE);
739     else if (!cur_win->slider_is_3D)
740 	XFillRectangle(XtDisplay(w), XtWindow(w), cur_win->scrollbar_gc,
741 		       1, top, cur_win->pane_width - 2, length);
742     else {
743 	if (dofill & FILL_TOP) {
744 	    int tbot;
745 	    tbot = bot - ((dofill & FILL_BOT) ? 2 : 0);
746 	    if (tbot <= top)
747 		tbot = top + 1;
748 	    if (top + (SP_HT - 2) < tbot)
749 		tbot = top + (SP_HT - 2);
750 	    XCopyArea(dpy, cur_win->slider_pixmap, XtWindow(w),
751 		      cur_win->scrollbar_gc,
752 		      0, 0, cur_win->pane_width - 2, (UINT) (tbot - top),
753 		      2, top);
754 
755 	    top = tbot;
756 	}
757 	if (dofill & FILL_BOT) {
758 	    int btop = max(top, bot - (SP_HT - 2));
759 	    XCopyArea(dpy, cur_win->slider_pixmap, XtWindow(w),
760 		      cur_win->scrollbar_gc,
761 		      0, SP_HT - (bot - btop),
762 		      cur_win->pane_width - 2, (UINT) (bot - btop),
763 		      2, btop);
764 	    bot = btop;
765 
766 	}
767 	if (top < bot) {
768 	    XFillRectangle(XtDisplay(w), XtWindow(w), cur_win->scrollbar_gc,
769 			   2, top,
770 			   cur_win->pane_width - 2, (UINT) (bot - top));
771 	}
772     }
773 }
774 
775 #define MIN_THUMB_SIZE 8
776 
777 static void
update_thumb(int barnum,int newtop,int newlen)778 update_thumb(int barnum, int newtop, int newlen)
779 {
780     int oldtop, oldbot, newbot, totlen;
781     int f = cur_win->slider_is_3D ? 2 : 0;
782     Widget w = cur_win->scrollbars[barnum];
783 
784     oldtop = cur_win->scrollinfo[barnum].top;
785     oldbot = cur_win->scrollinfo[barnum].bot;
786     totlen = cur_win->scrollinfo[barnum].totlen;
787     newtop = min(newtop, totlen - 3);
788     newbot = newtop + max(newlen, MIN_THUMB_SIZE);
789     newbot = min(newbot, totlen);
790     cur_win->scrollinfo[barnum].top = newtop;
791     cur_win->scrollinfo[barnum].bot = newbot;
792 
793     if (!cur_win->scrollinfo[barnum].exposed)
794 	return;
795 
796     if (XtIsRealized(w)) {
797 	if (newtop < oldtop) {
798 	    int tbot = min(newbot, oldtop + f);
799 	    draw_thumb(w, newtop, tbot,
800 		       FILL_TOP | ((tbot == newbot) ? FILL_BOT : 0));
801 	}
802 	if (newtop > oldtop) {
803 	    draw_thumb(w, oldtop, min(newtop, oldbot), 0);
804 	    if (cur_win->slider_is_3D && newtop < oldbot)
805 		draw_thumb(w, newtop, min(newtop + f, oldbot), FILL_TOP);
806 	}
807 	if (newbot < oldbot) {
808 	    draw_thumb(w, max(newbot, oldtop), oldbot, 0);
809 	    if (cur_win->slider_is_3D && oldtop < newbot)
810 		draw_thumb(w, max(newbot - f, oldtop), newbot, FILL_BOT);
811 	}
812 	if (newbot > oldbot) {
813 	    int btop = max(newtop, oldbot - f);
814 	    draw_thumb(w, btop, newbot,
815 		       FILL_BOT | ((btop == newtop) ? FILL_TOP : 0));
816 	}
817     }
818 }
819 
820 /*ARGSUSED*/
821 static void
x_expose_scrollbar(Widget w,XtPointer unused GCC_UNUSED,XEvent * ev,Boolean * continue_to_dispatch GCC_UNUSED)822 x_expose_scrollbar(Widget w,
823 		   XtPointer unused GCC_UNUSED,
824 		   XEvent *ev,
825 		   Boolean *continue_to_dispatch GCC_UNUSED)
826 {
827     int i;
828 
829     if (ev->type != Expose)
830 	return;
831 
832     for (i = 0; i < cur_win->nscrollbars; i++) {
833 	if (cur_win->scrollbars[i] == w) {
834 	    int top, bot;
835 	    top = cur_win->scrollinfo[i].top;
836 	    bot = cur_win->scrollinfo[i].bot;
837 	    cur_win->scrollinfo[i].top = -1;
838 	    cur_win->scrollinfo[i].bot = -1;
839 	    cur_win->scrollinfo[i].exposed = True;
840 	    update_thumb(i, top, bot - top);
841 	}
842     }
843 }
844 #endif /* OPT_KEV_SCROLLBARS  */
845 #endif /* OPT_XAW_SCROLLBARS  */
846 #endif /* MOTIF_WIDGETS */
847 
848 #if OPT_KEV_DRAGGING
849 static void
do_scroll(Widget w,XEvent * event,String * params,Cardinal * num_params)850 do_scroll(Widget w,
851 	  XEvent *event,
852 	  String *params,
853 	  Cardinal *num_params)
854 {
855     static enum {
856 	none, forward, backward, drag
857     } scrollmode = none;
858     int pos;
859     WINDOW *wp;
860     int i;
861     XEvent nev;
862     int count;
863 
864     /*
865      * Return immediately if behind in processing motion events.  Note:
866      * If this is taken out, scrolling is actually smoother, but sometimes
867      * takes a while to catch up.  I should think that performance would
868      * be horrible on a slow server.
869      */
870 
871     if (scrollmode == drag
872 	&& event->type == MotionNotify
873 	&& x_has_events()
874 	&& XtAppPeekEvent(cur_win->app_context, &nev)
875 	&& (nev.type == MotionNotify || nev.type == ButtonRelease))
876 	return;
877 
878     if (*num_params != 1)
879 	return;
880 
881     /* Determine vertical position */
882     switch (event->type) {
883     case MotionNotify:
884 	pos = event->xmotion.y;
885 	break;
886     case ButtonPress:
887     case ButtonRelease:
888 	pos = event->xbutton.y;
889 	break;
890     default:
891 	return;
892     }
893 
894     /* Determine scrollbar number and corresponding vile window */
895     i = 0;
896     for_each_visible_window(wp) {
897 	if (cur_win->scrollbars[i] == w)
898 	    break;
899 	i++;
900     }
901 
902     if (!wp)
903 	return;
904 
905     if (pos < 0)
906 	pos = 0;
907     if (pos > cur_win->scrollinfo[i].totlen)
908 	pos = cur_win->scrollinfo[i].totlen;
909 
910     switch (**params) {
911     case 'E':			/* End */
912 	if (cur_win->scroll_repeat_id != (XtIntervalId) 0) {
913 	    XtRemoveTimeOut(cur_win->scroll_repeat_id);
914 	    cur_win->scroll_repeat_id = (XtIntervalId) 0;
915 	}
916 	set_pointer(XtWindow(w), curs_sb_v_double_arrow);
917 	scrollmode = none;
918 	break;
919     case 'F':			/* Forward */
920 	if (scrollmode != none)
921 	    break;
922 	count = (pos / cur_win->char_height) + 1;
923 	scrollmode = forward;
924 	set_pointer(XtWindow(w), curs_sb_up_arrow);
925 	goto do_scroll_common;
926     case 'B':			/* Backward */
927 	if (scrollmode != none)
928 	    break;
929 	count = -((pos / cur_win->char_height) + 1);
930 	scrollmode = backward;
931 	set_pointer(XtWindow(w), curs_sb_down_arrow);
932       do_scroll_common:
933 	set_curwp(wp);
934 	mvdnwind(TRUE, count);
935 	cur_win->scroll_repeat_id =
936 	    XtAppAddTimeOut(cur_win->app_context,
937 			    (ULONG) cur_win->scroll_repeat_timeout,
938 			    repeat_scroll,
939 			    (XtPointer) (long) count);
940 	(void) update(TRUE);
941 	break;
942     case 'S':			/* StartDrag */
943 	if (scrollmode == none) {
944 	    set_curwp(wp);
945 	    scrollmode = drag;
946 	    set_pointer(XtWindow(w), curs_sb_right_arrow);
947 	}
948 	/* FALLTHRU */
949     case 'D':			/* Drag */
950 	if (scrollmode == drag) {
951 	    int lcur = line_no(curwp->w_bufp, curwp->w_line.l);
952 	    int ltarg = (vl_line_count(curwp->w_bufp) * pos
953 			 / cur_win->scrollinfo[i].totlen) + 1;
954 	    mvupwind(TRUE, lcur - ltarg);
955 	    (void) update(TRUE);
956 	}
957 	break;
958     }
959 }
960 
961 /*ARGSUSED*/
962 static void
repeat_scroll(XtPointer count,XtIntervalId * id GCC_UNUSED)963 repeat_scroll(XtPointer count,
964 	      XtIntervalId * id GCC_UNUSED)
965 {
966     cur_win->scroll_repeat_id =
967 	XtAppAddTimeOut(cur_win->app_context,
968 			(ULONG) cur_win->scroll_repeat_interval,
969 			repeat_scroll,
970 			(XtPointer) count);
971     mvdnwind(TRUE, (int) (long) count);
972     (void) update(TRUE);
973     XSync(dpy, False);
974 }
975 #endif /* OPT_KEV_DRAGGING */
976 
977 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
978 static void
resize_bar(Widget w,XEvent * event,String * params,Cardinal * num_params)979 resize_bar(Widget w,
980 	   XEvent *event,
981 	   String *params,
982 	   Cardinal *num_params)
983 {
984     static int motion_permitted = False;
985     static int root_y;
986     int pos = 0;		/* stifle -Wall */
987     WINDOW *wp;
988     int i;
989     XEvent nev;
990 
991     /* Return immediately if behind in processing motion events */
992     if (motion_permitted
993 	&& event->type == MotionNotify
994 	&& x_has_events()
995 	&& XtAppPeekEvent(cur_win->app_context, &nev)
996 	&& (nev.type == MotionNotify || nev.type == ButtonRelease))
997 	return;
998 
999     if (*num_params != 1)
1000 	return;
1001 
1002     switch (**params) {
1003     case 'S':			/* Start */
1004 	motion_permitted = True;
1005 	root_y = event->xbutton.y_root - event->xbutton.y;
1006 	return;
1007     case 'D':			/* Drag */
1008 	if (!motion_permitted)
1009 	    return;
1010 	/*
1011 	 * We use kind of convoluted mechanism to determine the vertical
1012 	 * position with respect to the widget which we are moving. The
1013 	 * reason is that the x,y position from the event structure is
1014 	 * unreliable since the widget may have moved in between the
1015 	 * time when the event was first generated (at the display
1016 	 * server) and the time at which the event is received (in
1017 	 * this code here).  So we keep track of the vertical position
1018 	 * of the widget with respect to the root window and use the
1019 	 * corresponding field in the event structure to determine
1020 	 * the vertical position of the pointer with respect to the
1021 	 * widget of interest.  In the past, XQueryPointer() was
1022 	 * used to determine the position of the pointer, but this is
1023 	 * not really what we want since the position returned by
1024 	 * XQueryPointer is the position of the pointer at a time
1025 	 * after the event was both generated and received (and thus
1026 	 * inaccurate).  This is why the resize grip would sometimes
1027 	 * "follow" the mouse pointer after the button was released.
1028 	 */
1029 	pos = event->xmotion.y_root - root_y;
1030 	break;
1031     case 'E':			/* End */
1032 	if (!motion_permitted)
1033 	    return;
1034 	pos = event->xbutton.y_root - root_y;
1035 	motion_permitted = False;
1036 	break;
1037     }
1038 
1039     /* Determine grip number and corresponding vile window (above grip) */
1040     i = 0;
1041     for_each_visible_window(wp) {
1042 	if (cur_win->grips[i] == w)
1043 	    break;
1044 	i++;
1045     }
1046 
1047     if (!wp)
1048 	return;
1049 
1050     if (pos < 0)
1051 	pos -= cur_win->char_height;
1052     pos = pos / cur_win->char_height;
1053 
1054     if (pos) {
1055 	int nlines;
1056 	if (pos >= wp->w_wndp->w_ntrows)
1057 	    pos = wp->w_wndp->w_ntrows - 1;
1058 
1059 	nlines = wp->w_ntrows + pos;
1060 	if (nlines < 1)
1061 	    nlines = 1;
1062 	root_y += (nlines - wp->w_ntrows) * cur_win->char_height;
1063 	set_curwp(wp);
1064 	resize(TRUE, nlines);
1065 	(void) update(TRUE);
1066     }
1067 }
1068 #endif /* OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS */
1069 
1070 void
gui_update_scrollbar(WINDOW * uwp)1071 gui_update_scrollbar(WINDOW *uwp)
1072 {
1073     WINDOW *wp;
1074     int i;
1075     int lnum, lcnt;
1076 
1077 #if !OPT_KEV_SCROLLBARS && !OPT_XAW_SCROLLBARS
1078     if (dont_update_sb)
1079 	return;
1080 #endif /* !OPT_KEV_SCROLLBARS */
1081 
1082     i = 0;
1083     for_each_visible_window(wp) {
1084 	if (wp == uwp)
1085 	    break;
1086 	i++;
1087     }
1088     if (wp == 0)
1089 	return;
1090     if (i >= cur_win->nscrollbars || (wp->w_flag & WFSBAR)) {
1091 	/*
1092 	 * update_scrollbar_sizes will recursively invoke gui_update_scrollbar,
1093 	 * but with WFSBAR disabled.
1094 	 */
1095 	update_scrollbar_sizes();
1096 	return;
1097     }
1098 
1099     lnum = line_no(wp->w_bufp, wp->w_line.l);
1100     lnum = (lnum > 0) ? lnum : 1;
1101     lcnt = vl_line_count(wp->w_bufp);
1102 #if MOTIF_WIDGETS
1103     lcnt += 1;
1104     XtVaSetValues(cur_win->scrollbars[i],
1105 		  Nval(XmNmaximum, lcnt + wp->w_ntrows),
1106 		  Nval(XmNsliderSize, wp->w_ntrows),
1107 		  Nval(XmNvalue, lnum),
1108 		  Nval(XmNpageIncrement, ((wp->w_ntrows > 1)
1109 					  ? wp->w_ntrows - 1
1110 					  : 1)),
1111 		  NULL);
1112 #else
1113 #if OPT_XAW_SCROLLBARS
1114     XawScrollbarSetThumb(cur_win->scrollbars[i],
1115 			 ((float) (lnum - 1)) / (float) max(lcnt, wp->w_ntrows),
1116 			 ((float) wp->w_ntrows) / (float) max(lcnt, wp->w_ntrows));
1117 #else
1118 #if OPT_KEV_SCROLLBARS
1119     {
1120 	int top, len;
1121 	lcnt = max(lcnt, 1);
1122 	len = (min(lcnt, wp->w_ntrows) * cur_win->scrollinfo[i].totlen
1123 	       / lcnt) + 1;
1124 	top = ((lnum - 1) * cur_win->scrollinfo[i].totlen)
1125 	    / lcnt;
1126 	update_thumb(i, top, len);
1127     }
1128 #endif /* OPT_KEV_SCROLLBARS */
1129 #endif /* OPT_XAW_SCROLLBARS */
1130 #endif /* MOTIF_WIDGETS */
1131 }
1132 
1133 #define XtNnormalShape		"normalShape"
1134 #define XtCNormalShape		"NormalShape"
1135 #define XtNwatchShape		"watchShape"
1136 #define XtCWatchShape		"WatchShape"
1137 
1138 #define XtNforkOnStartup	"forkOnStartup"
1139 #define XtCForkOnStartup	"ForkOnStartup"
1140 
1141 #define XtNscrollbarWidth	"scrollbarWidth"
1142 #define XtCScrollbarWidth	"ScrollbarWidth"
1143 #define XtNfocusFollowsMouse	"focusFollowsMouse"
1144 #define XtCFocusFollowsMouse	"FocusFollowsMouse"
1145 #define XtNscrollbarOnLeft	"scrollbarOnLeft"
1146 #define XtCScrollbarOnLeft	"ScrollbarOnLeft"
1147 #define XtNmultiClickTime	"multiClickTime"
1148 #define XtCMultiClickTime	"MultiClickTime"
1149 #define XtNcharClass		"charClass"
1150 #define XtNwheelScrollAmount    "wheelScrollAmount"
1151 #define XtCWheelScrollAmount    "WheelScrollAmount"
1152 #define XtCCharClass		"CharClass"
1153 #if OPT_KEV_DRAGGING
1154 #define	XtNscrollRepeatTimeout	"scrollRepeatTimeout"
1155 #define	XtCScrollRepeatTimeout	"ScrollRepeatTimeout"
1156 #endif
1157 #define	XtNscrollRepeatInterval	"scrollRepeatInterval"
1158 #define	XtCScrollRepeatInterval	"ScrollRepeatInterval"
1159 #define XtNpersistentSelections	"persistentSelections"
1160 #define XtCPersistentSelections	"PersistentSelections"
1161 #define XtNselectionSetsDOT	"selectionSetsDOT"
1162 #define XtCSelectionSetsDOT	"SelectionSetsDOT"
1163 #define XtNblinkInterval	"blinkInterval"
1164 #define XtCBlinkInterval	"BlinkInterval"
1165 #define XtNsliderIsSolid	"sliderIsSolid"
1166 #define XtCSliderIsSolid	"SliderIsSolid"
1167 #define	XtNfocusForeground	"focusForeground"
1168 #define	XtNfocusBackground	"focusBackground"
1169 
1170 #define XtNfcolor0		"fcolor0"
1171 #define XtCFcolor0		"Fcolor0"
1172 #define XtNbcolor0		"bcolor0"
1173 #define XtCBcolor0		"Bcolor0"
1174 
1175 #define XtNfcolor1		"fcolor1"
1176 #define XtCFcolor1		"Fcolor1"
1177 #define XtNbcolor1		"bcolor1"
1178 #define XtCBcolor1		"Bcolor1"
1179 
1180 #define XtNfcolor2		"fcolor2"
1181 #define XtCFcolor2		"Fcolor2"
1182 #define XtNbcolor2		"bcolor2"
1183 #define XtCBcolor2		"Bcolor2"
1184 
1185 #define XtNfcolor3		"fcolor3"
1186 #define XtCFcolor3		"Fcolor3"
1187 #define XtNbcolor3		"bcolor3"
1188 #define XtCBcolor3		"Bcolor3"
1189 
1190 #define XtNfcolor4		"fcolor4"
1191 #define XtCFcolor4		"Fcolor4"
1192 #define XtNbcolor4		"bcolor4"
1193 #define XtCBcolor4		"Bcolor4"
1194 
1195 #define XtNfcolor5		"fcolor5"
1196 #define XtCFcolor5		"Fcolor5"
1197 #define XtNbcolor5		"bcolor5"
1198 #define XtCBcolor5		"Bcolor5"
1199 
1200 #define XtNfcolor6		"fcolor6"
1201 #define XtCFcolor6		"Fcolor6"
1202 #define XtNbcolor6		"bcolor6"
1203 #define XtCBcolor6		"Bcolor6"
1204 
1205 #define XtNfcolor7		"fcolor7"
1206 #define XtCFcolor7		"Fcolor7"
1207 #define XtNbcolor7		"bcolor7"
1208 #define XtCBcolor7		"Bcolor7"
1209 
1210 #define XtNfcolor8		"fcolor8"
1211 #define XtCFcolor8		"Fcolor8"
1212 #define XtNbcolor8		"bcolor8"
1213 #define XtCBcolor8		"Bcolor8"
1214 
1215 #define XtNfcolor9		"fcolor9"
1216 #define XtCFcolor9		"Fcolor9"
1217 #define XtNbcolor9		"bcolor9"
1218 #define XtCBcolor9		"Bcolor9"
1219 
1220 #define XtNfcolorA		"fcolor10"
1221 #define XtCFcolorA		"Fcolor10"
1222 #define XtNbcolorA		"bcolor10"
1223 #define XtCBcolorA		"Bcolor10"
1224 
1225 #define XtNfcolorB		"fcolor11"
1226 #define XtCFcolorB		"Fcolor11"
1227 #define XtNbcolorB		"bcolor11"
1228 #define XtCBcolorB		"Bcolor11"
1229 
1230 #define XtNfcolorC		"fcolor12"
1231 #define XtCFcolorC		"Fcolor12"
1232 #define XtNbcolorC		"bcolor12"
1233 #define XtCBcolorC		"Bcolor12"
1234 
1235 #define XtNfcolorD		"fcolor13"
1236 #define XtCFcolorD		"Fcolor13"
1237 #define XtNbcolorD		"bcolor13"
1238 #define XtCBcolorD		"Bcolor13"
1239 
1240 #define XtNfcolorE		"fcolor14"
1241 #define XtCFcolorE		"Fcolor14"
1242 #define XtNbcolorE		"bcolor14"
1243 #define XtCBcolorE		"Bcolor14"
1244 
1245 #define XtNfcolorF		"fcolor15"
1246 #define XtCFcolorF		"Fcolor15"
1247 #define XtNbcolorF		"bcolor15"
1248 #define XtCBcolorF		"Bcolor15"
1249 
1250 /*
1251  * xvile has two categories of color resources: (a) those which depend only
1252  * on the X default colors, and (b) those which may depend on fixups to the
1253  * foreground/background colors within xvile.  In the latter case, we cannot
1254  * simply evaluate all of the resources at once; they are fetched via a
1255  * sub-resource call.
1256  */
1257 #define XRES_TOP_COLOR(name, class, value, dft) \
1258     { \
1259 	name, \
1260 	class, \
1261 	XtRPixel, \
1262 	sizeof(Pixel), \
1263 	XtOffset(TextWindow, value), \
1264 	XtRString, \
1265 	(XtPointer) dft \
1266     }
1267 
1268 #define XRES_SUB_COLOR(name, class, value, dft) \
1269     { \
1270 	name, \
1271 	class, \
1272 	XtRPixel, \
1273 	sizeof(Pixel), \
1274 	XtOffset(TextWindow, value), \
1275 	XtRPixel, \
1276 	(XtPointer) dft \
1277     }
1278 
1279 #define XRES_FG(name, class, value) \
1280     XRES_TOP_COLOR(name, class, value, XtDefaultForeground)
1281 
1282 #define XRES_BG(name, class, value) \
1283     XRES_TOP_COLOR(name, class, value, XtDefaultBackground)
1284 
1285 /*
1286  * Other resources, for readability.
1287  */
1288 #define XRES_BOOL(name, class, value, dft) \
1289     { \
1290 	name, \
1291 	class, \
1292 	XtRBool, \
1293 	sizeof(Bool), \
1294 	XtOffset(TextWindow, value), \
1295 	XtRImmediate, \
1296 	(XtPointer) dft \
1297     }
1298 
1299 #define XRES_DIMENSION(name, class, value, dft) \
1300     { \
1301 	name, \
1302 	class, \
1303 	XtRDimension, \
1304 	sizeof(cur_win->value), \
1305 	XtOffset(TextWindow, value), \
1306 	XtRImmediate, \
1307 	(XtPointer) dft \
1308     }
1309 
1310 #define XRES_INTEGER(name, class, value, dft) \
1311     { \
1312 	name, \
1313 	class, \
1314 	XtRInt, \
1315 	sizeof(cur_win->value), \
1316 	XtOffset(TextWindow, value), \
1317 	XtRImmediate, \
1318 	(XtPointer) dft \
1319     }
1320 
1321 #define XRES_STRING(name, class, value, dft) \
1322     { \
1323 	name, \
1324 	class, \
1325 	XtRString, \
1326 	sizeof(String *), \
1327 	XtOffset(TextWindow, value), \
1328 	XtRImmediate, \
1329 	(XtPointer) dft \
1330     }
1331 
1332 static XtResource app_resources[] =
1333 {
1334 #if OPT_KEV_DRAGGING
1335     XRES_INTEGER(XtNscrollRepeatTimeout, XtCScrollRepeatTimeout,
1336 		 scroll_repeat_timeout, 500),	/* 1/2 second */
1337 #endif				/* OPT_KEV_DRAGGING */
1338     XRES_INTEGER(XtNscrollRepeatInterval, XtCScrollRepeatInterval,
1339 		 scroll_repeat_interval, 60),	/* 60 milliseconds */
1340     XRES_BOOL(XtNreverseVideo, XtCReverseVideo, reverse_video, False),
1341     XRES_STRING(XtNiconName, XtCIconName, iconname, ""),
1342     XRES_STRING(XtNgeometry, XtCGeometry, geometry, "80x36"),
1343 #ifdef XRENDERFONT
1344     XRES_STRING(XtNfontFamily, XtCString, starting_fontname, "monospace"),
1345 #else
1346     XRES_STRING(XtNfont, XtCFont, starting_fontname, "fixed"),
1347 #endif
1348     XRES_FG(XtNforeground, XtCForeground, fg),
1349     XRES_BG(XtNbackground, XtCBackground, bg),
1350     XRES_BOOL(XtNforkOnStartup, XtCForkOnStartup, fork_on_startup, False),
1351     XRES_BOOL(XtNfocusFollowsMouse, XtCFocusFollowsMouse,
1352 	      focus_follows_mouse, False),
1353     XRES_INTEGER(XtNmultiClickTime, XtCMultiClickTime, click_timeout, 600),
1354     XRES_STRING(XtNcharClass, XtCCharClass, multi_click_char_class, NULL),
1355     XRES_BOOL(XtNscrollbarOnLeft, XtCScrollbarOnLeft, scrollbar_on_left, False),
1356     XRES_INTEGER(XtNscrollbarWidth, XtCScrollbarWidth, pane_width, PANE_WIDTH_DEFAULT),
1357     XRES_INTEGER(XtNwheelScrollAmount, XtCWheelScrollAmount,
1358 		 wheel_scroll_amount, 3),
1359 #if OPT_MENUS
1360     XRES_DIMENSION(XtNmenuHeight, XtCMenuHeight, menu_height, MENU_HEIGHT_DEFAULT),
1361 #endif
1362 #if OPT_MENUS_COLORED
1363     XRES_FG(XtNmenuForeground, XtCMenuForeground, menubar_fg),
1364     XRES_BG(XtNmenuBackground, XtCMenuBackground, menubar_bg),
1365 #endif
1366     XRES_BOOL(XtNpersistentSelections, XtCPersistentSelections,
1367 	      persistent_selections, True),
1368     XRES_BOOL(XtNselectionSetsDOT, XtCSelectionSetsDOT, selection_sets_DOT, False),
1369     XRES_INTEGER(XtNblinkInterval, XtCBlinkInterval,
1370 		 blink_interval, -666),		/* 2/3 second; only when highlighted */
1371 #if OPT_INPUT_METHOD
1372     XRES_BOOL(XtNopenIm, XtCOpenIm, open_im, True),
1373     XRES_STRING(XtNinputMethod, XtCInputMethod, rs_inputMethod, ""),
1374     XRES_STRING(XtNpreeditType, XtCPreeditType,
1375 		rs_preeditType, "OverTheSpot,Root"),
1376 #ifndef DEFXIMFONT
1377 #define DEFXIMFONT		"*"
1378 #endif
1379 #define XtNximFont		"ximFont"
1380 #define XtCXimFont		"XimFont"
1381     XRES_STRING(XtNximFont, XtCXimFont, rs_imFont, DEFXIMFONT)
1382 #endif				/* OPT_INPUT_METHOD */
1383 };
1384 
1385 /*
1386  * The reason for the sub-resource tables is to allow xvile to use the main
1387  * window's foreground/background color as the default value for other color
1388  * resources.  That makes setting up color resources simpler, but complicates
1389  * the program.
1390  *
1391  * The call that retrieves the subresources initializes _all_ of the resources
1392  * for the given name/class.  So there are additional resources in these tables
1393  * which cannot be moved to the app_resources table.
1394  */
1395 static XtResource color_resources[] =
1396 {
1397     XRES_SUB_COLOR(XtNbcolor0, XtCBcolor0, colors_bg[0], &cur_win_rec.bg),
1398     XRES_SUB_COLOR(XtNbcolor1, XtCBcolor1, colors_bg[1], &cur_win_rec.bg),
1399     XRES_SUB_COLOR(XtNbcolor2, XtCBcolor2, colors_bg[2], &cur_win_rec.bg),
1400     XRES_SUB_COLOR(XtNbcolor3, XtCBcolor3, colors_bg[3], &cur_win_rec.bg),
1401     XRES_SUB_COLOR(XtNbcolor4, XtCBcolor4, colors_bg[4], &cur_win_rec.bg),
1402     XRES_SUB_COLOR(XtNbcolor5, XtCBcolor5, colors_bg[5], &cur_win_rec.bg),
1403     XRES_SUB_COLOR(XtNbcolor6, XtCBcolor6, colors_bg[6], &cur_win_rec.bg),
1404     XRES_SUB_COLOR(XtNbcolor7, XtCBcolor7, colors_bg[7], &cur_win_rec.bg),
1405     XRES_SUB_COLOR(XtNbcolor8, XtCBcolor8, colors_bg[8], &cur_win_rec.bg),
1406     XRES_SUB_COLOR(XtNbcolor9, XtCBcolor9, colors_bg[9], &cur_win_rec.bg),
1407     XRES_SUB_COLOR(XtNbcolorA, XtCBcolorA, colors_bg[10], &cur_win_rec.bg),
1408     XRES_SUB_COLOR(XtNbcolorB, XtCBcolorB, colors_bg[11], &cur_win_rec.bg),
1409     XRES_SUB_COLOR(XtNbcolorC, XtCBcolorC, colors_bg[12], &cur_win_rec.bg),
1410     XRES_SUB_COLOR(XtNbcolorD, XtCBcolorD, colors_bg[13], &cur_win_rec.bg),
1411     XRES_SUB_COLOR(XtNbcolorE, XtCBcolorE, colors_bg[14], &cur_win_rec.bg),
1412     XRES_SUB_COLOR(XtNbcolorF, XtCBcolorF, colors_bg[15], &cur_win_rec.bg),
1413     XRES_SUB_COLOR(XtNfcolor0, XtCFcolor0, colors_fg[0], &cur_win_rec.fg),
1414     XRES_TOP_COLOR(XtNfcolor1, XtCFcolor1, colors_fg[1], "rgb:ff/0/0"),
1415     XRES_TOP_COLOR(XtNfcolor2, XtCFcolor2, colors_fg[2], "rgb:0/ff/0"),
1416     XRES_TOP_COLOR(XtNfcolor3, XtCFcolor3, colors_fg[3], "rgb:a5/2a/2a"),
1417     XRES_TOP_COLOR(XtNfcolor4, XtCFcolor4, colors_fg[4], "rgb:0/0/ff"),
1418     XRES_TOP_COLOR(XtNfcolor5, XtCFcolor5, colors_fg[5], "rgb:ff/0/ff"),
1419     XRES_TOP_COLOR(XtNfcolor6, XtCFcolor6, colors_fg[6], "rgb:0/ff/ff"),
1420     XRES_TOP_COLOR(XtNfcolor7, XtCFcolor7, colors_fg[7], "rgb:e6/e6/e6"),
1421     XRES_TOP_COLOR(XtNfcolor8, XtCFcolor8, colors_fg[8], "rgb:be/be/be"),
1422     XRES_TOP_COLOR(XtNfcolor9, XtCFcolor9, colors_fg[9], "rgb:ff/7f/7f"),
1423     XRES_TOP_COLOR(XtNfcolorA, XtCFcolorA, colors_fg[10], "rgb:7f/ff/7f"),
1424     XRES_TOP_COLOR(XtNfcolorB, XtCFcolorB, colors_fg[11], "rgb:ff/ff/0"),
1425     XRES_TOP_COLOR(XtNfcolorC, XtCFcolorC, colors_fg[12], "rgb:7f/7f/ff"),
1426     XRES_TOP_COLOR(XtNfcolorD, XtCFcolorD, colors_fg[13], "rgb:ff/7f/ff"),
1427     XRES_TOP_COLOR(XtNfcolorE, XtCFcolorE, colors_fg[14], "rgb:7f/ff/ff"),
1428     XRES_TOP_COLOR(XtNfcolorF, XtCFcolorF, colors_fg[15], "rgb:ff/ff/ff"),
1429 };
1430 
1431 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
1432 static XtResource scrollbar_resources[] =
1433 {
1434     XRES_SUB_COLOR(XtNforeground, XtCForeground, scrollbar_fg, &cur_win_rec.fg),
1435     XRES_SUB_COLOR(XtNbackground, XtCBackground, scrollbar_bg, &cur_win_rec.bg),
1436     XRES_BOOL(XtNsliderIsSolid, XtCSliderIsSolid, slider_is_solid, False),
1437 };
1438 #endif
1439 
1440 static XtResource modeline_resources[] =
1441 {
1442     XRES_SUB_COLOR(XtNforeground, XtCForeground, modeline_fg, &cur_win_rec.bg),
1443     XRES_SUB_COLOR(XtNbackground, XtCBackground, modeline_bg, &cur_win_rec.fg),
1444     XRES_SUB_COLOR(XtNfocusForeground, XtCForeground, modeline_focus_fg, &cur_win_rec.bg),
1445     XRES_SUB_COLOR(XtNfocusBackground, XtCBackground, modeline_focus_bg, &cur_win_rec.fg),
1446 };
1447 
1448 static XtResource selection_resources[] =
1449 {
1450     XRES_SUB_COLOR(XtNforeground, XtCBackground, selection_fg, &cur_win_rec.bg),
1451     XRES_SUB_COLOR(XtNbackground, XtCForeground, selection_bg, &cur_win_rec.fg),
1452 };
1453 
1454 /*
1455  * We resort to a bit of trickery for the cursor resources.  Note that the
1456  * default foreground and background for the cursor is the same as that for
1457  * the rest of the window.  This would render the cursor invisible!  This
1458  * condition actually indicates that usual technique of inverting the
1459  * foreground and background colors should be used, the rationale being
1460  * that no (sane) user would want to set the cursor foreground and
1461  * background to be the same as the rest of the window.
1462  */
1463 
1464 static XtResource cursor_resources[] =
1465 {
1466     XRES_SUB_COLOR(XtNforeground, XtCForeground, cursor_fg, &cur_win_rec.fg),
1467     XRES_SUB_COLOR(XtNbackground, XtCBackground, cursor_bg, &cur_win_rec.bg),
1468 };
1469 
1470 static XtResource pointer_resources[] =
1471 {
1472     XRES_SUB_COLOR(XtNforeground, XtCForeground, pointer_fg, &cur_win_rec.fg),
1473     XRES_SUB_COLOR(XtNbackground, XtCBackground, pointer_bg, &cur_win_rec.bg),
1474     {
1475 	XtNnormalShape,
1476 	XtCNormalShape,
1477 	XtRCursor,
1478 	sizeof(Cursor),
1479 	XtOffset(TextWindow, normal_pointer),
1480 	XtRString,
1481 	(XtPointer) "xterm"
1482     },
1483 #if OPT_WORKING
1484     {
1485 	XtNwatchShape,
1486 	XtCWatchShape,
1487 	XtRCursor,
1488 	sizeof(Cursor),
1489 	XtOffset(TextWindow, watch_pointer),
1490 	XtRString,
1491 	(XtPointer) "watch"
1492     },
1493 #endif
1494 };
1495 
1496 #define CHECK_MIN_MAX(v,min,max)	\
1497 	do {				\
1498 	    if ((v) > (max))		\
1499 		(v)=(max);		\
1500 	    else if ((v) < (min))	\
1501 		(v) = (min);		\
1502 	} one_time
1503 static GCC_NORETURN void initial_error_handler(String message);
1504 static void
initial_error_handler(String message)1505 initial_error_handler(String message)
1506 {
1507     TRACE(("initial_error_handler:%s\n", NonNull(message)));
1508     fprintf(stderr, "%s: %s\n", prog_arg, NonNull(message));
1509     print_usage(BADEXIT);
1510 }
1511 
1512 static GCC_NORETURN void runtime_error_handler(String message);
1513 static void
runtime_error_handler(String message)1514 runtime_error_handler(String message)
1515 {
1516     TRACE(("runtime_error_handler:%s\n", NonNull(message)));
1517     fprintf(stderr, "%s: %s\n", prog_arg, NonNull(message));
1518     imdying(SIGINT);		/* save files and exit, do not abort() */
1519 }
1520 
1521 /***====================================================================***/
1522 
1523 #ifdef XRENDERFONT
1524 static unsigned
maskToShift(unsigned long mask)1525 maskToShift(unsigned long mask)
1526 {
1527     unsigned result = 0;
1528     if (mask != 0) {
1529 	while ((mask & 1) == 0) {
1530 	    mask >>= 1;
1531 	    ++result;
1532 	}
1533     }
1534     return result;
1535 }
1536 
1537 static unsigned
maskToWidth(unsigned long mask)1538 maskToWidth(unsigned long mask)
1539 {
1540     unsigned result = 0;
1541     while (mask != 0) {
1542 	if ((mask & 1) != 0)
1543 	    ++result;
1544 	mask >>= 1;
1545     }
1546     return result;
1547 }
1548 
1549 static XVisualInfo *
getVisualInfo(TextWindow tw)1550 getVisualInfo(TextWindow tw)
1551 {
1552 #define MYFMT "getVisualInfo \
1553 depth %d, \
1554 type %d (%s), \
1555 size %d \
1556 rgb masks (%04lx/%04lx/%04lx)\n"
1557 #define MYARG \
1558        vi->depth,\
1559        vi->class,\
1560        ((vi->class & 1) ? "dynamic" : "static"),\
1561        vi->colormap_size,\
1562        vi->red_mask,\
1563        vi->green_mask,\
1564        vi->blue_mask
1565 
1566     XVisualInfo myTemplate;
1567 
1568     if (tw->visInfo == 0 && tw->numVisuals == 0) {
1569 	myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
1570 								XDefaultScreen(dpy)));
1571 	tw->visInfo = XGetVisualInfo(dpy, (long) VisualIDMask,
1572 				     &myTemplate, &tw->numVisuals);
1573 
1574 	if ((tw->visInfo != 0) && (tw->numVisuals > 0)) {
1575 	    XVisualInfo *vi = tw->visInfo;
1576 	    tw->rgb_widths[0] = maskToWidth(vi->red_mask);
1577 	    tw->rgb_widths[1] = maskToWidth(vi->green_mask);
1578 	    tw->rgb_widths[2] = maskToWidth(vi->blue_mask);
1579 	    tw->rgb_shifts[0] = maskToShift(vi->red_mask);
1580 	    tw->rgb_shifts[1] = maskToShift(vi->green_mask);
1581 	    tw->rgb_shifts[2] = maskToShift(vi->blue_mask);
1582 
1583 	    tw->has_rgb = ((vi->red_mask != 0) &&
1584 			   (vi->green_mask != 0) &&
1585 			   (vi->blue_mask != 0) &&
1586 			   ((vi->red_mask & vi->green_mask) == 0) &&
1587 			   ((vi->green_mask & vi->blue_mask) == 0) &&
1588 			   ((vi->blue_mask & vi->red_mask) == 0) &&
1589 			   (vi->class == TrueColor
1590 			    || vi->class == DirectColor));
1591 
1592 	    TRACE((MYFMT, MYARG));
1593 	    TRACE(("...shifts %u/%u/%u\n",
1594 		   tw->rgb_shifts[0],
1595 		   tw->rgb_shifts[1],
1596 		   tw->rgb_shifts[2]));
1597 	    TRACE(("...widths %u/%u/%u\n",
1598 		   tw->rgb_widths[0],
1599 		   tw->rgb_widths[1],
1600 		   tw->rgb_widths[2]));
1601 	}
1602     }
1603     return (tw->visInfo != 0) && (tw->numVisuals > 0) ? tw->visInfo : NULL;
1604 #undef MYFMT
1605 #undef MYARG
1606 }
1607 #endif /* XRENDERFONT */
1608 
1609 /***====================================================================***/
1610 
1611 #ifdef XRENDERFONT
1612 static void
pixelToXftColor(TextWindow tw,XftColor * target,Pixel source)1613 pixelToXftColor(TextWindow tw, XftColor *target, Pixel source)
1614 {
1615 #define UnMaskIt(name,nn) \
1616 	((unsigned short)((def.pixel & tw->visInfo->name ##_mask) >> tw->rgb_shifts[nn]))
1617 #define UnMaskIt2(name,nn) \
1618 	(unsigned short)((((UnMaskIt(name,nn) << 8) \
1619 			   |UnMaskIt(name,nn))) << (8 - tw->rgb_widths[nn]))
1620     XColor def;
1621     Boolean result = True;
1622 
1623     memset(&def, 0, sizeof(def));
1624     def.pixel = source;
1625 
1626     if ((tw->visInfo = getVisualInfo(tw)) != NULL && tw->has_rgb) {
1627 	/* *INDENT-EQLS* */
1628 	def.red   = UnMaskIt2(red, 0);
1629 	def.green = UnMaskIt2(green, 1);
1630 	def.blue  = UnMaskIt2(blue, 2);
1631     } else if (!XQueryColor(dpy, tw->colormap, &def)) {
1632 	result    = False;
1633     }
1634 
1635     if (result) {
1636 	/* *INDENT-EQLS* */
1637 	target->pixel       = source;
1638 	target->color.red   = def.red;
1639 	target->color.green = def.green;
1640 	target->color.blue  = def.blue;
1641 	target->color.alpha = 0xffff;
1642     } else {
1643 	memset(target, 0, sizeof(*target));
1644     }
1645 
1646     TRACE2((".. pixelToXftColor %06lx -> %04x/%04x/%04x\n",
1647 	    source,
1648 	    target->color.red,
1649 	    target->color.green,
1650 	    target->color.blue));
1651 }
1652 #endif
1653 
1654 #define MIN_UDIFF  0x1000
1655 #define UDIFF(a,b) ((a)>(b)?(a)-(b):(b)-(a))
1656 
1657 /*
1658  * Returns true if the RGB components are close enough to make distinguishing
1659  * two colors difficult.
1660  */
1661 static Boolean
SamePixel(Pixel a,Pixel b)1662 SamePixel(Pixel a, Pixel b)
1663 {
1664     Boolean result = True;
1665 
1666     if (a != b) {
1667 	result = False;
1668 	if (cur_win->top_widget != 0) {
1669 #ifdef XRENDERFONT
1670 	    XftColor a_color;
1671 	    XftColor b_color;
1672 
1673 	    pixelToXftColor(cur_win, &a_color, a);
1674 	    pixelToXftColor(cur_win, &b_color, b);
1675 
1676 	    if (UDIFF(a_color.color.red, b_color.color.red) < MIN_UDIFF
1677 		&& UDIFF(a_color.color.green, b_color.color.green) < MIN_UDIFF
1678 		&& UDIFF(a_color.color.blue, b_color.color.blue) < MIN_UDIFF)
1679 		result = True;
1680 #else
1681 	    XColor a_color;
1682 	    XColor b_color;
1683 
1684 	    a_color.pixel = a;
1685 	    b_color.pixel = b;
1686 
1687 	    if (XQueryColor(dpy, cur_win->colormap, &a_color)
1688 		&& XQueryColor(dpy, cur_win->colormap, &b_color)) {
1689 		if (UDIFF(a_color.red, b_color.red) < MIN_UDIFF
1690 		    && UDIFF(a_color.green, b_color.green) < MIN_UDIFF
1691 		    && UDIFF(a_color.blue, b_color.blue) < MIN_UDIFF)
1692 		    result = True;
1693 	    } else {
1694 		TRACE(("FIXME: SamePixel failed\n"));
1695 	    }
1696 #endif
1697 	} else {
1698 	    TRACE(("FIXME: SamePixel cannot compute (too soon)\n"));
1699 	}
1700     }
1701     return result;
1702 }
1703 
1704 /***====================================================================***/
1705 
1706 #if OPT_TRACE
1707 static Boolean
retraceBoolean(Boolean code)1708 retraceBoolean(Boolean code)
1709 {
1710     Trace(T_RETURN "%s\n", code ? "True" : "False");
1711     return code;
1712 }
1713 
1714 static const char *
traceColorGC(TextWindow win,ColorGC * source)1715 traceColorGC(TextWindow win, ColorGC * source)
1716 {
1717     static char temp[20];
1718     const char *result = "?";
1719 
1720 #define if_CASE(name) \
1721     	if (source == &win->name) \
1722 		result = #name
1723 
1724 #define if_MANY(name) \
1725 	if (source >= &win->name[0] && \
1726 	    source < &win->name[NCOLORS]) { \
1727 	    sprintf(temp, "%s[%d]", #name, (int)(source - win->name)); \
1728 	    result = temp; \
1729 	}
1730     /* *INDENT-OFF* */
1731     if_CASE(tt_info);
1732     else if_CASE(rt_info);
1733     else if_CASE(ss_info);
1734     else if_CASE(rs_info);
1735     else if_CASE(mm_info);
1736     else if_CASE(rm_info);
1737     else if_CASE(oo_info);
1738     else if_CASE(ro_info);
1739     else if_CASE(cc_info);
1740     else if_CASE(rc_info);
1741     else if_MANY(fg_info)
1742     else if_MANY(bg_info)
1743     /* *INDENT-ON* */
1744     return result;
1745 }
1746 #define returnBoolean(code) return retraceBoolean(code)
1747 #else
1748 #define returnBoolean(code) return(code)
1749 #endif
1750 
1751 /*
1752  * Set parameters in a ColorGC so that a subsequent makeColorGC can evaluate
1753  * the parameters to allocate a GC.
1754  */
1755 static void
initColorGC(TextWindow win,ColorGC * data,Pixel new_fg,Pixel new_bg)1756 initColorGC(TextWindow win, ColorGC * data, Pixel new_fg, Pixel new_bg)
1757 {
1758     (void) win;
1759 
1760     data->gcmask = GCForeground | GCBackground | GCGraphicsExposures;
1761 #ifndef XRENDERFONT
1762     data->gcmask |= GCFont;
1763     data->gcvals.font = win->fonts.norm->fid;
1764 #endif
1765     data->gcvals.graphics_exposures = False;
1766 
1767     data->gcvals.foreground = new_fg;
1768     data->gcvals.background = new_bg;
1769     data->state = sgINIT;
1770 }
1771 
1772 static void
freeColorGC(ColorGC * target)1773 freeColorGC(ColorGC * target)
1774 {
1775     if (target->gc != 0) {
1776 	XFreeGC(dpy, target->gc);
1777 	memset(target, 0, sizeof(*target));
1778     }
1779 }
1780 
1781 static void
copyColorGC(TextWindow win,ColorGC * target,ColorGC * source)1782 copyColorGC(TextWindow win, ColorGC * target, ColorGC * source)
1783 {
1784     if (target != source) {
1785 	(void) win;
1786 	TRACE(("copyColorGC %s", traceColorGC(win, source)));
1787 	TRACE((" -> %s\n", traceColorGC(win, target)));
1788 	*target = *source;
1789 	target->gc = NULL;
1790 	if (target->state >= sgINIT)
1791 	    target->state = sgREDO;
1792     }
1793 }
1794 
1795 /*
1796  * Given the parameters in gcmask/gcvals, allocate a GC if none exists, and
1797  * return pointer to the target.
1798  */
1799 ColorGC *
makeColorGC(TextWindow win,ColorGC * target)1800 makeColorGC(TextWindow win, ColorGC * target)
1801 {
1802     int changed = 0;
1803 
1804     (void) win;
1805 
1806     if (target->gc == 0) {
1807 	target->gc = XCreateGC(dpy, DefaultRootWindow(dpy), target->gcmask, &target->gcvals);
1808 	changed = 1;
1809     } else if (target->state < sgMADE) {
1810 	XChangeGC(dpy, target->gc, target->gcmask, &target->gcvals);
1811 	changed = 2;
1812     }
1813 
1814     if (changed) {
1815 	(void) changed;
1816 
1817 	TRACE(("makeColorGC(%s) %06lx/%06lx -> %p (%s)\n",
1818 	       traceColorGC(win, target),
1819 	       (long) target->gcvals.foreground,
1820 	       (long) target->gcvals.background,
1821 	       (void *) target->gc,
1822 	       (changed == 1) ? "created" : "changed"));
1823     }
1824 #ifdef XRENDERFONT
1825     if (changed) {
1826 	pixelToXftColor(win, &target->xft, target->gcvals.foreground);
1827     }
1828 #endif
1829     target->state = sgMADE;
1830     return target;
1831 }
1832 
1833 static void
monochrome_cursor(TextWindow win)1834 monochrome_cursor(TextWindow win)
1835 {
1836     TRACE((T_CALLED "monochrome_cursor\n"));
1837     win->is_color_cursor = False;
1838     win->cursor_fg = cur_win->bg;	/* undo our trickery */
1839     win->cursor_bg = cur_win->fg;
1840     copyColorGC(win, &win->cc_info, &win->rt_info);
1841     copyColorGC(win, &win->rc_info, &win->tt_info);
1842     returnVoid();
1843 }
1844 
1845 static int
color_cursor(TextWindow win)1846 color_cursor(TextWindow win)
1847 {
1848     TRACE((T_CALLED "color_cursor\n"));
1849     initColorGC(win, &cur_win->cc_info, cur_win->fg, cur_win->bg);
1850     initColorGC(win, &cur_win->rc_info, cur_win->fg, cur_win->bg);
1851 
1852     if (win->cc_info.gc == 0
1853 	|| win->rc_info.gc == 0) {
1854 	freeColorGC(&win->cc_info);
1855 	freeColorGC(&win->rc_info);
1856 	monochrome_cursor(win);
1857     }
1858 
1859     win->is_color_cursor = True;
1860     returnCode(TRUE);
1861 }
1862 
1863 static void
reset_color_gcs(void)1864 reset_color_gcs(void)
1865 {
1866     int n;
1867 
1868     for (n = 0; n < NCOLORS; n++) {
1869 	cur_win->fg_info[n].state = sgREDO;
1870 	cur_win->bg_info[n].state = sgREDO;
1871     }
1872 }
1873 
1874 ColorGC *
x_get_color_gc(TextWindow win,int n,Bool normal)1875 x_get_color_gc(TextWindow win, int n, Bool normal)
1876 {
1877     ColorGC *data;
1878 
1879     assert(n >= 0 && n < NCOLORS);
1880 
1881     if (n < 0 || n >= NCOLORS)
1882 	n = 0;			/* shouldn't happen */
1883     data = (normal
1884 	    ? &(win->fg_info[n])
1885 	    : &(win->bg_info[n]));
1886     if (win->screen_depth == 1) {
1887 	copyColorGC(win, data, (normal ? &win->tt_info : &win->rt_info));
1888     } else if (data->state == sgREDO) {
1889 	Pixel new_fg = data->gcvals.foreground;
1890 	Pixel new_bg = data->gcvals.background;
1891 
1892 	if (win->bg_follows_fg) {
1893 	    new_fg = (normal
1894 		      ? win->colors_fg[n]
1895 		      : win->colors_bg[n]);
1896 	    new_bg = (normal
1897 		      ? win->colors_bg[n]
1898 		      : win->colors_fg[n]);
1899 	} else {
1900 	    if (normal) {
1901 		new_fg = win->colors_fg[n];
1902 		new_bg = win->bg;
1903 	    } else {
1904 		new_fg = win->bg;
1905 		new_fg = win->colors_fg[n];
1906 	    }
1907 	}
1908 	if (new_fg == new_bg) {
1909 	    new_fg = normal ? win->fg : win->bg;
1910 	    new_bg = normal ? win->bg : win->fg;
1911 	}
1912 	initColorGC(win, data, new_fg, new_bg);
1913     }
1914     return data;
1915 }
1916 
1917 #if OPT_TRACE
1918 static char *
ColorsOf(Pixel pixel)1919 ColorsOf(Pixel pixel)
1920 {
1921     static char result[80];
1922 
1923     XColor color;
1924 
1925     color.pixel = pixel;
1926     XQueryColor(dpy, cur_win->colormap, &color);
1927     sprintf(result, "%4X/%4X/%4X", color.red, color.green, color.blue);
1928     return result;
1929 }
1930 
1931 static const char *
visibleAtoms(Atom value)1932 visibleAtoms(Atom value)
1933 {
1934     const char *result = 0;
1935     if (value == None)
1936 	result = "None";
1937     else if (value == XA_ATOM)
1938 	result = "XA_ATOM";
1939     else if (value == GetAtom(CLIPBOARD))
1940 	result = "XA_CLIPBOARD";
1941     else if (value == GetAtom(COMPOUND_TEXT))
1942 	result = "XA_COMPOUND_TEXT";
1943     else if (value == GetAtom(MULTIPLE))
1944 	result = "MULTIPLE";
1945     else if (value == XA_PRIMARY)
1946 	result = "XA_PRIMARY";
1947     else if (value == XA_STRING)
1948 	result = "XA_STRING";
1949     else if (value == GetAtom(TARGETS))
1950 	result = "XA_TARGETS";
1951     else if (value == GetAtom(TEXT))
1952 	result = "XA_TEXT";
1953     else if (value == GetAtom(TIMESTAMP))
1954 	result = "XA_TIMESTAMP";
1955     else if (value == GetAtom(UTF8_STRING))
1956 	result = "XA_UTF8_STRING";
1957     else
1958 	result = "unknown";
1959     return result;
1960 }
1961 #endif
1962 
1963 #ifndef PIXMAP_ROOTDIR
1964 #define PIXMAP_ROOTDIR "/usr/share/pixmaps/"
1965 #endif
1966 
1967 #ifdef HAVE_LIBXPM
1968 static int
getVisualDepth(void)1969 getVisualDepth(void)
1970 {
1971     XVisualInfo myTemplate, *visInfoPtr;
1972     int numFound;
1973     int result = 0;
1974 
1975     myTemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,
1976 							    XDefaultScreen(dpy)));
1977     visInfoPtr = XGetVisualInfo(dpy, (long) VisualIDMask,
1978 				&myTemplate, &numFound);
1979     if (visInfoPtr != 0) {
1980 	if (numFound != 0) {
1981 	    result = visInfoPtr->depth;
1982 	}
1983 	XFree(visInfoPtr);
1984     }
1985     return result;
1986 }
1987 #endif /* HAVE_LIBXPM */
1988 
1989 static char *
x_find_icon(char ** work,int * state,const char * suffix)1990 x_find_icon(char **work, int *state, const char *suffix)
1991 {
1992     const char *filename = cur_win->iconname;
1993     const char *prefix = "";
1994     char *result = 0;
1995     size_t length;
1996 
1997     switch (*state) {
1998     case 0:
1999 	suffix = "";
2000 	break;
2001     case 1:
2002 	break;
2003     case 2:
2004 	if (!strncmp(filename, "/", 1) ||
2005 	    !strncmp(filename, "./", 2) ||
2006 	    !strncmp(filename, "../", 3))
2007 	    goto giveup;
2008 	prefix = PIXMAP_ROOTDIR;
2009 	suffix = "";
2010 	break;
2011     case 3:
2012 	prefix = PIXMAP_ROOTDIR;
2013 	break;
2014       giveup:
2015     default:
2016 	*state = -1;
2017 	break;
2018     }
2019     if (*state >= 0) {
2020 	if (*work) {
2021 	    free(*work);
2022 	    *work = 0;
2023 	}
2024 	length = 3 + strlen(prefix) + strlen(filename) + strlen(suffix);
2025 	if ((result = malloc(length)) != 0) {
2026 	    sprintf(result, "%s%s%s", prefix, filename, suffix);
2027 	    *work = result;
2028 	}
2029 	*state += 1;
2030 	TRACE(("x_find_icon %d:%s\n", *state, result));
2031     }
2032     return result;
2033 }
2034 
2035 static void
x_load_icon(void)2036 x_load_icon(void)
2037 {
2038     Pixmap myIcon = 0;
2039     Pixmap myMask = 0;
2040     char *workname = 0;
2041 
2042     TRACE((T_CALLED "x_load_icon\n"));
2043     /*
2044      * Use the compiled-in icon as a resource default.
2045      */
2046 #if OPT_X11_ICON
2047     {
2048 #ifdef VILE_ICON
2049 # include VILE_ICON
2050 #else
2051 # ifdef HAVE_LIBXPM
2052 #  include <icons/vile.xpm>
2053 # else
2054 #  if SYS_VMS
2055 #   include "icons/vile.xbm"
2056 #  else
2057 #   include <icons/vile.xbm>
2058 #  endif
2059 # endif
2060 #endif
2061 #ifdef HAVE_LIBXPM
2062 	if (XpmCreatePixmapFromData(dpy,
2063 				    DefaultRootWindow(dpy),
2064 				    vile, &myIcon, &myMask, 0) != 0) {
2065 	    myIcon = 0;
2066 	    myMask = 0;
2067 	}
2068 #else
2069 	myIcon = XCreateBitmapFromData(dpy,
2070 				       DefaultRootWindow(dpy),
2071 				       (char *) vile_bits,
2072 				       vile_width,
2073 				       vile_height);
2074 #endif
2075     }
2076 #endif /* OPT_X11_ICON */
2077 
2078     if (!isEmpty(cur_win->iconname)) {
2079 	int state = 0;
2080 #ifdef HAVE_LIBXPM
2081 	while (x_find_icon(&workname, &state, ".xpm") != 0) {
2082 	    Pixmap resIcon = 0;
2083 	    Pixmap shapemask = 0;
2084 	    XpmAttributes attributes;
2085 
2086 	    attributes.depth = (unsigned) getVisualDepth();
2087 	    attributes.valuemask = XpmDepth;
2088 
2089 	    if (XpmReadFileToPixmap(dpy,
2090 				    DefaultRootWindow(dpy),
2091 				    workname,
2092 				    &resIcon,
2093 				    &shapemask,
2094 				    &attributes) == XpmSuccess) {
2095 		myIcon = resIcon;
2096 		myMask = shapemask;
2097 		TRACE(("...success\n"));
2098 		break;
2099 	    }
2100 	}
2101 #else
2102 	while (x_find_icon(&workname, &state, ".xbm") != 0) {
2103 	    Pixmap resIcon = 0;
2104 	    unsigned width;
2105 	    unsigned height;
2106 	    int x_hot;
2107 	    int y_hot;
2108 
2109 	    if (XReadBitmapFile(dpy,
2110 				DefaultRootWindow(dpy),
2111 				workname,
2112 				&width, &height,
2113 				&resIcon,
2114 				&x_hot, &y_hot) == BitmapSuccess) {
2115 		myIcon = resIcon;
2116 		TRACE(("...success\n"));
2117 		break;
2118 	    }
2119 	}
2120 #endif
2121     }
2122 
2123     if (myIcon != 0) {
2124 	XWMHints *hints = XGetWMHints(dpy, XtWindow(cur_win->top_widget));
2125 	if (!hints)
2126 	    hints = XAllocWMHints();
2127 
2128 	if (hints) {
2129 	    hints->flags |= IconPixmapHint;
2130 	    hints->icon_pixmap = myIcon;
2131 	    if (myMask) {
2132 		hints->flags |= IconMaskHint;
2133 		hints->icon_mask = myMask;
2134 	    }
2135 
2136 	    XSetWMHints(dpy, XtWindow(cur_win->top_widget), hints);
2137 	    XFree(hints);
2138 	}
2139     }
2140 
2141     if (workname != 0)
2142 	free(workname);
2143 
2144     returnVoid();
2145 }
2146 
2147 /* ARGSUSED */
2148 int
x_preparse_args(int * pargc,char *** pargv)2149 x_preparse_args(int *pargc, char ***pargv)
2150 {
2151     XVileFont *pfont;
2152     XGCValues gcvals;
2153     ULONG gcmask;
2154     int geo_mask, startx, starty;
2155     int i;
2156     int status = TRUE;
2157     Cardinal start_cols, start_rows;
2158     const char *xvile_class = MY_CLASS;
2159     static XrmOptionDescRec options[] =
2160     {
2161 	{"-t", (char *) 0, XrmoptionSkipArg, (caddr_t) 0},
2162 	{"-fork", "*forkOnStartup", XrmoptionNoArg, "true"},
2163 	{"+fork", "*forkOnStartup", XrmoptionNoArg, "false"},
2164 	{"-leftbar", "*scrollbarOnLeft", XrmoptionNoArg, "true"},
2165 	{"-rightbar", "*scrollbarOnLeft", XrmoptionNoArg, "false"},
2166 	{"-class", NULL, XrmoptionSkipArg, (caddr_t) 0},
2167     };
2168 #if MOTIF_WIDGETS
2169     static XtActionsRec new_actions[] =
2170     {
2171 	{"ConfigureBar", configure_bar}
2172     };
2173     static String scrollbars_translations =
2174     "#override \n\
2175 		Ctrl<Btn1Down>:ConfigureBar(Split) \n\
2176 		Ctrl<Btn2Down>:ConfigureBar(Kill) \n\
2177 		Ctrl<Btn3Down>:ConfigureBar(Only)";
2178     static String fallback_resources[] =
2179     {
2180 	"*scrollPane.background:grey80",
2181 	"*scrollbar.background:grey60",
2182 	NULL
2183     };
2184 #else
2185 #if OPT_KEV_DRAGGING
2186     static XtActionsRec new_actions[] =
2187     {
2188 	{"ConfigureBar", configure_bar},
2189 	{"DoScroll", do_scroll},
2190 	{"ResizeBar", resize_bar}
2191     };
2192     static String scrollbars_translations =
2193     "#override \n\
2194 		Ctrl<Btn1Down>:ConfigureBar(Split) \n\
2195 		Ctrl<Btn2Down>:ConfigureBar(Kill) \n\
2196 		Ctrl<Btn3Down>:ConfigureBar(Only) \n\
2197 		<Btn1Down>:DoScroll(Forward) \n\
2198 		<Btn2Down>:DoScroll(StartDrag) \n\
2199 		<Btn3Down>:DoScroll(Backward) \n\
2200 		<Btn2Motion>:DoScroll(Drag) \n\
2201 		<BtnUp>:DoScroll(End)";
2202     static String resizeGrip_translations =
2203     "#override \n\
2204 		<BtnDown>:ResizeBar(Start) \n\
2205 		<BtnMotion>:ResizeBar(Drag) \n\
2206 		<BtnUp>:ResizeBar(End)";
2207     static String fallback_resources[] =
2208     {
2209 	NULL
2210     };
2211 #else
2212     static XtActionsRec new_actions[] =
2213     {
2214 	{"ConfigureBar", configure_bar},
2215 	{"ResizeBar", resize_bar}
2216     };
2217     static String scrollbars_translations =
2218     "#override \n\
2219 		Ctrl<Btn1Down>:ConfigureBar(Split) \n\
2220 		Ctrl<Btn2Down>:ConfigureBar(Kill) \n\
2221 		Ctrl<Btn3Down>:ConfigureBar(Only)";
2222     static String resizeGrip_translations =
2223     "#override \n\
2224 		<BtnDown>:ResizeBar(Start) \n\
2225 		<BtnMotion>:ResizeBar(Drag) \n\
2226 		<BtnUp>:ResizeBar(End)";
2227     static String fallback_resources[] =
2228     {
2229 	NULL
2230     };
2231 #endif /* OPT_KEV_DRAGGING */
2232 #if OPT_XAW_SCROLLBARS
2233     static char solid_pixmap_bits[] =
2234     {'\003', '\003'};
2235 #endif
2236     static char stippled_pixmap_bits[] =
2237     {'\002', '\001'};
2238 #endif /* MOTIF_WIDGETS */
2239 
2240     TRACE((T_CALLED "x_preparse_args\n"));
2241 
2242     for (i = 1; i < (*pargc) - 1; i++) {
2243 	char *param = (*pargv)[i];
2244 	size_t len = strlen(param);
2245 	if (len > 1) {
2246 #if OPT_TITLE
2247 	    /*
2248 	     * If user sets the title explicitly, he probably will not like
2249 	     * allowing xvile to set it automatically when visiting a new
2250 	     * buffer.
2251 	     */
2252 	    if (!strncmp(param, "-title", len))
2253 		auto_set_title = FALSE;
2254 #endif
2255 	    if (!strncmp(param, "-class", len))
2256 		xvile_class = (*pargv)[++i];
2257 	}
2258     }
2259     if (isEmpty(xvile_class))
2260 	xvile_class = MY_CLASS;
2261 
2262     XtSetErrorHandler(initial_error_handler);
2263     memset(cur_win, 0, sizeof(*cur_win));
2264     cur_win->top_widget = XtVaAppInitialize(&cur_win->app_context,
2265 					    xvile_class,
2266 					    options, XtNumber(options),
2267 					    pargc, *pargv,
2268 					    fallback_resources,
2269 					    Nval(XtNgeometry, NULL),
2270 					    Nval(XtNinput, TRUE),
2271 					    NULL);
2272     XtSetErrorHandler(runtime_error_handler);
2273     dpy = XtDisplay(cur_win->top_widget);
2274 
2275     XtVaGetValues(cur_win->top_widget,
2276 		  XtNcolormap, &cur_win->colormap,
2277 		  NULL);
2278 
2279     XtVaGetValues(cur_win->top_widget,
2280 		  XtNdepth, &cur_win->screen_depth,
2281 		  NULL);
2282 
2283     XtGetApplicationResources(cur_win->top_widget,
2284 			      (XtPointer) cur_win,
2285 			      app_resources,
2286 			      XtNumber(app_resources),
2287 			      (ArgList) 0,
2288 			      0);
2289 
2290     TRACE(("** ApplicationResources:\n"));
2291 
2292 #if OPT_KEV_DRAGGING
2293     TRACE_RES_I(XtNscrollRepeatTimeout, scroll_repeat_timeout);
2294 #endif
2295     TRACE_RES_I(XtNscrollRepeatInterval, scroll_repeat_interval);
2296     TRACE_RES_B(XtNreverseVideo, reverse_video);
2297 #ifdef XRENDERFONT
2298     TRACE_RES_S(XtNfontFamily, starting_fontname);
2299 #else
2300     TRACE_RES_S(XtNfont, starting_fontname);
2301 #endif
2302     TRACE_RES_S(XtNgeometry, geometry);
2303     TRACE_RES_P(XtNforeground, fg);
2304     TRACE_RES_P(XtNbackground, bg);
2305     TRACE_RES_B(XtNforkOnStartup, fork_on_startup);
2306     TRACE_RES_B(XtNfocusFollowsMouse, focus_follows_mouse);
2307     TRACE_RES_I(XtNmultiClickTime, click_timeout);
2308     TRACE_RES_S(XtNcharClass, multi_click_char_class);
2309     TRACE_RES_B(XtNscrollbarOnLeft, scrollbar_on_left);
2310     TRACE_RES_I(XtNscrollbarWidth, pane_width);
2311     TRACE_RES_I(XtNwheelScrollAmount, wheel_scroll_amount);
2312 #if OPT_MENUS
2313     TRACE_RES_I(XtNmenuHeight, menu_height);
2314 #endif
2315 #if OPT_MENUS_COLORED
2316     TRACE_RES_P(XtNmenuForeground, menubar_fg);
2317     TRACE_RES_P(XtNmenuBackground, menubar_bg);
2318 #endif
2319     TRACE_RES_B(XtNpersistentSelections, persistent_selections);
2320     TRACE_RES_B(XtNselectionSetsDOT, selection_sets_DOT);
2321     TRACE_RES_I(XtNblinkInterval, blink_interval);
2322 #if OPT_INPUT_METHOD
2323     TRACE_RES_B(XtNopenIm, open_im);
2324     TRACE_RES_B(XtNinputMethod, rs_inputMethod);
2325     TRACE_RES_B(XtNpreeditType, rs_preeditType);
2326     TRACE_RES_B(XtNximFont, rs_imFont);
2327 #endif
2328 
2329     if (cur_win->fork_on_startup)
2330 	(void) newprocessgroup(TRUE, 1);
2331 
2332     if (SamePixel(cur_win->bg, cur_win->fg))
2333 	cur_win->fg = BlackPixel(dpy, DefaultScreen(dpy));
2334     if (SamePixel(cur_win->bg, cur_win->fg))
2335 	cur_win->bg = WhitePixel(dpy, DefaultScreen(dpy));
2336 
2337     if (cur_win->reverse_video) {
2338 	Pixel temp = cur_win->fg;
2339 	cur_win->fg = cur_win->bg;
2340 	cur_win->bg = temp;
2341     }
2342     cur_win->default_fg = cur_win->fg;
2343     cur_win->default_bg = cur_win->bg;
2344 
2345 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
2346     XtGetSubresources(cur_win->top_widget,
2347 		      (XtPointer) cur_win,
2348 		      "scrollbar",
2349 		      "Scrollbar",
2350 		      scrollbar_resources,
2351 		      XtNumber(scrollbar_resources),
2352 		      (ArgList) 0,
2353 		      0);
2354     TRACE(("SubResources:scrollbar\n"));
2355     TRACE_RES_P(XtNforeground, scrollbar_fg);
2356     TRACE_RES_P(XtNbackground, scrollbar_bg);
2357     TRACE_RES_B(XtNsliderIsSolid, slider_is_solid);
2358 #endif /* OPT_KEV_SCROLLBARS */
2359 
2360     XtGetSubresources(cur_win->top_widget,
2361 		      (XtPointer) cur_win,
2362 		      "modeline",
2363 		      "Modeline",
2364 		      modeline_resources,
2365 		      XtNumber(modeline_resources),
2366 		      (ArgList) 0,
2367 		      0);
2368     TRACE(("SubResources:modeline\n"));
2369     TRACE_RES_P(XtNforeground, modeline_fg);
2370     TRACE_RES_P(XtNbackground, modeline_bg);
2371     TRACE_RES_P(XtNfocusForeground, modeline_focus_fg);
2372     TRACE_RES_P(XtNfocusBackground, modeline_focus_bg);
2373 
2374     XtGetSubresources(cur_win->top_widget,
2375 		      (XtPointer) cur_win,
2376 		      "selection",
2377 		      "Selection",
2378 		      selection_resources,
2379 		      XtNumber(selection_resources),
2380 		      (ArgList) 0,
2381 		      0);
2382     TRACE(("SubResources:selection\n"));
2383     TRACE_RES_P(XtNforeground, selection_fg);
2384     TRACE_RES_P(XtNbackground, selection_bg);
2385 
2386     XtGetSubresources(cur_win->top_widget,
2387 		      (XtPointer) cur_win,
2388 		      "cursor",
2389 		      "Cursor",
2390 		      cursor_resources,
2391 		      XtNumber(cursor_resources),
2392 		      (ArgList) 0,
2393 		      0);
2394     TRACE(("SubResources:cursor\n"));
2395     TRACE_RES_P(XtNforeground, cursor_fg);
2396     TRACE_RES_P(XtNbackground, cursor_bg);
2397 
2398     XtGetSubresources(cur_win->top_widget,
2399 		      (XtPointer) cur_win,
2400 		      "pointer",
2401 		      "Pointer",
2402 		      pointer_resources,
2403 		      XtNumber(pointer_resources),
2404 		      (ArgList) 0,
2405 		      0);
2406     TRACE(("SubResources:pointer\n"));
2407     TRACE_RES_P(XtNforeground, pointer_fg);
2408     TRACE_RES_P(XtNbackground, pointer_bg);
2409     TRACE_RES_P(XtNnormalShape, normal_pointer);
2410 #if OPT_WORKING
2411     TRACE_RES_P(XtNwatchShape, watch_pointer);
2412 #endif
2413 
2414     /*
2415      * Try to keep the default for fcolor0 looking like something other than
2416      * white.
2417      */
2418     if (SamePixel(cur_win_rec.fg, WhitePixel(dpy, DefaultScreen(dpy)))) {
2419 	static Pixel black;
2420 	TRACE(("force default value of fcolor0 to Black\n"));
2421 	black = BlackPixel(dpy, DefaultScreen(dpy));
2422 	color_resources[0].default_addr = (XtPointer) &black;
2423     }
2424 
2425     XtGetSubresources(cur_win->top_widget,
2426 		      (XtPointer) cur_win,
2427 		      "color",
2428 		      "Color",
2429 		      color_resources,
2430 		      XtNumber(color_resources),
2431 		      (ArgList) 0,
2432 		      0);
2433     TRACE(("SubResources:color\n"));
2434     TRACE_RES_P(XtNfcolor0, colors_fg[0]);
2435     TRACE_RES_P(XtNbcolor0, colors_bg[0]);
2436     TRACE_RES_P(XtNfcolor1, colors_fg[1]);
2437     TRACE_RES_P(XtNbcolor1, colors_bg[1]);
2438     TRACE_RES_P(XtNfcolor2, colors_fg[2]);
2439     TRACE_RES_P(XtNbcolor2, colors_bg[2]);
2440     TRACE_RES_P(XtNfcolor3, colors_fg[3]);
2441     TRACE_RES_P(XtNbcolor3, colors_bg[3]);
2442     TRACE_RES_P(XtNfcolor4, colors_fg[4]);
2443     TRACE_RES_P(XtNbcolor4, colors_bg[4]);
2444     TRACE_RES_P(XtNfcolor5, colors_fg[5]);
2445     TRACE_RES_P(XtNbcolor5, colors_bg[5]);
2446     TRACE_RES_P(XtNfcolor6, colors_fg[6]);
2447     TRACE_RES_P(XtNbcolor6, colors_bg[6]);
2448     TRACE_RES_P(XtNfcolor7, colors_fg[7]);
2449     TRACE_RES_P(XtNbcolor7, colors_bg[7]);
2450     TRACE_RES_P(XtNfcolor8, colors_fg[8]);
2451     TRACE_RES_P(XtNbcolor8, colors_bg[8]);
2452     TRACE_RES_P(XtNfcolor9, colors_fg[9]);
2453     TRACE_RES_P(XtNbcolor9, colors_bg[9]);
2454     TRACE_RES_P(XtNfcolorA, colors_fg[10]);
2455     TRACE_RES_P(XtNbcolorA, colors_bg[10]);
2456     TRACE_RES_P(XtNfcolorB, colors_fg[11]);
2457     TRACE_RES_P(XtNbcolorB, colors_bg[11]);
2458     TRACE_RES_P(XtNfcolorC, colors_fg[12]);
2459     TRACE_RES_P(XtNbcolorC, colors_bg[12]);
2460     TRACE_RES_P(XtNfcolorD, colors_fg[13]);
2461     TRACE_RES_P(XtNbcolorD, colors_bg[13]);
2462     TRACE_RES_P(XtNfcolorE, colors_fg[14]);
2463     TRACE_RES_P(XtNbcolorE, colors_bg[14]);
2464     TRACE_RES_P(XtNfcolorF, colors_fg[15]);
2465     TRACE_RES_P(XtNbcolorF, colors_bg[15]);
2466 
2467     pfont = xvileQueryFont(dpy, cur_win, cur_win->starting_fontname);
2468     if (!pfont) {
2469 	pfont = xvileQueryFont(dpy, cur_win, FONTNAME);
2470 	if (!pfont) {
2471 	    if (strcmp(cur_win->starting_fontname, FONTNAME)) {
2472 		(void) fprintf(stderr,
2473 			       "couldn't get font \"%s\" or \"%s\", exiting\n",
2474 			       cur_win->starting_fontname, FONTNAME);
2475 	    } else {
2476 		(void) fprintf(stderr,
2477 			       "couldn't get font \"%s\", exiting\n",
2478 			       FONTNAME);
2479 	    }
2480 	    ExitProgram(BADEXIT);
2481 	}
2482     }
2483     default_font = strmalloc(cur_win->starting_fontname);
2484     (void) set_character_class(cur_win->multi_click_char_class);
2485 
2486     /*
2487      * Look at our copy of the geometry resource to obtain the dimensions of
2488      * the window in characters.  We've provided a default value of 80x36
2489      * so there'll always be something to parse.  We still need to check
2490      * the return mask since the user may specify a position, but no size.
2491      */
2492     geo_mask = XParseGeometry(cur_win->geometry,
2493 			      &startx, &starty,
2494 			      &start_cols, &start_rows);
2495 
2496     cur_win->rows = (geo_mask & HeightValue) ? start_rows : 36;
2497     cur_win->cols = (geo_mask & WidthValue) ? start_cols : 80;
2498 
2499     /*
2500      * Fix up the geometry resource of top level shell providing initial
2501      * position if so requested by user.
2502      */
2503 
2504     if (geo_mask & (XValue | YValue)) {
2505 	char *gp = cur_win->geometry;
2506 	while (*gp && *gp != '+' && *gp != '-')
2507 	    gp++;		/* skip over width and height */
2508 	if (*gp)
2509 	    XtVaSetValues(cur_win->top_widget,
2510 			  Nval(XtNgeometry, gp),
2511 			  NULL);
2512     }
2513 
2514     /* Sanity check values obtained from XtGetApplicationResources */
2515     CHECK_MIN_MAX(cur_win->pane_width, PANE_WIDTH_MIN, PANE_WIDTH_MAX);
2516 
2517 #if MOTIF_WIDGETS
2518     cur_win->form_widget =
2519 	XtVaCreateManagedWidget("form",
2520 				xmFormWidgetClass,
2521 				cur_win->top_widget,
2522 				NULL);
2523 #else
2524 #if ATHENA_WIDGETS && OPT_MENUS
2525     cur_win->pane_widget =
2526 	XtVaCreateManagedWidget("pane",
2527 				panedWidgetClass,
2528 				cur_win->top_widget,
2529 				NULL);
2530     cur_win->menu_widget =
2531 	XtVaCreateManagedWidget("menubar",
2532 				boxWidgetClass,
2533 				cur_win->pane_widget,
2534 				Nval(XtNshowGrip, False),
2535 				NULL);
2536     cur_win->form_widget =
2537 	XtVaCreateManagedWidget("form",
2538 #if KEV_WIDGETS			/* FIXME */
2539 				bbWidgetClass,
2540 #else
2541 				formWidgetClass,
2542 #endif
2543 				cur_win->pane_widget,
2544 				Nval(XtNwidth, (x_width(cur_win)
2545 						+ cur_win->pane_width
2546 						+ 2)),
2547 				Nval(XtNheight, x_height(cur_win)),
2548 				Nval(XtNbackground, cur_win->bg),
2549 				Sval(XtNbottom, XtChainBottom),
2550 				Sval(XtNleft, XtChainLeft),
2551 				Sval(XtNright, XtChainRight),
2552 				Nval(XtNfromVert, cur_win->menu_widget),
2553 				Nval(XtNvertDistance, 0),
2554 				NULL);
2555 #else
2556 #if ATHENA_WIDGETS
2557     cur_win->form_widget =
2558 	XtVaCreateManagedWidget("form",
2559 #if KEV_WIDGETS			/* FIXME */
2560 				bbWidgetClass,
2561 #else
2562 				formWidgetClass,
2563 #endif
2564 				cur_win->top_widget,
2565 				XtNwidth, (x_width(cur_win)
2566 					   + cur_win->pane_width
2567 					   + 2),
2568 				XtNheight, x_height(cur_win),
2569 				XtNbackground, cur_win->bg,
2570 				XtNbottom, XtChainBottom,
2571 				XtNleft, XtChainLeft,
2572 				XtNright, XtChainRight,
2573 				NULL);
2574 #else
2575 #if NO_WIDGETS
2576     cur_win->form_widget =
2577 	XtVaCreateManagedWidget("form",
2578 				bbWidgetClass,
2579 				cur_win->top_widget,
2580 				Nval(XtNwidth, (x_width(cur_win)
2581 						+ cur_win->pane_width
2582 						+ 2)),
2583 				Nval(XtNheight, x_height(cur_win)),
2584 				Nval(XtNbackground, cur_win->bg),
2585 				NULL);
2586 #endif /* NO_WIDGETS */
2587 #endif /* ATHENA_WIDGETS */
2588 #endif /* ATHENA_WIDGETS && OPT_MENUS */
2589 #endif /* MOTIF_WIDGETS */
2590 
2591 #if OPT_MENUS
2592 #if MOTIF_WIDGETS
2593     cur_win->menu_widget = XmCreateMenuBar(cur_win->form_widget, "menub",
2594 					   NULL, 0);
2595 
2596     XtVaSetValues(cur_win->menu_widget,
2597 		  Nval(XmNtopAttachment, XmATTACH_FORM),
2598 		  Nval(XmNleftAttachment, XmATTACH_FORM),
2599 		  Nval(XmNbottomAttachment, XmATTACH_NONE),
2600 		  Nval(XmNrightAttachment, XmATTACH_FORM),
2601 		  NULL);
2602     XtManageChild(cur_win->menu_widget);
2603 #endif
2604     status = do_menu(cur_win->menu_widget);
2605 #endif
2606 
2607     cur_win->screen =
2608 	XtVaCreateManagedWidget("screen",
2609 #if MOTIF_WIDGETS
2610 				xmPrimitiveWidgetClass,
2611 #else
2612 				coreWidgetClass,
2613 #endif
2614 				cur_win->form_widget,
2615 				Nval(XtNwidth, x_width(cur_win)),
2616 				Nval(XtNheight, x_height(cur_win)),
2617 				Nval(XtNborderWidth, 0),
2618 				Nval(XtNbackground, cur_win->bg),
2619 #if MOTIF_WIDGETS
2620 				Nval(XmNhighlightThickness, 0),
2621 				Nval(XmNresizable, TRUE),
2622 				Nval(XmNbottomAttachment, XmATTACH_FORM),
2623 #if OPT_MENUS
2624 				Nval(XmNtopAttachment, XmATTACH_WIDGET),
2625 				Nval(XmNtopWidget, cur_win->menu_widget),
2626 				Nval(XmNtopOffset, 2),
2627 #else
2628 				Nval(XmNtopAttachment, XmATTACH_FORM),
2629 #endif
2630 				Nval(XmNleftAttachment, XmATTACH_FORM),
2631 				Nval(XmNrightAttachment, XmATTACH_NONE),
2632 #else
2633 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
2634 #if !OPT_KEV_SCROLLBARS
2635 				Sval(XtNleft, XtChainLeft),
2636 				Sval(XtNright, XtChainRight),
2637 #endif
2638 				Nval(XtNx, (cur_win->scrollbar_on_left
2639 					    ? cur_win->pane_width + 2
2640 					    : 0)),
2641 				Nval(XtNy, 0),
2642 #endif /* OPT_KEV_SCROLLBARS */
2643 #endif /* MOTIF_WIDGETS */
2644 				NULL);
2645 
2646 #if defined(LESSTIF_VERSION)
2647     /*
2648      * Lesstif (0.81) seems to install translations that cause certain
2649      * keys (like TAB) to manipulate the focus in addition to their
2650      * functions within xvile.  This leads to frustration when you are
2651      * trying to insert text, and find the focus shifting to a scroll
2652      * bar or whatever.  To fix this problem, we remove those nasty
2653      * translations here.
2654      *
2655      * Aside from this little nit, "lesstif" seems to work admirably
2656      * with xvile.  Just build with screen=motif.  You can find
2657      * lesstif at:  ftp://ftp.lesstif.org/pub/hungry/lesstif
2658      */
2659 
2660     XtUninstallTranslations(cur_win->screen);
2661 #endif /* LESSTIF_VERSION */
2662 
2663     /* Initialize graphics context for display of normal and reverse text */
2664     initColorGC(cur_win, &cur_win->tt_info, cur_win->fg, cur_win->bg);
2665 
2666     cur_win->exposed = FALSE;
2667     cur_win->visibility = VisibilityUnobscured;
2668 
2669     initColorGC(cur_win, &cur_win->rt_info, cur_win->bg, cur_win->fg);
2670 
2671     cur_win->bg_follows_fg = (Boolean) (gbcolor == ENUM_FCOLOR);
2672 
2673 #define COLOR_FMT " %06lx %s"
2674 #define COLOR_ARG(name) name, ColorsOf(name)
2675 #define COLOR_ONE(name) "%-20s " COLOR_FMT "\n", #name, COLOR_ARG(cur_win->name)
2676 
2677     TRACE(("colors_fg/colors_bg pixel values:\n"));
2678     for (i = 0; i < NCOLORS; i++) {
2679 	ctrans[i] = i;
2680 	TRACE(("   [%2d] " COLOR_FMT " " COLOR_FMT "\n", i,
2681 	       COLOR_ARG(cur_win->colors_fg[i]),
2682 	       COLOR_ARG(cur_win->colors_bg[i])));
2683     }
2684     reset_color_gcs();
2685 
2686     TRACE((COLOR_ONE(fg)));
2687     TRACE((COLOR_ONE(bg)));
2688 
2689     TRACE((COLOR_ONE(selection_fg)));
2690     TRACE((COLOR_ONE(selection_bg)));
2691 
2692     /* Initialize graphics context for display of selections */
2693     if (
2694 #ifdef XRENDERFONT
2695 	   0 &&			/* FIXME - difference should not exist */
2696 #endif
2697 	   (cur_win->screen_depth == 1
2698 	    || SamePixel(cur_win->selection_bg, cur_win->selection_fg)
2699 	    || (SamePixel(cur_win->fg, cur_win->selection_fg)
2700 		&& SamePixel(cur_win->bg, cur_win->selection_bg))
2701 	    || (SamePixel(cur_win->fg, cur_win->selection_bg)
2702 		&& SamePixel(cur_win->bg, cur_win->selection_fg)))) {
2703 	TRACE(("...Forcing selection GC to reverse\n"));
2704 	copyColorGC(cur_win, &cur_win->ss_info, &cur_win->rt_info);
2705 	copyColorGC(cur_win, &cur_win->rs_info, &cur_win->tt_info);
2706     } else {
2707 	initColorGC(cur_win, &cur_win->ss_info,
2708 		    cur_win->selection_fg, cur_win->selection_bg);
2709 	initColorGC(cur_win, &cur_win->rs_info,
2710 		    cur_win->selection_bg, cur_win->selection_fg);
2711 	TRACE(("...Created selection GC\n"));
2712     }
2713 
2714     TRACE((COLOR_ONE(modeline_fg)));
2715     TRACE((COLOR_ONE(modeline_bg)));
2716 
2717     /*
2718      * Initialize graphics context for display of normal modelines.
2719      * Portions of the modeline are never displayed in reverse video (wrt
2720      * the modeline) so there is no corresponding reverse video gc.
2721      */
2722     if (cur_win->screen_depth == 1
2723 	|| SamePixel(cur_win->modeline_bg, cur_win->modeline_fg)
2724 	|| (SamePixel(cur_win->fg, cur_win->modeline_fg)
2725 	    && SamePixel(cur_win->bg, cur_win->modeline_bg))
2726 	|| (SamePixel(cur_win->fg, cur_win->modeline_bg)
2727 	    && SamePixel(cur_win->bg, cur_win->modeline_fg))) {
2728 	copyColorGC(cur_win, &cur_win->oo_info, &cur_win->rt_info);
2729 	copyColorGC(cur_win, &cur_win->ro_info, &cur_win->tt_info);
2730 	TRACE(("...Forcing modeline GC to reverse\n"));
2731     } else {
2732 	initColorGC(cur_win, &cur_win->oo_info,
2733 		    cur_win->modeline_fg, cur_win->modeline_bg);
2734 	initColorGC(cur_win, &cur_win->ro_info,
2735 		    cur_win->modeline_bg, cur_win->modeline_fg);
2736 	TRACE(("...Created modeline GC\n"));
2737     }
2738 
2739     TRACE((COLOR_ONE(modeline_focus_fg)));
2740     TRACE((COLOR_ONE(modeline_focus_bg)));
2741 
2742     /*
2743      * Initialize graphics context for display of modelines which indicate
2744      * that the corresponding window has focus.
2745      */
2746     if (cur_win->screen_depth == 1
2747 	|| SamePixel(cur_win->modeline_focus_bg, cur_win->modeline_focus_fg)
2748 	|| (SamePixel(cur_win->fg, cur_win->modeline_focus_fg)
2749 	    && SamePixel(cur_win->bg, cur_win->modeline_focus_bg))
2750 	|| (SamePixel(cur_win->fg, cur_win->modeline_focus_bg)
2751 	    && SamePixel(cur_win->bg, cur_win->modeline_focus_fg))) {
2752 	copyColorGC(cur_win, &cur_win->mm_info, &cur_win->rt_info);
2753 	copyColorGC(cur_win, &cur_win->rm_info, &cur_win->tt_info);
2754 	TRACE(("...Forcing modeline focus GC to reverse\n"));
2755     } else {
2756 	initColorGC(cur_win, &cur_win->mm_info,
2757 		    cur_win->modeline_focus_fg, cur_win->modeline_focus_bg);
2758 	initColorGC(cur_win, &cur_win->rm_info,
2759 		    cur_win->modeline_focus_bg, cur_win->modeline_focus_fg);
2760 	TRACE(("...Created modeline focus GC\n"));
2761     }
2762 
2763     TRACE((COLOR_ONE(cursor_fg)));
2764     TRACE((COLOR_ONE(cursor_bg)));
2765 
2766     /* Initialize cursor graphics context and flag which indicates how to
2767      * display cursor.
2768      */
2769     if (cur_win->screen_depth == 1
2770 	|| SamePixel(cur_win->cursor_bg, cur_win->cursor_fg)
2771 	|| (SamePixel(cur_win->fg, cur_win->cursor_fg)
2772 	    && SamePixel(cur_win->bg, cur_win->cursor_bg))
2773 	|| (SamePixel(cur_win->fg, cur_win->cursor_bg)
2774 	    && SamePixel(cur_win->bg, cur_win->cursor_fg))) {
2775 	monochrome_cursor(cur_win);
2776 	TRACE(("...Forcing monochrome cursor\n"));
2777     } else if (color_cursor(cur_win)) {
2778 	TRACE(("...Created color cursor\n"));
2779 	x_set_cursor_color(-1);
2780     }
2781 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
2782     if (SamePixel(cur_win->scrollbar_bg, cur_win->scrollbar_fg)) {
2783 	cur_win->scrollbar_bg = cur_win->bg;
2784 	cur_win->scrollbar_fg = cur_win->fg;
2785     }
2786     if (cur_win->screen_depth == 1
2787 	|| too_light_or_too_dark(cur_win->scrollbar_fg))
2788 	cur_win->slider_is_solid = False;
2789 #endif /* OPT_KEV_SCROLLBARS */
2790 
2791 #if OPT_XAW_SCROLLBARS
2792     cur_win->thumb_bm =
2793 	XCreateBitmapFromData(dpy, DefaultRootWindow(dpy),
2794 			      cur_win->slider_is_solid
2795 			      ? solid_pixmap_bits
2796 			      : stippled_pixmap_bits,
2797 			      2, 2);
2798 #endif /* OPT_XAW_SCROLLBARS */
2799 
2800 #if OPT_KEV_SCROLLBARS
2801     gcvals.background = cur_win->scrollbar_bg;
2802     if (!cur_win->slider_is_solid) {
2803 	gcmask = GCFillStyle | GCStipple | GCForeground | GCBackground;
2804 	gcvals.foreground = cur_win->scrollbar_fg;
2805 	gcvals.fill_style = FillOpaqueStippled;
2806 	gcvals.stipple =
2807 	    XCreatePixmapFromBitmapData(dpy,
2808 					DefaultRootWindow(dpy),
2809 					stippled_pixmap_bits,
2810 					2, 2,
2811 					1L, 0L,
2812 					1);
2813     } else {
2814 	gcmask = GCForeground | GCBackground;
2815 	gcvals.foreground = cur_win->scrollbar_fg;
2816     }
2817     gcmask |= GCGraphicsExposures;
2818     gcvals.graphics_exposures = False;
2819     cur_win->scrollbar_gc = XCreateGC(dpy,
2820 				      DefaultRootWindow(dpy),
2821 				      gcmask, &gcvals);
2822 
2823     if (cur_win->screen_depth >= 6 && cur_win->slider_is_solid) {
2824 	Pixel fg_light, fg_dark, bg_light, bg_dark;
2825 	if (alloc_shadows(cur_win->scrollbar_fg, &fg_light, &fg_dark)
2826 	    && alloc_shadows(cur_win->scrollbar_bg, &bg_light, &bg_dark)) {
2827 	    GC gc;
2828 	    Pixmap my_slider_pixmap;
2829 	    cur_win->slider_is_3D = True;
2830 
2831 	    cur_win->trough_pixmap =
2832 		XCreatePixmap(dpy, DefaultRootWindow(dpy),
2833 			      cur_win->pane_width + 2,
2834 			      16, cur_win->screen_depth);
2835 
2836 #define TROUGH_HT 16
2837 	    gcvals.foreground = cur_win->scrollbar_bg;
2838 	    gc = XCreateGC(dpy, DefaultRootWindow(dpy), gcmask, &gcvals);
2839 	    XFillRectangle(dpy, cur_win->trough_pixmap, gc, 0, 0,
2840 			   cur_win->pane_width + 2, TROUGH_HT);
2841 	    XSetForeground(dpy, gc, bg_dark);
2842 	    XFillRectangle(dpy, cur_win->trough_pixmap, gc, 0, 0, 2, TROUGH_HT);
2843 	    XSetForeground(dpy, gc, bg_light);
2844 	    XFillRectangle(dpy, cur_win->trough_pixmap, gc,
2845 			   (int) cur_win->pane_width, 0, 2, TROUGH_HT);
2846 
2847 	    my_slider_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
2848 					     cur_win->pane_width - 2,
2849 					     SP_HT,
2850 					     cur_win->screen_depth);
2851 
2852 	    XSetForeground(dpy, gc, cur_win->scrollbar_fg);
2853 	    XFillRectangle(dpy, my_slider_pixmap, gc, 0, 0,
2854 			   cur_win->pane_width - 2, SP_HT);
2855 	    XSetForeground(dpy, gc, fg_light);
2856 	    XFillRectangle(dpy, my_slider_pixmap, gc, 0, 0, 2, SP_HT);
2857 	    XSetForeground(dpy, gc, fg_dark);
2858 	    XFillRectangle(dpy, my_slider_pixmap, gc,
2859 			   (int) cur_win->pane_width - 4, 0, 2, SP_HT);
2860 
2861 	    XSetTile(dpy, cur_win->scrollbar_gc, my_slider_pixmap);
2862 	    XSetFillStyle(dpy, cur_win->scrollbar_gc, FillTiled);
2863 	    XSetTSOrigin(dpy, cur_win->scrollbar_gc, 2, 0);
2864 
2865 	    cur_win->slider_pixmap =
2866 		XCreatePixmap(dpy, DefaultRootWindow(dpy),
2867 			      cur_win->pane_width - 2,
2868 			      SP_HT, cur_win->screen_depth);
2869 	    XCopyArea(dpy, my_slider_pixmap, cur_win->slider_pixmap, gc,
2870 		      0, 0, cur_win->pane_width - 2, SP_HT, 0, 0);
2871 
2872 	    /* Draw top bevel */
2873 	    XSetForeground(dpy, gc, fg_light);
2874 	    XDrawLine(dpy, cur_win->slider_pixmap, gc,
2875 		      0, 0, (int) cur_win->pane_width - 3, 0);
2876 	    XDrawLine(dpy, cur_win->slider_pixmap, gc,
2877 		      0, 1, (int) cur_win->pane_width - 4, 1);
2878 
2879 	    /* Draw bottom bevel */
2880 	    XSetForeground(dpy, gc, fg_dark);
2881 	    XDrawLine(dpy, cur_win->slider_pixmap, gc,
2882 		      2, SP_HT - 2, (int) cur_win->pane_width - 3, SP_HT - 2);
2883 	    XDrawLine(dpy, cur_win->slider_pixmap, gc,
2884 		      1, SP_HT - 1, (int) cur_win->pane_width - 3, SP_HT - 1);
2885 
2886 	    XFreeGC(dpy, gc);
2887 	}
2888     }
2889 #endif /* OPT_KEV_SCROLLBARS */
2890 
2891     XtAppAddActions(cur_win->app_context, new_actions, XtNumber(new_actions));
2892     cur_win->my_scrollbars_trans = XtParseTranslationTable(scrollbars_translations);
2893 
2894 #if MOTIF_WIDGETS
2895     cur_win->pane =
2896 	XtVaCreateManagedWidget("scrollPane",
2897 				xmPanedWindowWidgetClass,
2898 				cur_win->form_widget,
2899 				Nval(XtNwidth, cur_win->pane_width),
2900 				Nval(XmNbottomAttachment, XmATTACH_FORM),
2901 				Nval(XmNtraversalOn, False),	/* Added by gdr */
2902 #if OPT_MENUS
2903 				Nval(XmNtopAttachment, XmATTACH_WIDGET),
2904 				Nval(XmNtopWidget, cur_win->menu_widget),
2905 #else
2906 				Nval(XmNtopAttachment, XmATTACH_FORM),
2907 #endif
2908 				Nval(XmNleftAttachment, XmATTACH_WIDGET),
2909 				Nval(XmNleftWidget, cur_win->screen),
2910 				Nval(XmNrightAttachment, XmATTACH_FORM),
2911 				Nval(XmNspacing, cur_win->char_height),
2912 				Nval(XmNsashIndent, 2),
2913 				Nval(XmNsashWidth, cur_win->pane_width - 4),
2914 				Nval(XmNmarginHeight, 0),
2915 				Nval(XmNmarginWidth, 0),
2916 				Nval(XmNseparatorOn, FALSE),
2917 				NULL);
2918 #else
2919 #if OPT_XAW_SCROLLBARS
2920     cur_win->my_resizeGrip_trans = XtParseTranslationTable(resizeGrip_translations);
2921     cur_win->pane =
2922 	XtVaCreateManagedWidget("scrollPane",
2923 				formWidgetClass,
2924 				cur_win->form_widget,
2925 				Nval(XtNwidth, cur_win->pane_width + 2),
2926 				Nval(XtNheight, ((int) x_height(cur_win)
2927 						 - cur_win->char_height)),
2928 				Nval(XtNx, (cur_win->scrollbar_on_left
2929 					    ? 0
2930 					    : x_width(cur_win))),
2931 				Nval(XtNy, 0),
2932 				Nval(XtNborderWidth, 0),
2933 				Nval(XtNbackground, cur_win->modeline_bg),
2934 				Nval(XtNfromHoriz, (cur_win->scrollbar_on_left
2935 						    ? NULL
2936 						    : cur_win->screen)),
2937 				Nval(XtNhorizDistance, 0),
2938 				Sval(XtNleft, ((cur_win->scrollbar_on_left)
2939 					       ? XtChainLeft
2940 					       : XtChainRight)),
2941 				Sval(XtNright, ((cur_win->scrollbar_on_left)
2942 						? XtChainLeft
2943 						: XtChainRight)),
2944 				NULL);
2945     if (cur_win->scrollbar_on_left)
2946 	XtVaSetValues(cur_win->screen,
2947 		      Nval(XtNfromHoriz, cur_win->pane),
2948 		      NULL);
2949 #else
2950 #if OPT_KEV_SCROLLBARS
2951     cur_win->my_resizeGrip_trans = XtParseTranslationTable(resizeGrip_translations);
2952     cur_win->pane =
2953 	XtVaCreateManagedWidget("scrollPane",
2954 				bbWidgetClass,
2955 				cur_win->form_widget,
2956 				Nval(XtNwidth, cur_win->pane_width + 2),
2957 				Nval(XtNheight, ((int) x_height(cur_win)
2958 						 - cur_win->char_height)),
2959 				Nval(XtNx, (cur_win->scrollbar_on_left
2960 					    ? 0
2961 					    : x_width(cur_win))),
2962 				Nval(XtNy, 0),
2963 				Nval(XtNborderWidth, 0),
2964 				Nval(XtNbackground, cur_win->modeline_bg),
2965 				NULL);
2966 #endif /* OPT_KEV_SCROLLBARS */
2967 #endif /* OPT_XAW_SCROLLBARS */
2968 #endif /* MOTIF_WIDGETS */
2969 
2970 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
2971     curs_sb_v_double_arrow = XCreateFontCursor(dpy, XC_sb_v_double_arrow);
2972     curs_sb_up_arrow = XCreateFontCursor(dpy, XC_sb_up_arrow);
2973     curs_sb_down_arrow = XCreateFontCursor(dpy, XC_sb_down_arrow);
2974     curs_sb_left_arrow = XCreateFontCursor(dpy, XC_sb_left_arrow);
2975     curs_sb_right_arrow = XCreateFontCursor(dpy, XC_sb_right_arrow);
2976     curs_double_arrow = XCreateFontCursor(dpy, XC_double_arrow);
2977 #endif
2978 
2979 #if OPT_KEV_SCROLLBARS
2980     cur_win->nscrollbars = 0;
2981 #else
2982     cur_win->nscrollbars = -1;
2983 #endif
2984 
2985     /*
2986      * Move scrollbar to the left if requested via the resources.
2987      * Note that this is handled elsewhere for NO_WIDGETS.
2988      */
2989     if (cur_win->scrollbar_on_left) {
2990 #if MOTIF_WIDGETS
2991 	XtVaSetValues(cur_win->pane,
2992 		      Nval(XmNleftAttachment, XmATTACH_FORM),
2993 		      Nval(XmNrightAttachment, XmATTACH_WIDGET),
2994 		      Nval(XmNrightWidget, cur_win->screen),
2995 		      NULL);
2996 	XtVaSetValues(cur_win->screen,
2997 		      Nval(XmNleftAttachment, XmATTACH_NONE),
2998 		      Nval(XmNrightAttachment, XmATTACH_FORM),
2999 		      NULL);
3000 #endif /* !MOTIF_WIDGETS */
3001     }
3002 
3003     XtAddEventHandler(cur_win->screen,
3004 		      KeyPressMask,
3005 		      FALSE,
3006 		      x_key_press,
3007 		      (XtPointer) 0);
3008 
3009     XtAddEventHandler(cur_win->screen,
3010 		      (EventMask) (ButtonPressMask | ButtonReleaseMask
3011 				   | (cur_win->focus_follows_mouse ? PointerMotionMask
3012 				      : (Button1MotionMask | Button3MotionMask))
3013 				   | ExposureMask | VisibilityChangeMask),
3014 		      TRUE,
3015 		      x_process_event,
3016 		      (XtPointer) 0);
3017 
3018     XtAddEventHandler(cur_win->top_widget,
3019 		      StructureNotifyMask,
3020 		      FALSE,
3021 		      x_configure_window,
3022 		      (XtPointer) 0);
3023 
3024     XtAddEventHandler(cur_win->top_widget,
3025 		      EnterWindowMask | LeaveWindowMask | FocusChangeMask,
3026 		      FALSE,
3027 		      x_change_focus,
3028 		      (XtPointer) 0);
3029 
3030     /* Realize the widget, but don't do the mapping (yet...) */
3031     XtSetMappedWhenManaged(cur_win->top_widget, False);
3032     XtRealizeWidget(cur_win->top_widget);
3033 
3034     /* Now that the widget hierarchy is realized, we can fetch
3035        some crucial dimensions and set the window manager hints
3036        dealing with resize appropriately. */
3037     {
3038 	Dimension new_width, new_height;
3039 	XtVaGetValues(cur_win->top_widget,
3040 		      XtNheight, &cur_win->top_height,
3041 		      XtNwidth, &cur_win->top_width,
3042 		      NULL);
3043 #if ATHENA_WIDGETS && OPT_MENUS
3044 	XtVaGetValues(cur_win->menu_widget,
3045 		      XtNheight, &cur_win->menu_height,
3046 		      XtNwidth, &new_width,
3047 		      NULL);
3048 #endif
3049 	XtVaGetValues(cur_win->screen,
3050 		      XtNheight, &new_height,
3051 		      XtNwidth, &new_width,
3052 		      NULL);
3053 
3054 	cur_win->base_width = cur_win->top_width - new_width;
3055 	cur_win->base_height = cur_win->top_height - new_height;
3056 
3057 	/* Ugly hack:  If the window manager chooses not to respect
3058 	   the min_height hint, it may instead choose to use base_height
3059 	   as min_height instead.  I believe that the reason for this
3060 	   is because O'Reilly's Xlib Programming Manual, Vol 1
3061 	   has lead some window manager implementors astray.  It says:
3062 
3063 	   In R4, the base_width and base_height fields have been
3064 	   added to the XSizeHints structure.  They are used with
3065 	   the width_inc and height_inc fields to indicate to the
3066 	   window manager that it should resize the window in
3067 	   steps -- in units of a certain number of pixels
3068 	   instead of single pixels.  The window manager resizes
3069 	   the window to any multiple of width_inc in width and
3070 	   height_inc in height, but no smaller than min_width
3071 	   and min_height and no bigger than max_width and
3072 	   max_height.  If you think about it, min_width and
3073 	   min_height and base_width and base_height have
3074 	   basically the same purpose.  Therefore, base_width and
3075 	   base_height take priority over min_width and
3076 	   min_height, so only one of these pairs should be set.
3077 
3078 	   We are indeed lucky that most window managers have chosen to
3079 	   ignore the last two sentences in the above paragraph.  These
3080 	   two pairs of values *do not* serve the same purpose.  I can
3081 	   see where they're coming from...  if you have a minimum
3082 	   height that you want the application to be, you could simply
3083 	   set base_height to be the this minimum height which would be
3084 	   the real_base_height + N*unit_height where N is a
3085 	   non-negative integer.  But what they're forgetting in all
3086 	   this is that the window manager reports the size in units
3087 	   to the user and the size it reports will likely be off by N.
3088 
3089 	   Unfortunately, enlightenment 0.16.3 (and probably other
3090 	   versions too) do seem to only use base_height and
3091 	   base_width to determine the smallest window size and this
3092 	   is causing some problems for xvile with no menu bars.
3093 	   Specifically, I've seen BadValue errors from the guts of Xt
3094 	   when cur_win->screen gets resized down to zero.  So we make
3095 	   sure that base_height is non-zero and hope the user doesn't
3096 	   notice that extra pixel of height.  */
3097 	if (cur_win->base_height == 0)
3098 	    cur_win->base_height = 1;
3099 
3100 	XtVaSetValues(cur_win->top_widget,
3101 #if XtSpecificationRelease >= 4
3102 		      Nval(XtNbaseHeight, cur_win->base_height),
3103 		      Nval(XtNbaseWidth, cur_win->base_width),
3104 #endif
3105 		      Nval(XtNminHeight, (cur_win->base_height
3106 					  + MINROWS * cur_win->char_height)),
3107 		      Nval(XtNminWidth, (cur_win->base_width
3108 					 + MINCOLS * cur_win->char_width)),
3109 		      Nval(XtNheightInc, cur_win->char_height),
3110 		      Nval(XtNwidthInc, cur_win->char_width),
3111 		      NULL);
3112     }
3113     /* According to the docs, this should map the widget too... */
3114     XtSetMappedWhenManaged(cur_win->top_widget, True);
3115     /* ... but an explicit map calls seems to be necessary anyway */
3116     XtMapWidget(cur_win->top_widget);
3117 
3118     cur_win->win = XtWindow(cur_win->screen);
3119 
3120 #if OPT_INPUT_METHOD
3121     init_xlocale();
3122     if (okCTYPE2(vl_wide_enc)) {
3123 	term.set_enc(enc_UTF8);
3124     }
3125 #endif
3126 
3127     x_load_icon();
3128 
3129     /* We wish to participate in the "delete window" protocol */
3130     {
3131 	Atom atoms[2];
3132 	i = 0;
3133 	atoms[i++] = GetAtom(WM_DELETE_WINDOW);
3134 	XSetWMProtocols(dpy,
3135 			XtWindow(cur_win->top_widget),
3136 			atoms,
3137 			i);
3138     }
3139     XtAddEventHandler(cur_win->top_widget,
3140 		      NoEventMask,
3141 		      TRUE,
3142 		      x_wm_delwin,
3143 		      (XtPointer) 0);
3144 
3145     set_pointer(XtWindow(cur_win->screen), cur_win->normal_pointer);
3146 
3147     returnCode(status);
3148 }
3149 
3150 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
3151 static Boolean
3152 #define TooLight(color) ((color) > 0xfff0)
3153 #define TooDark(color)  ((color) < 0x0020)
too_light_or_too_dark(Pixel pixel)3154 too_light_or_too_dark(Pixel pixel)
3155 {
3156     XColor color;
3157 
3158     color.pixel = pixel;
3159     XQueryColor(dpy, cur_win->colormap, &color);
3160 
3161     return (Boolean) ((TooLight(color.red) &&
3162 		       TooLight(color.green) &&
3163 		       TooLight(color.blue)) ||
3164 		      (TooDark(color.red) &&
3165 		       TooDark(color.green) &&
3166 		       TooDark(color.blue)));
3167 }
3168 #endif
3169 
3170 #if OPT_KEV_SCROLLBARS
3171 static Boolean
alloc_shadows(Pixel pixel,Pixel * light,Pixel * dark)3172 alloc_shadows(Pixel pixel, Pixel *light, Pixel *dark)
3173 {
3174     XColor color;
3175     ULONG lred, lgreen, lblue, dred, dgreen, dblue;
3176 
3177     color.pixel = pixel;
3178     XQueryColor(dpy, cur_win->colormap, &color);
3179 
3180     if ((color.red > 0xfff0 && color.green > 0xfff0 && color.blue > 0xfff0)
3181 	|| (color.red < 0x0020 && color.green < 0x0020 && color.blue < 0x0020))
3182 	return False;		/* It'll look awful! */
3183 
3184 #define MAXINTENS ((ULONG)65535L)
3185 #define PlusFortyPercent(v) (ULONG) ((7 * (long) (v)) / 5)
3186     lred = PlusFortyPercent(color.red);
3187     lred = min(lred, MAXINTENS);
3188     lred = max(lred, (color.red + MAXINTENS) / 2);
3189 
3190     lgreen = PlusFortyPercent(color.green);
3191     lgreen = min(lgreen, MAXINTENS);
3192     lgreen = max(lgreen, (color.green + MAXINTENS) / 2);
3193 
3194     lblue = PlusFortyPercent(color.blue);
3195     lblue = min(lblue, MAXINTENS);
3196     lblue = max(lblue, (color.blue + MAXINTENS) / 2);
3197 
3198 #define MinusFortyPercent(v) (ULONG) ((3 * (long) (v)) / 5)
3199 
3200     dred = MinusFortyPercent(color.red);
3201     dgreen = MinusFortyPercent(color.green);
3202     dblue = MinusFortyPercent(color.blue);
3203 
3204     color.red = (USHORT) lred;
3205     color.green = (USHORT) lgreen;
3206     color.blue = (USHORT) lblue;
3207 
3208     if (!XAllocColor(dpy, cur_win->colormap, &color))
3209 	return False;
3210 
3211     *light = color.pixel;
3212 
3213     color.red = (USHORT) dred;
3214     color.green = (USHORT) dgreen;
3215     color.blue = (USHORT) dblue;
3216 
3217     if (!XAllocColor(dpy, cur_win->colormap, &color))
3218 	return False;
3219 
3220     *dark = color.pixel;
3221 
3222     return True;
3223 }
3224 #endif
3225 
3226 #if OPT_MULTIBYTE
3227 void
x_set_font_encoding(ENC_CHOICES new_encoding)3228 x_set_font_encoding(ENC_CHOICES new_encoding)
3229 {
3230     ENC_CHOICES old_encoding = font_encoding;
3231 
3232     if (old_encoding != new_encoding) {
3233 	TRACE(("set $font-encoding to %s\n", encoding2s(new_encoding)));
3234 	font_encoding = new_encoding;
3235 	set_winflags(TRUE, WFHARD);
3236     }
3237 }
3238 #endif
3239 
3240 void
x_set_fontname(TextWindow tw,const char * fname)3241 x_set_fontname(TextWindow tw, const char *fname)
3242 {
3243     char *newfont;
3244 
3245     if (fname != 0
3246 	&& (newfont = strmalloc(fname)) != 0) {
3247 	FreeIfNeeded(tw->fontname);
3248 	tw->fontname = newfont;
3249     }
3250 }
3251 
3252 char *
x_current_fontname(void)3253 x_current_fontname(void)
3254 {
3255     return cur_win->fontname;
3256 }
3257 
3258 #if OPT_MENUS
3259 Widget
x_menu_widget(void)3260 x_menu_widget(void)
3261 {
3262     return cur_win->menu_widget;
3263 }
3264 
3265 int
x_menu_height(void)3266 x_menu_height(void)
3267 {
3268     return cur_win->menu_height;
3269 }
3270 #endif /* OPT_MENUS */
3271 
3272 #if OPT_MENUS_COLORED
3273 int
x_menu_foreground(void)3274 x_menu_foreground(void)
3275 {
3276     return (int) cur_win->menubar_fg;
3277 }
3278 
3279 int
x_menu_background(void)3280 x_menu_background(void)
3281 {
3282     return (int) cur_win->menubar_bg;
3283 }
3284 #endif /* OPT_MENUS_COLORED */
3285 
3286 int
x_setfont(const char * fname)3287 x_setfont(const char *fname)
3288 {
3289     XVileFont *pfont;
3290     Dimension oldw;
3291     Dimension oldh;
3292     int code = 1;
3293 
3294     TRACE((T_CALLED "x_setfont(%s)\n", fname));
3295     if (cur_win) {
3296 	XVileFonts oldf = cur_win->fonts;
3297 	oldw = (Dimension) x_width(cur_win);
3298 	oldh = (Dimension) x_height(cur_win);
3299 	code = 0;
3300 	if ((pfont = xvileQueryFont(dpy, cur_win, fname)) != 0) {
3301 #ifndef XRENDERFONT
3302 	    XSetFont(dpy, GetColorGC(cur_win, tt_info), pfont->fid);
3303 	    XSetFont(dpy, GetColorGC(cur_win, rt_info), pfont->fid);
3304 	    XSetFont(dpy, GetColorGC(cur_win, ss_info), pfont->fid);
3305 	    XSetFont(dpy, GetColorGC(cur_win, rs_info), pfont->fid);
3306 	    XSetFont(dpy, GetColorGC(cur_win, cc_info), pfont->fid);
3307 	    XSetFont(dpy, GetColorGC(cur_win, rc_info), pfont->fid);
3308 	    XSetFont(dpy, GetColorGC(cur_win, mm_info), pfont->fid);
3309 	    XSetFont(dpy, GetColorGC(cur_win, rm_info), pfont->fid);
3310 	    XSetFont(dpy, GetColorGC(cur_win, oo_info), pfont->fid);
3311 	    XSetFont(dpy, GetColorGC(cur_win, ro_info), pfont->fid);
3312 	    if (GetColorGC(cur_win, tt_info) != GetColorGC(cur_win, rs_info)) {
3313 		XSetFont(dpy, GetColorGC(cur_win, ss_info), pfont->fid);
3314 		XSetFont(dpy, GetColorGC(cur_win, rs_info), pfont->fid);
3315 	    }
3316 #endif
3317 	    reset_color_gcs();
3318 	    xvileCloseFonts(dpy, &oldf);
3319 
3320 	    /* if size changed, resize it, otherwise refresh */
3321 	    if (oldw != x_width(cur_win) || oldh != x_height(cur_win)) {
3322 		XtVaSetValues(cur_win->top_widget,
3323 			      Nval(XtNminHeight, (cur_win->base_height
3324 						  + MINROWS * cur_win->char_height)),
3325 			      Nval(XtNminWidth, (cur_win->base_width
3326 						 + MINCOLS * cur_win->char_width)),
3327 			      Nval(XtNheightInc, cur_win->char_height),
3328 			      Nval(XtNwidthInc, cur_win->char_width),
3329 			      NULL);
3330 		update_scrollbar_sizes();
3331 		XClearWindow(dpy, cur_win->win);
3332 		x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
3333 		XResizeWindow(dpy, XtWindow(cur_win->top_widget),
3334 			      x_width(cur_win) + (unsigned) cur_win->base_width,
3335 			      x_height(cur_win) + (unsigned) cur_win->base_height);
3336 
3337 	    } else {
3338 		XClearWindow(dpy, cur_win->win);
3339 		x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
3340 		x_flush();
3341 	    }
3342 
3343 	    code = 1;
3344 	}
3345     }
3346     returnCode(code);
3347 }
3348 
3349 static void
x_set_encoding(ENC_CHOICES code)3350 x_set_encoding(ENC_CHOICES code)
3351 {
3352     term_encoding = code;
3353     TRACE(("x11:set_encoding %s\n", encoding2s(code)));
3354 }
3355 
3356 static ENC_CHOICES
x_get_encoding(void)3357 x_get_encoding(void)
3358 {
3359     return term_encoding;
3360 }
3361 
3362 static void
x_open(void)3363 x_open(void)
3364 {
3365     static int already_open;
3366 
3367 #if OPT_COLOR
3368     static const char *initpalettestr = "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15";
3369 #endif
3370 
3371     TRACE((T_CALLED "x_open\n"));
3372 
3373     if (!already_open) {
3374 #if OPT_COLOR
3375 	set_colors(NCOLORS);
3376 	set_ctrans(initpalettestr);	/* should be set_palette() */
3377 #endif
3378 	kqinit(cur_win);
3379 	cur_win->scrollbars = NULL;
3380 	cur_win->maxscrollbars = 0;
3381 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
3382 	cur_win->scrollinfo = NULL;
3383 	cur_win->grips = NULL;
3384 #endif
3385 
3386 	/* main code assumes that it can access a cell at nrow x ncol */
3387 	term.maxcols = term.cols = (int) cur_win->cols;
3388 	term.maxrows = term.rows = (int) cur_win->rows;
3389 
3390 	if (check_scrollbar_allocs() != TRUE) {
3391 	    fprintf(stderr, "Cannot allocate scrollbars\n");
3392 	    ExitProgram(BADEXIT);
3393 	}
3394 	already_open = TRUE;
3395     }
3396     returnVoid();
3397 }
3398 
3399 #if OPT_INPUT_METHOD
3400 static void
CloseInputMethod(void)3401 CloseInputMethod(void)
3402 {
3403     if (cur_win->xim) {
3404 	XCloseIM(cur_win->xim);
3405 	cur_win->xim = 0;
3406 	TRACE(("freed cur_win->xim\n"));
3407     }
3408 }
3409 #else
3410 #define CloseInputMethod()	/* nothing */
3411 #endif
3412 
3413 static void
x_close(void)3414 x_close(void)
3415 {
3416     /* FIXME: Free pixmaps and GCs !!! */
3417 
3418     if (cur_win->top_widget) {
3419 #if NO_LEAKS
3420 	XtDestroyWidget(cur_win->top_widget);
3421 #endif
3422 	cur_win->top_widget = 0;
3423 	CloseInputMethod();
3424 	XtCloseDisplay(dpy);	/* need this if $xshell left subprocesses */
3425     }
3426 }
3427 
3428 static void
x_touch(TextWindow tw,int sc,int sr,UINT ec,UINT er)3429 x_touch(TextWindow tw, int sc, int sr, UINT ec, UINT er)
3430 {
3431     UINT r;
3432     UINT c;
3433 
3434     if (er > tw->rows)
3435 	er = tw->rows;
3436     if (ec > tw->cols)
3437 	ec = tw->cols;
3438 
3439     for (r = (UINT) sr; r < er; r++) {
3440 	MARK_LINE_DIRTY(r);
3441 	for (c = (UINT) sc; c < ec; c++) {
3442 	    if (CELL_TEXT(r, c) != ' ' || CELL_ATTR(r, c)) {
3443 		MARK_CELL_DIRTY(r, c);
3444 	    }
3445 	}
3446     }
3447 }
3448 
3449 static void
wait_for_scroll(TextWindow tw)3450 wait_for_scroll(TextWindow tw)
3451 {
3452     XEvent ev;
3453     int sc, sr;
3454     UINT ec, er;
3455     XGraphicsExposeEvent *gev;
3456 
3457     for_ever {			/* loop looking for a gfx expose or no expose */
3458 	if (XCheckTypedEvent(dpy, NoExpose, &ev))
3459 	    return;
3460 	if (XCheckTypedEvent(dpy, GraphicsExpose, &ev)) {
3461 	    gev = (XGraphicsExposeEvent *) & ev;
3462 	    sc = gev->x / tw->char_width;
3463 	    sr = gev->y / tw->char_height;
3464 	    ec = (UINT) CEIL(gev->x + gev->width, tw->char_width);
3465 	    er = (UINT) CEIL(gev->y + gev->height, tw->char_height);
3466 	    x_touch(tw, sc, sr, ec, er);
3467 	    if (gev->count == 0)
3468 		return;
3469 	}
3470 	XSync(dpy, False);
3471     }
3472 }
3473 
3474 static int
x_set_palette(const char * thePalette)3475 x_set_palette(const char *thePalette)
3476 {
3477     int rc;
3478 
3479     TRACE(("x_set_palette(%s)\n", thePalette));
3480     rc = set_ctrans(thePalette);
3481     x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
3482     x_flush();
3483 
3484     return rc;
3485 }
3486 
3487 static void
x_scroll(int from,int to,int count)3488 x_scroll(int from, int to, int count)
3489 {
3490     if (cur_win->visibility == VisibilityFullyObscured)
3491 	return;			/* Why bother? */
3492 
3493     if (from == to)
3494 	return;			/* shouldn't happen */
3495 
3496     XCopyArea(dpy,
3497 	      cur_win->win,
3498 	      cur_win->win,
3499 	      GetColorGC(cur_win, tt_info),
3500 	      x_pos(cur_win, 0), y_pos(cur_win, from),
3501 	      x_width(cur_win), (UINT) (count * cur_win->char_height),
3502 	      x_pos(cur_win, 0), y_pos(cur_win, to));
3503     if (from < to)
3504 	XClearArea(dpy, cur_win->win,
3505 		   x_pos(cur_win, 0), y_pos(cur_win, from),
3506 		   x_width(cur_win), (UINT) ((to - from) * cur_win->char_height),
3507 		   FALSE);
3508     else
3509 	XClearArea(dpy, cur_win->win,
3510 		   x_pos(cur_win, 0), y_pos(cur_win, to + count),
3511 		   x_width(cur_win), (UINT) ((from - to) * cur_win->char_height),
3512 		   FALSE);
3513     if (cur_win->visibility == VisibilityPartiallyObscured) {
3514 	XFlush(dpy);
3515 	wait_for_scroll(cur_win);
3516     }
3517 }
3518 
3519 /* See above comment regarding CLEAR_THRESH */
3520 #define NONDIRTY_THRESH 16
3521 
3522 /* make sure the screen looks like we want it to */
3523 static void
x_flush(void)3524 x_flush(void)
3525 {
3526     int r, c, sc, ec, cleanlen;
3527     VIDEO_ATTR attr;
3528 
3529     if (cur_win->visibility == VisibilityFullyObscured || !cur_win->exposed)
3530 	return;			/* Why bother? */
3531 
3532     /*
3533      * Write out cursor _before_ rest of the screen in order to avoid
3534      * flickering / winking effect noticeable on some display servers.  This
3535      * means that the old cursor position (if different from the current
3536      * one) will be cleared after the new cursor is displayed.
3537      */
3538 
3539     if (ttrow >= 0 && ttrow < term.rows && ttcol >= 0 && ttcol < term.cols
3540 	&& !cur_win->wipe_permitted) {
3541 	CLEAR_CELL_DIRTY(ttrow, ttcol);
3542 	display_cursor((XtPointer) 0, (XtIntervalId *) 0);
3543     }
3544 
3545     /* sometimes we're the last to know about resizing... */
3546     if ((int) cur_win->rows > term.maxrows)
3547 	cur_win->rows = (UINT) term.maxrows;
3548 
3549     for (r = 0; r < (int) cur_win->rows; r++) {
3550 	if (!IS_DIRTY_LINE(r))
3551 	    continue;
3552 	if (r != ttrow)
3553 	    CLEAR_LINE_DIRTY(r);
3554 
3555 	/*
3556 	 * The following code will cause monospaced fonts with ink outside
3557 	 * the bounding box to be cleaned up.
3558 	 */
3559 	if (cur_win->left_ink || cur_win->right_ink)
3560 	    for (c = 0; c < term.cols;) {
3561 		while (c < term.cols && !IS_DIRTY(r, c))
3562 		    c++;
3563 		if (c >= term.cols)
3564 		    break;
3565 		if (cur_win->left_ink && c > 0)
3566 		    MARK_CELL_DIRTY(r, c - 1);
3567 		while (c < term.cols && IS_DIRTY(r, c))
3568 		    c++;
3569 		if (cur_win->right_ink && c < term.cols) {
3570 		    MARK_CELL_DIRTY(r, c);
3571 		    c++;
3572 		}
3573 	    }
3574 
3575 	c = 0;
3576 	while (c < term.cols) {
3577 	    /* Find the beginning of the next dirty sequence */
3578 	    while (c < term.cols && !IS_DIRTY(r, c))
3579 		c++;
3580 	    if (c >= term.cols)
3581 		break;
3582 	    if (r == ttrow && c == ttcol && !cur_win->wipe_permitted) {
3583 		c++;
3584 		continue;
3585 	    }
3586 	    CLEAR_CELL_DIRTY(r, c);
3587 	    sc = ec = c;
3588 	    attr = VATTRIB(CELL_ATTR(r, c));
3589 	    cleanlen = NONDIRTY_THRESH;
3590 	    c++;
3591 	    /*
3592 	     * Scan until we find the end of line, a cell with a different
3593 	     * attribute, a sequence of NONDIRTY_THRESH non-dirty chars, or
3594 	     * the cursor position.
3595 	     */
3596 	    while (c < term.cols) {
3597 		if (attr != VATTRIB(CELL_ATTR(r, c)))
3598 		    break;
3599 		else if (r == ttrow && c == ttcol && !cur_win->wipe_permitted) {
3600 		    c++;
3601 		    break;
3602 		} else if (IS_DIRTY(r, c)) {
3603 		    ec = c;
3604 		    cleanlen = NONDIRTY_THRESH;
3605 		    CLEAR_CELL_DIRTY(r, c);
3606 		} else if (--cleanlen <= 0)
3607 		    break;
3608 		c++;
3609 	    }
3610 	    /* write out the portion from sc thru ec */
3611 	    xvileDraw(dpy, cur_win, &CELL_TEXT(r, sc), ec - sc + 1,
3612 		      (UINT) VATTRIB(CELL_ATTR(r, sc)), r, sc);
3613 	}
3614     }
3615     XFlush(dpy);
3616 }
3617 
3618 /* selection processing stuff */
3619 
3620 /* multi-click code stolen from xterm */
3621 /*
3622  * double click table for cut and paste in 8 bits
3623  *
3624  * This table is divided in four parts :
3625  *
3626  *	- control characters	[0,0x1f] U [0x80,0x9f]
3627  *	- separators		[0x20,0x3f] U [0xa0,0xb9]
3628  *	- binding characters	[0x40,0x7f] U [0xc0,0xff]
3629  *	- exceptions
3630  */
3631 static int charClass[256] =
3632 {
3633 /* NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL */
3634     32, 1, 1, 1, 1, 1, 1, 1,
3635 /*  BS   HT   NL   VT   NP   CR   SO   SI */
3636     1, 32, 1, 1, 1, 1, 1, 1,
3637 /* DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB */
3638     1, 1, 1, 1, 1, 1, 1, 1,
3639 /* CAN   EM  SUB  ESC   FS   GS   RS   US */
3640     1, 1, 1, 1, 1, 1, 1, 1,
3641 /*  SP    !    "    #    $    %    &    ' */
3642     32, 33, 34, 35, 36, 37, 38, 39,
3643 /*   (    )    *    +    ,    -    .    / */
3644     40, 41, 42, 43, 44, 45, 46, 47,
3645 /*   0    1    2    3    4    5    6    7 */
3646     48, 48, 48, 48, 48, 48, 48, 48,
3647 /*   8    9    :    ;    <    =    >    ? */
3648     48, 48, 58, 59, 60, 61, 62, 63,
3649 /*   @    A    B    C    D    E    F    G */
3650     64, 48, 48, 48, 48, 48, 48, 48,
3651 /*   H    I    J    K    L    M    N    O */
3652     48, 48, 48, 48, 48, 48, 48, 48,
3653 /*   P    Q    R    S    T    U    V    W */
3654     48, 48, 48, 48, 48, 48, 48, 48,
3655 /*   X    Y    Z    [    \    ]    ^    _ */
3656     48, 48, 48, 91, 92, 93, 94, 48,
3657 /*   `    a    b    c    d    e    f    g */
3658     96, 48, 48, 48, 48, 48, 48, 48,
3659 /*   h    i    j    k    l    m    n    o */
3660     48, 48, 48, 48, 48, 48, 48, 48,
3661 /*   p    q    r    s    t    u    v    w */
3662     48, 48, 48, 48, 48, 48, 48, 48,
3663 /*   x    y    z    {    |    }    ~  DEL */
3664     48, 48, 48, 123, 124, 125, 126, 1,
3665 /* x80  x81  x82  x83  IND  NEL  SSA  ESA */
3666     1, 1, 1, 1, 1, 1, 1, 1,
3667 /* HTS  HTJ  VTS  PLD  PLU   RI  SS2  SS3 */
3668     1, 1, 1, 1, 1, 1, 1, 1,
3669 /* DCS  PU1  PU2  STS  CCH   MW  SPA  EPA */
3670     1, 1, 1, 1, 1, 1, 1, 1,
3671 /* x98  x99  x9A  CSI   ST  OSC   PM  APC */
3672     1, 1, 1, 1, 1, 1, 1, 1,
3673 /*   -    i   c/    L   ox   Y-    |   So */
3674     160, 161, 162, 163, 164, 165, 166, 167,
3675 /*  ..   c0   ip   <<    _        R0    - */
3676     168, 169, 170, 171, 172, 173, 174, 175,
3677 /*   o   +-    2    3    '    u   q|    . */
3678     176, 177, 178, 179, 180, 181, 182, 183,
3679 /*   ,    1    2   >>  1/4  1/2  3/4    ? */
3680     184, 185, 186, 187, 188, 189, 190, 191,
3681 /*  A`   A'   A^   A~   A:   Ao   AE   C, */
3682     48, 48, 48, 48, 48, 48, 48, 48,
3683 /*  E`   E'   E^   E:   I`   I'   I^   I: */
3684     48, 48, 48, 48, 48, 48, 48, 48,
3685 /*  D-   N~   O`   O'   O^   O~   O:    X */
3686     48, 48, 48, 48, 48, 48, 48, 216,
3687 /*  O/   U`   U'   U^   U:   Y'    P    B */
3688     48, 48, 48, 48, 48, 48, 48, 48,
3689 /*  a`   a'   a^   a~   a:   ao   ae   c, */
3690     48, 48, 48, 48, 48, 48, 48, 48,
3691 /*  e`   e'   e^   e:    i`  i'   i^   i: */
3692     48, 48, 48, 48, 48, 48, 48, 48,
3693 /*   d   n~   o`   o'   o^   o~   o:   -: */
3694     48, 48, 48, 48, 48, 48, 48, 248,
3695 /*  o/   u`   u'   u^   u:   y'    P   y: */
3696     48, 48, 48, 48, 48, 48, 48, 48};
3697 
3698 /* low, high are in the range 0..255 */
3699 static int
set_character_class_range(int low,int high,int value)3700 set_character_class_range(int low, int high, int value)
3701 {
3702 
3703     if (low < 0 || high > 255 || high < low)
3704 	return (-1);
3705 
3706     for (; low <= high; low++)
3707 	charClass[low] = value;
3708 
3709     return (0);
3710 }
3711 
3712 /*
3713  * set_character_class - takes a string of the form
3714  *
3715  *                 low[-high]:val[,low[-high]:val[...]]
3716  *
3717  * and sets the indicated ranges to the indicated values.
3718  */
3719 
3720 static int
set_character_class(const char * s)3721 set_character_class(const char *s)
3722 {
3723     int i;			/* iterator, index into s */
3724     int len;			/* length of s */
3725     int acc;			/* accumulator */
3726     int low, high;		/* bounds of range [0..127] */
3727     int base;			/* 8, 10, 16 (octal, decimal, hex) */
3728     int numbers;		/* count of numbers per range */
3729     int digits;			/* count of digits in a number */
3730     static const char *errfmt = "xvile:  %s in range string \"%s\" (position %d)\n";
3731 
3732     if (!s || !s[0])
3733 	return -1;
3734 
3735     base = 10;			/* in case we ever add octal, hex */
3736     low = high = -1;		/* out of range */
3737 
3738     for (i = 0, len = (int) strlen(s), acc = 0, numbers = digits = 0;
3739 	 i < len; i++) {
3740 	int c = s[i];
3741 
3742 	if (isSpace(c)) {
3743 	    continue;
3744 	} else if (isDigit(c)) {
3745 	    acc = acc * base + (c - '0');
3746 	    digits++;
3747 	    continue;
3748 	} else if (c == '-') {
3749 	    low = acc;
3750 	    acc = 0;
3751 	    if (digits == 0) {
3752 		(void) fprintf(stderr, errfmt, "missing number", s, i);
3753 		return (-1);
3754 	    }
3755 	    digits = 0;
3756 	    numbers++;
3757 	    continue;
3758 	} else if (c == ':') {
3759 	    if (numbers == 0)
3760 		low = acc;
3761 	    else if (numbers == 1)
3762 		high = acc;
3763 	    else {
3764 		(void) fprintf(stderr, errfmt, "too many numbers",
3765 			       s, i);
3766 		return (-1);
3767 	    }
3768 	    digits = 0;
3769 	    numbers++;
3770 	    acc = 0;
3771 	    continue;
3772 	} else if (c == ',') {
3773 	    /*
3774 	     * now, process it
3775 	     */
3776 
3777 	    if (high < 0) {
3778 		high = low;
3779 		numbers++;
3780 	    }
3781 	    if (numbers != 2) {
3782 		(void) fprintf(stderr, errfmt, "bad value number",
3783 			       s, i);
3784 	    } else if (set_character_class_range(low, high, acc) != 0) {
3785 		(void) fprintf(stderr, errfmt, "bad range", s, i);
3786 	    }
3787 	    low = high = -1;
3788 	    acc = 0;
3789 	    digits = 0;
3790 	    numbers = 0;
3791 	    continue;
3792 	} else {
3793 	    (void) fprintf(stderr, errfmt, "bad character", s, i);
3794 	    return (-1);
3795 	}			/* end if else if ... else */
3796 
3797     }
3798 
3799     if (low < 0 && high < 0)
3800 	return (0);
3801 
3802     /*
3803      * now, process it
3804      */
3805 
3806     if (high < 0)
3807 	high = low;
3808     if (numbers < 1 || numbers > 2) {
3809 	(void) fprintf(stderr, errfmt, "bad value number", s, i);
3810     } else if (set_character_class_range(low, high, acc) != 0) {
3811 	(void) fprintf(stderr, errfmt, "bad range", s, i);
3812     }
3813     return (0);
3814 }
3815 
3816 #if OPT_MULTIBYTE
3817 static int
pasting_utf8(TBUFF * p,int c)3818 pasting_utf8(TBUFF *p, int c)
3819 {
3820     int result = FALSE;
3821     if (b_is_utfXX(curbp)) {
3822 	char temp[2];
3823 	int n, limit, len;
3824 
3825 	temp[0] = (char) c;
3826 	if (vl_conv_to_utf32((UINT *) 0, temp, (B_COUNT) 6) > 0) {
3827 	    result = TRUE;
3828 	} else if ((limit = (int) tb_length(p)) > 0) {
3829 	    for (n = limit - 1; n > 0; --n) {
3830 		len = vl_conv_to_utf32((UINT *) 0,
3831 				       tb_values(p) + n,
3832 				       (B_COUNT) 6);
3833 		if (len > 0) {
3834 		    result = TRUE;
3835 		    break;
3836 		}
3837 	    }
3838 	}
3839     }
3840     return result;
3841 }
3842 #define PastingUTF8(p,c) pasting_utf8(p,c)
3843 #else
3844 #define PastingUTF8(p,c) FALSE
3845 #endif
3846 
3847 /*
3848  * Copy a single character into the paste-buffer, quoting it if necessary
3849  */
3850 static int
add2paste(TBUFF ** p,int c)3851 add2paste(TBUFF **p, int c)
3852 {
3853     int result;
3854 
3855     TRACE2(("add2paste '%04o'\n", c));
3856     if (c == '\n' || isBlank(c)) {
3857 	/*EMPTY */ ;
3858     } else if (isSpecial(c) ||
3859 	       isreturn(c) ||
3860 	       !(isPrint(c) || PastingUTF8(*p, c))) {
3861 	(void) tb_append(p, quotec);
3862     }
3863     result = (tb_append(p, c) != 0);
3864     TRACE2(("...added %d:%s\n", tb_length(*p), tb_visible(*p)));
3865     return result;
3866 }
3867 
3868 /*
3869  * Copy the selection into the PasteBuf buffer.  If we are pasting into a
3870  * window, check to see if:
3871  *
3872  *	+ the window's buffer is modifiable (if not, don't waste time copying
3873  *	  text!)
3874  *	+ the buffer uses 'autoindent' mode (if so, do some heuristics
3875  *	  for placement of the pasted text -- we may put it on lines by
3876  *	  itself, above or below the current line)
3877  */
3878 #define OLD_PASTE 0
3879 
3880 static int
copy_paste(TBUFF ** p,char * value,size_t length)3881 copy_paste(TBUFF **p, char *value, size_t length)
3882 {
3883     WINDOW *wp = row2window(ttrow);
3884     BUFFER *bp = valid_window(wp) ? wp->w_bufp : 0;
3885     int status;
3886 
3887     if (valid_buffer(bp) && b_val(bp, MDVIEW))
3888 	return FALSE;
3889 
3890     status = TRUE;
3891 
3892     if (valid_buffer(bp) && (b_val(bp, MDCINDENT) || b_val(bp, MDAIND))) {
3893 
3894 #if OLD_PASTE
3895 	/*
3896 	 * If the cursor points before the first nonwhite on
3897 	 * the line, convert the insert into an 'O' command.
3898 	 * If it points to the end of the line, convert it into
3899 	 * an 'o' command.  Otherwise (if it is within the
3900 	 * nonwhite portion of the line), assume the user knows
3901 	 * what (s)he is doing.
3902 	 */
3903 #endif
3904 	if (setwmark(ttrow, ttcol)) {	/* MK gets cursor */
3905 #if OLD_PASTE
3906 	    LINE *lp = MK.l;
3907 	    int first = firstchar(lp);
3908 	    int last = lastchar(lp);
3909 	    CMDFUNC *f = 0;
3910 
3911 	    /* If the line contains only a single nonwhite,
3912 	     * we will insert before it.
3913 	     */
3914 	    if (first >= MK.o)
3915 		f = &f_openup_no_aindent;
3916 	    else if (last <= MK.o)
3917 		f = &f_opendown_no_aindent;
3918 	    if (insertmode) {
3919 		if ((*value != '\n') && MK.o == 0)
3920 		    (void) tb_append(p, '\n');
3921 	    } else if (f) {
3922 		char *pstr;
3923 		/* we're _replacing_ the default
3924 		   insertion command, so reinit */
3925 		tb_init(p, esc_c);
3926 		pstr = fnc2pstr(f);
3927 		tb_bappend(p, pstr + 1, (size_t) *pstr);
3928 	    }
3929 #endif
3930 	}
3931     }
3932 
3933     while (length-- > 0) {
3934 	if (!add2paste(p, CharOf(*value++))) {
3935 	    status = FALSE;
3936 	    break;
3937 	}
3938     }
3939 
3940     return status;
3941 }
3942 
3943 static Atom *
GetSelectionTargets(void)3944 GetSelectionTargets(void)
3945 {
3946     static Atom result[10];
3947     if (result[0] == 0) {
3948 	Atom *tp = result;
3949 #if OPT_MULTIBYTE
3950 	*tp++ = GetAtom(UTF8_STRING);
3951 	*tp++ = GetAtom(COMPOUND_TEXT);
3952 #endif
3953 	*tp++ = GetAtom(TEXT);
3954 	*tp++ = XA_STRING;
3955 	*tp = None;
3956 #if OPT_TRACE
3957 	TRACE(("SelectionTargets:\n"));
3958 	for (tp = result; *tp != None; ++tp) {
3959 	    TRACE((">%s\n", visibleAtoms(*tp)));
3960 	}
3961 #endif
3962     }
3963     return result;
3964 }
3965 
3966 static void
insert_selection(Atom * selection,char * value,size_t length)3967 insert_selection(Atom *selection, char *value, size_t length)
3968 {
3969     int do_ins;
3970     char *s = NULL;		/* stifle warning */
3971 
3972     /* should be impossible to hit this with existing paste */
3973     /* XXX massive hack -- leave out 'i' if in prompt line */
3974     do_ins = !insertmode
3975 	&& (!onMsgRow(cur_win) || *selection == GetAtom(CLIPBOARD))
3976 	&& ((s = fnc2pstr(&f_insert_no_aindent)) != NULL);
3977 
3978     if (tb_init(&PasteBuf, esc_c)) {
3979 	if ((do_ins && !tb_bappend(&PasteBuf, s + 1, (size_t) CharOf(*s)))
3980 	    || !copy_paste(&PasteBuf, value, length)
3981 	    || (do_ins && !tb_append(&PasteBuf, esc_c)))
3982 	    tb_free(&PasteBuf);
3983     }
3984 }
3985 
3986 /* ARGSUSED */
3987 static void
x_get_selection(Widget w GCC_UNUSED,XtPointer cldat,Atom * selection,Atom * target,XtPointer value,ULONG * length,int * format)3988 x_get_selection(Widget w GCC_UNUSED,
3989 		XtPointer cldat,
3990 		Atom *selection,
3991 		Atom *target,
3992 		XtPointer value,
3993 		ULONG * length,
3994 		int *format)
3995 {
3996     SelectionList *list = (SelectionList *) cldat;
3997 
3998     TRACE(("x_get_selection(selection %s, target %s, format %d, length %lu)\n",
3999 	   visibleAtoms(*selection),
4000 	   visibleAtoms(*target),
4001 	   *format,
4002 	   length ? *length : 0));
4003 
4004     if (length != 0 && value != NULL) {
4005 	if (*format != 8) {
4006 	    kbd_alarm();	/* can't handle incoming data */
4007 	} else if (*target == XA_STRING || *target == GetAtom(TEXT)) {
4008 	    insert_selection(selection, (char *) value, (size_t) *length);
4009 	    XtFree((char *) value);
4010 	} else
4011 #if OPT_MULTIBYTE
4012 	    if ((*target == GetAtom(COMPOUND_TEXT))
4013 		|| (*target == GetAtom(UTF8_STRING))) {
4014 	    XTextProperty text_prop;
4015 	    char **text_list = NULL;
4016 	    int text_list_count, n;
4017 
4018 	    text_prop.value = (unsigned char *) value;
4019 	    text_prop.encoding = *target;
4020 	    text_prop.format = *format;
4021 	    text_prop.nitems = *length;
4022 
4023 #ifdef HAVE_XUTF8TEXTPROPERTYTOTEXTLIST
4024 	    if (Xutf8TextPropertyToTextList(dpy, &text_prop,
4025 					    &text_list,
4026 					    &text_list_count) < 0) {
4027 		TRACE(("Conversion failed\n"));
4028 		text_list = NULL;
4029 	    }
4030 #elif defined(HAVE_XMBTEXTPROPERTYTOTEXTLIST)
4031 	    if (XmbTextPropertyToTextList(dpy, &text_prop,
4032 					  &text_list,
4033 					  &text_list_count) < 0) {
4034 		TRACE(("Conversion failed\n"));
4035 		text_list = NULL;
4036 	    }
4037 #endif
4038 	    if (text_list != NULL) {
4039 		for (n = 0; n < text_list_count; ++n) {
4040 		    TRACE(("Inserting:%s\n", text_list[n]));
4041 		    insert_selection(selection,
4042 				     text_list[n],
4043 				     strlen(text_list[n]));
4044 		}
4045 		XFreeStringList(text_list);
4046 		XtFree((char *) value);
4047 	    } else {
4048 		kbd_alarm();	/* can't handle incoming data */
4049 	    }
4050 	} else
4051 #endif
4052 	{
4053 	    kbd_alarm();	/* can't handle incoming data */
4054 	}
4055 	XtFree((char *) list);
4056     } else if (cldat != 0) {
4057 	if (list->targets[0] != None) {
4058 	    Atom newTarget = list->targets[0];
4059 
4060 	    list->targets++;
4061 	    XtGetSelectionValue(cur_win->top_widget,
4062 				*selection,
4063 				newTarget,
4064 				x_get_selection,
4065 				(XtPointer) (list),	/* client data */
4066 				list->time);
4067 	} else {
4068 	    XtFree((char *) list);
4069 	}
4070     }
4071 }
4072 
4073 static void
x_paste_selection(Atom selection)4074 x_paste_selection(Atom selection)
4075 {
4076     if (cur_win->have_selection && IsPrimary(selection)) {
4077 	/* local transfer */
4078 	UCHAR *data = 0;
4079 	size_t len_st = 0;
4080 	ULONG len_ul;
4081 
4082 	Atom target = XA_STRING;
4083 	int format = 8;
4084 
4085 	if (!x_get_selected_text(&data, &len_st)) {
4086 	    kbd_alarm();
4087 	    return;
4088 	}
4089 	len_ul = (ULONG) len_st;	/* Ugh. */
4090 	x_get_selection(cur_win->top_widget, NULL, &selection, &target,
4091 			(XtPointer) data, &len_ul, &format);
4092     } else {
4093 	Atom *targets = GetSelectionTargets();
4094 	SelectionList *list = (void *) XtNew(SelectionList);
4095 	Time ev_time = XtLastTimestampProcessed(dpy);
4096 
4097 	list->targets = targets + 1;
4098 	list->time = ev_time;
4099 	XtGetSelectionValue(cur_win->top_widget,
4100 			    selection,
4101 			    targets[0],
4102 			    x_get_selection,
4103 			    (XtPointer) (list),		/* client data */
4104 			    ev_time);
4105     }
4106 }
4107 
4108 static Boolean
x_get_selected_text(UCHAR ** datp,size_t * lenp)4109 x_get_selected_text(UCHAR ** datp, size_t *lenp)
4110 {
4111     UCHAR *data = 0;
4112     UCHAR *dp = 0;
4113     size_t length;
4114     KILL *kp;			/* pointer into kill register */
4115 
4116     /* FIXME: Can't select message line */
4117 
4118     if (!cur_win->have_selection)
4119 	return False;
4120 
4121     sel_yank(SEL_KREG);
4122     for (length = 0, kp = kbs[SEL_KREG].kbufh; kp; kp = kp->d_next)
4123 	length += KbSize(SEL_KREG, kp);
4124     if (length == 0
4125 	|| (dp = data = (UCHAR *) XtMalloc((Cardinal) (length
4126 						       * sizeof(UCHAR)))) == 0
4127 	|| (kp = kbs[SEL_KREG].kbufh) == 0)
4128 	return False;
4129 
4130     while (kp != NULL) {
4131 	size_t len = KbSize(SEL_KREG, kp);
4132 	(void) memcpy((char *) dp, (char *) kp->d_chunk, len);
4133 	kp = kp->d_next;
4134 	dp += len;
4135     }
4136 
4137     *lenp = length;
4138     *datp = data;
4139     return True;
4140 }
4141 
4142 static Boolean
x_get_clipboard_text(UCHAR ** datp,size_t * lenp)4143 x_get_clipboard_text(UCHAR ** datp, size_t *lenp)
4144 {
4145     UCHAR *data = 0;
4146     UCHAR *dp = 0;
4147     size_t length;
4148     KILL *kp;			/* pointer into kill register */
4149 
4150     for (length = 0, kp = kbs[CLIP_KREG].kbufh; kp; kp = kp->d_next)
4151 	length += KbSize(CLIP_KREG, kp);
4152     if (length == 0
4153 	|| (dp = data = (UCHAR *) XtMalloc((Cardinal) (length
4154 						       * sizeof(UCHAR)))) == 0
4155 	|| (kp = kbs[CLIP_KREG].kbufh) == 0)
4156 	return False;
4157 
4158     while (kp != NULL) {
4159 	size_t len = KbSize(CLIP_KREG, kp);
4160 	(void) memcpy((char *) dp, (char *) kp->d_chunk, len);
4161 	kp = kp->d_next;
4162 	dp += len;
4163     }
4164 
4165     *lenp = length;
4166     *datp = data;
4167     return True;
4168 }
4169 
4170 /* ARGSUSED */
4171 static Boolean
x_convert_selection(Widget w GCC_UNUSED,Atom * selection,Atom * target,Atom * type,XtPointer * value,ULONG * length,int * format)4172 x_convert_selection(Widget w GCC_UNUSED,
4173 		    Atom *selection,
4174 		    Atom *target,
4175 		    Atom *type,
4176 		    XtPointer *value,
4177 		    ULONG * length,
4178 		    int *format)
4179 {
4180     Boolean result = False;
4181 
4182     TRACE((T_CALLED
4183 	   "x_convert_selection(selection %s, target %s)\n",
4184 	   visibleAtoms(*selection),
4185 	   visibleAtoms(*target)));
4186 
4187     if (!cur_win->have_selection && IsPrimary(*selection))
4188 	returnBoolean(False);
4189 
4190     /*
4191      * The ICCCM requires us to handle the following targets:  TARGETS,
4192      * MULTIPLE, and TIMESTAMP.  MULTIPLE and TIMESTAMP are handled by the Xt
4193      * intrinsics.  Below, we handle TARGETS, STRING, and TEXT.
4194      *
4195      * The STRING and TEXT targets are what xvile uses to transfer selected
4196      * text to another client.
4197      *
4198      * TARGETS is simply a list of the targets we support (including the ones
4199      * handled by the Xt intrinsics).
4200      */
4201 
4202     if (*target == GetAtom(TARGETS)) {
4203 	Atom *tp;
4204 	Atom *sp;
4205 
4206 #define NTARGS 10
4207 
4208 	tp = (void *) XtMalloc((Cardinal) (NTARGS * sizeof(Atom)));
4209 	*(Atom **) value = tp;
4210 
4211 	if (tp != NULL) {
4212 	    *tp++ = GetAtom(TARGETS);
4213 	    *tp++ = GetAtom(MULTIPLE);
4214 	    *tp++ = GetAtom(TIMESTAMP);
4215 	    for (sp = GetSelectionTargets(); *sp != None; ++sp)
4216 		*tp++ = *sp;
4217 
4218 	    *type = XA_ATOM;
4219 	    *length = (ULONG) (tp - *(Atom **) value);
4220 	    *format = 32;	/* width of the data being transferred */
4221 	    result = True;
4222 	}
4223     } else if (*target == XA_STRING || *target == GetAtom(TEXT)) {
4224 	*type = XA_STRING;
4225 	*format = 8;
4226 	if (IsPrimary(*selection))
4227 	    result = x_get_selected_text((UCHAR **) value, (size_t *) length);
4228 	else			/* CLIPBOARD */
4229 	    result = x_get_clipboard_text((UCHAR **) value, (size_t *) length);
4230     }
4231 #if OPT_MULTIBYTE
4232     else if (*target == GetAtom(UTF8_STRING)) {
4233 	*type = *target;
4234 	*format = 8;
4235 	if (IsPrimary(*selection))
4236 	    result = x_get_selected_text((UCHAR **) value, (size_t *) length);
4237 	else			/* CLIPBOARD */
4238 	    result = x_get_clipboard_text((UCHAR **) value, (size_t *) length);
4239     } else if (*target == GetAtom(COMPOUND_TEXT)) {
4240 	*type = *target;
4241 	*format = 8;
4242 	if (IsPrimary(*selection))
4243 	    result = x_get_selected_text((UCHAR **) value, (size_t *) length);
4244 	else			/* CLIPBOARD */
4245 	    result = x_get_clipboard_text((UCHAR **) value, (size_t *) length);
4246     }
4247 #endif /* OPT_MULTIBYTE */
4248 
4249     returnBoolean(result);
4250 }
4251 
4252 /* ARGSUSED */
4253 static void
x_lose_selection(Widget w GCC_UNUSED,Atom * selection)4254 x_lose_selection(Widget w GCC_UNUSED,
4255 		 Atom *selection)
4256 {
4257     if (IsPrimary(*selection)) {
4258 	cur_win->have_selection = False;
4259 	cur_win->was_on_msgline = False;
4260 	sel_release();
4261 	(void) update(TRUE);
4262     } else {
4263 	/* Free up the data in the kill buffer (how do we do this?) */
4264     }
4265 }
4266 
4267 void
own_selection(void)4268 own_selection(void)
4269 {
4270     x_own_selection(XA_PRIMARY);
4271 }
4272 
4273 static void
x_own_selection(Atom selection)4274 x_own_selection(Atom selection)
4275 {
4276     /*
4277      * Note:  we've been told that the Hummingbird X Server (which runs on a
4278      * PC) updates the contents of the clipboard only if we remove the next
4279      * line, causing this program to assert the selection on each call.  We
4280      * don't do that, however, since it would violate the sense of the ICCCM,
4281      * which is minimizing network traffic.
4282      *
4283      * Kev's note on the above comment (which I assume was written by Tom):
4284      * I've added some new code for dealing with clipboards in now.  It
4285      * may well be that the clipboard will work properly now.  Of course,
4286      * you'll need to run the copy-to-clipboard command from vile.  If
4287      * you're on a Sun keyboard, you might want to bind this to the Copy
4288      * key (F16).  I may also think about doing a sort of timer mechanism
4289      * which asserts ownership of the clipboard if a certain amount of
4290      * time has gone by with no activity.
4291      */
4292     if (!cur_win->have_selection || !IsPrimary(selection)) {
4293 	TRACE(("x_own_selection\n"));
4294 
4295 	cur_win->have_selection =
4296 	    XtOwnSelection(cur_win->top_widget,
4297 			   selection,
4298 			   XtLastTimestampProcessed(dpy),
4299 			   x_convert_selection,
4300 			   x_lose_selection,
4301 			   (XtSelectionDoneProc) 0);
4302     }
4303 }
4304 
4305 static void
scroll_selection(XtPointer rowcol,XtIntervalId * idp)4306 scroll_selection(XtPointer rowcol,
4307 		 XtIntervalId * idp)
4308 {
4309     int row, col;
4310     if (*idp == cur_win->sel_scroll_id)
4311 	XtRemoveTimeOut(cur_win->sel_scroll_id);	/* shouldn't happen */
4312     cur_win->sel_scroll_id = (XtIntervalId) 0;
4313 
4314     row = (((long) rowcol) >> 16) & 0xffff;
4315     col = ((long) rowcol) & 0xffff;
4316 #define sign_extend16(n) \
4317     	if ((n) & 0x8000) \
4318 		n = n | (int)((unsigned)(~0) << 16)
4319     sign_extend16(row);
4320     sign_extend16(col);
4321     extend_selection(cur_win, row, col, TRUE);
4322 }
4323 
4324 static int
line_count_and_interval(long scroll_count,ULONG * ip)4325 line_count_and_interval(long scroll_count, ULONG * ip)
4326 {
4327     scroll_count = scroll_count / 4 - 2;
4328     if (scroll_count <= 0) {
4329 	*ip = (ULONG) ((1 - scroll_count) * cur_win->scroll_repeat_interval);
4330 	return 1;
4331     } else {
4332 	/*
4333 	 * FIXME: figure out a cleaner way to do this or something like it...
4334 	 */
4335 	if (scroll_count > 450)
4336 	    scroll_count *= 1024;
4337 	else if (scroll_count > 350)
4338 	    scroll_count *= 128;
4339 	else if (scroll_count > 275)
4340 	    scroll_count *= 64;
4341 	else if (scroll_count > 200)
4342 	    scroll_count *= 16;
4343 	else if (scroll_count > 150)
4344 	    scroll_count *= 8;
4345 	else if (scroll_count > 100)
4346 	    scroll_count *= 4;
4347 	else if (scroll_count > 75)
4348 	    scroll_count *= 3;
4349 	else if (scroll_count > 50)
4350 	    scroll_count *= 2;
4351 	*ip = (ULONG) cur_win->scroll_repeat_interval;
4352 	return (int) scroll_count;
4353     }
4354 }
4355 
4356 static void
extend_selection(TextWindow tw GCC_UNUSED,int nr,int nc,Bool wipe)4357 extend_selection(TextWindow tw GCC_UNUSED,
4358 		 int nr,
4359 		 int nc,
4360 		 Bool wipe)
4361 {
4362     static long scroll_count = 0;
4363     long rowcol = 0;
4364     ULONG interval = 0;
4365 
4366     if (cur_win->sel_scroll_id != (XtIntervalId) 0) {
4367 	if (nr < curwp->w_toprow || nr >= mode_row(curwp))
4368 	    return;		/* Only let timer extend selection */
4369 	XtRemoveTimeOut(cur_win->sel_scroll_id);
4370 	cur_win->sel_scroll_id = (XtIntervalId) 0;
4371     }
4372 
4373     if (nr < curwp->w_toprow) {
4374 	if (wipe) {
4375 	    mvupwind(TRUE, line_count_and_interval(scroll_count++, &interval));
4376 	    rowcol = (nr << 16) | (nc & 0xffff);
4377 	} else {
4378 	    scroll_count = 0;
4379 	}
4380 	nr = curwp->w_toprow;
4381     } else if (nr >= mode_row(curwp)) {
4382 	if (wipe) {
4383 	    mvdnwind(TRUE, line_count_and_interval(scroll_count++, &interval));
4384 	    rowcol = (nr << 16) | (nc & 0xffff);
4385 	} else {
4386 	    scroll_count = 0;
4387 	}
4388 	nr = mode_row(curwp) - 1;
4389     } else {
4390 	scroll_count = 0;
4391     }
4392     if (setcursor(nr, nc) && sel_extend(wipe, TRUE)) {
4393 	cur_win->did_select = True;
4394 	(void) update(TRUE);
4395 	if (scroll_count > 0) {
4396 	    x_flush();
4397 	    cur_win->sel_scroll_id =
4398 		XtAppAddTimeOut(cur_win->app_context,
4399 				interval,
4400 				scroll_selection,
4401 				(XtPointer) rowcol);
4402 	}
4403     } else {
4404 	kbd_alarm();
4405     }
4406 }
4407 
4408 static void
multi_click(TextWindow tw,int nr,int nc)4409 multi_click(TextWindow tw, int nr, int nc)
4410 {
4411     VIDEO_TEXT *p;
4412     int cclass;
4413     int sc = nc;
4414     int oc = nc;
4415     WINDOW *wp;
4416 
4417     tw->numclicks++;
4418 
4419     if ((wp = row2window(nr)) != 0 && nr == mode_row(wp)) {
4420 	set_curwp(wp);
4421 	sel_release();
4422 	(void) update(TRUE);
4423     } else {
4424 	switch (tw->numclicks) {
4425 	case 0:
4426 	case 1:		/* shouldn't happen */
4427 	    mlforce("BUG: 0 or 1 multiclick value.");
4428 	    return;
4429 	case 2:		/* word */
4430 #if OPT_HYPERTEXT
4431 	    if (setcursor(nr, nc) && exechypercmd(0, 0)) {
4432 		(void) update(TRUE);
4433 		return;
4434 	    }
4435 #endif
4436 	    /* find word start */
4437 	    p = &CELL_TEXT(nr, sc);
4438 	    cclass = charClass[CharOf(*p)];
4439 	    do {
4440 		--sc;
4441 		--p;
4442 	    } while (sc >= 0 && charClass[*p] == cclass);
4443 	    sc++;
4444 	    /* and end */
4445 	    p = &CELL_TEXT(nr, nc);
4446 	    cclass = charClass[CharOf(*p)];
4447 	    do {
4448 		++nc;
4449 		++p;
4450 	    } while (nc < (int) tw->cols && charClass[*p] == cclass);
4451 	    --nc;
4452 
4453 	    if (setcursor(nr, sc)) {
4454 		(void) sel_begin();
4455 		extend_selection(tw, nr, nc, FALSE);
4456 		(void) setcursor(nr, oc);
4457 		/* FIXME: Too many updates */
4458 		(void) update(TRUE);
4459 	    }
4460 	    return;
4461 	case 3:		/* line (doesn't include trailing newline) */
4462 	    if (setcursor(nr, sc)) {
4463 		MARK saveDOT;
4464 		saveDOT = DOT;
4465 		(void) gotobol(0, 0);
4466 		(void) sel_begin();
4467 		(void) gotoeol(FALSE, 0);
4468 		(void) sel_extend(FALSE, TRUE);
4469 		DOT = saveDOT;
4470 		cur_win->did_select = True;
4471 		(void) update(TRUE);
4472 	    }
4473 	    return;
4474 	case 4:		/* document (doesn't include trailing newline) */
4475 	    if (setcursor(nr, sc)) {
4476 		MARK saveDOT;
4477 		saveDOT = DOT;
4478 		(void) gotobob(0, 0);
4479 		(void) sel_begin();
4480 		(void) gotoeob(FALSE, 0);
4481 		(void) gotoeol(FALSE, 0);
4482 		(void) sel_extend(FALSE, TRUE);
4483 		DOT = saveDOT;
4484 		cur_win->did_select = True;
4485 		(void) update(TRUE);
4486 	    }
4487 	    return;
4488 	default:
4489 	    /*
4490 	     * This provides a mechanism for getting rid of the
4491 	     * selection.
4492 	     */
4493 	    sel_release();
4494 	    (void) update(TRUE);
4495 	    return;
4496 	}
4497     }
4498 }
4499 
4500 static void
start_selection(TextWindow tw,XButtonPressedEvent * ev,int nr,int nc)4501 start_selection(TextWindow tw, XButtonPressedEvent * ev, int nr, int nc)
4502 {
4503     tw->wipe_permitted = FALSE;
4504     if ((tw->lasttime != 0)
4505 	&& (absol(ev->time - tw->lasttime) < tw->click_timeout)) {
4506 	/* FIXME: This code used to ignore multiple clicks which
4507 	 *      spanned rows.  Do we still want this behavior?
4508 	 *        If so, we'll have to (re)implement it.
4509 	 */
4510 	multi_click(tw, nr, nc);
4511     } else {
4512 	WINDOW *wp;
4513 
4514 	beginDisplay();
4515 
4516 	tw->lasttime = ev->time;
4517 	tw->numclicks = 1;
4518 	tw->was_on_msgline = onMsgRow(tw);
4519 
4520 	if ((wp = row2window(nr)) != 0) {
4521 	    set_curwp(wp);
4522 	}
4523 	tw->prevDOT = DOT;
4524 
4525 	/*
4526 	 * If we're on the message line, do nothing.
4527 	 *
4528 	 * If we're on a mode line, make the window whose mode line we're
4529 	 * on the current window.
4530 	 *
4531 	 * Otherwise update the cursor position in whatever window we're
4532 	 * in and set things up so that the current position can be the
4533 	 * possible start of a selection.
4534 	 */
4535 	if (reading_msg_line) {
4536 	    /* EMPTY */ ;
4537 	} else if (wp != 0 && nr == mode_row(wp)) {
4538 	    (void) update(TRUE);
4539 	} else if (setcursor(nr, nc)) {
4540 	    if (!cur_win->persistent_selections) {
4541 		sel_yank(SEL_KREG);
4542 		sel_release();
4543 	    }
4544 	    (void) sel_begin();
4545 	    (void) update(TRUE);
4546 	    tw->wipe_permitted = TRUE;
4547 	    /* force the editor to notice the changed DOT, if it cares */
4548 	    kqadd(cur_win, KEY_Mouse);
4549 	}
4550 	endofDisplay();
4551     }
4552 }
4553 
4554 /* this doesn't need to do anything.  it's invoked when we do
4555 	shove KEY_Mouse back on the input stream, to force the
4556 	main editor code to notice that DOT has moved. */
4557 /*ARGSUSED*/
4558 int
mouse_motion(int f GCC_UNUSED,int n GCC_UNUSED)4559 mouse_motion(int f GCC_UNUSED, int n GCC_UNUSED)
4560 {
4561     return TRUE;
4562 }
4563 
4564 /*ARGSUSED*/
4565 int
copy_to_clipboard(int f GCC_UNUSED,int n GCC_UNUSED)4566 copy_to_clipboard(int f GCC_UNUSED, int n GCC_UNUSED)
4567 {
4568     if (!cur_win->have_selection) {
4569 	kbd_alarm();
4570 	return FALSE;
4571     }
4572 
4573     sel_yank(CLIP_KREG);
4574     x_own_selection(GetAtom(CLIPBOARD));
4575 
4576     return TRUE;
4577 }
4578 
4579 /*ARGSUSED*/
4580 int
paste_from_clipboard(int f GCC_UNUSED,int n GCC_UNUSED)4581 paste_from_clipboard(int f GCC_UNUSED, int n GCC_UNUSED)
4582 {
4583     x_paste_selection(GetAtom(CLIPBOARD));
4584     return TRUE;
4585 }
4586 
4587 /*ARGSUSED*/
4588 int
paste_from_primary(int f GCC_UNUSED,int n GCC_UNUSED)4589 paste_from_primary(int f GCC_UNUSED, int n GCC_UNUSED)
4590 {
4591     x_paste_selection(XA_PRIMARY);
4592     return TRUE;
4593 }
4594 
4595 static XMotionEvent *
compress_motion(XMotionEvent * ev)4596 compress_motion(XMotionEvent * ev)
4597 {
4598     XEvent nev;
4599 
4600     while (XPending(ev->display)) {
4601 	XPeekEvent(ev->display, &nev);
4602 	if (nev.type == MotionNotify &&
4603 	    nev.xmotion.window == ev->window &&
4604 	    nev.xmotion.subwindow == ev->subwindow) {
4605 	    XNextEvent(ev->display, (XEvent *) ev);
4606 	} else
4607 	    break;
4608     }
4609     return ev;
4610 }
4611 
4612 /*
4613  * handle non keyboard events associated with vile screen
4614  */
4615 /*ARGSUSED*/
4616 static void
x_process_event(Widget w GCC_UNUSED,XtPointer unused GCC_UNUSED,XEvent * ev,Boolean * continue_to_dispatch GCC_UNUSED)4617 x_process_event(Widget w GCC_UNUSED,
4618 		XtPointer unused GCC_UNUSED,
4619 		XEvent *ev,
4620 		Boolean *continue_to_dispatch GCC_UNUSED)
4621 {
4622     int sc, sr;
4623     UINT ec, er;
4624 
4625     int nr, nc;
4626     static int onr = -1, onc = -1;
4627 
4628     XMotionEvent *mev;
4629     XExposeEvent *gev;
4630     Bool do_sel;
4631     WINDOW *wp;
4632     Bool ignore = vile_is_busy;
4633 
4634     switch (ev->type) {
4635     case Expose:
4636 	gev = (XExposeEvent *) ev;
4637 	sc = gev->x / cur_win->char_width;
4638 	sr = gev->y / cur_win->char_height;
4639 	ec = (UINT) CEIL(gev->x + gev->width, cur_win->char_width);
4640 	er = (UINT) CEIL(gev->y + gev->height, cur_win->char_height);
4641 	x_touch(cur_win, sc, sr, ec, er);
4642 	cur_win->exposed = TRUE;
4643 	if (ev->xexpose.count == 0)
4644 	    x_flush();
4645 	break;
4646 
4647     case VisibilityNotify:
4648 	cur_win->visibility = ev->xvisibility.state;
4649 	XSetGraphicsExposures(dpy,
4650 			      GetColorGC(cur_win, tt_info),
4651 			      cur_win->visibility != VisibilityUnobscured);
4652 	break;
4653 
4654     case MotionNotify:
4655 	if (ignore)
4656 	    break;
4657 	do_sel = cur_win->wipe_permitted;
4658 	if (!(ev->xmotion.state & (Button1Mask | Button3Mask))) {
4659 	    if (!cur_win->focus_follows_mouse)
4660 		return;
4661 	    else
4662 		do_sel = FALSE;
4663 	}
4664 	mev = compress_motion((XMotionEvent *) ev);
4665 	nc = mev->x / cur_win->char_width;
4666 	nr = mev->y / cur_win->char_height;
4667 
4668 	if (nr < 0)
4669 	    nr = -1;		/* want to be out of bounds to force scrolling */
4670 	else if (nr > (int) cur_win->rows)
4671 	    nr = (int) cur_win->rows;
4672 
4673 	if (nc < 0)
4674 	    nc = 0;
4675 	else if (nc >= (int) cur_win->cols)
4676 	    nc = (int) (cur_win->cols - 1);
4677 
4678 	/* ignore any spurious motion during a multi-cick */
4679 	if (cur_win->numclicks > 1
4680 	    && cur_win->lasttime != 0
4681 	    && (absol(ev->xmotion.time - cur_win->lasttime) < cur_win->click_timeout))
4682 	    return;
4683 	if (do_sel) {
4684 	    if (ev->xbutton.state & ControlMask) {
4685 		(void) sel_setshape(rgn_RECTANGLE);
4686 	    }
4687 	    if (nr != onr || nc != onc)
4688 		extend_selection(cur_win, nr, nc, True);
4689 	    onr = nr;
4690 	    onc = nc;
4691 	} else {
4692 	    if (!reading_msg_line && (wp = row2window(nr)) && wp != curwp) {
4693 		(void) set_curwp(wp);
4694 		(void) update(TRUE);
4695 	    }
4696 	}
4697 	break;
4698     case ButtonPress:
4699 	if (ignore)
4700 	    break;
4701 	nc = ev->xbutton.x / cur_win->char_width;
4702 	nr = ev->xbutton.y / cur_win->char_height;
4703 	TRACE(("ButtonPress #%d (%d,%d)\n", ev->xbutton.button, nr, nc));
4704 	switch (ev->xbutton.button) {
4705 	case Button1:		/* move button and set selection point */
4706 	    start_selection(cur_win, (XButtonPressedEvent *) ev, nr, nc);
4707 	    onr = nr;
4708 	    onc = nc;
4709 	    break;
4710 	case Button2:		/* paste selection */
4711 	    /*
4712 	     * If shifted, paste at mouse.  Otherwise, paste at the last
4713 	     * position marked before beginning a selection.
4714 	     */
4715 	    if (ev->xbutton.state & ShiftMask) {
4716 		if (!setcursor(nr, nc)) {
4717 		    kbd_alarm();	/* don't know how to paste here */
4718 		    break;
4719 		}
4720 	    }
4721 	    x_paste_selection(XA_PRIMARY);
4722 	    break;
4723 	case Button3:		/* end/extend selection */
4724 	    if (((wp = row2window(nr)) != 0) && sel_buffer() == wp->w_bufp)
4725 		(void) set_curwp(wp);
4726 	    if (ev->xbutton.state & ControlMask)
4727 		(void) sel_setshape(rgn_RECTANGLE);
4728 	    cur_win->wipe_permitted = True;
4729 	    cur_win->prevDOT = DOT;
4730 	    extend_selection(cur_win, nr, nc, False);
4731 	    break;
4732 	case Button4:
4733 	    if (cur_win->wheel_scroll_amount < 0)
4734 		backpage(FALSE, 1);
4735 	    else
4736 		mvupwind(TRUE, cur_win->wheel_scroll_amount);
4737 	    (void) update(TRUE);
4738 	    break;
4739 	case Button5:
4740 	    if (cur_win->wheel_scroll_amount < 0)
4741 		forwpage(FALSE, 1);
4742 	    else
4743 		mvdnwind(TRUE, cur_win->wheel_scroll_amount);
4744 	    (void) update(TRUE);
4745 	    break;
4746 	}
4747 	break;
4748     case ButtonRelease:
4749 	if (ignore)
4750 	    break;
4751 	TRACE(("ButtonRelease #%d (%d,%d)%s\n",
4752 	       ev->xbutton.button,
4753 	       ev->xbutton.y / cur_win->char_height,
4754 	       ev->xbutton.x / cur_win->char_width,
4755 	       cur_win->did_select ? ": did_select" : ""));
4756 	switch (ev->xbutton.button) {
4757 	case Button1:
4758 	    if (cur_win->persistent_selections)
4759 		sel_yank(SEL_KREG);
4760 
4761 	    /* FALLTHRU */
4762 	case Button3:
4763 	    if (cur_win->sel_scroll_id != ((XtIntervalId) 0)) {
4764 		XtRemoveTimeOut(cur_win->sel_scroll_id);
4765 		cur_win->sel_scroll_id = (XtIntervalId) 0;
4766 	    }
4767 	    if (cur_win->did_select && !cur_win->selection_sets_DOT) {
4768 		restore_dot(cur_win->prevDOT);
4769 		(void) update(TRUE);
4770 	    }
4771 	    cur_win->did_select = False;
4772 	    cur_win->wipe_permitted = False;
4773 	    display_cursor((XtPointer) 0, (XtIntervalId *) 0);
4774 	    break;
4775 	}
4776 	break;
4777     }
4778 }
4779 
4780 /*ARGSUSED*/
4781 static void
x_configure_window(Widget w GCC_UNUSED,XtPointer unused GCC_UNUSED,XEvent * ev,Boolean * continue_to_dispatch GCC_UNUSED)4782 x_configure_window(Widget w GCC_UNUSED,
4783 		   XtPointer unused GCC_UNUSED,
4784 		   XEvent *ev,
4785 		   Boolean *continue_to_dispatch GCC_UNUSED)
4786 {
4787     int nr, nc;
4788     Dimension new_width, new_height;
4789 
4790     if (ev->type != ConfigureNotify)
4791 	return;
4792 
4793     if (ev->xconfigure.height == cur_win->top_height
4794 	&& ev->xconfigure.width == cur_win->top_width)
4795 	return;
4796 
4797     XtVaGetValues(cur_win->top_widget,
4798 		  XtNheight, &new_height,
4799 		  XtNwidth, &new_width,
4800 		  NULL);
4801     new_height = (Dimension) (((int) (new_height - cur_win->base_height)
4802 			       / cur_win->char_height)
4803 			      * cur_win->char_height);
4804     new_width = (Dimension) (((int) (new_width - cur_win->base_width)
4805 			      / cur_win->char_width)
4806 			     * cur_win->char_width);
4807 
4808     /* Check to make sure the dimensions are sane both here and below
4809        to avoid BadMatch errors */
4810     nr = ((int) new_height / cur_win->char_height);
4811     nc = ((int) new_width / cur_win->char_width);
4812 
4813     if (nr < MINROWS || nc < MINCOLS) {
4814 	gui_resize(nc, nr);
4815 	/* Calling XResizeWindow will cause another ConfigureNotify
4816 	 * event, so we should return early and let this event occur.
4817 	 */
4818 	return;
4819     }
4820 #if MOTIF_WIDGETS
4821     XtVaSetValues(cur_win->form_widget,
4822 		  Nval(XmNresizePolicy, XmRESIZE_NONE),
4823 		  NULL);
4824     {
4825 	WidgetList children;
4826 	Cardinal nchildren;
4827 	XtVaGetValues(cur_win->form_widget,
4828 		      XmNchildren, &children,
4829 		      XmNnumChildren, &nchildren,
4830 		      NULL);
4831 	XtUnmanageChildren(children, nchildren);
4832     }
4833 #else
4834 #if NO_WIDGETS || ATHENA_WIDGETS
4835     XtVaSetValues(cur_win->form_widget,
4836 		  Nval(XtNwidth, new_width + cur_win->pane_width + 2),
4837 		  Nval(XtNheight, new_height),
4838 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
4839 		  Nval(XtNx, (cur_win->scrollbar_on_left
4840 			      ? (cur_win->pane_width + 2)
4841 			      : 0)),
4842 #endif
4843 		  NULL);
4844 #endif /* NO_WIDGETS */
4845 #endif /* MOTIF_WIDGETS */
4846     XtVaSetValues(cur_win->screen,
4847 		  Nval(XtNheight, new_height),
4848 		  Nval(XtNwidth, new_width),
4849 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
4850 		  Nval(XtNx, (cur_win->scrollbar_on_left
4851 			      ? cur_win->pane_width + 2
4852 			      : 0)),
4853 #endif
4854 		  NULL);
4855     XtVaSetValues(cur_win->pane,
4856 #if !OPT_KEV_SCROLLBARS && !OPT_XAW_SCROLLBARS
4857 		  Nval(XtNwidth, cur_win->pane_width),
4858 #else /* OPT_KEV_SCROLLBARS */
4859 		  Nval(XtNx, (cur_win->scrollbar_on_left
4860 			      ? 0
4861 			      : new_width)),
4862 		  Nval(XtNwidth, cur_win->pane_width + 2),
4863 		  Nval(XtNheight, new_height - cur_win->char_height),
4864 #endif /* OPT_KEV_SCROLLBARS */
4865 		  NULL);
4866 #if MOTIF_WIDGETS
4867     {
4868 	WidgetList children;
4869 	Cardinal nchildren;
4870 	XtVaGetValues(cur_win->form_widget,
4871 		      XmNchildren, &children,
4872 		      XmNnumChildren, &nchildren,
4873 		      NULL);
4874 	XtManageChildren(children, nchildren);
4875     }
4876     XtVaSetValues(cur_win->form_widget,
4877 		  Nval(XmNresizePolicy, XmRESIZE_ANY),
4878 		  NULL);
4879 #endif /* MOTIF_WIDGETS */
4880 
4881     XtVaGetValues(cur_win->top_widget,
4882 		  XtNheight, &cur_win->top_height,
4883 		  XtNwidth, &cur_win->top_width,
4884 		  NULL);
4885     XtVaGetValues(cur_win->screen,
4886 		  XtNheight, &new_height,
4887 		  XtNwidth, &new_width,
4888 		  NULL);
4889 
4890     nr = (int) (new_height / cur_win->char_height);
4891     nc = (int) (new_width / cur_win->char_width);
4892 
4893     if (nr < MINROWS || nc < MINCOLS) {
4894 	gui_resize(nc, nr);
4895 	/* Calling XResizeWindow will cause another ConfigureNotify
4896 	 * event, so we should return early and let this event occur.
4897 	 */
4898 	return;
4899     }
4900 
4901     if (nc != (int) cur_win->cols
4902 	|| nr != (int) cur_win->rows) {
4903 	newscreensize(nr, nc);
4904 	cur_win->rows = (UINT) nr;
4905 	cur_win->cols = (UINT) nc;
4906 	if (check_scrollbar_allocs() == TRUE)	/* no allocation failure */
4907 	    update_scrollbar_sizes();
4908     }
4909 #if MOTIF_WIDGETS
4910     lookfor_sb_resize = FALSE;
4911 #endif
4912 }
4913 
4914 void
gui_resize(int cols,int rows)4915 gui_resize(int cols, int rows)
4916 {
4917     if (cols < MINCOLS)
4918 	cols = MINCOLS;
4919     if (rows < MINROWS)
4920 	rows = MINROWS;
4921 
4922     XResizeWindow(dpy, XtWindow(cur_win->top_widget),
4923 		  (UINT) (cols * cur_win->char_width + cur_win->base_width),
4924 		  (UINT) (rows * cur_win->char_height + cur_win->base_height));
4925     /* This should cause a ConfigureNotify event */
4926 }
4927 
4928 static int
check_scrollbar_allocs(void)4929 check_scrollbar_allocs(void)
4930 {
4931     int newmax = (int) (cur_win->rows / 2);
4932     int oldmax = cur_win->maxscrollbars;
4933 
4934     if (newmax > oldmax) {
4935 
4936 	GROW(cur_win->scrollbars, Widget, oldmax, newmax);
4937 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
4938 	GROW(cur_win->scrollinfo, ScrollInfo, oldmax, newmax);
4939 	GROW(cur_win->grips, Widget, oldmax, newmax);
4940 #endif
4941 
4942 	cur_win->maxscrollbars = newmax;
4943     }
4944     return TRUE;
4945 }
4946 
4947 static void
configure_bar(Widget w,XEvent * event,String * params,Cardinal * num_params)4948 configure_bar(Widget w,
4949 	      XEvent *event,
4950 	      String *params,
4951 	      Cardinal *num_params)
4952 {
4953     WINDOW *wp;
4954     int i;
4955 
4956     if (*num_params != 1
4957 	|| (event->type != ButtonPress && event->type != ButtonRelease))
4958 	return;
4959 
4960     i = 0;
4961     for_each_visible_window(wp) {
4962 	if (cur_win->scrollbars[i] == w) {
4963 	    if (strcmp(params[0], "Only") == 0) {
4964 		set_curwp(wp);
4965 		onlywind(TRUE, 0);
4966 	    } else if (strcmp(params[0], "Kill") == 0) {
4967 		set_curwp(wp);
4968 		delwind(TRUE, 0);
4969 	    } else if (strcmp(params[0], "Split") == 0) {
4970 		if (wp->w_ntrows < 3) {
4971 		    kbd_alarm();
4972 		    break;
4973 		} else {
4974 		    int newsize;
4975 		    set_curwp(wp);
4976 		    newsize = CEIL(event->xbutton.y, cur_win->char_height) - 1;
4977 		    if (newsize > wp->w_ntrows - 2)
4978 			newsize = wp->w_ntrows - 2;
4979 		    else if (newsize < 1)
4980 			newsize = 1;
4981 		    splitwind(TRUE, 1);
4982 		    resize(TRUE, newsize);
4983 		}
4984 	    }
4985 	    (void) update(TRUE);
4986 	    break;
4987 	}
4988 	i++;
4989     }
4990 }
4991 
4992 #if MOTIF_WIDGETS
4993 static void
pane_button(Widget w GCC_UNUSED,XtPointer unused GCC_UNUSED,XEvent * ev GCC_UNUSED,Boolean * continue_to_dispatch GCC_UNUSED)4994 pane_button(Widget w GCC_UNUSED,
4995 	    XtPointer unused GCC_UNUSED,
4996 	    XEvent *ev GCC_UNUSED,
4997 	    Boolean *continue_to_dispatch GCC_UNUSED)
4998 {
4999     lookfor_sb_resize = TRUE;
5000 }
5001 #endif /* MOTIF_WIDGETS */
5002 
5003 /*ARGSUSED*/
5004 static void
x_change_focus(Widget w GCC_UNUSED,XtPointer unused GCC_UNUSED,XEvent * ev,Boolean * continue_to_dispatch GCC_UNUSED)5005 x_change_focus(Widget w GCC_UNUSED,
5006 	       XtPointer unused GCC_UNUSED,
5007 	       XEvent *ev,
5008 	       Boolean *continue_to_dispatch GCC_UNUSED)
5009 {
5010     static int got_focus_event = FALSE;
5011 
5012     TRACE(("x11:x_change_focus(type=%d)\n", ev->type));
5013     switch (ev->type) {
5014     case EnterNotify:
5015 	TRACE(("... EnterNotify\n"));
5016 	if (!ev->xcrossing.focus || got_focus_event)
5017 	    return;
5018 	goto focus_in;
5019     case FocusIn:
5020 	TRACE(("... FocusIn\n"));
5021 	got_focus_event = TRUE;
5022       focus_in:
5023 	cur_win->show_cursor = True;
5024 #if MOTIF_WIDGETS
5025 	XmProcessTraversal(cur_win->screen, XmTRAVERSE_CURRENT);
5026 #else /* NO_WIDGETS */
5027 	XtSetKeyboardFocus(w, cur_win->screen);
5028 #endif
5029 	x_flush();
5030 	break;
5031     case LeaveNotify:
5032 	TRACE(("... LeaveNotify\n"));
5033 	if (!ev->xcrossing.focus
5034 	    || got_focus_event
5035 	    || ev->xcrossing.detail == NotifyInferior)
5036 	    return;
5037 	goto focus_out;
5038     case FocusOut:
5039 	TRACE(("... FocusOut\n"));
5040 	got_focus_event = TRUE;
5041       focus_out:
5042 	cur_win->show_cursor = False;
5043 	x_flush();
5044 	break;
5045     }
5046 }
5047 
5048 /*ARGSUSED*/
5049 static void
x_wm_delwin(Widget w GCC_UNUSED,XtPointer unused GCC_UNUSED,XEvent * ev,Boolean * continue_to_dispatch GCC_UNUSED)5050 x_wm_delwin(Widget w GCC_UNUSED,
5051 	    XtPointer unused GCC_UNUSED,
5052 	    XEvent *ev,
5053 	    Boolean *continue_to_dispatch GCC_UNUSED)
5054 {
5055     if (ev->type == ClientMessage
5056 	&& ev->xclient.message_type == GetAtom(WM_PROTOCOLS)
5057 	&& (Atom) ev->xclient.data.l[0] == GetAtom(WM_DELETE_WINDOW)) {
5058 	quit(FALSE, 0);		/* quit might not return */
5059 	(void) update(TRUE);
5060     }
5061 }
5062 
5063 /*
5064  * Return true if we want to disable reports of the cursor position because the
5065  * cursor really should be on the message-line.
5066  */
5067 #if VILE_NEVER
5068 int
x_on_msgline(void)5069 x_on_msgline(void)
5070 {
5071     return reading_msg_line || cur_win->was_on_msgline;
5072 }
5073 #endif
5074 
5075 /*
5076  * Because we poll our input-characters in 'x_getc()', it is possible to have
5077  * exposure-events pending while doing lengthy processes (e.g., reading from a
5078  * pipe).  This procedure is invoked from a timer-handler and is designed to
5079  * handle the exposure-events, and to get keypress-events (i.e., for stopping a
5080  * lengthy process).
5081  */
5082 void
x_move_events(void)5083 x_move_events(void)
5084 {
5085     XEvent ev;
5086 
5087     while (x_has_events()
5088 	   && !kqfull(cur_win)) {
5089 
5090 	/* Get and dispatch next event */
5091 	XtAppNextEvent(cur_win->app_context, &ev);
5092 
5093 	/*
5094 	 * Ignore or save certain events which could get us into trouble with
5095 	 * reentrancy.
5096 	 */
5097 	switch (ev.type) {
5098 	case ButtonPress:
5099 	case ButtonRelease:
5100 	case MotionNotify:
5101 	    /* Ignore the event */
5102 	    continue;
5103 
5104 	case ClientMessage:
5105 	case SelectionClear:
5106 	case SelectionNotify:
5107 	case SelectionRequest:
5108 	case ConfigureNotify:
5109 	case ConfigureRequest:
5110 	case PropertyNotify:
5111 	case ReparentNotify:
5112 	case ResizeRequest:
5113 	    /* Queue for later processing.  */
5114 	    evqadd(&ev);
5115 	    continue;
5116 
5117 	default:
5118 	    /* do nothing here...we'll dispatch the event below */
5119 	    break;
5120 	}
5121 
5122 	XtDispatchEvent(&ev);
5123 
5124 	/*
5125 	 * If the event was a keypress, check it to see if it was an
5126 	 * interrupt character.  We check here to make sure that the
5127 	 * queue was non-empty, because not all keypresses put
5128 	 * characters into the queue.  We assume that intrc will not
5129 	 * appear in any multi-character sequence generated by a key
5130 	 * press, or that if it does, it will be the last character in
5131 	 * the sequence.  If this is a bad assumption, we will have to
5132 	 * keep track of what the state of the queue was prior to the
5133 	 * keypress and scan the characters added to the queue as a
5134 	 * result of the keypress.
5135 	 */
5136 
5137 	if (!kqempty(cur_win) && ev.type == KeyPress) {
5138 	    int c = kqpop(cur_win);
5139 	    if (c == intrc) {
5140 		kqadd(cur_win, esc_c);
5141 #if SYS_VMS
5142 		kbd_alarm();	/* signals? */
5143 #else
5144 		(void) signal_pg(SIGINT);
5145 #endif
5146 	    } else
5147 		kqadd(cur_win, c);
5148 	}
5149     }
5150 }
5151 
5152 #if OPT_WORKING
5153 void
x_working(void)5154 x_working(void)
5155 {
5156     cur_win->want_to_work = TRUE;
5157 }
5158 
5159 static int
x_has_events(void)5160 x_has_events(void)
5161 {
5162     if (cur_win->want_to_work == TRUE) {
5163 	x_set_watch_cursor(TRUE);
5164 	cur_win->want_to_work = FALSE;
5165     }
5166     return (int) (XtAppPending(cur_win->app_context) & XtIMXEvent);
5167 }
5168 
5169 static void
x_set_watch_cursor(int onflag)5170 x_set_watch_cursor(int onflag)
5171 {
5172     static int watch_is_on = FALSE;
5173 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
5174     int i;
5175 #endif
5176 
5177     if (onflag == watch_is_on)
5178 	return;
5179 
5180     watch_is_on = onflag;
5181 
5182     if (onflag) {
5183 	set_pointer(XtWindow(cur_win->screen), cur_win->watch_pointer);
5184 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
5185 	for (i = 0; i < cur_win->nscrollbars; i++) {
5186 	    set_pointer(XtWindow(cur_win->scrollbars[i]), cur_win->watch_pointer);
5187 	    if (i < cur_win->nscrollbars - 1)
5188 		set_pointer(XtWindow(cur_win->grips[i]), cur_win->watch_pointer);
5189 	}
5190 #endif /* OPT_KEV_SCROLLBARS */
5191     } else {
5192 	set_pointer(XtWindow(cur_win->screen), cur_win->normal_pointer);
5193 #if OPT_KEV_SCROLLBARS || OPT_XAW_SCROLLBARS
5194 	for (i = 0; i < cur_win->nscrollbars; i++) {
5195 	    set_pointer(XtWindow(cur_win->scrollbars[i]),
5196 			curs_sb_v_double_arrow);
5197 	    if (i < cur_win->nscrollbars - 1)
5198 		set_pointer(XtWindow(cur_win->grips[i]),
5199 			    curs_double_arrow);
5200 	}
5201 #endif /* OPT_KEV_SCROLLBARS */
5202     }
5203 }
5204 #endif /* OPT_WORKING */
5205 
5206 static int
evqempty(void)5207 evqempty(void)
5208 {
5209     return evqhead == NULL;
5210 }
5211 
5212 static void
evqadd(const XEvent * evp)5213 evqadd(const XEvent *evp)
5214 {
5215     struct eventqueue *newentry;
5216     newentry = typealloc(struct eventqueue);
5217     if (newentry == NULL)
5218 	return;			/* FIXME: Need method for indicating error */
5219     newentry->next = NULL;
5220     newentry->event = *evp;
5221     if (evqhead == NULL)
5222 	evqhead = evqtail = newentry;
5223     else {
5224 	evqtail->next = newentry;
5225 	evqtail = newentry;
5226     }
5227 }
5228 
5229 static void
evqdel(XEvent * evp)5230 evqdel(XEvent *evp)
5231 {
5232     struct eventqueue *delentry = evqhead;
5233     if (delentry == NULL)
5234 	return;			/* should not happen */
5235     *evp = delentry->event;
5236     evqhead = delentry->next;
5237     if (evqhead == NULL)
5238 	evqtail = NULL;
5239     free((char *) delentry);
5240 }
5241 
5242 static void
kqinit(TextWindow tw)5243 kqinit(TextWindow tw)
5244 {
5245     tw->kqhead = 0;
5246     tw->kqtail = 0;
5247 }
5248 
5249 static int
kqempty(TextWindow tw)5250 kqempty(TextWindow tw)
5251 {
5252     return tw->kqhead == tw->kqtail;
5253 }
5254 
5255 static int
kqfull(TextWindow tw)5256 kqfull(TextWindow tw)
5257 {
5258     return tw->kqhead == (tw->kqtail + 1) % KQSIZE;
5259 }
5260 
5261 static int
kqdel(TextWindow tw)5262 kqdel(TextWindow tw)
5263 {
5264     int c;
5265     c = tw->kq[tw->kqhead];
5266     tw->kqhead = (tw->kqhead + 1) % KQSIZE;
5267     return c;
5268 }
5269 
5270 static void
kqadd(TextWindow tw,int c)5271 kqadd(TextWindow tw, int c)
5272 {
5273     tw->kq[tw->kqtail] = c;
5274     tw->kqtail = (tw->kqtail + 1) % KQSIZE;
5275 }
5276 
5277 static int
kqpop(TextWindow tw)5278 kqpop(TextWindow tw)
5279 {
5280     if (--(tw->kqtail) < 0)
5281 	tw->kqtail = KQSIZE - 1;
5282     return (tw->kq[tw->kqtail]);
5283 }
5284 
5285 /*ARGSUSED*/
5286 static void
display_cursor(XtPointer client_data GCC_UNUSED,XtIntervalId * idp)5287 display_cursor(XtPointer client_data GCC_UNUSED, XtIntervalId * idp)
5288 {
5289     static Bool am_blinking = FALSE;
5290     int the_col = (ttcol >= term.cols) ? term.cols - 1 : ttcol;
5291 
5292     /*
5293      * Return immediately if we are either in the process of making a
5294      * selection (by wiping with the mouse) or if the cursor is already
5295      * displayed and display_cursor() is being called explicitly from the
5296      * event loop in x_getc.
5297      */
5298     if (cur_win->wipe_permitted) {
5299 	am_blinking = FALSE;
5300 	if (cur_win->blink_id != (XtIntervalId) 0) {
5301 	    XtRemoveTimeOut(cur_win->blink_id);
5302 	    cur_win->blink_id = (XtIntervalId) 0;
5303 	}
5304 	return;
5305     }
5306 
5307     if (IS_DIRTY(ttrow, the_col) && idp == (XtIntervalId *) 0) {
5308 	return;
5309     }
5310 
5311     if (cur_win->show_cursor) {
5312 	if (cur_win->blink_interval > 0
5313 	    || (cur_win->blink_interval < 0 && IS_REVERSED(ttrow, the_col))) {
5314 	    if (idp != (XtIntervalId *) 0 || !am_blinking) {
5315 		/* Set timer to get blinking */
5316 		cur_win->blink_id =
5317 		    XtAppAddTimeOut(cur_win->app_context,
5318 				    (ULONG) max(cur_win->blink_interval,
5319 						-cur_win->blink_interval),
5320 				    display_cursor,
5321 				    (XtPointer) 0);
5322 		cur_win->blink_status ^= BLINK_TOGGLE;
5323 		am_blinking = TRUE;
5324 	    } else {
5325 		cur_win->blink_status &= ~BLINK_TOGGLE;
5326 	    }
5327 	} else {
5328 	    am_blinking = FALSE;
5329 	    cur_win->blink_status &= ~BLINK_TOGGLE;
5330 	    if (cur_win->blink_id != (XtIntervalId) 0) {
5331 		XtRemoveTimeOut(cur_win->blink_id);
5332 		cur_win->blink_id = (XtIntervalId) 0;
5333 	    }
5334 	}
5335 
5336 	MARK_CELL_DIRTY(ttrow, the_col);
5337 	MARK_LINE_DIRTY(ttrow);
5338 	xvileDraw(dpy, cur_win, &CELL_TEXT(ttrow, the_col), 1,
5339 		  (UINT) (VATTRIB(CELL_ATTR(ttrow, the_col))
5340 			  ^ ((cur_win->blink_status & BLINK_TOGGLE)
5341 			     ? 0 : VACURS)),
5342 		  ttrow, the_col);
5343     } else {
5344 	/* This code will get called when the window no longer has the focus. */
5345 	if (cur_win->blink_id != (XtIntervalId) 0) {
5346 	    XtRemoveTimeOut(cur_win->blink_id);
5347 	    cur_win->blink_id = (XtIntervalId) 0;
5348 	}
5349 	am_blinking = FALSE;
5350 	MARK_CELL_DIRTY(ttrow, the_col);
5351 	MARK_LINE_DIRTY(ttrow);
5352 	xvileDraw(dpy, cur_win, &CELL_TEXT(ttrow, the_col), 1,
5353 		  (UINT) VATTRIB(CELL_ATTR(ttrow, the_col)), ttrow, the_col);
5354 	XDrawRectangle(dpy, cur_win->win,
5355 		       (IS_REVERSED(ttrow, the_col)
5356 			? GetColorGC(cur_win, cc_info)
5357 			: GetColorGC(cur_win, rc_info)),
5358 		       x_pos(cur_win, ttcol), y_pos(cur_win, ttrow),
5359 		       (UINT) (cur_win->char_width - 1),
5360 		       (UINT) (cur_win->char_height - 1));
5361     }
5362 }
5363 
5364 /*
5365  * main event loop.  this means we'll be stuck if an event that needs
5366  * instant processing comes in while its off doing other work, but
5367  * there's no (easy) way around that.
5368  */
5369 static int
x_getc(void)5370 x_getc(void)
5371 {
5372     int c;
5373 
5374     while (!evqempty()) {
5375 	XEvent ev;
5376 	evqdel(&ev);
5377 	XtDispatchEvent(&ev);
5378     }
5379 #if OPT_WORKING
5380     x_set_watch_cursor(FALSE);
5381 #endif
5382     x_start_autocolor_timer();
5383     for_ever {
5384 
5385 	if (tb_more(PasteBuf)) {	/* handle any queued pasted text */
5386 #if OPT_MULTIBYTE
5387 	    UINT result;
5388 	    int check;
5389 	    int limit = (int) tb_length(PasteBuf);
5390 	    int offset = (int) PasteBuf->tb_last;
5391 	    char *data = tb_values(PasteBuf) + offset;
5392 
5393 	    check = vl_conv_to_utf32(&result, data, (B_COUNT) (limit - offset));
5394 	    if (check > 0) {
5395 		c = (int) result;
5396 		PasteBuf->tb_last += (size_t) check;
5397 	    } else
5398 #endif
5399 		c = tb_next(PasteBuf);
5400 	    c = (c | (int) NOREMAP);	/* pasted chars are not subject to mapping */
5401 	    cur_win->pasting = True;
5402 	    break;
5403 	} else if (cur_win->pasting) {
5404 	    /*
5405 	     * Set the default position for new pasting to just past the newly
5406 	     * inserted text.
5407 	     *
5408 	     * Except - when the insert ended with a newline and is at the
5409 	     * beginning of a line.  That makes whole-line select/paste
5410 	     * behave "normally".
5411 	     */
5412 	    if (DOT.o < llength(DOT.l)
5413 		&& !insertmode
5414 		&& !(DOT.o == 0
5415 		     && cur_win->last_getc == (NOREMAP | '\n'))) {
5416 		DOT.o += BytesAt(DOT.l, DOT.o);
5417 		/* Advance DOT so that consecutive pastes come out right */
5418 	    }
5419 	    cur_win->pasting = False;
5420 	    update(TRUE);	/* make sure ttrow & ttcol are valid */
5421 	}
5422 
5423 	if (!kqempty(cur_win)) {
5424 	    c = kqdel(cur_win);
5425 	    break;
5426 	}
5427 
5428 	/*
5429 	 * Get and dispatch as many X events as possible.  This permits
5430 	 * the editor to catch up if it gets behind in processing keyboard
5431 	 * events since the keyboard queue will likely have something in it.
5432 	 * update() will check for typeahead and will defer its processing
5433 	 * until there is nothing more in the keyboard queue.
5434 	 */
5435 
5436 	do {
5437 	    XEvent ev;
5438 	    XtAppNextEvent(cur_win->app_context, &ev);
5439 	    XtDispatchEvent(&ev);
5440 	} while (x_has_events()
5441 		 && !kqfull(cur_win));
5442     }
5443 
5444     x_stop_autocolor_timer();
5445 
5446     if (c != ((int) NOREMAP | esc_c))
5447 	cur_win->last_getc = c;
5448     return c;
5449 }
5450 
5451 /*
5452  * Another event loop used for determining type-ahead.
5453  *
5454  * milli - milliseconds to wait for type-ahead
5455  */
5456 int
x_milli_sleep(int milli)5457 x_milli_sleep(int milli)
5458 {
5459     int status;
5460     XtIntervalId timeoutid = 0;
5461     int timedout;
5462     int olddkr;
5463 
5464     if (!cur_win->exposed)
5465 	return FALSE;
5466 
5467     olddkr = im_waiting(TRUE);
5468 
5469     status = !kqempty(cur_win) || tb_more(PasteBuf);
5470 
5471     if (!status) {
5472 
5473 	if (milli) {
5474 	    timedout = 0;
5475 	    timeoutid = XtAppAddTimeOut(cur_win->app_context,
5476 					(ULONG) milli,
5477 					x_typahead_timeout,
5478 					(XtPointer) &timedout);
5479 	} else
5480 	    timedout = 1;
5481 
5482 	while (kqempty(cur_win) && !evqempty()) {
5483 	    XEvent ev;
5484 	    evqdel(&ev);
5485 	    XtDispatchEvent(&ev);
5486 	}
5487 #if OPT_WORKING
5488 	x_set_watch_cursor(FALSE);
5489 #endif
5490 
5491 	/*
5492 	 * Process pending events until we get some keyboard input.
5493 	 * Note that we do not block here.
5494 	 */
5495 	while (kqempty(cur_win) &&
5496 	       x_has_events()) {
5497 	    XEvent ev;
5498 	    XtAppNextEvent(cur_win->app_context, &ev);
5499 	    XtDispatchEvent(&ev);
5500 	}
5501 
5502 	/* Now wait for timer and process events as necessary. */
5503 	while (!timedout && kqempty(cur_win)) {
5504 	    XtAppProcessEvent(cur_win->app_context, (XtInputMask) XtIMAll);
5505 	}
5506 
5507 	if (!timedout)
5508 	    XtRemoveTimeOut(timeoutid);
5509 
5510 	status = !kqempty(cur_win);
5511     }
5512 
5513     (void) im_waiting(olddkr);
5514 
5515     return status;
5516 }
5517 
5518 static int
x_typeahead(void)5519 x_typeahead(void)
5520 {
5521     return x_milli_sleep(0);
5522 }
5523 
5524 /*ARGSUSED*/
5525 static void
x_typahead_timeout(XtPointer flagp,XtIntervalId * id GCC_UNUSED)5526 x_typahead_timeout(XtPointer flagp, XtIntervalId * id GCC_UNUSED)
5527 {
5528     *(int *) flagp = 1;
5529 }
5530 
5531 /*ARGSUSED*/
5532 static void
x_key_press(Widget w GCC_UNUSED,XtPointer unused GCC_UNUSED,XEvent * ev,Boolean * continue_to_dispatch GCC_UNUSED)5533 x_key_press(Widget w GCC_UNUSED,
5534 	    XtPointer unused GCC_UNUSED,
5535 	    XEvent *ev,
5536 	    Boolean *continue_to_dispatch GCC_UNUSED)
5537 {
5538     char buffer[128];
5539     KeySym keysym;
5540     int num;
5541 
5542 #if OPT_INPUT_METHOD
5543     UINT uch;
5544 #endif
5545 
5546     int i;
5547     size_t n;
5548     /* *INDENT-OFF* */
5549     static const struct {
5550 	KeySym key;
5551 	int code;
5552     } escapes[] = {
5553 	/* Arrow keys */
5554 	{ XK_Up,	KEY_Up },
5555 	{ XK_Down,	KEY_Down },
5556 	{ XK_Right,	KEY_Right },
5557 	{ XK_Left,	KEY_Left },
5558 	/* page scroll */
5559 	{ XK_Next,	KEY_Next },
5560 	{ XK_Prior,	KEY_Prior },
5561 	{ XK_Home,	KEY_Home },
5562 	{ XK_End,	KEY_End },
5563 	/* editing */
5564 	{ XK_Insert,	KEY_Insert },
5565 	{ XK_Delete,	KEY_Delete },
5566 	{ XK_Find,	KEY_Find },
5567 	{ XK_Select,	KEY_Select },
5568 	/* command keys */
5569 	{ XK_Menu,	KEY_Menu },
5570 	{ XK_Help,	KEY_Help },
5571 	/* function keys */
5572 	{ XK_F1,	KEY_F1 },
5573 	{ XK_F2,	KEY_F2 },
5574 	{ XK_F3,	KEY_F3 },
5575 	{ XK_F4,	KEY_F4 },
5576 	{ XK_F5,	KEY_F5 },
5577 	{ XK_F6,	KEY_F6 },
5578 	{ XK_F7,	KEY_F7 },
5579 	{ XK_F8,	KEY_F8 },
5580 	{ XK_F9,	KEY_F9 },
5581 	{ XK_F10,	KEY_F10 },
5582 	{ XK_F11,	KEY_F11 },
5583 	{ XK_F12,	KEY_F12 },
5584 	{ XK_F13,	KEY_F13 },
5585 	{ XK_F14,	KEY_F14 },
5586 	{ XK_F15,	KEY_F15 },
5587 	{ XK_F16,	KEY_F16 },
5588 	{ XK_F17,	KEY_F17 },
5589 	{ XK_F18,	KEY_F18 },
5590 	{ XK_F19,	KEY_F19 },
5591 	{ XK_F20,	KEY_F20 },
5592 #if defined(XK_F21) && defined(KEY_F21)
5593 	{ XK_F21,	KEY_F21 },
5594 	{ XK_F22,	KEY_F22 },
5595 	{ XK_F23,	KEY_F23 },
5596 	{ XK_F24,	KEY_F24 },
5597 	{ XK_F25,	KEY_F25 },
5598 	{ XK_F26,	KEY_F26 },
5599 	{ XK_F27,	KEY_F27 },
5600 	{ XK_F28,	KEY_F28 },
5601 	{ XK_F29,	KEY_F29 },
5602 	{ XK_F30,	KEY_F30 },
5603 	{ XK_F31,	KEY_F31 },
5604 	{ XK_F32,	KEY_F32 },
5605 	{ XK_F33,	KEY_F33 },
5606 	{ XK_F34,	KEY_F34 },
5607 	{ XK_F35,	KEY_F35 },
5608 #endif
5609 	/* keypad function keys */
5610 	{ XK_KP_F1,	KEY_KP_F1 },
5611 	{ XK_KP_F2,	KEY_KP_F2 },
5612 	{ XK_KP_F3,	KEY_KP_F3 },
5613 	{ XK_KP_F4,	KEY_KP_F4 },
5614 #if defined(XK_KP_Up)
5615 	{ XK_KP_Up,	KEY_Up },
5616 	{ XK_KP_Down,	KEY_Down },
5617 	{ XK_KP_Right,	KEY_Right },
5618 	{ XK_KP_Left,	KEY_Left },
5619 	{ XK_KP_Next,	KEY_Next },
5620 	{ XK_KP_Prior,	KEY_Prior },
5621 	{ XK_KP_Home,	KEY_Home },
5622 	{ XK_KP_End,	KEY_End },
5623 	{ XK_KP_Insert, KEY_Insert },
5624 	{ XK_KP_Delete, KEY_Delete },
5625 #endif
5626 #ifdef  XK_ISO_Left_Tab
5627 	{ XK_ISO_Left_Tab, KEY_Tab },	/* with shift, becomes back-tab */
5628 #endif
5629     };
5630     /* *INDENT-ON* */
5631 
5632     if (ev->type != KeyPress)
5633 	return;
5634 
5635     x_start_autocolor_timer();
5636 
5637 #if OPT_INPUT_METHOD
5638     if (!XFilterEvent(ev, *(&ev->xkey.window))) {
5639 	if (cur_win->imInputContext != NULL) {
5640 	    Status status_return;
5641 #ifdef HAVE_XUTF8LOOKUPSTRING
5642 	    if (b_is_utfXX(curbp)) {
5643 		num = Xutf8LookupString(cur_win->imInputContext,
5644 					(XKeyPressedEvent *) ev, buffer,
5645 					(int) sizeof(buffer), &keysym,
5646 					&status_return);
5647 	    } else
5648 #endif
5649 	    {
5650 		num = XmbLookupString(cur_win->imInputContext,
5651 				      (XKeyPressedEvent *) ev, buffer,
5652 				      (int) sizeof(buffer), &keysym,
5653 				      &status_return);
5654 	    }
5655 	} else {
5656 	    num = XLookupString((XKeyPressedEvent *) ev, buffer,
5657 				(int) sizeof(buffer), &keysym,
5658 				(XComposeStatus *) 0);
5659 	}
5660     } else {
5661 	return;
5662     }
5663 #else
5664     num = XLookupString((XKeyPressedEvent *) ev, buffer, sizeof(buffer),
5665 			&keysym, (XComposeStatus *) 0);
5666 #endif
5667 
5668     TRACE((T_CALLED "x_key_press(0x%4X) = %.*s (%s%s%s%s%s%s%s%s)\n",
5669 	   (int) keysym,
5670 	   ((num > 0 && keysym < 256) ? num : 0),
5671 	   buffer,
5672 	   (ev->xkey.state & ShiftMask) ? "Shift" : "",
5673 	   (ev->xkey.state & LockMask) ? "Lock" : "",
5674 	   (ev->xkey.state & ControlMask) ? "Ctrl" : "",
5675 	   (ev->xkey.state & Mod1Mask) ? "Mod1" : "",
5676 	   (ev->xkey.state & Mod2Mask) ? "Mod2" : "",
5677 	   (ev->xkey.state & Mod3Mask) ? "Mod3" : "",
5678 	   (ev->xkey.state & Mod4Mask) ? "Mod4" : "",
5679 	   (ev->xkey.state & Mod5Mask) ? "Mod5" : ""));
5680 
5681     if (num <= 0) {
5682 	unsigned modifier = 0;
5683 
5684 	if (ev->xkey.state & ShiftMask)
5685 	    modifier |= mod_SHIFT;
5686 	if (ev->xkey.state & ControlMask)
5687 	    modifier |= mod_CTRL;
5688 	if (ev->xkey.state & Mod1Mask)
5689 	    modifier |= mod_ALT;
5690 	if (modifier != 0)
5691 	    modifier |= mod_KEY;
5692 
5693 	for (n = 0; n < TABLESIZE(escapes); n++) {
5694 	    if (keysym == escapes[n].key) {
5695 		TRACE(("ADD-FKEY %#x\n", escapes[n].code));
5696 		kqadd(cur_win, (int) modifier | escapes[n].code);
5697 		break;
5698 	    }
5699 	}
5700     } else {
5701 	unsigned modifier = 0;
5702 
5703 	if (ev->xkey.state & ShiftMask)
5704 	    modifier |= mod_SHIFT;
5705 	if (modifier != 0)
5706 	    modifier |= mod_KEY;
5707 	TRACE(("modifier %#x\n", modifier));
5708 
5709 	if (num == 1 && (ev->xkey.state & Mod1Mask)) {
5710 	    TRACE(("ADD-META %#x\n", CharOf(buffer[0])));
5711 	    buffer[0] |= (char) HIGHBIT;
5712 	}
5713 
5714 	/* FIXME: Should do something about queue full conditions */
5715 	for (i = 0; i < num && !kqfull(cur_win); i++) {
5716 	    int ch = CharOf(buffer[i]);
5717 	    if (isCntrl(ch)) {
5718 		TRACE(("ADD-CTRL %#x\n", ch));
5719 		kqadd(cur_win, (int) modifier | ch);
5720 	    }
5721 #if OPT_INPUT_METHOD
5722 	    else if (i == 0
5723 		     && num > 1
5724 		     && cur_win->imInputContext != NULL
5725 		     && b_is_utfXX(curbp)
5726 		     && vl_conv_to_utf32(&uch, buffer, (B_COUNT) num) == num) {
5727 		TRACE(("ADD-CHAR %#x\n", uch));
5728 		kqadd(cur_win, (int) uch);
5729 		break;
5730 	    }
5731 #endif
5732 	    else {
5733 		TRACE(("ADD-CHAR %#x\n", ch));
5734 		kqadd(cur_win, ch);
5735 	    }
5736 	}
5737     }
5738     returnVoid();
5739 }
5740 
5741 /*
5742  * change reverse video status
5743  */
5744 static void
x_rev(UINT state)5745 x_rev(UINT state)
5746 {
5747     cur_win->reverse = (int) state;
5748 }
5749 
5750 #if OPT_COLOR
5751 static void
x_set_foreground(int color)5752 x_set_foreground(int color)
5753 {
5754     TRACE(("x_set_foreground(%d), cur_win->fg was %#lx\n", color, cur_win->fg));
5755     cur_win->fg = (color >= 0 && color < NCOLORS)
5756 	? cur_win->colors_fg[color]
5757 	: cur_win->default_fg;
5758     TRACE(("...cur_win->fg = %#lx%s\n", cur_win->fg, cur_win->fg ==
5759 	   cur_win->default_fg ? " (default)" : ""));
5760 
5761     XSetForeground(dpy, GetColorGC(cur_win, tt_info), cur_win->fg);
5762     XSetBackground(dpy, GetColorGC(cur_win, rt_info), cur_win->fg);
5763 
5764     x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
5765     x_flush();
5766 }
5767 
5768 static void
x_set_background(int color)5769 x_set_background(int color)
5770 {
5771     TRACE(("x_set_background(%d), cur_win->bg was %#lx\n", color, cur_win->bg));
5772     cur_win->bg = (color >= 0 && color < NCOLORS)
5773 	? (SamePixel(cur_win->colors_bg[color], cur_win->default_bg)
5774 	   ? cur_win->colors_fg[color]
5775 	   : cur_win->colors_bg[color])
5776 	: cur_win->default_bg;
5777     TRACE(("...cur_win->bg = %#lx%s\n",
5778 	   cur_win->bg,
5779 	   cur_win->bg == cur_win->default_bg ? " (default)" : ""));
5780 
5781     if (color == ENUM_FCOLOR) {
5782 	XSetBackground(dpy, GetColorGC(cur_win, tt_info), cur_win->default_bg);
5783 	XSetForeground(dpy, GetColorGC(cur_win, rt_info), cur_win->default_bg);
5784     } else {
5785 	XSetBackground(dpy, GetColorGC(cur_win, tt_info), cur_win->bg);
5786 	XSetForeground(dpy, GetColorGC(cur_win, rt_info), cur_win->bg);
5787     }
5788     cur_win->bg_follows_fg = (Boolean) (color == ENUM_FCOLOR);
5789     TRACE(("...cur_win->bg_follows_fg = %#x\n", cur_win->bg_follows_fg));
5790 
5791     reset_color_gcs();
5792 
5793     XtVaSetValues(cur_win->screen,
5794 		  Nval(XtNbackground, cur_win->bg),
5795 		  NULL);
5796 
5797     x_touch(cur_win, 0, 0, cur_win->cols, cur_win->rows);
5798     x_flush();
5799 }
5800 
5801 static void
x_set_cursor_color(int color)5802 x_set_cursor_color(int color)
5803 {
5804     Pixel fg, bg;
5805     ColorGC *data;
5806 
5807     TRACE((T_CALLED "x_set_cursor_color(%d)\n", color));
5808     if (cur_win->is_color_cursor == False) {
5809 	if (!color_cursor(cur_win)) {
5810 	    gccolor = -1;
5811 	    return;
5812 	}
5813     }
5814 
5815     fg = (color >= 0 && color < NCOLORS)
5816 	? (SamePixel(cur_win->colors_bg[color], cur_win->default_bg)
5817 	   ? cur_win->colors_bg[color]
5818 	   : cur_win->colors_fg[color])
5819 	: cur_win->cursor_fg;
5820 
5821     bg = (color >= 0 && color < NCOLORS)
5822 	? (SamePixel(cur_win->colors_bg[color], cur_win->default_bg)
5823 	   ? cur_win->colors_fg[color]
5824 	   : cur_win->colors_bg[color])
5825 	: cur_win->cursor_bg;
5826 
5827     data = &cur_win->cc_info;
5828     data->state = sgINIT;
5829     data->gcmask = GCForeground | GCBackground;
5830     data->gcvals.background = bg;
5831     data->gcvals.foreground = fg;
5832     makeColorGC(cur_win, data);
5833 
5834     data = &cur_win->rc_info;
5835     data->state = sgINIT;
5836     data->gcmask = GCForeground | GCBackground;
5837     data->gcvals.foreground = bg;
5838     data->gcvals.background = fg;
5839     makeColorGC(cur_win, data);
5840 
5841     returnVoid();
5842 }
5843 
5844 #endif
5845 
5846 /* beep */
5847 static void
x_beep(void)5848 x_beep(void)
5849 {
5850 #if OPT_FLASH
5851     if (global_g_val(GMDFLASH)) {
5852 	beginDisplay();
5853 	XGrabServer(dpy);
5854 	XSetFunction(dpy, GetColorGC(cur_win, tt_info), GXxor);
5855 	XSetBackground(dpy, GetColorGC(cur_win, tt_info), 0L);
5856 	XSetForeground(dpy,
5857 		       GetColorGC(cur_win, tt_info),
5858 		       cur_win->fg ^ cur_win->bg);
5859 	XFillRectangle(dpy,
5860 		       cur_win->win,
5861 		       GetColorGC(cur_win, tt_info),
5862 		       0, 0,
5863 		       x_width(cur_win),
5864 		       x_height(cur_win));
5865 	XFlush(dpy);
5866 	catnap(90, FALSE);
5867 	XFillRectangle(dpy,
5868 		       cur_win->win,
5869 		       GetColorGC(cur_win, tt_info),
5870 		       0, 0,
5871 		       x_width(cur_win),
5872 		       x_height(cur_win));
5873 	XFlush(dpy);
5874 	XSetFunction(dpy, GetColorGC(cur_win, tt_info), GXcopy);
5875 	XSetBackground(dpy, GetColorGC(cur_win, tt_info), cur_win->bg);
5876 	XSetForeground(dpy, GetColorGC(cur_win, tt_info), cur_win->fg);
5877 	XUngrabServer(dpy);
5878 	endofDisplay();
5879     } else
5880 #endif
5881 	XBell(dpy, 0);
5882 }
5883 
5884 #if NO_LEAKS
5885 void
x11_leaks(void)5886 x11_leaks(void)
5887 {
5888     if (cur_win != 0) {
5889 	FreeIfNeeded(cur_win->fontname);
5890     }
5891 #if OPT_MENUS
5892     init_menus();
5893 #endif
5894 }
5895 #endif /* NO_LEAKS */
5896 
5897 #ifdef USE_SET_WM_NAME
5898 static char x_window_name[NFILEN];
5899 #endif
5900 
5901 static char x_icon_name[NFILEN];
5902 
5903 void
x_set_icon_name(const char * name)5904 x_set_icon_name(const char *name)
5905 {
5906     XTextProperty Prop;
5907 
5908     (void) strncpy0(x_icon_name, name, (size_t) NFILEN);
5909 
5910     Prop.value = (UCHAR *) x_icon_name;
5911     Prop.encoding = XA_STRING;
5912     Prop.format = 8;
5913     Prop.nitems = strlen(x_icon_name);
5914 
5915     XSetWMIconName(dpy, XtWindow(cur_win->top_widget), &Prop);
5916     TRACE(("x_set_icon_name(%s)\n", name));
5917 }
5918 
5919 char *
x_get_icon_name(void)5920 x_get_icon_name(void)
5921 {
5922     return x_icon_name;
5923 }
5924 
5925 void
x_set_window_name(const char * name)5926 x_set_window_name(const char *name)
5927 {
5928     if (name != 0 && strcmp(name, x_get_window_name())) {
5929 #ifdef USE_SET_WM_NAME
5930 	XTextProperty Prop;
5931 
5932 	(void) strncpy0(x_window_name, name, NFILEN);
5933 
5934 	Prop.value = (UCHAR *) x_window_name;
5935 	Prop.encoding = XA_STRING;
5936 	Prop.format = 8;
5937 	Prop.nitems = strlen(x_window_name);
5938 
5939 	XSetWMName(dpy, XtWindow(cur_win->top_widget), &Prop);
5940 	TRACE(("x_set_window_name(%s)\n", name));
5941 #else
5942 	XtVaSetValues(cur_win->top_widget,
5943 		      Nval(XtNtitle, name),
5944 		      NULL);
5945 #endif
5946     }
5947 }
5948 
5949 const char *
x_get_window_name(void)5950 x_get_window_name(void)
5951 {
5952     const char *result;
5953 #ifdef USE_SET_WM_NAME
5954     result = x_window_name;
5955 #else
5956     result = "";
5957     if (cur_win->top_widget != 0) {
5958 	XtVaGetValues(cur_win->top_widget, XtNtitle, &result, NULL);
5959     }
5960 #endif
5961     TRACE(("x_get_window_name(%s)\n", result));
5962     return result;
5963 }
5964 
5965 char *
x_get_display_name(void)5966 x_get_display_name(void)
5967 {
5968     return XDisplayString(dpy);
5969 }
5970 
5971 static void
watched_input_callback(XtPointer fd,int * src GCC_UNUSED,XtInputId * id GCC_UNUSED)5972 watched_input_callback(XtPointer fd,
5973 		       int *src GCC_UNUSED,
5974 		       XtInputId * id GCC_UNUSED)
5975 {
5976     dowatchcallback((int) (long) fd);
5977 }
5978 
5979 static int
x_watchfd(int fd,WATCHTYPE type,long * idp)5980 x_watchfd(int fd, WATCHTYPE type, long *idp)
5981 {
5982     *idp = (long) XtAppAddInput(cur_win->app_context,
5983 				fd,
5984 				(XtPointer) ((type & WATCHREAD)
5985 					     ? XtInputReadMask
5986 					     : ((type & WATCHWRITE)
5987 						? XtInputWriteMask
5988 						: XtInputExceptMask)),
5989 				watched_input_callback,
5990 				(XtPointer) (long) fd);
5991     return TRUE;
5992 }
5993 
5994 static void
x_unwatchfd(int fd GCC_UNUSED,long id)5995 x_unwatchfd(int fd GCC_UNUSED, long id)
5996 {
5997     XtRemoveInput((XtInputId) id);
5998 }
5999 
6000 /* Autocolor functions
6001  *
6002  * Note that these are self contained and could be moved to another
6003  * file if desired.
6004  */
6005 
6006 static XtIntervalId x_autocolor_timeout_id;
6007 
6008 static void
x_start_autocolor_timer()6009 x_start_autocolor_timer()
6010 {
6011 #if OPT_COLOR&&!SMALLER
6012     int millisecs = global_b_val(VAL_AUTOCOLOR);
6013     x_stop_autocolor_timer();
6014     if (millisecs > 0)
6015 	x_autocolor_timeout_id = XtAppAddTimeOut(cur_win->app_context,
6016 						 (ULONG) millisecs,
6017 						 x_autocolor_timeout,
6018 						 (XtPointer) 0);
6019 #endif
6020 }
6021 
6022 static void
x_stop_autocolor_timer()6023 x_stop_autocolor_timer()
6024 {
6025     if (x_autocolor_timeout_id != 0)
6026 	XtRemoveTimeOut(x_autocolor_timeout_id);
6027     x_autocolor_timeout_id = 0;
6028 }
6029 
6030 static void
x_autocolor_timeout(XtPointer data GCC_UNUSED,XtIntervalId * id GCC_UNUSED)6031 x_autocolor_timeout(XtPointer data GCC_UNUSED, XtIntervalId * id GCC_UNUSED)
6032 {
6033     if (kqempty(cur_win)) {
6034 	XClientMessageEvent ev;
6035 
6036 	autocolor();
6037 	XSync(dpy, False);
6038 
6039 	/* Send a null message to ourselves to prevent stalling in
6040 	   the event loop. */
6041 	ev.type = ClientMessage;
6042 	ev.serial = 0;
6043 	ev.send_event = True;
6044 	ev.display = dpy;
6045 	ev.window = cur_win->win;
6046 	ev.message_type = None;
6047 	ev.format = 8;
6048 	XSendEvent(dpy, cur_win->win, False, (long) 0, (XEvent *) &ev);
6049     }
6050 }
6051 
6052 /*
6053  * Return true if the given character would be printable.  Not all characters
6054  * are printable.
6055  */
6056 int
gui_isprint(int ch)6057 gui_isprint(int ch)
6058 {
6059     XVileFont *pf = cur_win->fonts.norm;
6060     int result = TRUE;
6061 
6062     if (ch >= 0 && pf != NULL) {
6063 #ifdef XRENDERFONT
6064 	if (!XftGlyphExists(dpy, pf, (FcChar32) ch)) {
6065 	    result = FALSE;
6066 	}
6067 #else
6068 	static XCharStruct dft, *tmp = &dft;
6069 	XCharStruct *pc = 0;
6070 
6071 	if (pf->per_char != 0
6072 	    && !pf->all_chars_exist) {
6073 
6074 	    if (pf->max_byte1 == 0) {
6075 		if (ch > 255) {
6076 		    result = FALSE;
6077 		} else {
6078 		    CI_GET_CHAR_INFO_1D(pf, (unsigned) ch, tmp, pc);
6079 		    if (pc == 0 || CI_NONEXISTCHAR(pc)) {
6080 			result = FALSE;
6081 		    }
6082 		}
6083 	    } else {
6084 		CI_GET_CHAR_INFO_2D(pf, CharOf((ch >> 8)), CharOf(ch), tmp, pc);
6085 		if (pc == 0 || CI_NONEXISTCHAR(pc)) {
6086 		    result = FALSE;
6087 		}
6088 	    }
6089 	}
6090 #endif
6091     }
6092     return result;
6093 }
6094 
6095 #if OPT_INPUT_METHOD
6096 /*
6097  * This is more or less stolen straight from XFree86 xterm.
6098  * This should support all European type languages.
6099  */
6100 
6101 /* adapted from IntrinsicI.h */
6102 #define MyStackAlloc(size, stack_cache_array)     \
6103     ((size) <= sizeof(stack_cache_array)	  \
6104     ?  (XtPointer)(stack_cache_array)		  \
6105     :  (XtPointer)malloc((size_t)(size)))
6106 
6107 #define MyStackFree(pointer, stack_cache_array) \
6108     if ((pointer) != ((char *)(stack_cache_array))) free(pointer)
6109 /*
6110  *  For OverTheSpot, client has to inform the position for XIM preedit.
6111  */
6112 static void
PreeditPosition(void)6113 PreeditPosition(void)
6114 {
6115     XPoint spot;
6116     XVaNestedList list;
6117 
6118     if (cur_win->imInputContext) {
6119 	spot.x = (short) x_pos(cur_win, ttcol);
6120 	spot.y = (short) y_pos(cur_win, ttrow);
6121 	list = XVaCreateNestedList(0,
6122 				   XNSpotLocation, &spot,
6123 				   XNForeground, cur_win->fg,
6124 				   XNBackground, cur_win->bg,
6125 				   NULL);
6126 	XSetICValues(cur_win->imInputContext, XNPreeditAttributes, list, NULL);
6127 	XFree(list);
6128     }
6129 }
6130 
6131 /* limit this feature to recent XFree86 since X11R6.x core dumps */
6132 #if defined(XtSpecificationRelease) && XtSpecificationRelease >= 6 && defined(X_HAVE_UTF8_STRING)
6133 #define USE_XIM_INSTANTIATE_CB
6134 
6135 static void
xim_instantiate_cb(Display * display,XPointer client_data GCC_UNUSED,XPointer call_data GCC_UNUSED)6136 xim_instantiate_cb(Display *display,
6137 		   XPointer client_data GCC_UNUSED,
6138 		   XPointer call_data GCC_UNUSED)
6139 {
6140     TRACE(("xim_instantiate_cb\n"));
6141     if (display == XtDisplay(cur_win->screen)) {
6142 	init_xlocale();
6143     }
6144 }
6145 
6146 static void
xim_destroy_cb(XIM im GCC_UNUSED,XPointer client_data GCC_UNUSED,XPointer call_data GCC_UNUSED)6147 xim_destroy_cb(XIM im GCC_UNUSED,
6148 	       XPointer client_data GCC_UNUSED,
6149 	       XPointer call_data GCC_UNUSED)
6150 {
6151     TRACE(("xim_destroy_cb\n"));
6152     cur_win->xim = NULL;
6153 
6154     XRegisterIMInstantiateCallback(XtDisplay(cur_win->screen),
6155 				   NULL, NULL, NULL,
6156 				   xim_instantiate_cb, NULL);
6157 }
6158 #endif /* X11R6+ */
6159 
6160 static void
xim_real_init(void)6161 xim_real_init(void)
6162 {
6163     unsigned i, j;
6164     char *p, *s, *t, *ns, *end, buf[32];
6165     char *save_ctype = 0;
6166     XIMStyle input_style = 0;
6167     XIMStyles *xim_styles = NULL;
6168     Bool found;
6169     static struct {
6170 	const char *name;
6171 	unsigned long code;
6172     } known_style[] = {
6173 	{
6174 	    "OverTheSpot", (XIMPreeditPosition | XIMStatusNothing)
6175 	},
6176 	{
6177 	    "OffTheSpot", (XIMPreeditArea | XIMStatusArea)
6178 	},
6179 	{
6180 	    "Root", (XIMPreeditNothing | XIMStatusNothing)
6181 	},
6182     };
6183 
6184     cur_win->imInputContext = NULL;
6185 
6186     if (cur_win->cannot_im) {
6187 	return;
6188     }
6189 
6190     if (okCTYPE2(vl_wide_enc))
6191 	save_ctype = setlocale(LC_CTYPE, vl_wide_enc.locale);
6192 
6193     TRACE((T_CALLED "init_xlocale:\n  inputMethod:%s\n  preeditType:%s\n",
6194 	   NonNull(cur_win->rs_inputMethod),
6195 	   NonNull(cur_win->rs_preeditType)));
6196 
6197     if (isEmpty(cur_win->rs_inputMethod)) {
6198 	if ((p = XSetLocaleModifiers("@im=none")) != NULL && *p)
6199 	    cur_win->xim = XOpenIM(dpy, NULL, NULL, NULL);
6200     } else {
6201 	s = cur_win->rs_inputMethod;
6202 	i = (unsigned) (5 + strlen(s));
6203 	t = (char *) MyStackAlloc(i, buf);
6204 	if (t == NULL) {
6205 	    fprintf(stderr, "Cannot allocate buffer for input-method\n");
6206 	    ExitProgram(BADEXIT);
6207 	} else {
6208 	    for (ns = s; ns && *s;) {
6209 		while (*s && isSpace(CharOf(*s)))
6210 		    s++;
6211 		if (!*s)
6212 		    break;
6213 		if ((ns = end = strchr(s, ',')) == 0)
6214 		    end = s + strlen(s);
6215 		while ((end != s) && isSpace(CharOf(end[-1])))
6216 		    end--;
6217 
6218 		if (end != s) {
6219 		    strcpy(t, "@im=");
6220 		    strncat(t, s, (size_t) (end - s));
6221 
6222 		    if ((p = XSetLocaleModifiers(t)) != 0 && *p
6223 			&& (cur_win->xim = XOpenIM(XtDisplay(cur_win->screen),
6224 						   NULL,
6225 						   NULL,
6226 						   NULL)) != 0)
6227 			break;
6228 
6229 		}
6230 		s = ns + 1;
6231 	    }
6232 	    MyStackFree(t, buf);
6233 	}
6234     }
6235 
6236     if (cur_win->xim == NULL
6237 	&& (p = XSetLocaleModifiers("")) != NULL
6238 	&& *p) {
6239 	cur_win->xim = XOpenIM(dpy, NULL, NULL, NULL);
6240     }
6241 
6242     if (cur_win->xim == NULL) {
6243 	fprintf(stderr, "Failed to open input method\n");
6244 	cur_win->cannot_im = True;
6245 	returnVoid();
6246     }
6247 
6248     if (XGetIMValues(cur_win->xim, XNQueryInputStyle, &xim_styles, NULL)
6249 	|| !xim_styles
6250 	|| !xim_styles->count_styles) {
6251 	fprintf(stderr, "input method doesn't support any style\n");
6252 	CloseInputMethod();
6253 	cur_win->cannot_im = True;
6254 	returnVoid();
6255     }
6256 
6257     found = False;
6258     for (s = cur_win->rs_preeditType; s && !found;) {
6259 	while (*s && isSpace(CharOf(*s)))
6260 	    s++;
6261 	if (!*s)
6262 	    break;
6263 	if ((ns = end = strchr(s, ',')) != 0)
6264 	    ns++;
6265 	else
6266 	    end = s + strlen(s);
6267 	while ((end != s) && isSpace(CharOf(end[-1])))
6268 	    end--;
6269 
6270 	if (end != s) {		/* just in case we have a spurious comma */
6271 	    TRACE(("looking for style '%.*s'\n", (int) (end - s), s));
6272 	    for (i = 0; i < XtNumber(known_style); i++) {
6273 		if ((int) strlen(known_style[i].name) == (end - s)
6274 		    && !strncmp(s, known_style[i].name, (size_t) (end - s))) {
6275 		    input_style = known_style[i].code;
6276 		    for (j = 0; j < xim_styles->count_styles; j++) {
6277 			if (input_style == xim_styles->supported_styles[j]) {
6278 			    found = True;
6279 			    break;
6280 			}
6281 		    }
6282 		    if (found)
6283 			break;
6284 		}
6285 	    }
6286 	}
6287 
6288 	s = ns;
6289     }
6290     XFree(xim_styles);
6291 
6292     if (!found) {
6293 	fprintf(stderr,
6294 		"input method doesn't support my preedit type (%s)\n",
6295 		cur_win->rs_preeditType);
6296 	CloseInputMethod();
6297 	cur_win->cannot_im = True;
6298 	returnVoid();
6299     }
6300 
6301     /*
6302      * Check for styles we do not yet support.
6303      */
6304     TRACE(("input_style %#lx\n", input_style));
6305     if (input_style == (XIMPreeditArea | XIMStatusArea)) {
6306 	fprintf(stderr,
6307 		"This program doesn't support the 'OffTheSpot' preedit type\n");
6308 	CloseInputMethod();
6309 	cur_win->cannot_im = True;
6310 	returnVoid();
6311     }
6312 
6313     /*
6314      * For XIMPreeditPosition (or OverTheSpot), XIM client has to
6315      * prepare a font.
6316      * The font has to be locale-dependent XFontSet, whereas
6317      * XTerm use Unicode font.  This leads a problem that the
6318      * same font cannot be used for XIM preedit.
6319      */
6320     if (input_style != (XIMPreeditNothing | XIMStatusNothing)) {
6321 	char **missing_charset_list;
6322 	int missing_charset_count;
6323 	char *def_string;
6324 	XVaNestedList p_list;
6325 	XPoint spot =
6326 	{0, 0};
6327 	XFontStruct **fonts;
6328 	char **font_name_list;
6329 
6330 	cur_win->imFontSet = XCreateFontSet(XtDisplay(cur_win->screen),
6331 					    cur_win->rs_imFont,
6332 					    &missing_charset_list,
6333 					    &missing_charset_count,
6334 					    &def_string);
6335 	if (cur_win->imFontSet == NULL) {
6336 	    fprintf(stderr, "Preparation of font set "
6337 		    "\"%s\" for XIM failed.\n", cur_win->rs_imFont);
6338 	    cur_win->imFontSet = XCreateFontSet(XtDisplay(cur_win->screen),
6339 						DEFXIMFONT,
6340 						&missing_charset_list,
6341 						&missing_charset_count,
6342 						&def_string);
6343 	}
6344 	if (cur_win->imFontSet == NULL) {
6345 	    fprintf(stderr, "Preparation of default font set "
6346 		    "\"%s\" for XIM failed.\n", DEFXIMFONT);
6347 	    CloseInputMethod();
6348 	    cur_win->cannot_im = True;
6349 	    returnVoid();
6350 	}
6351 	(void) XExtentsOfFontSet(cur_win->imFontSet);
6352 	j = (unsigned) XFontsOfFontSet(cur_win->imFontSet, &fonts, &font_name_list);
6353 	for (i = 0, cur_win->imFontHeight = 0; i < j; i++) {
6354 	    if (cur_win->imFontHeight < (*fonts)->ascent)
6355 		cur_win->imFontHeight = (*fonts)->ascent;
6356 	}
6357 	p_list = XVaCreateNestedList(0,
6358 				     XNSpotLocation, &spot,
6359 				     XNFontSet, cur_win->imFontSet,
6360 				     NULL);
6361 	cur_win->imInputContext = XCreateIC(cur_win->xim,
6362 					    XNInputStyle, input_style,
6363 					    XNClientWindow, cur_win->win,
6364 					    XNFocusWindow, cur_win->win,
6365 					    XNPreeditAttributes, p_list,
6366 					    NULL);
6367     } else {
6368 	cur_win->imInputContext = XCreateIC(cur_win->xim, XNInputStyle, input_style,
6369 					    XNClientWindow, cur_win->win,
6370 					    XNFocusWindow, cur_win->win,
6371 					    NULL);
6372     }
6373 
6374     if (!cur_win->imInputContext) {
6375 	fprintf(stderr, "Failed to create input context\n");
6376 	CloseInputMethod();
6377     }
6378 #if defined(USE_XIM_INSTANTIATE_CB)
6379     else {
6380 	XIMCallback destroy_cb;
6381 
6382 	destroy_cb.callback = xim_destroy_cb;
6383 	destroy_cb.client_data = NULL;
6384 	if (XSetIMValues(cur_win->xim, XNDestroyCallback, &destroy_cb, NULL))
6385 	    fprintf(stderr, "Could not set destroy callback to IM\n");
6386     }
6387 #endif
6388 
6389     if (save_ctype != 0)
6390 	setlocale(LC_CTYPE, save_ctype);
6391 
6392     returnVoid();
6393 }
6394 
6395 static void
init_xlocale(void)6396 init_xlocale(void)
6397 {
6398     if (cur_win->open_im) {
6399 	xim_real_init();
6400 
6401 #if defined(USE_XIM_INSTANTIATE_CB)
6402 	if (cur_win->imInputContext == NULL && !cur_win->cannot_im) {
6403 	    sleep(3);
6404 	    XRegisterIMInstantiateCallback(XtDisplay(cur_win->screen),
6405 					   NULL, NULL, NULL,
6406 					   xim_instantiate_cb, NULL);
6407 	}
6408 #endif
6409     }
6410 }
6411 #else
6412 #define PreeditPosition()
6413 #endif /* OPT_INPUT_METHOD */
6414 
6415 static void
x_move(int row,int col)6416 x_move(int row, int col)
6417 {
6418     psc_move(row, col);
6419     PreeditPosition();
6420 }
6421 
6422 static const char *
ae_names(XVileAtom n)6423 ae_names(XVileAtom n)
6424 {
6425     const char *result = 0;
6426 
6427 #define DATA(name) case ae ## name: result = #name; break
6428     switch (n) {
6429     case aeMAX:
6430 	break;
6431 	DATA(AVERAGE_WIDTH);
6432 	DATA(CHARSET_ENCODING);
6433 	DATA(CHARSET_REGISTRY);
6434 	DATA(CLIPBOARD);
6435 	DATA(COMPOUND_TEXT);
6436 	DATA(FONT);
6437 	DATA(FOUNDRY);
6438 	DATA(MULTIPLE);
6439 	DATA(NONE);
6440 	DATA(PIXEL_SIZE);
6441 	DATA(RESOLUTION_X);
6442 	DATA(RESOLUTION_Y);
6443 	DATA(SETWIDTH_NAME);
6444 	DATA(SLANT);
6445 	DATA(SPACING);
6446 	DATA(TARGETS);
6447 	DATA(TEXT);
6448 	DATA(TIMESTAMP);
6449 	DATA(UTF8_STRING);
6450 	DATA(WEIGHT_NAME);
6451 	DATA(WM_DELETE_WINDOW);
6452 	DATA(WM_PROTOCOLS);
6453     }
6454 #undef DATA
6455     return result;
6456 }
6457 
6458 Atom
xvileAtom(XVileAtom n)6459 xvileAtom(XVileAtom n)
6460 {
6461     static Atom xvile_atoms[aeMAX];
6462     Atom result = None;
6463 
6464     if (n < aeMAX) {
6465 	if (xvile_atoms[n] == None) {
6466 	    xvile_atoms[n] = XInternAtom(dpy, ae_names(n), False);
6467 	}
6468 	result = xvile_atoms[n];
6469     }
6470     return result;
6471 }
6472 
6473 TERM term =
6474 {
6475     0,				/* these four values are set dynamically at
6476 				 * open time */
6477     0,
6478     0,
6479     0,
6480     x_set_encoding,
6481     x_get_encoding,
6482     x_open,
6483     x_close,
6484     nullterm_kopen,
6485     nullterm_kclose,
6486     nullterm_clean,
6487     nullterm_unclean,
6488     nullterm_openup,
6489     x_getc,
6490     psc_putchar,
6491     x_typeahead,
6492     psc_flush,
6493     x_move,
6494     psc_eeol,
6495     psc_eeop,
6496     x_beep,
6497     x_rev,
6498     nullterm_setdescrip,
6499     x_set_foreground,
6500     x_set_background,
6501     x_set_palette,
6502     x_set_cursor_color,
6503     x_scroll,
6504     x_flush,
6505     nullterm_icursor,
6506 #if OPT_TITLE
6507     x_set_window_name,
6508 #else
6509     nullterm_settitle,
6510 #endif
6511     x_watchfd,
6512     x_unwatchfd,
6513     nullterm_cursorvis,
6514     nullterm_mopen,
6515     nullterm_mclose,
6516     nullterm_mevent,
6517 };
6518 
6519 #endif /* DISP_X11 && XTOOLKIT */
6520