1 /*
2  * Copyright (C) 1997-2009, Michael Jennings
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 static const char cvs_ident[] = "$Id: windows.c 51650 2010-08-26 01:34:13Z lucas $";
25 
26 #include "config.h"
27 #include "feature.h"
28 
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <X11/cursorfont.h>
34 
35 #include "buttons.h"
36 #include "command.h"
37 #include "e.h"
38 #include "events.h"
39 #include "font.h"
40 #include "startup.h"
41 #include "menus.h"
42 #include "options.h"
43 #include "pixmap.h"
44 #include "screen.h"
45 #include "scrollbar.h"
46 #include "term.h"
47 #include "windows.h"
48 
49 XWindowAttributes attr;
50 XSetWindowAttributes Attributes;
51 XSizeHints szHint = {
52     PMinSize | PResizeInc | PBaseSize,
53     0, 0, 80, 24,               /* x, y, width, height */
54     1, 1,                       /* Min width, height */
55     0, 0,                       /* Max width, height - unused */
56     1, 1,                       /* increments: width, height */
57     {1, 1},                     /* increments: x, y */
58     {0, 0},                     /* Aspect ratio - unused */
59     0, 0,                       /* base size: width, height */
60     NorthWestGravity            /* gravity */
61 };
62 Cursor TermWin_cursor;          /* cursor for vt window */
63 
64 void
set_text_property(Window win,char * propname,char * value)65 set_text_property(Window win, char *propname, char *value)
66 {
67     XTextProperty prop;
68     Atom atom;
69 
70     ASSERT(propname != NULL);
71 
72     if (!value) {
73         atom = XInternAtom(Xdisplay, propname, True);
74         if (atom == None) {
75             return;
76         }
77         XDeleteProperty(Xdisplay, win, atom);
78     } else {
79         atom = XInternAtom(Xdisplay, propname, False);
80         prop.value = (unsigned char *) value;
81         prop.encoding = XA_STRING;
82         prop.format = 8;
83         prop.nitems = strlen(value);
84         XSetTextProperty(Xdisplay, win, &prop, atom);
85     }
86 }
87 
88 unsigned long
get_tint_by_color_name(const char * color)89 get_tint_by_color_name(const char *color)
90 {
91     XColor wcol, xcol;
92     unsigned long r, g, b, t;
93 
94     wcol.pixel = WhitePixel(Xdisplay, Xscreen);
95     XQueryColor(Xdisplay, cmap, &wcol);
96 
97     D_PIXMAP(("Tint string is \"%s\", white color is rgbi:%d/%d/%d\n", color, wcol.red, wcol.green, wcol.blue));
98     if (!XParseColor(Xdisplay, cmap, color, &xcol)) {
99         libast_print_error("Unable to parse tint color \"%s\".  Ignoring.\n", color);
100         return 0xffffff;
101     }
102 
103     D_PIXMAP(("RGB values for color are %d/%d/%d\n", xcol.red, xcol.green, xcol.blue));
104     if ((wcol.flags & DoRed) && (xcol.flags & DoRed)) {
105         r = (xcol.red << 8) / wcol.red;
106         D_PIXMAP(("Got red == %lu\n", r));
107         if (r >= 0x100)
108             r = 0xff;
109     } else {
110         r = 0xff;
111     }
112     if ((wcol.flags & DoGreen) && (xcol.flags & DoGreen)) {
113         g = (xcol.green << 8) / wcol.green;
114         D_PIXMAP(("Got green == %lu\n", g));
115         if (g >= 0x100)
116             g = 0xff;
117     } else {
118         g = 0xff;
119     }
120     if ((wcol.flags & DoBlue) && (xcol.flags & DoBlue)) {
121         b = (xcol.blue << 8) / wcol.blue;
122         D_PIXMAP(("Got blue == %lu\n", b));
123         if (b >= 0x100)
124             b = 0xff;
125     } else {
126         b = 0xff;
127     }
128     t = (r << 16) | (g << 8) | b;
129     D_PIXMAP(("Final tint is 0x%06x\n", t));
130     return t;
131 }
132 
133 Pixel
get_bottom_shadow_color(Pixel norm_color,const char * type)134 get_bottom_shadow_color(Pixel norm_color, const char *type)
135 {
136 
137     XColor xcol;
138 
139     xcol.pixel = norm_color;
140     XQueryColor(Xdisplay, cmap, &xcol);
141 
142     xcol.red /= 2;
143     xcol.green /= 2;
144     xcol.blue /= 2;
145 
146     if (!XAllocColor(Xdisplay, cmap, &xcol)) {
147         libast_print_error("Unable to allocate \"%s\" (0x%08x:  0x%04x, 0x%04x, 0x%04x) in the color map.\n", type, xcol.pixel, xcol.red,
148                     xcol.green, xcol.blue);
149         xcol.pixel = PixColors[minColor];
150     }
151     return (xcol.pixel);
152 }
153 
154 Pixel
get_top_shadow_color(Pixel norm_color,const char * type)155 get_top_shadow_color(Pixel norm_color, const char *type)
156 {
157 
158     XColor xcol, white;
159 
160 # ifdef PREFER_24BIT
161     white.red = white.green = white.blue = r = g = b = ~0;
162     XAllocColor(Xdisplay, cmap, &white);
163 # else
164     white.pixel = WhitePixel(Xdisplay, Xscreen);
165     XQueryColor(Xdisplay, cmap, &white);
166 # endif
167 
168     xcol.pixel = norm_color;
169     XQueryColor(Xdisplay, cmap, &xcol);
170 
171     xcol.red = MAX((white.red / 5), xcol.red);
172     xcol.green = MAX((white.green / 5), xcol.green);
173     xcol.blue = MAX((white.blue / 5), xcol.blue);
174 
175     xcol.red = MIN(white.red, (xcol.red * 7) / 5);
176     xcol.green = MIN(white.green, (xcol.green * 7) / 5);
177     xcol.blue = MIN(white.blue, (xcol.blue * 7) / 5);
178 
179     if (!XAllocColor(Xdisplay, cmap, &xcol)) {
180         libast_print_error("Unable to allocate \"%s\" (0x%08x:  0x%04x, 0x%04x, 0x%04x) in the color map.\n", type, xcol.pixel, xcol.red,
181                     xcol.green, xcol.blue);
182         xcol.pixel = PixColors[WhiteColor];
183     }
184     return (xcol.pixel);
185 }
186 
187 Pixel
get_color_by_name(const char * name,const char * fallback)188 get_color_by_name(const char *name, const char *fallback)
189 {
190     XColor xcol;
191 
192     if (!name) {
193         if (!fallback) {
194             return ((Pixel) - 1);
195         } else {
196             name = fallback;
197         }
198     } else if (isdigit(*name)) {
199         unsigned long c;
200 
201         c = strtoul(name, (char **) NULL, 0);
202         if (c <= 15) {
203             name = rs_color[c + minColor];
204         }
205     }
206     if (!XParseColor(Xdisplay, cmap, name, &xcol)) {
207         libast_print_warning("Unable to resolve \"%s\" as a color name.  Falling back on \"%s\".\n", name, NONULL(fallback));
208         name = fallback;
209         if (name) {
210             if (!XParseColor(Xdisplay, cmap, name, &xcol)) {
211                 libast_print_warning
212                     ("Unable to resolve \"%s\" as a color name.  This should never fail.  Please repair/restore your RGB database.\n",
213                      name);
214                 return ((Pixel) - 1);
215             }
216         } else {
217             return ((Pixel) - 1);
218         }
219     }
220     if (!XAllocColor(Xdisplay, cmap, &xcol)) {
221         libast_print_warning("Unable to allocate \"%s\" (0x%08x:  0x%04x, 0x%04x, 0x%04x) in the color map.  Falling back on \"%s\".\n",
222                       name, xcol.pixel, xcol.red, xcol.green, xcol.blue, NONULL(fallback));
223         name = fallback;
224         if (name) {
225             if (!XAllocColor(Xdisplay, cmap, &xcol)) {
226                 libast_print_warning("Unable to allocate \"%s\" (0x%08x:  0x%04x, 0x%04x, 0x%04x) in the color map.\n", name, xcol.pixel,
227                               xcol.red, xcol.green, xcol.blue);
228                 return ((Pixel) - 1);
229             }
230         } else {
231             return ((Pixel) - 1);
232         }
233     }
234     return (xcol.pixel);
235 }
236 
237 Pixel
get_color_by_pixel(Pixel pixel,Pixel fallback)238 get_color_by_pixel(Pixel pixel, Pixel fallback)
239 {
240     XColor xcol;
241 
242     xcol.pixel = pixel;
243     if (!XQueryColor(Xdisplay, cmap, &xcol)) {
244         libast_print_warning("Unable to convert pixel value 0x%08x to an XColor structure.  Falling back on 0x%08x.\n", pixel, fallback);
245         xcol.pixel = fallback;
246         if (!XQueryColor(Xdisplay, cmap, &xcol)) {
247             libast_print_warning("Unable to convert pixel value 0x%08x to an XColor structure.\n", xcol.pixel);
248             return ((Pixel) 0);
249         }
250     }
251     if (!XAllocColor(Xdisplay, cmap, &xcol)) {
252         libast_print_warning("Unable to allocate 0x%08x (0x%04x, 0x%04x, 0x%04x) in the color map.  Falling back on 0x%08x.\n", xcol.pixel,
253                       xcol.red, xcol.green, xcol.blue, fallback);
254         xcol.pixel = fallback;
255         if (!XAllocColor(Xdisplay, cmap, &xcol)) {
256             libast_print_warning("Unable to allocate 0x%08x (0x%04x, 0x%04x, 0x%04x) in the color map.\n", xcol.pixel, xcol.red,
257                           xcol.green, xcol.blue);
258             return ((Pixel) 0);
259         }
260     }
261     return (xcol.pixel);
262 }
263 
264 void
process_colors(void)265 process_colors(void)
266 {
267     int i;
268     Pixel pixel;
269 
270     for (i = 0; i < NRS_COLORS; i++) {
271         D_COLORS(("Adding color %d of %d (%s)\n", i, NRS_COLORS, def_colorName[i]));
272         if ((Xdepth <= 2) || ((pixel = get_color_by_name(rs_color[i], def_colorName[i])) == (Pixel) (-1))) {
273             switch (i) {
274                 case fgColor:
275                     pixel = WhitePixel(Xdisplay, Xscreen);
276                     break;
277                 case bgColor:
278                     pixel = BlackPixel(Xdisplay, Xscreen);
279                     break;
280 #ifndef NO_CURSORCOLOR
281                 case cursorColor:
282                     pixel = PixColors[bgColor];
283                     break;
284                 case cursorColor2:
285                     pixel = PixColors[fgColor];
286                     break;
287 #endif /* NO_CURSORCOLOR */
288 #ifndef NO_BOLDUNDERLINE
289                 case colorBD:
290                     pixel = PixColors[fgColor];
291                     break;
292                 case colorUL:
293                     pixel = PixColors[fgColor];
294                     break;
295 #endif
296 #ifdef ESCREEN
297                 case ES_COLOR_CURRENT:
298                     pixel = PixColors[YellowColor];
299                     break;
300                 case ES_COLOR_ACTIVE:
301                     pixel = PixColors[BlueColor];
302                     break;
303 #endif
304                 case pointerColor:
305                     pixel = PixColors[fgColor];
306                     break;
307                 case borderColor:
308                     pixel = PixColors[bgColor];
309                     break;
310                 default:
311                     pixel = PixColors[fgColor]; /* None */
312                     break;
313             }
314         }
315         D_COLORS(("Pixel : %x\n", pixel));
316         PixColors[i] = pixel;
317     }
318 
319     if (Xdepth <= 2) {          /* Monochrome */
320         PixColors[topShadowColor] = PixColors[fgColor];
321         PixColors[bottomShadowColor] = PixColors[fgColor];
322         PixColors[unfocusedTopShadowColor] = PixColors[fgColor];
323         PixColors[unfocusedBottomShadowColor] = PixColors[fgColor];
324 
325         PixColors[menuTopShadowColor] = PixColors[fgColor];
326         PixColors[menuBottomShadowColor] = PixColors[fgColor];
327         PixColors[unfocusedMenuTopShadowColor] = PixColors[fgColor];
328         PixColors[unfocusedMenuBottomShadowColor] = PixColors[fgColor];
329     } else {
330         PixColors[bottomShadowColor] = get_bottom_shadow_color(images[image_sb].norm->bg, "bottomShadowColor");
331         PixColors[unfocusedBottomShadowColor] =
332             get_bottom_shadow_color(images[image_sb].disabled->bg, "unfocusedBottomShadowColor");
333         PixColors[topShadowColor] = get_top_shadow_color(images[image_sb].norm->bg, "topShadowColor");
334         PixColors[unfocusedTopShadowColor] = get_top_shadow_color(images[image_sb].disabled->bg, "unfocusedTopShadowColor");
335 
336         PixColors[menuBottomShadowColor] = get_bottom_shadow_color(images[image_menu].norm->bg, "menuBottomShadowColor");
337         PixColors[unfocusedMenuBottomShadowColor] =
338             get_bottom_shadow_color(images[image_menu].disabled->bg, "unfocusedMenuBottomShadowColor");
339         PixColors[menuTopShadowColor] = get_top_shadow_color(images[image_menu].norm->bg, "menuTopShadowColor");
340         PixColors[unfocusedMenuTopShadowColor] =
341             get_top_shadow_color(images[image_menu].disabled->bg, "unfocusedMenuTopShadowColor");
342     }
343     stored_palette(SAVE);
344 }
345 
346 void
set_pointer_colors(const char * fg_name,const char * bg_name)347 set_pointer_colors(const char *fg_name, const char *bg_name)
348 {
349     XColor fg, bg;
350 
351     if (fg_name) {
352         fg.pixel = get_color_by_name(fg_name, COLOR_NAME(pointerColor));
353     } else {
354         fg.pixel = PixColors[pointerColor];
355     }
356     XQueryColor(Xdisplay, cmap, &fg);
357     if (bg_name) {
358         bg.pixel = get_color_by_name(bg_name, COLOR_NAME(bgColor));
359     } else {
360         bg.pixel = PixColors[bgColor];
361     }
362     XQueryColor(Xdisplay, cmap, &bg);
363     XRecolorCursor(Xdisplay, TermWin_cursor, &fg, &bg);
364 }
365 
366 /* Create_Windows() - Open and map the window */
367 void
Create_Windows(int argc,char * argv[])368 Create_Windows(int argc, char *argv[])
369 {
370 
371     Cursor cursor;
372     XClassHint classHint;
373     XWMHints wmHint;
374     Atom prop = None;
375     CARD32 val;
376     int x = 0, y = 0, flags;
377     unsigned int width = 0, height = 0;
378     MWMHints mwmhints;
379 
380     if (BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_BORDERLESS)) {
381         mwmhints.flags = MWM_HINTS_DECORATIONS;
382         mwmhints.decorations = 0;
383     } else {
384         mwmhints.flags = 0;
385     }
386     Attributes.colormap = cmap;
387 
388     szHint.base_width = (2 * TermWin.internalBorder + ((BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SCROLLBAR))
389                                                        ? (scrollbar_get_width() + (2 * scrollbar_get_shadow())) : 0));
390     szHint.base_height = (2 * TermWin.internalBorder) + bbar_calc_docked_height(BBAR_DOCKED);
391 
392     flags = (rs_geometry ? XParseGeometry(rs_geometry, &x, &y, &width, &height) : 0);
393     D_X11(("XParseGeometry(geom, %d, %d, %d, %d)\n", x, y, width, height));
394 
395     if (flags & WidthValue) {
396         szHint.width = width;
397         szHint.flags |= USSize;
398     }
399     if (flags & HeightValue) {
400         szHint.height = height;
401         szHint.flags |= USSize;
402     }
403     TERM_WINDOW_SET_COLS(szHint.width);
404     TERM_WINDOW_SET_ROWS(szHint.height);
405 
406     change_font(1, NULL);
407 
408     if (flags & XValue) {
409         if (flags & XNegative) {
410             x += (DisplayWidth(Xdisplay, Xscreen) - (szHint.width + TermWin.internalBorder));
411         }
412         szHint.x = x;
413         szHint.flags |= USPosition;
414     }
415     if (flags & YValue) {
416         if (flags & YNegative) {
417             y += (DisplayHeight(Xdisplay, Xscreen) - (szHint.height + TermWin.internalBorder));
418         }
419         szHint.y = y;
420         szHint.flags |= USPosition;
421     }
422     if (flags) {
423         D_X11(("Geometry values after parsing:  %dx%d%+d%+d\n", width, height, x, y));
424     }
425 
426     Attributes.background_pixel = PixColors[bgColor];
427     Attributes.border_pixel = PixColors[bgColor];
428     D_X11(("Size Hints:  x %d, y %d.  Width/Height:  Base %dx%d, Minimum %dx%d, Current %dx%d, Increment %dx%d\n",
429            szHint.x, szHint.y, szHint.base_width, szHint.base_height, szHint.min_width, szHint.min_height, szHint.width,
430            szHint.height, szHint.width_inc, szHint.height_inc));
431     TermWin.parent = XCreateWindow(Xdisplay, Xroot, szHint.x, szHint.y, szHint.width, szHint.height, 0, Xdepth, InputOutput,
432 #ifdef PREFER_24BIT
433                                    Xvisual,
434 #else
435                                    CopyFromParent,
436 #endif
437                                    CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect, &Attributes);
438 
439     xterm_seq(ESCSEQ_XTERM_TITLE, rs_title);
440     xterm_seq(ESCSEQ_XTERM_ICONNAME, rs_iconName);
441     classHint.res_name = (char *) rs_name;
442     classHint.res_class = APL_NAME;
443     wmHint.window_group = TermWin.parent;
444     wmHint.input = ((BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_NO_INPUT)) ? False : True);
445     wmHint.initial_state = (BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_ICONIC) ? IconicState : NormalState);
446     wmHint.window_group = TermWin.parent;
447     wmHint.flags = (InputHint | StateHint | WindowGroupHint);
448 #ifdef PIXMAP_SUPPORT
449     set_icon_pixmap(rs_icon, &wmHint);
450 #endif
451 
452     XSetWMProperties(Xdisplay, TermWin.parent, NULL, NULL, argv, argc, &szHint, &wmHint, &classHint);
453     XSelectInput(Xdisplay, Xroot, PropertyChangeMask);
454     XSelectInput(Xdisplay, TermWin.parent,
455                  (KeyPressMask | FocusChangeMask | StructureNotifyMask | VisibilityChangeMask | PropertyChangeMask));
456     if (mwmhints.flags) {
457         prop = XInternAtom(Xdisplay, "_MOTIF_WM_HINTS", False);
458         XChangeProperty(Xdisplay, TermWin.parent, prop, prop, 32,
459                         PropModeReplace, (unsigned char *) &mwmhints, PROP_MWM_HINTS_ELEMENTS);
460     }
461 
462     /* vt cursor: Black-on-White is standard, but this is more popular */
463     TermWin_cursor = XCreateFontCursor(Xdisplay, XC_xterm);
464     set_pointer_colors(NULL, NULL);
465 
466     /* cursor (menu/scrollbar): Black-on-White */
467     cursor = XCreateFontCursor(Xdisplay, XC_left_ptr);
468 
469     /* the vt window */
470     TermWin.x = (((BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SCROLLBAR))
471                   && !(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SCROLLBAR_RIGHT)))
472                  ? (scrollbar_get_width() + (2 * scrollbar_get_shadow())) : 0);
473     TermWin.y = bbar_calc_docked_height(BBAR_DOCKED_TOP);
474     TermWin.vt = XCreateWindow(Xdisplay, TermWin.parent, TermWin.x, TermWin.y, szHint.width, szHint.height,
475                                0, Xdepth, InputOutput, CopyFromParent,
476                                CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWColormap, &Attributes);
477     D_X11(("Created terminal window 0x%08x at %dx%d\n", TermWin.vt, TermWin.x, TermWin.y));
478     if (!(background_is_pixmap()) && !(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_BORDERLESS))) {
479         XSetWindowBackground(Xdisplay, TermWin.vt, PixColors[bgColor]);
480         XClearWindow(Xdisplay, TermWin.vt);
481     }
482     XDefineCursor(Xdisplay, TermWin.vt, TermWin_cursor);
483     TermWin.mask =
484         (EnterWindowMask | LeaveWindowMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | Button1MotionMask |
485          Button2MotionMask | Button3MotionMask);
486     XSelectInput(Xdisplay, TermWin.vt, TermWin.mask);
487 
488     /* If the user wants a specific desktop, tell the WM that */
489     if (rs_desktop != -1) {
490         val = rs_desktop;
491         XChangeProperty(Xdisplay, TermWin.parent, props[PROP_DESKTOP], XA_CARDINAL, 32, PropModeReplace, (unsigned char *) &val, 1);
492     }
493 
494     /* Make window sticky if requested */
495     if (BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_STICKY)) {
496         XChangeProperty(Xdisplay, TermWin.parent, props[PROP_EWMH_STATE], XA_ATOM, 32, PropModeReplace,
497                         (unsigned char *) &props[PROP_EWMH_STATE_STICKY], 1);
498     }
499 
500     /* Set startup ID property if given by the launching application. */
501     if (getenv("DESKTOP_STARTUP_ID")) {
502         Atom atom;
503         unsigned char *tmp = (spif_uchar_t *) getenv("DESKTOP_STARTUP_ID");
504 
505         atom = XInternAtom(Xdisplay, "UTF8_STRING", False);
506         XChangeProperty(Xdisplay, TermWin.parent, props[PROP_EWMH_STARTUP_ID], atom, 8, PropModeReplace, tmp, strlen(tmp) + 1);
507         unsetenv("DESKTOP_STARTUP_ID");
508     }
509 
510     /* Set window opacity if needed. */
511     if ((props[PROP_EWMH_OPACITY] != None) && (rs_opacity != 0xff)) {
512         XChangeProperty(Xdisplay, TermWin.parent, props[PROP_EWMH_OPACITY],
513                         XA_CARDINAL, 32, PropModeReplace, (spif_uchar_t *) &rs_opacity, 1);
514         XChangeProperty(Xdisplay, TermWin.vt, props[PROP_EWMH_OPACITY],
515                         XA_CARDINAL, 32, PropModeReplace, (spif_uchar_t *) &rs_opacity, 1);
516     }
517 
518     /* We're done creating our windows.  Now let's initialize the event subsystem to handle them. */
519     event_init_subsystem((event_dispatcher_t) process_x_event, (event_dispatcher_init_t) event_init_primary_dispatcher);
520 
521     XMapWindow(Xdisplay, TermWin.vt);
522     XMapWindow(Xdisplay, TermWin.parent);
523     XSetWindowBackground(Xdisplay, TermWin.vt, PixColors[bgColor]);
524 
525     render_simage(images[image_bg].current, TermWin.vt, TermWin_TotalWidth(), TermWin_TotalHeight(), image_bg, 0);
526     if (image_mode_is(image_bg, MODE_AUTO)) {
527         enl_ipc_sync();
528     }
529 
530     /* graphics context for the vt window */
531     {
532         XGCValues gcvalue;
533 
534         gcvalue.font = TermWin.font->fid;
535         gcvalue.foreground = PixColors[fgColor];
536         gcvalue.background = PixColors[bgColor];
537         gcvalue.graphics_exposures = 0;
538         TermWin.gc = LIBAST_X_CREATE_GC(GCForeground | GCBackground | GCFont | GCGraphicsExposures, &gcvalue);
539         D_X11(("Created GC 0x%08x for TermWin.gc\n", TermWin.gc));
540     }
541 
542     if (BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_NO_CURSOR)) {
543         scr_cursor_visible(0);
544     }
545 }
546 
547 /* resize window keeping one point (determined by window geometry) in place */
548 void
resize_parent(unsigned int width,unsigned int height)549 resize_parent(unsigned int width, unsigned int height)
550 {
551     XWindowAttributes attr;
552 
553     if (!(BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_RESIZE_GRAVITY)) || !XGetWindowAttributes(Xdisplay, TermWin.parent, &attr)) {
554         XResizeWindow(Xdisplay, TermWin.parent, width, height);
555     } else {
556         Window junkwin;
557         int x, y, scr_w, scr_h;
558         int dx = 0, dy = 0;
559 
560         scr_w = WidthOfScreen(attr.screen);
561         scr_h = HeightOfScreen(attr.screen);
562         dx = attr.width - width;
563         dy = attr.height - height;
564         XTranslateCoordinates(Xdisplay, TermWin.parent, attr.root, 0, 0, &x, &y, &junkwin);
565         /* Check position of the center of the window */
566         if (x < (scr_w - attr.width) / 2) {
567             /* left half */
568             dx = 0;
569         } else if (x == (scr_w - attr.width) / 2) {
570             /* exact center */
571             dx /= 2;
572         }
573         if (y < (scr_h - attr.height) / 2) {
574             /* top half */
575             dy = 0;
576         } else if (y == (scr_h - attr.height) / 2) {
577             /* exact center */
578             dy /= 2;
579         }
580         D_X11(("Calling XMoveResizeWindow(Xdisplay, 0x%08x, %d + %d, %d + %d, %d, %d)\n", TermWin.parent, x, dx, y, dy, width,
581                height));
582         XMoveResizeWindow(Xdisplay, TermWin.parent, x + dx, y + dy, width, height);
583     }
584 }
585 
586 /* good for toggling 80/132 columns */
587 void
set_width(unsigned short width)588 set_width(unsigned short width)
589 {
590     unsigned short height = TERM_WINDOW_GET_REPORTED_ROWS();
591 
592     if (width != TERM_WINDOW_GET_REPORTED_COLS()) {
593         width = szHint.base_width + width * TermWin.fwidth;
594         height = szHint.base_height + height * TermWin.fheight;
595 
596         resize_parent(width, height);
597         handle_resize(width, height);
598     }
599 }
600 
601 void
update_size_hints(void)602 update_size_hints(void)
603 {
604     D_X11(("Called.\n"));
605     szHint.base_width = (2 * TermWin.internalBorder) + ((scrollbar_is_visible())? (scrollbar_trough_width()) : (0));
606     szHint.base_height = (2 * TermWin.internalBorder) + bbar_calc_docked_height(BBAR_DOCKED);
607 
608     szHint.width_inc = TermWin.fwidth;
609     szHint.height_inc = TermWin.fheight;
610 
611     D_X11(("Size Hints:  base width/height == %lux%lu, width/height increment == %lux%lu\n", szHint.base_width, szHint.base_height,
612            szHint.width_inc, szHint.height_inc));
613 
614     szHint.min_width = szHint.base_width + szHint.width_inc;
615     szHint.min_height = szHint.base_height + szHint.height_inc;
616     szHint.width = szHint.base_width + TERM_WINDOW_GET_WIDTH();
617     szHint.height = szHint.base_height + TERM_WINDOW_GET_HEIGHT();
618     D_X11(("             Minimum width/height == %lux%lu, width/height == %lux%lu\n", szHint.min_width, szHint.min_height,
619            szHint.width, szHint.height));
620 
621     szHint.flags = PMinSize | PResizeInc | PBaseSize;
622     XSetWMNormalHints(Xdisplay, TermWin.parent, &szHint);
623 }
624 
625 /* Resize terminal window and scrollbar window */
626 void
term_resize(int width,int height)627 term_resize(int width, int height)
628 {
629     static int last_width = 0, last_height = 0;
630 
631     D_X11(("term_resize(%d, %d)\n", width, height));
632     TERM_WINDOW_SET_WIDTH();
633     TERM_WINDOW_SET_HEIGHT();
634     D_X11((" -> New TermWin width/height == %lux%lu\n", TERM_WINDOW_GET_WIDTH(), TERM_WINDOW_GET_HEIGHT()));
635     width = TERM_WINDOW_FULL_WIDTH();
636     height = TERM_WINDOW_FULL_HEIGHT();
637     XMoveResizeWindow(Xdisplay, TermWin.vt, ((BITFIELD_IS_SET(eterm_options, ETERM_OPTIONS_SCROLLBAR_RIGHT)) ? (0)
638                                              : ((scrollbar_is_visible())? (scrollbar_trough_width()) : (0))),
639                       bbar_calc_docked_height(BBAR_DOCKED_TOP), width, height);
640     if (width != last_width || height != last_height) {
641         render_simage(images[image_bg].current, TermWin.vt, width, height, image_bg, 0);
642         scr_reset();
643         scr_touch();
644         if (image_mode_is(image_bg, MODE_AUTO)) {
645             enl_ipc_sync();
646         }
647         last_width = width;
648         last_height = height;
649     }
650 #ifdef USE_XIM
651     xim_set_status_position();
652 #endif
653 }
654 
655 /* Resize due to font change; update size hints and child windows */
656 void
parent_resize(void)657 parent_resize(void)
658 {
659     D_X11(("Called.\n"));
660     update_size_hints();
661     resize_parent(szHint.width, szHint.height);
662     D_X11((" -> New parent width/height == %lux%lu\n", szHint.width, szHint.height));
663     term_resize(szHint.width, szHint.height);
664     scrollbar_resize(szHint.width, szHint.height - bbar_calc_docked_height(BBAR_DOCKED));
665     bbar_resize_all(szHint.width);
666 }
667 
668 void
handle_resize(unsigned int width,unsigned int height)669 handle_resize(unsigned int width, unsigned int height)
670 {
671     static short first_time = 1;
672     int new_ncol = (width - szHint.base_width) / TermWin.fwidth;
673     int new_nrow = (height - szHint.base_height) / TermWin.fheight;
674 
675     D_EVENTS(("handle_resize(%u, %u)\n", width, height));
676 
677     if (first_time || (new_ncol != TERM_WINDOW_GET_REPORTED_ROWS()) || (new_nrow != TERM_WINDOW_GET_REPORTED_COLS())) {
678         TERM_WINDOW_SET_COLS(new_ncol);
679         TERM_WINDOW_SET_ROWS(new_nrow);
680         term_resize(width, height);
681         szHint.width = szHint.base_width + TERM_WINDOW_GET_WIDTH();
682         szHint.height = szHint.base_height + TERM_WINDOW_GET_HEIGHT();
683         D_X11((" -> New szHint.width/height == %lux%lu\n", szHint.width, szHint.height));
684         scrollbar_resize(width, szHint.height - bbar_calc_docked_height(BBAR_DOCKED));
685         bbar_resize_all(szHint.width);
686         first_time = 0;
687     }
688 }
689 
690 void
handle_move(int x,int y)691 handle_move(int x, int y)
692 {
693     int dx, dy;
694 
695     if ((TermWin.x != x) || (TermWin.y != y)) {
696         dx = abs(TermWin.x - x);
697         dy = abs(TermWin.y - y);
698         TermWin.x = x;
699         TermWin.y = y;
700         /* If we've moved an even multiple of the screen size, there's no
701            need to redraw trans/viewport images; the images will line up. */
702         if (image_mode_any(MODE_TRANS | MODE_VIEWPORT)) {
703             if ((dx % DisplayWidth(Xdisplay, Xscreen)) || (dy % DisplayHeight(Xdisplay, Xscreen))) {
704                 redraw_images_by_mode(MODE_TRANS | MODE_VIEWPORT);
705             }
706         }
707     }
708 }
709 
710 #ifdef XTERM_COLOR_CHANGE
711 void
stored_palette(char op)712 stored_palette(char op)
713 {
714     static Pixel default_colors[NRS_COLORS + EXTRA_COLORS];
715     static unsigned char stored = 0;
716     unsigned int i;
717 
718     if (op == SAVE) {
719         for (i = 0; i < NRS_COLORS; i++) {
720             default_colors[i] = PixColors[i];
721         }
722         stored = 1;
723     } else if (op == RESTORE && stored) {
724         for (i = 0; i < NRS_COLORS; i++) {
725             PixColors[i] = default_colors[i];
726         }
727     }
728 }
729 
730 void
set_window_color(int idx,const char * color)731 set_window_color(int idx, const char *color)
732 {
733     XColor xcol;
734     int i;
735 
736     D_X11(("idx == %d, color == \"%s\"\n", idx, NONULL(color)));
737 
738     if (!color || *color == '\0')
739         return;
740 
741     /* handle color aliases */
742     if (isdigit(*color)) {
743         i = atoi(color);
744         if (i >= 8 && i <= 15) {        /* bright colors */
745             i -= 8;
746             PixColors[idx] = PixColors[minBright + i];
747         } else if (i >= 0 && i <= 7) {  /* normal colors */
748             PixColors[idx] = PixColors[minColor + i];
749         } else {
750             libast_print_warning("Color index %d is invalid.\n", i);
751             return;
752         }
753     } else if (XParseColor(Xdisplay, cmap, color, &xcol)) {
754         if (!XAllocColor(Xdisplay, cmap, &xcol)) {
755             libast_print_warning("Unable to allocate \"%s\" in the color map.\n", color);
756             return;
757         }
758         if ((idx > maxBright) && (idx < 256) && (PixColors[idx])) {
759             XFreeColors(Xdisplay, cmap, (unsigned long *) &(PixColors[idx]), 1, 0);
760         }
761         PixColors[idx] = xcol.pixel;
762     } else {
763         libast_print_warning("Unable to resolve \"%s\" as a color name.\n", color);
764         return;
765     }
766     set_colorfgbg();
767     scr_touch();
768     scr_refresh(DEFAULT_REFRESH);
769     redraw_image(image_bg);
770 }
771 #endif /* XTERM_COLOR_CHANGE */
772 
773 Window
find_window_by_coords(Window win,int win_x,int win_y,int rel_x,int rel_y)774 find_window_by_coords(Window win, int win_x, int win_y, int rel_x, int rel_y)
775 {
776     Window *children = NULL;
777     XWindowAttributes attr;
778     Window child = 0, parent_win = 0, root_win = 0;
779     int i;
780     unsigned int ww, wh, num;
781     int wx, wy;
782 
783     D_X11(("win 0x%08x at %d, %d.  Coords are %d, %d.\n", win, win_x, win_y, rel_x, rel_y));
784 
785     /* Bad or invisible window. */
786     if ((!XGetWindowAttributes(Xdisplay, win, &attr)) || (attr.map_state != IsViewable)) {
787         return None;
788     }
789     wx = attr.x + win_x;
790     wy = attr.y + win_y;
791     ww = attr.width;
792     wh = attr.height;
793 
794     if (!((rel_x >= wx) && (rel_y >= wy) && (rel_x < (int) (wx + ww)) && (rel_y < (int) (wy + wh)))) {
795         return None;
796     }
797 
798     if (!XQueryTree(Xdisplay, win, &root_win, &parent_win, &children, &num)) {
799         return win;
800     }
801     if (children) {
802         D_X11(("%d children.\n", num));
803         for (i = num - 1; i >= 0; i--) {
804             D_X11(("Trying children[%d] (0x%08x)\n", i, children[i]));
805             if ((child = find_window_by_coords(children[i], wx, wy, rel_x, rel_y)) != None) {
806                 D_X11(("Match!\n"));
807                 XFree(children);
808                 return child;
809             }
810         }
811         D_X11(("XFree(children)\n"));
812         XFree(children);
813     }
814     D_X11(("Returning 0x%08x\n", win));
815     return win;
816 }
817