Add support for copying to clipboard (based on patch by Dana Jansens).
[dana/urxvt.git] / src / main.C
1 /*----------------------------------------------------------------------*
2  * File:        main.C
3  *----------------------------------------------------------------------*
4  *
5  * All portions of code are copyright by their respective author/s.
6  * Copyright (c) 1992      John Bovey, University of Kent at Canterbury <jdb@ukc.ac.uk>
7  *                              - original version
8  * Copyright (c) 1994      Robert Nation <nation@rocket.sanders.lockheed.com>
9  *                              - extensive modifications
10  * Copyright (c) 1995      Garrett D'Amore <garrett@netcom.com>
11  * Copyright (c) 1997      mj olesen <olesen@me.QueensU.CA>
12  *                              - extensive modifications
13  * Copyright (c) 1997,1998 Oezguer Kesim <kesim@math.fu-berlin.de>
14  * Copyright (c) 1998-2001 Geoff Wing <gcw@pobox.com>
15  *                              - extensive modifications
16  * Copyright (c) 2003-2008 Marc Lehmann <pcg@goof.com>
17  *
18  * This program is free software; you can redistribute it and/or modify
19  * it under the terms of the GNU General Public License as published by
20  * the Free Software Foundation; either version 2 of the License, or
21  * (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program; if not, write to the Free Software
30  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31  *---------------------------------------------------------------------*/
32
33 #include "../config.h"          /* NECESSARY */
34 #include "rxvt.h"               /* NECESSARY */
35 #include "keyboard.h"
36 #include "rxvtperl.h"
37
38 #include <limits>
39
40 #include <csignal>
41 #include <cstring>
42
43 #include <termios.h>
44
45 #ifdef HAVE_XSETLOCALE
46 # define X_LOCALE
47 # include <X11/Xlocale.h>
48 #else
49 # ifdef HAVE_SETLOCALE
50 #  include <clocale>
51 # endif
52 #endif
53
54 vector<rxvt_term *> rxvt_term::termlist;
55
56 // used to tell global functions which terminal instance is "active"
57 rxvt_t rxvt_current_term;
58
59 static char curlocale[128], savelocale[128];
60
61 bool
62 rxvt_set_locale (const char *locale) NOTHROW
63 {
64   int size = strlen (locale) + 1;
65
66   if (size > sizeof (curlocale))
67     rxvt_fatal ("locale string too long, aborting.\n");
68
69   if (!locale || !memcmp (locale, curlocale, size))
70     return false;
71
72   memcpy (curlocale, locale, size);
73   setlocale (LC_CTYPE, curlocale);
74   return true;
75 }
76
77 void
78 rxvt_push_locale (const char *locale) NOTHROW
79 {
80   strcpy (savelocale, curlocale);
81   rxvt_set_locale (locale);
82 }
83
84 void
85 rxvt_pop_locale () NOTHROW
86 {
87   rxvt_set_locale (savelocale);
88 }
89
90 #if ENABLE_COMBINING
91 class rxvt_composite_vec rxvt_composite;
92
93 text_t rxvt_composite_vec::compose (unicode_t c1, unicode_t c2)
94 {
95   compose_char *cc;
96
97   // break compose chains, as stupid readline really likes to duplicate
98   // composing characters for some reason near the end of a line.
99   cc = (*this)[c1];
100   while (cc)
101     {
102       if (cc->c2 == c2) return c1;
103       cc = (*this)[cc->c1];
104     }
105
106   // check to see wether this combination already exists otherwise
107   for (cc = v.end (); cc-- > v.begin (); )
108     {
109       if (cc->c1 == c1 && cc->c2 == c2)
110         return COMPOSE_LO + (cc - v.begin ());
111     }
112
113   // allocate a new combination
114   if (v.size () == COMPOSE_HI - COMPOSE_LO + 1)
115     {
116       static int seen;
117
118       if (!seen++)
119         fprintf (stderr, "too many unrepresentable composite characters, try --enable-unicode3\n");
120
121       return REPLACEMENT_CHAR;
122     }
123
124   v.push_back (compose_char (c1, c2));
125
126   return v.size () - 1 + COMPOSE_LO;
127 }
128
129 int rxvt_composite_vec::expand (unicode_t c, wchar_t *r)
130 {
131   compose_char *cc = (*this)[c];
132
133   if (!cc)
134     {
135       if (r) *r = c;
136       return 1;
137     }
138
139   int len = expand (cc->c1, r);
140
141   if (r) r += len;
142
143   if (cc->c2 != NOCHAR)
144     {
145       len++;
146       if (r) *r++ = cc->c2;
147     }
148
149   return len;
150
151 }
152 #endif
153
154 rxvt_term::rxvt_term ()
155 {
156 #if HAVE_BG_PIXMAP
157   update_background_ev.set<rxvt_term, &rxvt_term::update_background_cb> (this);
158 #endif
159 #ifdef CURSOR_BLINK
160   cursor_blink_ev.set     <rxvt_term, &rxvt_term::cursor_blink_cb> (this); cursor_blink_ev.set (0., CURSOR_BLINK_INTERVAL);
161 #endif
162 #ifdef TEXT_BLINK
163   text_blink_ev.set       <rxvt_term, &rxvt_term::text_blink_cb>   (this); text_blink_ev.set (0., TEXT_BLINK_INTERVAL);
164 #endif
165 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
166   cont_scroll_ev.set      <rxvt_term, &rxvt_term::cont_scroll_cb>  (this);
167 #endif
168 #ifdef SELECTION_SCROLLING
169   sel_scroll_ev.set       <rxvt_term, &rxvt_term::sel_scroll_cb>   (this);
170 #endif
171 #if defined(MOUSE_WHEEL) && defined(MOUSE_SLIP_WHEELING)
172   slip_wheel_ev.set       <rxvt_term, &rxvt_term::slip_wheel_cb>   (this);
173 #endif
174 #if ENABLE_TRANSPARENCY || ENABLE_PERL
175   rootwin_ev.set          <rxvt_term, &rxvt_term::rootwin_cb> (this),
176 #endif
177   scrollbar_ev.set        <rxvt_term, &rxvt_term::x_cb>       (this),
178 #ifdef USE_XIM
179   im_ev.set               <rxvt_term, &rxvt_term::im_cb>      (this),
180 #endif
181 #ifdef POINTER_BLANK
182   pointer_ev.set          <rxvt_term, &rxvt_term::pointer_cb> (this);
183 #endif
184 #ifndef NO_BELL
185   bell_ev.set             <rxvt_term, &rxvt_term::bell_cb>    (this);
186 #endif
187   child_ev.set            <rxvt_term, &rxvt_term::child_cb>   (this);
188   flush_ev.set            <rxvt_term, &rxvt_term::flush_cb>   (this);
189   destroy_ev.set          <rxvt_term, &rxvt_term::destroy_cb> (this);
190   pty_ev.set              <rxvt_term, &rxvt_term::pty_cb>     (this);
191   incr_ev.set             <rxvt_term, &rxvt_term::incr_cb>    (this);
192   termwin_ev.set          <rxvt_term, &rxvt_term::x_cb>       (this);
193   vt_ev.set               <rxvt_term, &rxvt_term::x_cb>       (this);
194
195   cmdbuf_ptr = cmdbuf_endp = cmdbuf_base;
196
197   termlist.push_back (this);
198
199 #ifdef KEYSYM_RESOURCE
200   keyboard = new keyboard_manager;
201 #endif
202 }
203
204 // clean up the most important stuff, do *not* call x or free mem etc.
205 // for use before an emergency exit
206 void
207 rxvt_term::emergency_cleanup ()
208 {
209   if (cmd_pid)
210     kill (-cmd_pid, SIGHUP);
211
212   pty_ev.stop ();
213   delete pty; pty = 0;
214 }
215
216 rxvt_term::~rxvt_term ()
217 {
218   termlist.erase (find (termlist.begin (), termlist.end(), this));
219
220   emergency_cleanup ();
221
222 #if ENABLE_STYLES
223   for (int i = RS_styleCount; --i; )
224     if (fontset[i] != fontset[0])
225       delete fontset[i];
226 #endif
227   delete fontset[0];
228
229 #ifdef HAVE_BG_PIXMAP
230   bgPixmap.destroy ();
231 #endif
232 #ifdef HAVE_AFTERIMAGE
233   if (asv)
234     destroy_asvisual (asv, 0);
235   if (asimman)
236     destroy_image_manager (asimman, 0);
237 #endif
238
239   if (display)
240     {
241       selection_clear ();
242       selection_clear (true);
243
244 #ifdef USE_XIM
245       im_destroy ();
246 #endif
247       scrollBar.destroy ();
248       if (gc)   XFreeGC (dpy, gc);
249
250       delete drawable;
251       // destroy all windows
252       if (parent[0])
253         XDestroyWindow (dpy, parent[0]);
254
255       for (int i = 0; i < TOTAL_COLORS; i++)
256         if (ISSET_PIXCOLOR (i))
257           {
258             pix_colors_focused   [i].free (this);
259 #if OFF_FOCUS_FADING
260             pix_colors_unfocused [i].free (this);
261 #endif
262           }
263
264       clear ();
265
266       display->flush (); /* ideally .put should do this */
267       displays.put (display);
268     }
269
270   scr_release ();
271
272   /* clear all resources */
273   for (int i = 0; i < allocated.size (); i++)
274     free (allocated [i]);
275
276   free (selection.text);
277   free (selection.clip_text);
278   // TODO: manage env vars in child only(!)
279   free (env_display);
280   free (env_term);
281   free (locale);
282   free (v_buffer);
283   free (incr_buf);
284
285   delete envv;
286   delete argv;
287
288 #ifdef KEYSYM_RESOURCE
289   delete keyboard;
290 #endif
291 #ifndef NO_RESOURCES
292   XrmDestroyDatabase (option_db);
293 #endif
294 }
295
296 // child has exited, usually destroys
297 void
298 rxvt_term::child_cb (ev::child &w, int status)
299 {
300   HOOK_INVOKE ((this, HOOK_CHILD_EXIT, DT_INT, status, DT_END));
301
302   cmd_pid = 0;
303
304   if (!option (Opt_hold))
305     destroy ();
306 }
307
308 void
309 rxvt_term::destroy ()
310 {
311   if (destroy_ev.is_active ())
312     return;
313
314   HOOK_INVOKE ((this, HOOK_DESTROY, DT_END));
315
316 #if ENABLE_OVERLAY
317   scr_overlay_off ();
318 #endif
319
320   if (display)
321     {
322 #if USE_XIM
323       im_ev.stop (display);
324 #endif
325       scrollbar_ev.stop (display);
326 #if ENABLE_TRANSPARENCY || ENABLE_PERL
327       rootwin_ev.stop (display);
328 #endif
329       incr_ev.stop ();
330       termwin_ev.stop (display);
331       vt_ev.stop (display);
332     }
333
334   flush_ev.stop ();
335   pty_ev.stop ();
336 #ifdef CURSOR_BLINK
337   cursor_blink_ev.stop ();
338 #endif
339 #ifdef TEXT_BLINK
340   text_blink_ev.stop ();
341 #endif
342 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
343   cont_scroll_ev.stop ();
344 #endif
345 #ifdef SELECTION_SCROLLING
346   sel_scroll_ev.stop ();
347 #endif
348 #ifdef POINTER_BLANK
349   pointer_ev.stop ();
350 #endif
351
352   destroy_ev.start ();
353 }
354
355 void
356 rxvt_term::destroy_cb (ev::idle &w, int revents)
357 {
358   make_current ();
359
360   delete this;
361 }
362
363 /*----------------------------------------------------------------------*/
364 /*
365  * Exit gracefully, clearing the utmp entry and restoring tty attributes
366  * TODO: if debugging, this should free up any known resources if we can
367  */
368 static XErrorHandler old_xerror_handler;
369
370 static void
371 rxvt_emergency_cleanup ()
372 {
373   for (rxvt_term **t = rxvt_term::termlist.begin (); t < rxvt_term::termlist.end (); t++)
374     (*t)->emergency_cleanup ();
375 }
376
377 #if !ENABLE_MINIMAL
378 static void
379 print_x_error (Display *dpy, XErrorEvent *event)
380 {
381     char buffer[BUFSIZ];
382     char mesg[BUFSIZ];
383     char number[32];
384     char *mtype = "XlibMessage";
385     XGetErrorText(dpy, event->error_code, buffer, BUFSIZ);
386     XGetErrorDatabaseText(dpy, mtype, "XError", "X Error", mesg, BUFSIZ);
387     rxvt_warn ("An X Error occured, trying to continue after report.\n");
388     rxvt_warn ("%s:  %s\n", mesg, buffer);
389     XGetErrorDatabaseText(dpy, mtype, "MajorCode", "Request Major code %d", mesg, BUFSIZ);
390     rxvt_warn (strncat (mesg, "\n", BUFSIZ), event->request_code);
391     sprintf(number, "%d", event->request_code);
392     XGetErrorDatabaseText(dpy, "XRequest", number, "", buffer, BUFSIZ);
393     rxvt_warn ("(which is %s)\n", buffer);
394     if (event->request_code >= 128) {
395         XGetErrorDatabaseText(dpy, mtype, "MinorCode", "Request Minor code %d",
396                               mesg, BUFSIZ);
397         rxvt_warn (strncat (mesg, "\n", BUFSIZ), event->minor_code);
398     }
399     if ((event->error_code == BadWindow) ||
400                (event->error_code == BadPixmap) ||
401                (event->error_code == BadCursor) ||
402                (event->error_code == BadFont) ||
403                (event->error_code == BadDrawable) ||
404                (event->error_code == BadColor) ||
405                (event->error_code == BadGC) ||
406                (event->error_code == BadIDChoice) ||
407                (event->error_code == BadValue) ||
408                (event->error_code == BadAtom)) {
409         if (event->error_code == BadValue)
410             XGetErrorDatabaseText(dpy, mtype, "Value", "Value 0x%x",
411                                   mesg, BUFSIZ);
412         else if (event->error_code == BadAtom)
413             XGetErrorDatabaseText(dpy, mtype, "AtomID", "AtomID 0x%x",
414                                   mesg, BUFSIZ);
415         else
416             XGetErrorDatabaseText(dpy, mtype, "ResourceID", "ResourceID 0x%x",
417                                   mesg, BUFSIZ);
418         rxvt_warn (strncat (mesg, "\n", BUFSIZ), event->resourceid);
419     }
420     XGetErrorDatabaseText(dpy, mtype, "ErrorSerial", "Error Serial #%d",
421                           mesg, BUFSIZ);
422     rxvt_warn (strncat (mesg, "\n", BUFSIZ), event->serial);
423 }
424 #endif
425
426 int
427 rxvt_xerror_handler (Display *display, XErrorEvent *event)
428 {
429   if (GET_R->allowedxerror == -1)
430     GET_R->allowedxerror = event->error_code;
431   else
432     {
433       // GET_R is most likely not the terminal which caused the error,
434       // so just output the error and continue
435 #if ENABLE_MINIMAL
436       old_xerror_handler (display, event);
437 #else
438       print_x_error (display, event);
439 #endif
440     }
441
442   return 0;
443 }
444
445 int
446 rxvt_xioerror_handler (Display *display)
447 {
448   rxvt_warn ("X connection to '%s' broken, unable to recover, exiting.\n",
449              DisplayString (display));
450   rxvt_emergency_cleanup ();
451   _exit (EXIT_FAILURE);
452 }
453
454 static struct sig_handlers
455 {
456   ev::sig sw_term, sw_int;
457
458   /*
459    * Catch a fatal signal and tidy up before quitting
460    */
461   void sig_term (ev::sig &w, int revents);
462
463   sig_handlers ()
464   {
465     sw_term.set<sig_handlers, &sig_handlers::sig_term> (this);
466     sw_int .set<sig_handlers, &sig_handlers::sig_term> (this);
467   }
468 } sig_handlers;
469
470 void
471 sig_handlers::sig_term (ev::sig &w, int revents)
472 {
473   rxvt_emergency_cleanup ();
474   w.stop ();
475   kill (getpid (), w.signum);
476 }
477
478 char **rxvt_environ; // startup environment
479
480 void
481 rxvt_init ()
482 {
483   ptytty::init ();
484
485   if (!ev_default_loop (0))
486     rxvt_fatal ("cannot initialise libev (bad value for LIBEV_METHODS?)\n");
487
488   rxvt_environ = environ;
489
490   signal (SIGHUP,  SIG_IGN);
491   signal (SIGPIPE, SIG_IGN);
492
493   sig_handlers.sw_term.start (SIGTERM); ev_unref ();
494   sig_handlers.sw_int.start  (SIGINT);  ev_unref ();
495
496   /* need to trap SIGURG for SVR4 (Unixware) rlogin */
497   /* signal (SIGURG, SIG_DFL); */
498
499   old_xerror_handler = XSetErrorHandler ((XErrorHandler) rxvt_xerror_handler);
500   // TODO: handle this with exceptions and tolerate the memory loss
501   XSetIOErrorHandler (rxvt_xioerror_handler);
502
503   XrmInitialize ();
504 }
505
506 /*----------------------------------------------------------------------*/
507 /*
508  * window size/position calculations for XSizeHint and other storage.
509  * if width/height are non-zero then override calculated width/height
510  */
511 void
512 rxvt_term::window_calc (unsigned int newwidth, unsigned int newheight)
513 {
514   short recalc_x, recalc_y;
515   int x, y, flags;
516   unsigned int w, h;
517   unsigned int max_width, max_height;
518
519   szHint.flags = PMinSize | PResizeInc | PBaseSize | PWinGravity;
520   szHint.win_gravity = NorthWestGravity;
521   /* szHint.min_aspect.x = szHint.min_aspect.y = 1; */
522
523   recalc_x = recalc_y = 0;
524   flags = 0;
525
526   if (!parsed_geometry)
527     {
528       parsed_geometry = 1;
529
530       if (rs[Rs_geometry])
531         flags = XParseGeometry (rs[Rs_geometry], &x, &y, &w, &h);
532
533       if (flags & WidthValue)
534         {
535           if (!w)
536             rxvt_fatal ("illegal window geometry (width and height must be non-zero), aborting.\n");
537
538           ncol = clamp (w, 1, std::numeric_limits<int16_t>::max ());
539           szHint.flags |= USSize;
540         }
541
542       if (flags & HeightValue)
543         {
544           if (!h)
545             rxvt_fatal ("illegal window geometry (width and height must be non-zero), aborting.\n");
546
547           nrow = clamp (h, 1, std::numeric_limits<int16_t>::max ());
548           szHint.flags |= USSize;
549         }
550
551       if (flags & XValue)
552         {
553           szHint.x = x;
554           szHint.flags |= USPosition;
555
556           if (flags & XNegative)
557             {
558               recalc_x = 1;
559               szHint.win_gravity = NorthEastGravity;
560             }
561         }
562
563       if (flags & YValue)
564         {
565           szHint.y = y;
566           szHint.flags |= USPosition;
567
568           if (flags & YNegative)
569             {
570               recalc_y = 1;
571
572               if (szHint.win_gravity == NorthEastGravity)
573                 szHint.win_gravity = SouthEastGravity;
574               else
575                 szHint.win_gravity = SouthWestGravity;
576             }
577         }
578     }
579
580   /* TODO: BOUNDS */
581   width = ncol * fwidth;
582   height = nrow * fheight;
583   max_width = MAX_COLS * fwidth;
584   max_height = MAX_ROWS * fheight;
585
586   szHint.base_width = szHint.base_height = 2 * int_bwidth;
587
588   window_vt_x = window_vt_y = int_bwidth;
589
590   if (scrollBar.state)
591     {
592       int sb_w = scrollBar.total_width ();
593       szHint.base_width += sb_w;
594
595       if (!option (Opt_scrollBar_right))
596         window_vt_x += sb_w;
597     }
598
599   szHint.width_inc  = fwidth;
600   szHint.height_inc = fheight;
601   szHint.min_width  = szHint.base_width + szHint.width_inc;
602   szHint.min_height = szHint.base_height + szHint.height_inc;
603
604   if (newwidth && newwidth - szHint.base_width < max_width)
605     {
606       szHint.width = newwidth;
607       width = newwidth - szHint.base_width;
608     }
609   else
610     {
611       min_it (width, max_width);
612       szHint.width = szHint.base_width + width;
613     }
614
615   if (newheight && newheight - szHint.base_height < max_height)
616     {
617       szHint.height = newheight;
618       height = newheight - szHint.base_height;
619     }
620   else
621     {
622       min_it (height, max_height);
623       szHint.height = szHint.base_height + height;
624     }
625
626   if (recalc_x)
627     szHint.x += DisplayWidth  (dpy, display->screen) - szHint.width  - 2 * ext_bwidth;
628
629   if (recalc_y)
630     szHint.y += DisplayHeight (dpy, display->screen) - szHint.height - 2 * ext_bwidth;
631
632   ncol = width  / fwidth;
633   nrow = height / fheight;
634 }
635
636 /*----------------------------------------------------------------------*/
637 /*
638  * Tell the teletype handler what size the window is.
639  * Called after a window size change.
640  */
641 void
642 rxvt_term::tt_winch ()
643 {
644   if (pty->pty < 0)
645     return;
646
647   struct winsize ws;
648
649   ws.ws_col = ncol;
650   ws.ws_row = nrow;
651   ws.ws_xpixel = width;
652   ws.ws_ypixel = height;
653   ioctl (pty->pty, TIOCSWINSZ, &ws);
654
655 #if 0
656   // TIOCSWINSZ is supposed to do this automatically and correctly
657   if (cmd_pid)               /* force through to the command */
658     kill (-cmd_pid, SIGWINCH);
659 #endif
660 }
661
662 /*----------------------------------------------------------------------*/
663 /* set_fonts () - load and set the various fonts
664  *
665  * init = 1   - initialize
666  *
667  * fontname == FONT_UP  - switch to bigger font
668  * fontname == FONT_DN  - switch to smaller font
669  */
670 bool
671 rxvt_term::set_fonts ()
672 {
673   rxvt_fontset *fs = new rxvt_fontset (this);
674   rxvt_fontprop prop;
675
676   if (!fs
677       || !fs->populate (rs[Rs_font] ? rs[Rs_font] : "fixed")
678       || !fs->realize_font (1))
679     {
680       delete fs;
681       return false;
682     }
683
684 #if ENABLE_STYLES
685   for (int i = RS_styleCount; --i; )
686     if (fontset[i] != fontset[0])
687       delete fontset[i];
688 #endif
689
690   delete fontset[0];
691   fontset[0] = fs;
692
693   prop = (*fs)[1]->properties ();
694   prop.height += lineSpace;
695   prop.width += letterSpace;
696
697   fs->set_prop (prop, false);
698
699   fwidth  = prop.width;
700   fheight = prop.height;
701   fbase   = prop.ascent;
702
703   for (int style = 1; style < 4; style++)
704     {
705 #if ENABLE_STYLES
706       const char *res = rs[Rs_font + style];
707
708       if (res && !*res)
709         fontset[style] = fontset[0];
710       else
711         {
712           fontset[style] = fs = new rxvt_fontset (this);
713           rxvt_fontprop prop2 = prop;
714
715           if (res)
716             {
717               fs->populate (res);
718               fs->set_prop (prop2, false);
719             }
720           else
721             {
722               fs->populate (fontset[0]->fontdesc);
723
724               if (SET_STYLE (0, style) & RS_Bold)   prop2.weight = rxvt_fontprop::bold;
725               if (SET_STYLE (0, style) & RS_Italic) prop2.slant  = rxvt_fontprop::italic;
726
727               fs->set_prop (prop2, true);
728             }
729
730         }
731 #else
732       fontset[style] = fontset[0];
733 #endif
734     }
735
736   if (parent[0])
737     {
738       resize_all_windows (0, 0, 0);
739       scr_remap_chars ();
740       scr_touch (true);
741     }
742
743   return true;
744 }
745
746 void
747 rxvt_term::set_string_property (Atom prop, const char *str, int len)
748 {
749   XChangeProperty (dpy, parent[0],
750                    prop, XA_STRING, 8, PropModeReplace,
751                    (const unsigned char *)str, len >= 0 ? len : strlen (str));
752 }
753
754 void
755 rxvt_term::set_utf8_property (Atom prop, const char *str, int len)
756 {
757   wchar_t *ws = rxvt_mbstowcs (str, len);
758   char *s = rxvt_wcstoutf8 (ws);
759
760   XChangeProperty (dpy, parent[0],
761                    prop, xa[XA_UTF8_STRING], 8, PropModeReplace,
762                    (const unsigned char *)s, strlen (s));
763
764   free (s);
765   free (ws);
766 }
767
768 /*----------------------------------------------------------------------*/
769 /*----------------------------------------------------------------------*/
770 /* xterm sequences - title, iconName, color (exptl) */
771 void
772 rxvt_term::set_title (const char *str)
773 {
774   set_string_property (XA_WM_NAME, str);
775 #if ENABLE_EWMH
776   set_utf8_property (xa[XA_NET_WM_NAME], str);
777 #endif
778 }
779
780 void
781 rxvt_term::set_icon_name (const char *str)
782 {
783   set_string_property (XA_WM_ICON_NAME, str);
784 #if ENABLE_EWMH
785   set_utf8_property (xa[XA_NET_WM_ICON_NAME], str);
786 #endif
787 }
788
789 void
790 rxvt_term::set_window_color (int idx, const char *color)
791 {
792 #ifdef XTERM_COLOR_CHANGE
793   rxvt_color xcol;
794
795   if (color == NULL || *color == '\0')
796     return;
797
798   color = strdup (color);
799   allocated.push_back ((void *)color);
800   rs[Rs_color + idx] = color;
801
802   /* handle color aliases */
803   if (isdigit (*color))
804     {
805       int i = atoi (color);
806
807       if (i >= 8 && i <= 15)
808         {
809           /* bright colors */
810           pix_colors_focused[idx] = pix_colors_focused[minBrightCOLOR + i - 8];
811           goto done;
812         }
813
814       if (i >= 0 && i <= 7)
815         {
816           /* normal colors */
817           pix_colors_focused[idx] = pix_colors_focused[minCOLOR + i];
818           goto done;
819         }
820     }
821
822   set_color (xcol, color);
823
824   /*
825    * FIXME: should free colors here, but no idea how to do it so instead,
826    * so just keep gobbling up the colormap
827    */
828
829   pix_colors_focused[idx] = xcol;
830
831 done:
832   /*TODO: handle Color_BD, scrollbar background, etc. */
833
834   update_fade_color (idx);
835   recolour_cursor ();
836   scr_recolour ();
837 #endif /* XTERM_COLOR_CHANGE */
838 }
839
840 void
841 rxvt_term::recolour_cursor ()
842 {
843   XColor fg, bg;
844
845   (ISSET_PIXCOLOR (Color_pointer_fg)
846      ? pix_colors_focused[Color_pointer_fg]
847      : pix_colors_focused[Color_fg]).get (fg);
848
849   (ISSET_PIXCOLOR (Color_pointer_bg)
850      ? pix_colors_focused[Color_pointer_bg]
851      : pix_colors_focused[Color_bg]).get (bg);
852
853   XRecolorCursor (dpy, TermWin_cursor, &fg, &bg);
854 }
855
856 /*----------------------------------------------------------------------*/
857 /*
858  * find if fg/bg matches any of the normal (low-intensity) colors
859  */
860 void
861 rxvt_term::set_colorfgbg ()
862 {
863   unsigned int i;
864   const char *xpmb = "";
865   char fstr[sizeof ("default") + 1], bstr[sizeof ("default") + 1];
866
867   strcpy (fstr, "default");
868   strcpy (bstr, "default");
869   for (i = Color_Black; i <= Color_White; i++)
870     if (pix_colors[Color_fg] == pix_colors[i])
871       {
872         sprintf (fstr, "%d", (i - Color_Black));
873         break;
874       }
875
876   for (i = Color_Black; i <= Color_White; i++)
877     if (pix_colors[Color_bg] == pix_colors[i])
878       {
879         sprintf (bstr, "%d", (i - Color_Black));
880 #ifdef BG_IMAGE_FROM_FILE
881         xpmb = "default;";
882 #endif
883         break;
884       }
885
886   sprintf (env_colorfgbg, "COLORFGBG=%s;%s%s", fstr, xpmb, bstr);
887 }
888
889 /*----------------------------------------------------------------------*/
890
891 bool
892 rxvt_term::set_color (rxvt_color &color, const char *name)
893 {
894   if (color.set (this, name))
895     return true;
896
897   rxvt_warn ("can't get colour '%s', continuing without.\n", name);
898   return false;
899 }
900
901 void
902 rxvt_term::alias_color (int dst, int src)
903 {
904   pix_colors[dst].set (this, rs[Rs_color + dst] = rs[Rs_color + src]);
905 }
906
907 /* -------------------------------------------------------------------- *
908  * -                         WINDOW RESIZING                          - *
909  * -------------------------------------------------------------------- */
910 void
911 rxvt_term::resize_all_windows (unsigned int newwidth, unsigned int newheight, int ignoreparent)
912 {
913   int fix_screen;
914   int old_width  = szHint.width;
915   int old_height = szHint.height;
916
917   window_calc (newwidth, newheight);
918
919   bool set_hint = !HOOK_INVOKE ((this, HOOK_RESIZE_ALL_WINDOWS, DT_INT, newwidth, DT_INT, newheight, DT_END));
920
921   // to avoid races between us and the wm, we clear the incremental size hints around the xresizewindow
922   if (set_hint)
923     {
924       szHint.flags &= ~(PBaseSize | PResizeInc);
925       XSetWMNormalHints (dpy, parent[0], &szHint);
926       szHint.flags |= PBaseSize | PResizeInc;
927     }
928
929   if (!ignoreparent)
930     {
931 #ifdef SMART_RESIZE
932       /*
933        * resize by Marius Gedminas <marius.gedminas@uosis.mif.vu.lt>
934        * reposition window on resize depending on placement on screen
935        */
936       int x, y, x1, y1;
937       int dx, dy;
938       unsigned int unused_w1, unused_h1, unused_b1, unused_d1;
939       Window unused_cr;
940
941       XTranslateCoordinates (dpy, parent[0], display->root,
942                              0, 0, &x, &y, &unused_cr);
943       XGetGeometry (dpy, parent[0], &unused_cr, &x1, &y1,
944                     &unused_w1, &unused_h1, &unused_b1, &unused_d1);
945       /*
946        * if display->root isn't the parent window, a WM will probably have offset
947        * our position for handles and decorations.  Counter it
948        */
949       if (x1 != x || y1 != y)
950         {
951           x -= x1;
952           y -= y1;
953         }
954
955       x1 = (DisplayWidth  (dpy, display->screen) - old_width ) / 2;
956       y1 = (DisplayHeight (dpy, display->screen) - old_height) / 2;
957       dx = old_width  - szHint.width;
958       dy = old_height - szHint.height;
959
960       /* Check position of the center of the window */
961       if (x < x1)             /* left half */
962         dx = 0;
963       else if (x == x1)       /* exact center */
964         dx /= 2;
965       if (y < y1)             /* top half */
966         dy = 0;
967       else if (y == y1)       /* exact center */
968         dy /= 2;
969
970       XMoveResizeWindow (dpy, parent[0], x + dx, y + dy,
971                          szHint.width, szHint.height);
972 #else
973       XResizeWindow (dpy, parent[0], szHint.width, szHint.height);
974 #endif
975     }
976
977   if (set_hint)
978     XSetWMNormalHints (dpy, parent[0], &szHint);
979
980   fix_screen = ncol != prev_ncol || nrow != prev_nrow;
981
982   if (fix_screen || newwidth != old_width || newheight != old_height)
983     {
984       if (scrollBar.state)
985         scrollBar.resize ();
986
987       XMoveResizeWindow (dpy, vt,
988                          window_vt_x, window_vt_y,
989                          width, height);
990
991 #ifdef HAVE_BG_PIXMAP
992       if (bgPixmap.window_size_sensitive ())
993         update_background ();
994 #endif
995     }
996
997   if (fix_screen || old_height == 0)
998     scr_reset ();
999
1000   // TODO, with nvidia-8178, resizes kill the alpha channel, report if not fixed in newer version
1001   //scr_touch (false);
1002
1003 #ifdef HAVE_BG_PIXMAP
1004 //  TODO: this don't seem to have any effect - do we still need it ? If so - in which case exactly ?
1005 //  if (bgPixmap.pixmap)
1006 //    scr_touch (false);
1007 #endif
1008
1009 #ifdef USE_XIM
1010   IMSetPosition ();
1011 #endif
1012 }
1013
1014 /*
1015  * Set the width/height of the vt window in characters.  Units are pixels.
1016  * good for toggling 80/132 columns
1017  */
1018 void
1019 rxvt_term::set_widthheight (unsigned int newwidth, unsigned int newheight)
1020 {
1021   XWindowAttributes wattr;
1022
1023   if (newwidth == 0 || newheight == 0)
1024     {
1025       XGetWindowAttributes (dpy, display->root, &wattr);
1026
1027       if (newwidth == 0)
1028         newwidth = wattr.width - szHint.base_width;
1029       if (newheight == 0)
1030         newheight = wattr.height - szHint.base_height;
1031     }
1032
1033   if (newwidth != width || newheight != height)
1034     {
1035       newwidth += szHint.base_width;
1036       newheight += szHint.base_height;
1037       resize_all_windows (newwidth, newheight, 0);
1038     }
1039 }
1040
1041 /* -------------------------------------------------------------------- *
1042  * -                      X INPUT METHOD ROUTINES                     - *
1043  * -------------------------------------------------------------------- */
1044 #ifdef USE_XIM
1045
1046 void
1047 rxvt_term::im_set_color (unsigned long &fg, unsigned long &bg)
1048 {
1049   fg = pix_colors [Color_fg];
1050   bg = pix_colors [Color_bg];
1051 }
1052
1053 void
1054 rxvt_term::im_set_size (XRectangle &size)
1055 {
1056   // the int_bwidth terms make no sense to me
1057   size.x      = int_bwidth;
1058   size.y      = int_bwidth;
1059   size.width  = Width2Pixel (ncol) + int_bwidth;
1060   size.height = Height2Pixel (nrow) + int_bwidth;
1061 }
1062
1063 void
1064 rxvt_term::im_set_preedit_area (XRectangle &preedit_rect,
1065                                 XRectangle &status_rect,
1066                                 const XRectangle &needed_rect)
1067 {
1068   preedit_rect.x      = needed_rect.width;
1069   preedit_rect.y      = 0;
1070   preedit_rect.width  = Width2Pixel (ncol) - needed_rect.width + 1;
1071   preedit_rect.height = fheight;
1072
1073   status_rect.x       = 0;
1074   status_rect.y       = 0;
1075   status_rect.width   = needed_rect.width ? needed_rect.width : Width2Pixel (ncol) + 1;
1076   status_rect.height  = fheight;
1077 }
1078
1079 /* Checking whether input method is running. */
1080 bool
1081 rxvt_term::IMisRunning ()
1082 {
1083   Atom atom;
1084   Window win;
1085   char server[IMBUFSIZ];
1086
1087   /* get current locale modifier */
1088   if (char *p = XSetLocaleModifiers (0))
1089     {
1090       strcpy (server, "@server=");
1091       strncat (server, p + 4, IMBUFSIZ - 9); /* skip "@im=" */
1092
1093       if (p = strchr (server + 1, '@'))      /* first one only */
1094         *p = '\0';
1095
1096       atom = XInternAtom (dpy, server, False);
1097       win = XGetSelectionOwner (dpy, atom);
1098
1099       if (win != None)
1100         return True;
1101     }
1102
1103   return False;
1104 }
1105
1106 void
1107 rxvt_term::IMSendSpot ()
1108 {
1109   XPoint nspot;
1110   XVaNestedList preedit_attr;
1111
1112   if (!Input_Context
1113       || !focus
1114       || !(input_style & (XIMPreeditPosition | XIMPreeditCallbacks)))
1115     return;
1116
1117   im_set_position (nspot);
1118
1119   if (nspot.x == spot.x && nspot.y == spot.y)
1120     return;
1121
1122   spot = nspot;
1123
1124   preedit_attr = XVaCreateNestedList (0, XNSpotLocation, &spot, NULL);
1125   XSetICValues (Input_Context, XNPreeditAttributes, preedit_attr, NULL);
1126   XFree (preedit_attr);
1127 }
1128
1129 void
1130 rxvt_term::im_destroy ()
1131 {
1132   if (input_method)
1133     {
1134       if (Input_Context && input_method->xim)
1135         XDestroyIC (Input_Context);
1136
1137       display->put_xim (input_method);
1138       input_method = 0;
1139     }
1140
1141   Input_Context = 0;
1142 }
1143
1144 #ifdef ENABLE_XIM_ONTHESPOT
1145
1146 static void
1147 xim_preedit_start (XIC ic, XPointer client_data, XPointer call_data)
1148 {
1149   ((rxvt_term *)client_data)->make_current ();
1150   HOOK_INVOKE (((rxvt_term *)client_data, HOOK_XIM_PREEDIT_START, DT_END));
1151 }
1152
1153 static void
1154 xim_preedit_done (XIC ic, XPointer client_data, XPointer call_data)
1155 {
1156   ((rxvt_term *)client_data)->make_current ();
1157   HOOK_INVOKE (((rxvt_term *)client_data, HOOK_XIM_PREEDIT_DONE, DT_END));
1158 }
1159
1160 static void
1161 xim_preedit_draw (XIC ic, XPointer client_data, XIMPreeditDrawCallbackStruct *call_data)
1162 {
1163   rxvt_term *term = (rxvt_term *)client_data;
1164   XIMText *text = call_data->text;
1165
1166   term->make_current ();
1167
1168   if (text)
1169     {
1170       void *str;
1171
1172       if (!text->encoding_is_wchar && text->string.multi_byte)
1173         {
1174           // of course, X makes it ugly again
1175           if (term->rs[Rs_imLocale])
1176             SET_LOCALE (term->rs[Rs_imLocale]);
1177
1178           str = rxvt_temp_buf ((text->length + 1) * sizeof (wchar_t));
1179           mbstowcs ((wchar_t *)str, text->string.multi_byte, text->length + 1);
1180
1181           if (term->rs[Rs_imLocale])
1182             SET_LOCALE (term->locale);
1183         }
1184       else
1185         str = (void *)text->string.wide_char;
1186
1187       HOOK_INVOKE ((term, HOOK_XIM_PREEDIT_DRAW,
1188                     DT_INT, call_data->caret,
1189                     DT_INT, call_data->chg_first,
1190                     DT_INT, call_data->chg_length,
1191                     DT_LCS_LEN, (void *)text->feedback, text->feedback ? (int)text->length : 0,
1192                     DT_WCS_LEN, str, str ? (int)text->length : 0,
1193                     DT_END));
1194     }
1195   else
1196     HOOK_INVOKE ((term, HOOK_XIM_PREEDIT_DRAW,
1197                   DT_INT, call_data->caret,
1198                   DT_INT, call_data->chg_first,
1199                   DT_INT, call_data->chg_length,
1200                   DT_END));
1201 }
1202
1203 #if 0
1204 static void
1205 xim_preedit_caret (XIC ic, XPointer client_data, XIMPreeditCaretCallbackStruct *call_data)
1206 {
1207   ((rxvt_term *)client_data)->make_current ();
1208   HOOK_INVOKE (((rxvt_term *)client_data, HOOK_XIM_PREEDIT_CARET,
1209                 DT_INT, call_data->position,
1210                 DT_INT, call_data->direction,
1211                 DT_INT, call_data->style,
1212                 DT_END));
1213 }
1214 #endif
1215
1216 #endif
1217
1218 /*
1219  * Try to open a XIM with the current modifiers, then see if we can
1220  * open a suitable preedit type
1221  */
1222 bool
1223 rxvt_term::IM_get_IC (const char *modifiers)
1224 {
1225   int i, j, found;
1226   XIM xim;
1227   XPoint spot;
1228   XRectangle rect, status_rect, needed_rect;
1229   unsigned long fg, bg;
1230   const char *p;
1231   char **s;
1232   XIMStyles *xim_styles;
1233 #ifdef ENABLE_XIM_ONTHESPOT
1234   XIMCallback xcb[4];
1235 #endif
1236
1237   set_environ (envv);
1238
1239   if (! ((p = XSetLocaleModifiers (modifiers)) && *p))
1240     return false;
1241
1242   input_method = display->get_xim (locale, modifiers);
1243   if (input_method == NULL)
1244     return false;
1245
1246   xim = input_method->xim;
1247   spot.x = spot.y = -1;
1248
1249   xim_styles = NULL;
1250   if (XGetIMValues (xim, XNQueryInputStyle, &xim_styles, NULL)
1251       || !xim_styles || !xim_styles->count_styles)
1252     {
1253       im_destroy ();
1254       return false;
1255     }
1256
1257   const char *pet[] = { rs[Rs_preeditType], "OverTheSpot,OffTheSpot,Root,None" };
1258
1259   for (int pi = 0; pi < 2; pi++)
1260     {
1261       p = pet[pi];
1262
1263       if (!p)
1264         continue;
1265
1266       s = rxvt_splitcommastring (p);
1267
1268       for (i = found = 0; !found && s[i]; i++)
1269         {
1270           if (!strcmp (s[i], "OverTheSpot"))
1271             input_style = XIMPreeditPosition | XIMStatusNothing;
1272           else if (!strcmp (s[i], "OffTheSpot"))
1273             input_style = XIMPreeditArea | XIMStatusArea;
1274           else if (!strcmp (s[i], "Root"))
1275             input_style = XIMPreeditNothing | XIMStatusNothing;
1276           else if (!strcmp (s[i], "None"))
1277             input_style = XIMPreeditNone | XIMStatusNone;
1278 #ifdef ENABLE_XIM_ONTHESPOT
1279           else if (SHOULD_INVOKE (HOOK_XIM_PREEDIT_START) && !strcmp (s[i], "OnTheSpot"))
1280             input_style = XIMPreeditCallbacks | XIMStatusNothing;
1281 #endif
1282           else
1283             input_style = XIMPreeditNothing | XIMStatusNothing;
1284
1285           for (j = 0; j < xim_styles->count_styles; j++)
1286             if (input_style == xim_styles->supported_styles[j])
1287               {
1288                 rxvt_freecommastring (s);
1289
1290                 found = 1;
1291                 goto foundpet;
1292               }
1293
1294         }
1295
1296       rxvt_freecommastring (s);
1297     }
1298
1299 foundpet:
1300
1301   XFree (xim_styles);
1302
1303   if (!found)
1304     {
1305       im_destroy ();
1306       return false;
1307     }
1308
1309   XFontSet fs = 0;
1310   XVaNestedList preedit_attr = 0, status_attr = 0;
1311
1312   if (input_style & (XIMPreeditPosition | XIMPreeditArea))
1313     {
1314       // fake us a font-set, please
1315       char **missing_charset_list;
1316       int missing_charset_count;
1317       char *def_string;
1318       char pat[512];
1319
1320       sprintf (pat,
1321                "-*-*-*-R-*-*-%d-*-*-*-*-*-*,"
1322                "-*-*-*-R-*-*-%d-*-*-*-*-*-*,"
1323                "-*-*-*-R-*-*-%d-*-*-*-*-*-*,"
1324                "-*-*-*-R-*-*-%d-*-*-*-*-*-*,"
1325                "-*-*-*-R-*-*-%d-*-*-*-*-*-*,"
1326                "*",
1327                fheight,
1328                fheight + 1, fheight - 1,
1329                fheight - 2, fheight + 2);
1330
1331       fs = XCreateFontSet (dpy, rs[Rs_imFont] ? rs[Rs_imFont] : pat,
1332                            &missing_charset_list, &missing_charset_count, &def_string);
1333
1334       if (missing_charset_list)
1335         XFreeStringList (missing_charset_list);
1336
1337       if (!fs)
1338         {
1339           input_style &= ~(XIMPreeditPosition | XIMPreeditArea);
1340           rxvt_warn ("unable to create fontset for input method, try \"-pt Root\". Continuing.\n");
1341         }
1342     }
1343
1344   if (input_style & XIMPreeditPosition)
1345     {
1346       im_set_size (rect);
1347       im_set_position (spot);
1348       im_set_color (fg, bg);
1349
1350       preedit_attr = XVaCreateNestedList (0,
1351                                           XNForeground, fg,
1352                                           XNBackground, bg,
1353                                           XNArea, &rect,
1354                                           XNSpotLocation, &spot,
1355                                           XNFontSet, fs,
1356                                           NULL);
1357     }
1358   else if (input_style & XIMPreeditArea)
1359     {
1360       im_set_color (fg, bg);
1361
1362       /*
1363        * The necessary width of preedit area is unknown
1364        * until create input context.
1365        */
1366       needed_rect.width = 0;
1367       im_set_preedit_area (rect, status_rect, needed_rect);
1368
1369       preedit_attr = XVaCreateNestedList (0,
1370                                           XNForeground, fg,
1371                                           XNBackground, bg,
1372                                           XNArea, &rect,
1373                                           XNFontSet, fs,
1374                                           NULL);
1375       status_attr = XVaCreateNestedList (0,
1376                                          XNForeground, fg,
1377                                          XNBackground, bg,
1378                                          XNArea, &status_rect,
1379                                          XNFontSet, fs,
1380                                          NULL);
1381     }
1382 #if ENABLE_XIM_ONTHESPOT
1383   else if (input_style & XIMPreeditCallbacks)
1384     {
1385       im_set_position (spot);
1386
1387       xcb[0].client_data = (XPointer)this; xcb[0].callback = (XIMProc)xim_preedit_start;
1388       xcb[1].client_data = (XPointer)this; xcb[1].callback = (XIMProc)xim_preedit_done;
1389       xcb[2].client_data = (XPointer)this; xcb[2].callback = (XIMProc)xim_preedit_draw;
1390 # if 0
1391       xcb[3].client_data = (XPointer)this; xcb[3].callback = (XIMProc)xim_preedit_caret;
1392 # endif
1393
1394       preedit_attr = XVaCreateNestedList (0,
1395                                           XNSpotLocation, &spot,
1396                                           XNPreeditStartCallback, &xcb[0],
1397                                           XNPreeditDoneCallback , &xcb[1],
1398                                           XNPreeditDrawCallback , &xcb[2],
1399 # if 0
1400                                           XNPreeditCaretCallback, &xcb[3],
1401 # endif
1402                                           NULL);
1403     }
1404 #endif
1405
1406   Input_Context = XCreateIC (xim,
1407                              XNInputStyle, input_style,
1408                              XNClientWindow, vt,
1409                              XNFocusWindow, parent[0],
1410                              preedit_attr ? XNPreeditAttributes : NULL,
1411                              preedit_attr,
1412                              status_attr ? XNStatusAttributes : NULL,
1413                              status_attr, NULL);
1414
1415   if (preedit_attr) XFree (preedit_attr);
1416   if (status_attr)  XFree (status_attr);
1417   if (fs)           XFreeFontSet (dpy, fs);
1418
1419   if (Input_Context == NULL)
1420     {
1421       rxvt_warn ("failed to create input context, continuing without XIM.\n");
1422       im_destroy ();
1423       return false;
1424     }
1425
1426 #if 0
1427   // unfortunately, only the focus window is used by XIM, hard to fix
1428   if (!XGetICValues (Input_Context, XNFilterEvents, &vt_emask_xim, NULL))
1429     vt_select_input ();
1430 #endif
1431
1432   IMSetPosition ();
1433
1434   return true;
1435 }
1436
1437 void
1438 rxvt_term::im_cb ()
1439 {
1440   int i;
1441   const char *p;
1442   char **s;
1443   char buf[IMBUFSIZ];
1444
1445   make_current ();
1446
1447   im_destroy ();
1448
1449   if (Input_Context)
1450     return;
1451
1452 #if defined(HAVE_XSETLOCALE) || defined(HAVE_SETLOCALE)
1453   if (rs[Rs_imLocale])
1454     SET_LOCALE (rs[Rs_imLocale]);
1455 #endif
1456
1457   p = rs[Rs_inputMethod];
1458   if (p && *p)
1459     {
1460       bool found = false;
1461
1462       s = rxvt_splitcommastring (p);
1463
1464       for (i = 0; s[i]; i++)
1465         {
1466           if (*s[i])
1467             {
1468               strcpy (buf, "@im=");
1469               strncat (buf, s[i], IMBUFSIZ - 5);
1470               if (IM_get_IC (buf))
1471                 {
1472                   found = true;
1473                   break;
1474                 }
1475             }
1476         }
1477
1478       rxvt_freecommastring (s);
1479
1480       if (found)
1481         goto done;
1482     }
1483
1484   /* try with XMODIFIERS env. var. */
1485   if (IM_get_IC (""))
1486     goto done;
1487
1488   /* try with no modifiers base IF the user didn't specify an IM */
1489   if (IM_get_IC ("@im=none"))
1490     goto done;
1491
1492 done:
1493 #if defined(HAVE_XSETLOCALE) || defined(HAVE_SETLOCALE)
1494   if (rs[Rs_imLocale])
1495     SET_LOCALE (locale);
1496 #endif
1497 }
1498
1499 void
1500 rxvt_term::IMSetPosition ()
1501 {
1502   XRectangle preedit_rect, status_rect, *needed_rect;
1503   XVaNestedList preedit_attr, status_attr;
1504
1505   if (!Input_Context
1506       || !focus
1507       || !(input_style & (XIMPreeditArea | XIMPreeditPosition))
1508       || !IMisRunning ())
1509     return;
1510
1511   if (input_style & XIMPreeditPosition)
1512     {
1513       im_set_size (preedit_rect);
1514       preedit_attr = XVaCreateNestedList (0, XNArea, &preedit_rect, NULL);
1515
1516       XSetICValues (Input_Context,
1517                     XNPreeditAttributes, preedit_attr, NULL);
1518     }
1519   else
1520     {
1521       /* Getting the necessary width of preedit area */
1522       status_attr = XVaCreateNestedList (0, XNAreaNeeded, &needed_rect, NULL);
1523       XGetICValues (Input_Context, XNStatusAttributes, status_attr, NULL);
1524       XFree (status_attr);
1525
1526       im_set_preedit_area (preedit_rect, status_rect, *needed_rect);
1527       XFree (needed_rect);
1528
1529       preedit_attr = XVaCreateNestedList (0, XNArea, &preedit_rect, NULL);
1530       status_attr  = XVaCreateNestedList (0, XNArea, &status_rect,  NULL);
1531
1532       XSetICValues (Input_Context,
1533                     XNPreeditAttributes, preedit_attr,
1534                     XNStatusAttributes,  status_attr,
1535                     NULL);
1536
1537       XFree (status_attr);
1538     }
1539
1540    XFree (preedit_attr);
1541 }
1542 #endif /* USE_XIM */
1543
1544 void
1545 rxvt_term::get_window_origin (int &x, int &y)
1546 {
1547   Window cr;
1548   XTranslateCoordinates (dpy, parent[0], display->root, 0, 0, &x, &y, &cr);
1549 /*  fprintf (stderr, "origin is %+d%+d\n", x, y);*/
1550 }
1551
1552 Pixmap
1553 rxvt_term::get_pixmap_property (int prop_id)
1554 {
1555   if (prop_id > 0 && prop_id < NUM_XA)
1556     if (xa[prop_id])
1557       {
1558         int aformat;
1559         unsigned long nitems, bytes_after;
1560         Atom atype;
1561         unsigned char *prop = NULL;
1562         int result = XGetWindowProperty (dpy, display->root, xa[prop_id],
1563                                          0L, 1L, False, XA_PIXMAP, &atype, &aformat,
1564                                          &nitems, &bytes_after, &prop);
1565         if (result == Success && prop && atype == XA_PIXMAP)
1566           return *(Pixmap *)prop;
1567       }
1568
1569   return None;
1570 }
1571
1572 #ifdef HAVE_BG_PIXMAP
1573 # if TRACE_PIXMAPS
1574 #  undef update_background
1575 void
1576 rxvt_term::trace_update_background (const char *file, int line)
1577 {
1578   fprintf (stderr, "%s:%d:update_background()\n", file, line);
1579   update_background ();
1580 }
1581 # endif
1582
1583 void
1584 rxvt_term::update_background ()
1585 {
1586   if (update_background_ev.is_active ())
1587     return;
1588
1589   bgPixmap.invalidate ();
1590
1591   ev_tstamp to_wait = 0.5 - (ev::now () - bgPixmap.valid_since);
1592
1593   if (to_wait <= 0.)
1594     bgPixmap.render ();
1595   else
1596     update_background_ev.start (to_wait);
1597 }
1598
1599 void
1600 rxvt_term::update_background_cb (ev::timer &w, int revents)
1601 {
1602   make_current ();
1603
1604   update_background_ev.stop ();
1605   bgPixmap.render ();
1606   refresh_check ();
1607 }
1608
1609 #endif /* HAVE_BG_PIXMAP */
1610
1611 /*----------------------- end-of-file (C source) -----------------------*/