1 /* retawq/cursesx.c - a small xcurses implementation
2    This file is part of retawq (<http://retawq.sourceforge.net/>), a network
3    client created by Arne Thomassen; retawq is basically released under certain
4    versions of the GNU General Public License and WITHOUT ANY WARRANTY.
5    Read the file COPYING for license details, README for program information.
6    Copyright (C) 2004-2006 Arne Thomassen <arne@arne-thomassen.de>
7 */
8 
9 /* For the rest of the program, xcurses mode shall look like any normal curses
10    mode as far as possible. */
11 
12 #include "stuff.h"
13 #include "resource.h"
14 
15 #include <X11/Xlib.h>
16 #include <X11/Xatom.h>
17 #include <X11/Xutil.h>
18 #include <X11/cursorfont.h>
19 
20 static Display* xws_display;
21 static const_after_init Window xws_window;
22 static const_after_init GC xws_gc;
23 static const_after_init Colormap colormap;
24 static const_after_init unsigned long pixel_fg, pixel_bg, pixel_black,
25   pixel_white, colors[COLOR_PAIRS];
26 static const_after_init Atom WmProt, WmDelete;
27 static const_after_init Cursor cursor_arrow, cursor_textmark;
28 static const_after_init tBoolean use_cursor = falsE;
29 
30 typedef struct
31 { const XFontStruct* font;
32   int width, height, ascent;
33 } tXwsFont;
34 
35 static const tXwsFont *font_normal, *font_bold;
36 
37 static attr_t attr = 0;
38 
39 static WINDOW __stdscr;
40 WINDOW* stdscr = &__stdscr;
41 int COLS = 80, LINES = 25;
42 
43 #define SAI(x, y) ( ((y) * COLS) + (x) ) /* screen array index */
44 #define A_CHARTEXT (255)
45 
46 static mmask_t __mousemask = 0;
47 
48 typedef struct tGetmouse
49 { MEVENT data;
50   struct tGetmouse* next;
51 } tGetmouse;
52 
53 static tGetmouse* getmouse_list = NULL;
54 
55 static struct
56 { const char* text;
57   size_t size;
58   int x1, y1, x2, y2; /* selection region */
59   int gx, gy; /* coordinates of button-press event */
60 #if MIGHT_USE_SCROLL_BARS
61   int sbty, sbkd;
62 #endif
63   unsigned char state;
64     /* "&1": user is currently selecting; "&2": use x1/y1; "&4": use x2/y2;
65        "&8": scroll bar thumb tracking */
66 } sel0;
67 
68 #if MIGHT_USE_SCROLL_BARS
69 #define SCROLL_BAR_WIDTH (15)
70 #define SCROLL_BAR_DIST (2)
71 #define MIN_THUMB_HEIGHT (5)
72 static const_after_init unsigned long pixel_grey75;
73 static int sbc[8]; /* scroll bar coordinates */
74 #endif
75 
76 
77 /* Helper functions */
78 
xcurses_io_handler(void * data,__sunused tFdObservationFlags flags __cunused)79 static void xcurses_io_handler(void* data,
80   __sunused tFdObservationFlags flags __cunused)
81 { int fd = MY_POINTER_TO_INT(data);
82 #if CONFIG_DEBUG
83   char b[200]; sprint_safe(b, "xcurses_io_handler(): %d\n", fd); debugmsg(b);
84 #endif
85 #if NEED_FD_REGISTER
86   (void) fd_register_lookup(&fd);
87 #endif
88   XProcessInternalConnection(xws_display, fd);
89 }
90 
connection_do_watch(int fd)91 static void connection_do_watch(int fd)
92 {
93 #if NEED_FD_REGISTER
94 #define fdkXlib (fdkOther)
95   /* ("probably" a socket, but handled by Xlib, never by our lwIP code) */
96   fd_register(&fd, fdkXlib);
97 #endif
98   if (!fd_is_observable(fd)) fatal_tmofd(fd);
99   fd_observe(fd, xcurses_io_handler, MY_INT_TO_POINTER(fd), fdofRead);
100 }
101 
connection_watch(Display * display,__sunused XPointer client_data __cunused,int fd,Bool opening,__sunused XPointer * watch_data __cunused)102 static void connection_watch(Display* display,
103   __sunused XPointer client_data __cunused, int fd, Bool opening,
104   __sunused XPointer* watch_data __cunused)
105 {
106   if (display != xws_display) return; /* "can't happen"? */
107   if (fd < 0) return; /* "can't happen"? */
108   if (opening) connection_do_watch(fd);
109   else
110   {
111 #if NEED_FD_REGISTER
112     if (!fd_register_rlookup(&fd, fdkXlib)) fatal_error(0, "BUG: frr()");
113 #endif
114     fd_unobserve(fd);
115   }
116 }
117 
118 static unsigned char __must_redraw = 1 | 2 | 4;
must_redraw(unsigned char what)119 static __my_inline void must_redraw(unsigned char what)
120 /* <what>: "&1": cursor; "&2": contents; "&4": resized */
121 { __must_redraw |= what;
122 }
123 
load_font(const char * which)124 static tXwsFont* __init load_font(const char* which)
125 { tXwsFont* retval;
126   XFontStruct* font;
127   char buf[1024], buf2[1024];
128   int ascent;
129   sprint_safe(buf, "-misc-fixed-%s-r-normal--13-120-*-*-c-70-iso8859-1",which);
130   font = XLoadQueryFont(xws_display, buf);
131   if (font == NULL)
132   { sprint_safe(buf2, _("can't load font %s"), buf); fatal_error(0, buf2); }
133   retval = memory_allocate(sizeof(tXwsFont), mapPermanent);
134   retval->font = font; retval->ascent = ascent = font->ascent;
135   retval->height = ascent + font->descent;
136   retval->width = XTextWidth(font, "M", 1);
137   return(retval);
138 }
139 
use_font(const tXwsFont * font)140 static void use_font(const tXwsFont* font)
141 { (void) XSetFont(xws_display, xws_gc, font->font->fid);
142 }
143 
alloc_color(const char * name,unsigned long * pix)144 static tBoolean alloc_color(const char* name, /*@out@*/ unsigned long* pix)
145 { XColor color, dummy;
146   if (XAllocNamedColor(xws_display, colormap, name, &color, &dummy) != 0)
147   { *pix = color.pixel; return(truE); }
148   else return(falsE);
149 }
150 
151 #define set_fg(fg) XSetForeground(xws_display, xws_gc, fg)
152 
set_fgbg(unsigned long fg,unsigned long bg)153 static void set_fgbg(unsigned long fg, unsigned long bg)
154 { set_fg(fg); XSetBackground(xws_display, xws_gc, bg);
155 }
156 
157 #define draw_line(x1, y1, x2, y2) \
158   (void) XDrawLine(xws_display, xws_window, xws_gc, x1, y1, x2, y2)
159 
160 #define draw_rectangle(x, y, w, h) \
161   (void) XFillRectangle(xws_display, xws_window, xws_gc, x, y, w, h)
162 
163 #define draw_char(x, y, ch) \
164   do \
165   { unsigned char _c = (unsigned char) (ch); \
166     (void) XDrawImageString(xws_display, xws_window, xws_gc, (x), (y), \
167       (char*) (&_c), 1); \
168   } while (0)
169 
set_cursor(Cursor c)170 static void set_cursor(Cursor c)
171 { if (use_cursor) (void) XDefineCursor(xws_display, xws_window, c);
172 }
173 
coord_limit(int * _x,int * _y)174 static void coord_limit(int* _x, int* _y)
175 { int x = *_x, y = *_y;
176   if (x < 0) x = 0;
177   else if (x > COLS - 1) x = COLS - 1;
178   if (y < 0) y = 0;
179   else if (y > LINES - 1) y = LINES - 1;
180   *_x = x; *_y = y;
181 }
182 
coord_pixel2char(int gx,int gy,int * _x,int * _y,tBoolean fail_if_bad)183 static tBoolean coord_pixel2char(int gx, int gy, /*@out@*/ int* _x,
184   /*@out@*/ int* _y, tBoolean fail_if_bad)
185 { int x = gx / font_normal->width, y = gy / font_normal->height;
186   if (fail_if_bad)
187   { if ( (x < 0) || (x >= COLS) || (y < 0) || (y >= LINES) ) return(falsE); }
188   coord_limit(&x, &y);
189   *_x = x; *_y = y;
190   return(truE);
191 }
192 
coord_reorder(int * x1,int * y1,int * x2,int * y2)193 static void coord_reorder(int* x1, int* y1, int* x2, int* y2)
194 { int a;
195   if (*y1 > *y2)
196   { a = *x1; *x1 = *x2; *x2 = a;
197     a = *y1; *y1 = *y2; *y2 = a;
198   }
199   else if ( (*y1 == *y2) && (*x1 > *x2) ) { a = *x1; *x1 = *x2; *x2 = a; }
200 }
201 
reinit_stdscr_data(void)202 static void reinit_stdscr_data(void)
203 /* Call this whenever COLS or LINES changed. */
204 { chtype* text;
205   int prod = COLS * LINES, count;
206   stdscr->x = stdscr->y = 0; __dealloc(stdscr->text);
207   text = stdscr->text = __memory_allocate(prod * sizeof(chtype), mapOther);
208   for (count = 0; count < prod; count++) text[count] = ' ';
209   __dealloc(stdscr->attr);
210   stdscr->attr = memory_allocate(prod * sizeof(attr_t), mapOther);
211   must_redraw(1 | 2 | 4); sel0.state &= ~(1 | 2 | 4);
212 }
213 
214 
215 /* Initialization */
216 
initscr(void)217 WINDOW* __init initscr(void)
218 { int fd, screen, width, height, count, x, y;
219   XSizeHints* size_hints = XAllocSizeHints();
220   XWMHints* wm_hints = XAllocWMHints();
221   XClassHint* class_hint = XAllocClassHint();
222   Pixmap logo = None;
223 
224   xws_display = XOpenDisplay(NULL);
225   if (xws_display == NULL) { cant_open: fatal_error(0, _(strCantOpenXws)); }
226   fd = ConnectionNumber(xws_display);
227   if (fd < 0) goto cant_open; /* "can't happen"? */
228   connection_do_watch(fd);
229   if (!XAddConnectionWatch(xws_display, connection_watch, NULL))
230     fatal_error(0, _("can't watch X display connections"));
231   screen = DefaultScreen(xws_display);
232   colormap = XDefaultColormap(xws_display, screen);
233   pixel_black = BlackPixel(xws_display, screen);
234   pixel_white = WhitePixel(xws_display, screen);
235 #if OFWAX
236   pixel_fg = pixel_black;
237   if (!alloc_color("linen", &pixel_bg)) pixel_bg = pixel_white;
238 #else
239   if (config.flags & (cfColorsOff | cfColorsReverse))
240   { pixel_fg = pixel_black; pixel_bg = pixel_white; }
241   else { pixel_fg = pixel_white; pixel_bg = pixel_black; }
242 #endif
243 #if MIGHT_USE_SCROLL_BARS
244   if (!alloc_color("grey75", &pixel_grey75)) pixel_grey75 = pixel_bg;
245   my_memclr_arr(sbc);
246 #endif
247 
248   font_normal = load_font("medium"); font_bold = load_font("bold");
249 
250   width = 80 * font_normal->width; height = 25 * font_normal->height;
251 #if MIGHT_USE_SCROLL_BARS
252   width += SCROLL_BAR_WIDTH;
253 #endif
254   xws_window = XCreateSimpleWindow(xws_display, RootWindow(xws_display,
255     screen), 0, 0, width, height, 2, pixel_fg, pixel_bg);
256   if (xws_window == None)
257   { fail_window: fatal_error(0, _("can't create window")); }
258 
259   size_hints->flags = PSize | PResizeInc;
260   size_hints->width = width; size_hints->height = height;
261   size_hints->width_inc = font_normal->width;
262   size_hints->height_inc = font_normal->height;
263   wm_hints->flags = InputHint | StateHint | WindowGroupHint;
264   wm_hints->input = True; wm_hints->initial_state = NormalState;
265   wm_hints->window_group = xws_window;
266   if (logo != None)
267   { wm_hints->flags |= IconPixmapHint; wm_hints->icon_pixmap = logo; }
268   class_hint->res_name = unconstify(strRetawq);
269   class_hint->res_class = unconstify("Xedit");
270   XmbSetWMProperties(xws_display, xws_window, strProgramVersion,
271     strProgramVersion, NULL, 0, size_hints, wm_hints, class_hint);
272   (void) XStoreName(xws_display, xws_window, strProgramVersion);
273     /* (jwm reportedly needs this, XmbSetWMProperties() isn't enough) */
274   WmProt = XInternAtom(xws_display, "WM_PROTOCOLS", False);
275   WmDelete = XInternAtom(xws_display, "WM_DELETE_WINDOW", False);
276   if (WmDelete != None) XSetWMProtocols(xws_display, xws_window, &WmDelete, 1);
277   (void) XSelectInput(xws_display, xws_window, KeyPressMask | ButtonPressMask |
278     ButtonReleaseMask | Button1MotionMask | ExposureMask |
279     VisibilityChangeMask | StructureNotifyMask); /* CHECKME! */
280 
281   xws_gc = XCreateGC(xws_display, xws_window, 0, NULL);
282   if (xws_gc == None) goto fail_window;
283   for (count = 0; count < COLOR_PAIRS; count++) colors[count] = pixel_fg;
284   set_fgbg(pixel_fg, pixel_bg); use_font(font_normal);
285 
286   (void) XMapWindow(xws_display, xws_window);
287 
288   cursor_arrow = XCreateFontCursor(xws_display, XC_left_ptr);
289   cursor_textmark = XCreateFontCursor(xws_display, XC_xterm);
290   if ( (cursor_arrow != None) && (cursor_textmark != None) ) use_cursor = truE;
291 
292   if (env_termsize(&x, &y)) { COLS = x; LINES = y; }
293   my_memclr_var(__stdscr); reinit_stdscr_data();
294   set_cursor(cursor_textmark); (void) XFlush(xws_display); my_memclr_var(sel0);
295   return(stdscr);
296 }
297 
298 
299 /* Redrawing, event handling */
300 
301 static const chtype* rdr_t;
302 static const attr_t* rdr_a;
303 static int rdr_curx, rdr_cury, rdr_hlx1, rdr_hly1, rdr_hlx2, rdr_hly2;
304 static tBoolean rdr_use_hl;
305 
redraw_char(int x,int y)306 static one_caller void redraw_char(int x, int y)
307 { const int idx = SAI(x, y);
308   chtype c = rdr_t[idx];
309   attr_t a = rdr_a[idx];
310   const tXwsFont* font = ( (a & A_BOLD) ? font_bold : font_normal );
311   const int width = font->width, height = font->height, gx = x * width,
312     ay = y * height, gy = ay + font->ascent;
313 #if MIGHT_USE_COLORS
314   const tColorPairNumber cpn = ( (a & __A_COLORMARK) ?
315     ((a & __A_COLORPAIRMASK) >> __A_COLORPAIRSHIFT) : cpnDefault );
316   unsigned long fg = colors[cpn], bg = pixel_bg;
317 #else
318   unsigned long fg = pixel_fg, bg = pixel_bg;
319 #endif
320   if ( (x == rdr_curx) && (y == rdr_cury) ) a ^= A_REVERSE; /* cursor */
321   if (rdr_use_hl)
322   { if ( (y < rdr_hly1) || ( (y == rdr_hly1) && (x < rdr_hlx1) ) ) { }
323     else if ( (y > rdr_hly2) || ( (y == rdr_hly2) && (x > rdr_hlx2) ) ) { }
324     else a ^= A_REVERSE; /* selection */
325   }
326   if (a & A_REVERSE)
327   {
328 #if OFWAX
329     bg = pixel_grey75;
330 #else
331     { unsigned long xg = fg; fg = bg; bg = xg; }
332 #endif
333   }
334   set_fgbg(fg, bg);
335   if (!(a & A_ALTCHARSET)) { draw_ch: use_font(font); draw_char(gx, gy, c); }
336   else
337   { const int xmid = gx + width / 2, ymid = ay + height / 2;
338     draw_char(gx, gy, ' ');
339     switch (c)
340     { case 'A': draw_line(gx, ymid, gx + width - 1, ymid); break;
341       case 'B': draw_line(xmid, ay, xmid, ay + height - 1); break;
342       case 'C': draw_line(xmid, ymid, gx + width - 1, ymid);
343         draw_line(xmid, ymid, xmid, ay + height - 1); break;
344       case 'D': draw_line(gx, ymid, xmid, ymid);
345         draw_line(xmid, ymid, xmid, ay + height - 1); break;
346       case 'E': draw_line(xmid, ymid, xmid, ay);
347         draw_line(xmid, ymid, gx + width - 1, ymid); break;
348       case 'F': draw_line(gx, ymid, xmid, ymid);
349         draw_line(xmid, ymid, xmid, ay); break;
350       default: c = '?'; goto draw_ch; /*@notreached@*/ break;
351     }
352   }
353   if (a & A_UNDERLINE) draw_line(gx, gy + 1, gx + width - 1, gy + 1);
354 }
355 
356 #if MIGHT_USE_SCROLL_BARS
357 
redraw_scrollbar(void)358 static one_caller void redraw_scrollbar(void)
359 { int width = SCROLL_BAR_WIDTH, gh = LINES * font_normal->height,
360     x1 = COLS * font_normal->width, y1 = 0, x2 = x1 + width - 1, y2 = gh - 1;
361   my_memclr_arr(sbc); sbc[0] = x1;
362   set_fg(pixel_grey75); draw_rectangle(x1, y1, width, gh);
363   if (config.flags & cfUseScrollBars)
364   { int heights[3];
365     int ty1, ty2, th, ty3, ty4, xmiddle, arrow_size;
366     xcurses_confuser(3, heights, NULL);
367     if (heights[0] >= heights[2]) return; /* everything is visible */
368     x1 += SCROLL_BAR_DIST; x2 -= SCROLL_BAR_DIST;
369     xmiddle = x1 + ((x2 - x1) / 2); arrow_size = width - 5;
370     /* top arrow */
371     set_fg(pixel_white);
372     draw_line(xmiddle, SCROLL_BAR_DIST, x1, arrow_size + SCROLL_BAR_DIST);
373     set_fg(pixel_black);
374     draw_line(xmiddle, SCROLL_BAR_DIST, x2, arrow_size + SCROLL_BAR_DIST);
375     draw_line(x1, arrow_size + SCROLL_BAR_DIST, x2,
376       arrow_size + SCROLL_BAR_DIST);
377     /* bottom arrow */
378     set_fg(pixel_white);
379     draw_line(xmiddle, y2 - SCROLL_BAR_DIST, x1,
380       y2 - arrow_size - SCROLL_BAR_DIST);
381     draw_line(x1, y2 - arrow_size - SCROLL_BAR_DIST, x2,
382       y2 - arrow_size - SCROLL_BAR_DIST);
383     set_fg(pixel_black);
384     draw_line(xmiddle, y2 - SCROLL_BAR_DIST, x2,
385       y2 - arrow_size - SCROLL_BAR_DIST);
386     /* thumb */
387     ty3 = arrow_size + SCROLL_BAR_DIST + 1;
388     ty4 = y2 - arrow_size - SCROLL_BAR_DIST - 1;
389     if (ty4 - ty3 < 5) return; /* not enough space (window too small) */
390     th = ((ty4 - ty3) * heights[0]) / heights[2];
391     if (th < MIN_THUMB_HEIGHT) th = MIN_THUMB_HEIGHT; /* make thumb "usable" */
392     ty1 = ty3 + ((ty4 - ty3) * heights[1]) / heights[2];
393     ty2 = ty1 + th - 1;
394     if (ty2 > ty4) { ty2 = ty4; ty1 = ty2 - th + 1; if (ty1 < ty3) ty1 = ty3; }
395     set_fg(pixel_white); draw_line(x1, ty1, x2, ty1);
396     draw_line(x1, ty1, x1, ty2);
397     set_fg(pixel_black); draw_line(x2, ty1, x2, ty2);
398     draw_line(x1, ty2, x2, ty2);
399     sbc[1] = ty3; sbc[2] = ty1; sbc[3] = ty2; sbc[4] = ty4;
400     sbc[5] = gh; sbc[6] = heights[1]; sbc[7] = heights[2];
401   }
402 }
403 
404 #if 0 /* HAVE_GETTIMEOFDAY */
405 
406 #if HAVE_SYS_TIME_H
407 #include <sys/time.h>
408 #endif
409 
410 static tBoolean scroll_bar_timeout(/*@out@*/ int* _msec)
411 {
412 }
413 
414 static one_caller void scroll_bar_timeout_on(void)
415 { timeout_register(scroll_bar_timeout);
416 }
417 
418 static one_caller void scroll_bar_timeout_off(void)
419 { timeout_unregister(scroll_bar_timeout);
420 }
421 
422 #else
423 
scroll_bar_timeout_on(void)424 static one_caller void scroll_bar_timeout_on(void) { }
scroll_bar_timeout_off(void)425 static one_caller void scroll_bar_timeout_off(void) { }
426 
427 #endif /* #if HAVE_GETTIMEOFDAY */
428 
429 #endif /* #if MIGHT_USE_SCROLL_BARS */
430 
redraw_stdscr(void)431 static void redraw_stdscr(void)
432 { int x, y;
433   /* use some global variables to make all this frequently executed redrawing
434      much faster... */
435   rdr_t = stdscr->text; rdr_a = stdscr->attr;
436   rdr_curx = stdscr->x; rdr_cury = stdscr->y;
437   rdr_use_hl = cond2boolean( (sel0.state & (2 | 4)) == (2 | 4) );
438   if (rdr_use_hl)
439   { rdr_hlx1 = sel0.x1; rdr_hly1 = sel0.y1;
440     rdr_hlx2 = sel0.x2; rdr_hly2 = sel0.y2;
441     coord_reorder(&rdr_hlx1, &rdr_hly1, &rdr_hlx2, &rdr_hly2);
442   }
443   for (y = 0; y < LINES; y++) { for (x = 0; x < COLS; x++) redraw_char(x, y); }
444 #if MIGHT_USE_SCROLL_BARS
445   redraw_scrollbar();
446 #endif
447   /* use_font(font_normal); */ set_fgbg(pixel_fg, pixel_bg); __must_redraw = 0;
448 }
449 
write_key(tKey key)450 static void write_key(tKey key)
451 { (void) my_write_pipe(fd_xcurses2main_write, &key, sizeof(key));
452 }
453 
selection_highlight(int x2,int y2)454 static one_caller void selection_highlight(int x2, int y2)
455 { /* if ( (sel0.x2 != x2) || (sel0.y2 != y2) || (!(sel0.state & 4)) ) */
456   { sel0.x2 = x2; sel0.y2 = y2; sel0.state |= 4; redraw_stdscr(); }
457 }
458 
selection_unhighlight(void)459 static void selection_unhighlight(void)
460 { sel0.state = 0; redraw_stdscr(); /* KISS */
461 }
462 
selection_copy(XButtonEvent * be)463 static one_caller void selection_copy(/*const*/ XButtonEvent* be)
464 { int x1 = sel0.x1, y1 = sel0.y1, x2 = sel0.x2, y2 = sel0.y2, x, y;
465   size_t size;
466   char *text, *ptr;
467   tBoolean need_newline = falsE;
468   coord_reorder(&x1, &y1, &x2, &y2);
469   size = (y2 - y1 + 1) * (COLS + 1) + 1; /* better too much than... */
470   __dealloc(sel0.text);
471   sel0.text = text = ptr = __memory_allocate(size, mapOther);
472   x = x1;
473   for (y = y1; y <= y2; y++)
474   { int xend = ( (y == y2) ? x2 : COLS - 1 ), spacecount = 0;
475     if (need_newline) { need_newline = falsE; *ptr++ = '\n'; }
476     while (x <= xend)
477     { const char ch = stdscr->text[SAI(x, y)];
478       if (ch == ' ') spacecount++; /* (to ignore trailing space characters) */
479       else
480       { while (spacecount > 0) { *ptr++ = ' '; spacecount--; }
481         *ptr++ = ch;
482       }
483       x++;
484     }
485     if (x >= COLS - 1) need_newline = truE;
486     x = 0;
487   }
488   *ptr = '\0'; debugmsg("selection_copy(): *"); debugmsg(text);debugmsg("*\n");
489   sel0.size = ptr - text; /* exact, in contrast to <size> */
490   (void) XSetSelectionOwner(xws_display, XA_PRIMARY, xws_window, be->time);
491   if (XGetSelectionOwner(xws_display, XA_PRIMARY) != xws_window)
492     selection_unhighlight();
493 }
494 
selection_provide(const XSelectionRequestEvent * sre)495 static one_caller void selection_provide(const XSelectionRequestEvent* sre)
496 { XSelectionEvent e;
497   Window requestor;
498   if (sel0.text == NULL) return; /* "should not happen" */
499   requestor = sre->requestor;
500   my_memclr_var(e); e.type = SelectionNotify; e.display = sre->display;
501   e.requestor = requestor; e.selection = sre->selection;
502   e.target = sre->target; e.property = sre->property; e.time = sre->time;
503   (void) XChangeProperty(xws_display, requestor, e.property, XA_STRING, 8,
504     PropModeReplace, sel0.text, sel0.size);
505   (void) XSendEvent(xws_display, requestor, False, 0, (XEvent*) (&e));
506 }
507 
selection_paste(Time t)508 static one_caller void selection_paste(Time t)
509 { static tBoolean did_init = falsE;
510   static Atom dummy;
511   if (XGetSelectionOwner(xws_display, XA_PRIMARY) == None) return;
512   if (!did_init)
513   { dummy = XInternAtom(xws_display, "__RETAWQ_SELECTION", False);
514     did_init = truE;
515   }
516   (void) XConvertSelection(xws_display, XA_PRIMARY, XA_STRING, dummy,
517     xws_window, t);
518 }
519 
selection_do_paste(const XSelectionEvent * se)520 static one_caller void selection_do_paste(const XSelectionEvent* se)
521 { Atom prop = se->property, type;
522   int format;
523   unsigned long nitems, remaining;
524   unsigned char* value;
525   if (prop == None) return;
526   if (XGetWindowProperty(xws_display, xws_window, prop, 0, 1024, True,
527       AnyPropertyType, &type, &format, &nitems, &remaining, &value) != 0)
528     return;
529   if (format != 8) { debugmsg("do_paste(): bad format\n"); return; }
530   xcurses_confuser(0, value, &nitems);
531   XFree(value);
532   /* IMPLEMENTME: read the _whole_ value? */
533 }
534 
handle_event(XEvent * event)535 static one_caller void handle_event(/*const*/ XEvent* event)
536 { int width, height;
537   /*const*/ XKeyEvent* ke;
538   /*const*/ XButtonEvent* be;
539   unsigned int button;
540   KeySym sym;
541   char ch;
542   switch (event->type)
543   { case KeyPress:
544       ke = (/*const*/ XKeyEvent*) event;
545       (void) XLookupString(ke, &ch, 1, &sym, NULL);
546       if (IsModifierKey(sym)) { /* nothing */ }
547       else if (sym != NoSymbol)
548       { if ( (ke->state & ControlMask) && (my_islower(sym)) )
549           sym = (sym - 'a') + 1;
550         write_key(sym);
551       }
552       break;
553     case Expose:
554       if ( ((XExposeEvent*) event)->count <= 0 ) redraw_stdscr();
555       break;
556     case ButtonPress:
557       be = (/*const*/ XButtonEvent*) event;
558       button = be->button; sel0.state &= ~8;
559 #if MIGHT_USE_SCROLL_BARS
560       if ( (sbc[0] > 0) && (be->x >= sbc[0]) ) /* inside the scroll bar */
561       { unsigned char count, code;
562         int y;
563         if ( (button != Button1) || (sbc[1] <= 0) ) return;
564         y = be->y; code = 5;
565         for (count = 1; count <= 4; count++)
566         { if (y <= sbc[count]) { code = count; break; } }
567         if (code == 3) { sel0.state = 8; sel0.sbty = y; sel0.sbkd = sbc[6]; }
568         else { xcurses_confuser(1, &code, NULL); scroll_bar_timeout_on(); }
569       }
570       else
571 #endif
572       if (__mousemask != 0)
573       { mmask_t m = 0;
574         if (button == Button1) m |= BUTTON1_CLICKED;
575         else if (button == Button2) m |= BUTTON2_CLICKED;
576         else if (button == Button3) m |= BUTTON3_CLICKED;
577         m &= __mousemask;
578         if (m != 0)
579         { tGetmouse *list, *entry;
580           MEVENT* data;
581           int x, y;
582           if (!coord_pixel2char(be->x, be->y, &x, &y, truE)) return;
583           list = getmouse_list;
584           entry = memory_allocate(sizeof(tGetmouse), mapOther);
585           data = &(entry->data); data->x = x; data->y = y; data->bstate = m;
586           if (list == NULL) getmouse_list = entry;
587           else /* rare */
588           { while (list->next != NULL) list = list->next;
589             list->next = entry;
590           }
591           write_key(KEY_MOUSE);
592         }
593       }
594       else if (button == Button1) /* user starts text selection */
595       { int gx = be->x, gy = be->y, x, y;
596         if (!coord_pixel2char(gx, gy, &x, &y, falsE)) return;
597         sel0.gx = gx; sel0.gy = gy;
598         sel0.x1 = x; sel0.y1 = y; sel0.state |= 1 | 2;
599       }
600       else if ( (button == Button2) || (button == Button3) )
601         selection_paste(be->time);
602       break;
603     case ButtonRelease:
604       be = (/*const*/ XButtonEvent*) event; button = be->button;
605 #if MIGHT_USE_SCROLL_BARS
606       if (button == Button1) { sel0.state &= ~8; scroll_bar_timeout_off(); }
607 #endif
608       if (__mousemask != 0) goto not_sel;
609       if ( (button == Button1) && (sel0.state & 1) )
610       { int x, y;
611         if ( (be->x == sel0.gx) && (be->y == sel0.gy) )
612         { selection_unhighlight(); goto not_sel; }
613         if (!coord_pixel2char(be->x, be->y, &x, &y, falsE)) goto not_sel;
614         sel0.x2 = x; sel0.y2 = y; sel0.state |= 4; selection_copy(be);
615       }
616       not_sel: sel0.state &= ~1; break;
617     case MotionNotify:
618 #if MIGHT_USE_SCROLL_BARS
619       if (sel0.state & 8)
620       { const XMotionEvent* me = (const XMotionEvent*) event;
621         int y = me->y, ydiff = y - sel0.sbty, linediff =
622           (ydiff * sbc[7]) / (sbc[4] - sbc[1]), l = sel0.sbkd + linediff;
623         if (l <= 0) l = 0;
624         else if (l >= sbc[7]) l = sbc[7] - 1;
625         xcurses_confuser(2, &l, NULL);
626       }
627       else
628 #endif
629       if (sel0.state & 1) /* highlight the currently selected text */
630       { const XMotionEvent* me = (const XMotionEvent*) event;
631         int x2, y2;
632         if (coord_pixel2char(me->x, me->y, &x2, &y2, falsE))
633           selection_highlight(x2, y2);
634       }
635       break;
636     case SelectionClear:
637       if (sel0.state & (2 | 4)) selection_unhighlight();
638       break;
639     case SelectionRequest:
640       selection_provide((XSelectionRequestEvent*) event); break;
641     case SelectionNotify:
642       selection_do_paste((XSelectionEvent*) event); break;
643     case DestroyNotify:
644       do_finish: /* XCloseDisplay(xws_display); */
645       do_quit(); /*@notreached@*/ break;
646     case ConfigureNotify:
647       width = event->xconfigure.width;
648 #if MIGHT_USE_SCROLL_BARS
649       width -= SCROLL_BAR_WIDTH;
650 #endif
651       width /= font_normal->width;
652       if (width < CURSES_MINCOLS) width = CURSES_MINCOLS;
653       else if (width > CURSES_MAXCOLS) width = CURSES_MAXCOLS;
654       height = event->xconfigure.height / font_normal->height;
655       if (height < CURSES_MINLINES) height = CURSES_MINLINES;
656       else if (height > CURSES_MAXLINES) height = CURSES_MAXLINES;
657       COLS = width; LINES = height; reinit_stdscr_data(); redraw_stdscr();
658       write_key(KEY_RESIZE);
659       break;
660     case ClientMessage:
661       if ( (event->xclient.message_type == WmProt) &&
662            ( ((Atom)(event->xclient.data.l[0])) == WmDelete) )
663         goto do_finish;
664       break;
665   }
666 }
667 
668 
669 /* curses interface */
670 
addch(chtype c)671 int addch(chtype c)
672 { const int x = stdscr->x, idx = SAI(x, stdscr->y);
673   stdscr->text[idx] = c & A_CHARTEXT;
674   stdscr->attr[idx] = (c & ~A_CHARTEXT) | attr;
675   if (x < COLS - 1) stdscr->x++;
676   must_redraw(1 | 2);
677   return(OK);
678 }
679 
addstr(const char * _str)680 int addstr(const char* _str)
681 { const unsigned char* str = (const unsigned char*) _str; /* avoid sign ext. */
682   unsigned char ch;
683   while ( (ch = *str++) != '\0' ) (void) addch((chtype) ch);
684   return(OK);
685 }
686 
addnstr(const char * _str,int len)687 int addnstr(const char* _str, int len)
688 { const unsigned char* str = (const unsigned char*) _str; /* avoid sign ext. */
689   while (len-- > 0)
690   { const unsigned char ch = *str++;
691     if (ch == '\0') break;
692     (void) addch((chtype) ch);
693   }
694   return(OK);
695 }
696 
attron(attr_t a)697 int attron(attr_t a)
698 {
699 #if MIGHT_USE_COLORS
700   if (a & __A_COLORMARK) attr &= ~__A_COLORPAIRMASK; /* caller changes color */
701 #endif
702   attr |= a;
703   return(0);
704 }
705 
attroff(attr_t a)706 int attroff(attr_t a)
707 {
708 #if MIGHT_USE_COLORS
709   if (a & __A_COLORMARK) a |= __A_COLORPAIRMASK; /* caller turns color off */
710 #endif
711   attr &= ~a;
712   return(0);
713 }
714 
clrtoeol(void)715 int clrtoeol(void)
716 { int x = stdscr->x;
717   if (x < COLS)
718   { const int y = stdscr->y;
719     while (x < COLS)
720     { const int idx = SAI(x, y);
721       stdscr->text[idx] = ' '; stdscr->attr[idx] = 0; x++;
722     }
723     must_redraw(2);
724   }
725   return(OK);
726 }
727 
getch(void)728 int getch(void)
729 { tKey key;
730   if (my_read_pipe(fd_keyboard_input, &key, sizeof(key)) != sizeof(key))
731     fatal_error(0, "xcurses getch() failed");
732   return(key);
733 }
734 
getmouse(MEVENT * e)735 int getmouse(MEVENT* e)
736 { tGetmouse* entry = getmouse_list;
737   if (entry == NULL) return(ERR); /* "should not happen" */
738   my_memcpy(e, &(entry->data), sizeof(MEVENT));
739   getmouse_list = entry->next; memory_deallocate(entry);
740   return(OK);
741 }
742 
743 #if MIGHT_USE_COLORS
has_colors(void)744 int has_colors(void)
745 { return( (XDisplayCells(xws_display, DefaultScreen(xws_display)) > 2)
746     ? TRUE : FALSE );
747 }
748 #endif
749 
inch(void)750 chtype inch(void)
751 { const int idx = SAI(stdscr->x, stdscr->y);
752   return(stdscr->text[idx] | stdscr->attr[idx]);
753 }
754 
755 #if MIGHT_USE_COLORS
init_pair(short p,short fg,short bg)756 int init_pair(short p, short fg, short bg)
757 { unsigned long pix = pixel_fg, pix0;
758   const char* name;
759   switch (fg)
760   { case ccRed: name = "red"; break;
761     case ccBlue: name = "blue"; break;
762     default: goto out; /*@notreached@*/ break;
763   }
764   if ( (alloc_color(name, &pix0)) && (pix0 != pixel_bg) ) pix = pix0;
765   out:
766   colors[p] = pix;
767   return(OK);
768 }
769 #endif
770 
mousemask(mmask_t new_mask,mmask_t * old_mask)771 mmask_t mousemask(mmask_t new_mask, mmask_t* old_mask)
772 { if (old_mask != NULL) *old_mask = __mousemask;
773   __mousemask = new_mask;
774   set_cursor(__mousemask ? cursor_arrow : cursor_textmark);
775   return(__mousemask);
776 }
777 
move(int y,int x)778 int move(int y, int x)
779 { coord_limit(&x, &y);
780   stdscr->x = x; stdscr->y = y; must_redraw(1);
781   return(OK);
782 }
783 
mvaddch(int y,int x,chtype ch)784 int mvaddch(int y, int x, chtype ch)
785 { (void) move(y, x); (void) addch(ch);
786   return(OK);
787 }
788 
mvaddnstr(int y,int x,const char * str,int len)789 int mvaddnstr(int y, int x, const char* str, int len)
790 { (void) move(y, x); (void) addnstr(str, len);
791   return(OK);
792 }
793 
refresh(void)794 int refresh(void)
795 { loop:
796   while (XPending(xws_display) > 0)
797   { XEvent event;
798     (void) XNextEvent(xws_display, &event); /* CHECKME: return value? */
799     handle_event(&event);
800   }
801   if (__must_redraw) { redraw_stdscr(); goto loop; }
802   return(OK);
803 }
804