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