*** empty log message ***
[dana/urxvt.git] / src / rxvttoolkit.C
1 /*--------------------------------*-C-*---------------------------------*
2  * File:        rxvttoolkit.C
3  *----------------------------------------------------------------------*
4  *
5  * All portions of code are copyright by their respective author/s.
6  * Copyright (c) 2003-2004 Marc Lehmann <pcg@goof.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *----------------------------------------------------------------------*/
22
23 #include "../config.h"
24 #include <rxvt.h>
25 #include <rxvttoolkit.h>
26
27 #include <unistd.h>
28 #include <fcntl.h>
29
30 #include <sys/utsname.h>
31
32 #ifndef NO_SLOW_LINK_SUPPORT
33 # include <sys/socket.h>
34 # include <sys/un.h>
35 #endif
36
37 refcounted::refcounted (const char *id)
38 {
39   this->id = strdup (id);
40 }
41
42 refcounted::~refcounted ()
43 {
44   free (id);
45 }
46
47 template<class T>
48 T *refcache<T>::get (const char *id)
49 {
50   for (T **i = this->begin (); i < this->end (); ++i)
51     {
52       if (!strcmp (id, (*i)->id))
53         {
54           ++(*i)->referenced;
55           (*i)->ref_next ();
56           return *i;
57         }
58     }
59
60   T *obj = new T (id);
61
62   if (obj && obj->ref_init ())
63     {
64       obj->referenced = 1;
65       this->push_back (obj);
66       return obj;
67     }
68   else
69     {
70       delete obj;
71       return 0;
72     }
73 }
74
75 template<class T>
76 void refcache<T>::put (T *obj)
77 {
78   if (!obj)
79     return;
80
81   if (!--obj->referenced)
82     {
83       this->erase (find (this->begin (), this->end (), obj));
84       delete obj;
85     }
86 }
87
88 template<class T>
89 void refcache<T>::clear ()
90 {
91   while (this->size ())
92     put (*this->begin ());
93 }
94
95 /////////////////////////////////////////////////////////////////////////////
96
97 #ifdef USE_XIM
98 static void
99 #if XIMCB_PROTO_BROKEN
100 im_destroy_cb (XIC unused1, XPointer client_data, XPointer unused3)
101 #else
102 im_destroy_cb (XIM unused1, XPointer client_data, XPointer unused3)
103 #endif
104 {
105   rxvt_xim *xim = (rxvt_xim *)client_data;
106   rxvt_display *display = xim->display;
107
108   xim->xim = 0;
109
110   display->xims.erase (find (display->xims.begin (), display->xims.end (), xim));
111   display->im_change_cb ();
112 }
113
114 bool
115 rxvt_xim::ref_init ()
116 {
117   display = GET_R->display; //HACK: TODO
118
119   xim = XOpenIM (display->display, NULL, NULL, NULL);
120
121   if (!xim)
122     return false;
123
124   XIMCallback ximcallback;
125   ximcallback.client_data = (XPointer)this;
126   ximcallback.callback = im_destroy_cb;
127
128   XSetIMValues (xim, XNDestroyCallback, &ximcallback, NULL);
129
130   return true;
131 }
132
133 rxvt_xim::~rxvt_xim ()
134 {
135   if (xim)
136     XCloseIM (xim);
137 }
138 #endif
139
140 /////////////////////////////////////////////////////////////////////////////
141
142 rxvt_display::rxvt_display (const char *id)
143 : refcounted (id)
144 , x_ev (this, &rxvt_display::x_cb)
145 , selection_owner (0)
146 {
147 }
148
149 XrmDatabase
150 rxvt_display::get_resources ()
151 {
152   char *homedir = (char *)getenv ("HOME");
153   char fname[1024];
154
155   /*
156    * get resources using the X library function
157    */
158   char *displayResource, *xe;
159   XrmDatabase database, rdb1;
160
161   database = NULL;
162
163   // for ordering, see for example http://www.faqs.org/faqs/Xt-FAQ/ Subject: 20
164
165   // 6. System wide per application default file.
166
167   /* Add in $XAPPLRESDIR/Rxvt only; not bothering with XUSERFILESEARCHPATH */
168   if ((xe = (char *)getenv ("XAPPLRESDIR")))
169     {
170       snprintf (fname, sizeof (fname), "%s/%s", xe, RESCLASS);
171
172       if ((rdb1 = XrmGetFileDatabase (fname)))
173         XrmMergeDatabases (rdb1, &database);
174     }
175
176   // 5. User's per application default file.
177   // none
178
179   // 4. User's defaults file.
180   /* Get any Xserver defaults */
181   displayResource = XResourceManagerString (display);
182
183   if (displayResource != NULL)
184     {
185       if ((rdb1 = XrmGetStringDatabase (displayResource)))
186         XrmMergeDatabases (rdb1, &database);
187     }
188   else if (homedir)
189     {
190       snprintf (fname, sizeof (fname), "%s/.Xdefaults", homedir);
191
192       if ((rdb1 = XrmGetFileDatabase (fname)))
193         XrmMergeDatabases (rdb1, &database);
194     }
195
196   /* Get screen specific resources */
197   displayResource = XScreenResourceString (ScreenOfDisplay (display, screen));
198
199   if (displayResource != NULL)
200     {
201       if ((rdb1 = XrmGetStringDatabase (displayResource)))
202         /* Merge with screen-independent resources */
203         XrmMergeDatabases (rdb1, &database);
204
205       XFree (displayResource);
206     }
207
208   // 3. User's per host defaults file
209   /* Add in XENVIRONMENT file */
210   if ((xe = (char *)getenv ("XENVIRONMENT"))
211       && (rdb1 = XrmGetFileDatabase (xe)))
212     XrmMergeDatabases (rdb1, &database);
213   else if (homedir)
214     {
215       struct utsname un;
216
217       if (!uname (&un))
218         {
219           snprintf (fname, sizeof (fname), "%s/.Xdefaults-%s", homedir, un.nodename);
220
221           if ((rdb1 = XrmGetFileDatabase (fname)))
222             XrmMergeDatabases (rdb1, &database);
223         }
224     }
225
226   return database;
227 }
228
229 bool rxvt_display::ref_init ()
230 {
231 #ifdef LOCAL_X_IS_UNIX
232   if (id[0] == ':')
233     {
234       val = rxvt_malloc (5 + strlen (id) + 1);
235       strcpy (val, "unix/");
236       strcat (val, id);
237       display = XOpenDisplay (val);
238       free (val);
239     }
240   else
241 #endif
242     display = 0;
243
244   if (!display)
245     display = XOpenDisplay (id);
246
247   if (!display)
248     return false;
249
250   screen = DefaultScreen (display);
251   root   = DefaultRootWindow (display);
252   visual = DefaultVisual (display, screen);
253   cmap   = DefaultColormap (display, screen);
254   depth  = DefaultDepth (display, screen);
255
256   XrmSetDatabase (display, get_resources ());
257
258 #ifdef POINTER_BLANK
259   XColor blackcolour;
260   blackcolour.red   = 0;
261   blackcolour.green = 0;
262   blackcolour.blue  = 0;
263   Font f = XLoadFont (display, "fixed");
264   blank_cursor = XCreateGlyphCursor (display, f, f, ' ', ' ',
265                                      &blackcolour, &blackcolour);
266   XUnloadFont (display, f);
267 #endif
268
269 #ifdef PREFER_24BIT
270   /*
271    * If depth is not 24, look for a 24bit visual.
272    */
273   if (depth != 24)
274     {
275       XVisualInfo vinfo;
276
277       if (XMatchVisualInfo (display, screen, 24, TrueColor, &vinfo))
278         {
279           depth = 24;
280           visual = vinfo.visual;
281           cmap = XCreateColormap (display,
282                                   RootWindow (display, screen),
283                                   visual, AllocNone);
284         }
285     }
286 #endif
287
288   int fd = XConnectionNumber (display);
289
290 #ifndef NO_SLOW_LINK_SUPPORT
291   // try to detect wether we have a local connection.
292   // assume unix domains socket == local, everything else not
293   // TODO: might want to check for inet/127.0.0.1
294   is_local = 0;
295   sockaddr_un sa;
296   socklen_t sl = sizeof (sa);
297
298   if (!getsockname (fd, (sockaddr *)&sa, &sl))
299     is_local = sa.sun_family == AF_LOCAL;
300 #endif
301
302   x_ev.start (fd, EVENT_READ);
303   fcntl (fd, F_SETFD, FD_CLOEXEC);
304
305   XSelectInput (display, root, PropertyChangeMask);
306 #ifdef USE_XIM
307   xa_xim_servers = XInternAtom (display, "XIM_SERVERS", 0);
308 #endif
309
310   flush ();
311
312   return true;
313 }
314
315 void
316 rxvt_display::ref_next ()
317 {
318   // TODO: somehow check wether the database files/resources changed
319   // before re-loading/parsing
320   XrmDestroyDatabase (XrmGetDatabase (display));
321   XrmSetDatabase (display, get_resources ());
322 }
323
324 rxvt_display::~rxvt_display ()
325 {
326   if (!display)
327     return;
328
329 #ifdef POINTER_BLANK
330   XFreeCursor (display, blank_cursor);
331 #endif
332   x_ev.stop ();
333 #ifdef USE_XIM
334   xims.clear ();
335 #endif
336   XCloseDisplay (display);
337 }
338
339 #ifdef USE_XIM
340 void rxvt_display::im_change_cb ()
341 {
342   for (im_watcher **i = imw.begin (); i != imw.end (); ++i)
343     (*i)->call ();
344 }
345
346 void rxvt_display::im_change_check ()
347 {
348   // try to only call im_change_cb when a new input method
349   // registers, as xlib crashes due to a race otherwise.
350   Atom actual_type, *atoms;
351   int actual_format;
352   unsigned long nitems, bytes_after;
353
354   if (XGetWindowProperty (display, root, xa_xim_servers, 0L, 1000000L,
355                           False, XA_ATOM, &actual_type, &actual_format,
356                           &nitems, &bytes_after, (unsigned char **)&atoms)
357       != Success )
358     return;
359
360   if (actual_type == XA_ATOM  && actual_format == 32)
361     for (int i = 0; i < nitems; i++)
362       if (XGetSelectionOwner (display, atoms[i]))
363         {
364           im_change_cb ();
365           break;
366         }
367
368   XFree (atoms);
369 }
370 #endif
371
372 void rxvt_display::x_cb (io_watcher &w, short revents)
373 {
374   do
375     {
376       XEvent xev;
377       XNextEvent (display, &xev);
378
379 #ifdef USE_XIM
380       if (!XFilterEvent (&xev, None))
381         {
382           if (xev.type == PropertyNotify
383               && xev.xany.window == root
384               && xev.xproperty.atom == xa_xim_servers)
385             im_change_check ();
386 #endif
387           for (int i = xw.size (); i--; )
388             {
389               if (!xw[i])
390                 xw.erase_unordered (i);
391               else if (xw[i]->window == xev.xany.window)
392                 xw[i]->call (xev);
393             }
394 #ifdef USE_XIM
395         }
396 #endif
397     }
398   while (XEventsQueued (display, QueuedAlready));
399
400   XFlush (display);
401 }
402
403 void rxvt_display::flush ()
404 {
405   if (XEventsQueued (display, QueuedAlready))
406     x_cb (x_ev, EVENT_READ);
407
408   XFlush (display);
409 }
410
411 void rxvt_display::reg (xevent_watcher *w)
412 {
413   xw.push_back (w);
414   w->active = xw.size ();
415 }
416
417 void rxvt_display::unreg (xevent_watcher *w)
418 {
419   if (w->active)
420     xw[w->active - 1] = 0;
421 }
422
423 void rxvt_display::set_selection_owner (rxvt_term *owner)
424 {
425   if (selection_owner && selection_owner != owner)
426     selection_owner->selection_clear ();
427
428   selection_owner = owner;
429 }
430
431 #ifdef USE_XIM
432 void rxvt_display::reg (im_watcher *w)
433 {
434   imw.push_back (w);
435 }
436
437 void rxvt_display::unreg (im_watcher *w)
438 {
439   imw.erase (find (imw.begin (), imw.end (), w));
440 }
441
442 rxvt_xim *rxvt_display::get_xim (const char *locale, const char *modifiers)
443 {
444   char *id;
445   int l, m;
446
447   l = strlen (locale);
448   m = strlen (modifiers);
449
450   if (!(id = (char *)malloc (l + m + 2)))
451     return 0;
452
453   memcpy (id, locale, l); id[l] = '\n';
454   memcpy (id + l + 1, modifiers, m); id[l + m + 1] = 0;
455
456   rxvt_xim *xim = xims.get (id);
457
458   free (id);
459
460   return xim;
461 }
462
463 void rxvt_display::put_xim (rxvt_xim *xim)
464 {
465 #if XLIB_IS_RACEFREE
466   xims.put (xim);
467 #endif
468 }
469 #endif
470
471 Atom rxvt_display::atom (const char *name)
472 {
473   return XInternAtom (display, name, False);
474 }
475
476 /////////////////////////////////////////////////////////////////////////////
477
478 template class refcache<rxvt_display>;
479 refcache<rxvt_display> displays;
480
481 /////////////////////////////////////////////////////////////////////////////
482   
483 bool
484 rxvt_color::set (rxvt_display *display, Pixel p)
485 {
486 #if XFT
487   XColor xc;
488
489   xc.pixel = p;
490   if (!XQueryColor (display->display, display->cmap, &xc))
491     return false;
492
493   XRenderColor d;
494
495   d.red   = xc.red;
496   d.green = xc.green;
497   d.blue  = xc.blue;
498   d.alpha = 0xffff;
499
500   return
501     XftColorAllocValue (display->display, 
502                         display->visual,
503                         display->cmap,
504                         &d, &c);
505 #else
506   this->p = p;
507 #endif
508
509   return true;
510 }
511
512 bool
513 rxvt_color::set (rxvt_display *display, const char *name)
514 {
515 #if XFT
516   return XftColorAllocName (display->display, display->visual, display->cmap,
517                             name, &c);
518 #else
519   XColor xc;
520
521   if (XParseColor (display->display, display->cmap, name, &xc))
522     return set (display, xc.red, xc.green, xc.blue);
523
524   return false;
525 #endif
526 }
527
528 bool
529 rxvt_color::set (rxvt_display *display, unsigned short cr, unsigned short cg, unsigned short cb)
530 {
531   XColor xc;
532
533   xc.red   = cr;
534   xc.green = cg;
535   xc.blue  = cb;
536   xc.flags = DoRed | DoGreen | DoBlue;
537
538   if (XAllocColor (display->display, display->cmap, &xc))
539     return set (display, xc.pixel);
540
541   return false;
542 }
543
544 void 
545 rxvt_color::get (rxvt_display *display, unsigned short &cr, unsigned short &cg, unsigned short &cb)
546 {
547 #if XFT
548   cr = c.color.red;
549   cg = c.color.green;
550   cb = c.color.blue;
551 #else
552   XColor c;
553
554   c.pixel = p;
555   XQueryColor (display->display, display->cmap, &c);
556
557   cr = c.red;
558   cg = c.green;
559   cb = c.blue;
560 #endif
561 }
562
563 void 
564 rxvt_color::free (rxvt_display *display)
565 {
566 #if XFT
567   XftColorFree (display->display, display->visual, display->cmap, &c);
568 #else
569   XFreeColors (display->display, display->cmap, &p, 1, AllPlanes);
570 #endif
571 }
572
573 rxvt_color
574 rxvt_color::fade (rxvt_display *display, int percent)
575 {
576   percent = 100 - percent;
577
578   unsigned short cr, cg, cb;
579   rxvt_color faded;
580
581   get (display, cr, cg, cb);
582
583   faded.set (
584     display,
585     cr * percent / 100,
586     cg * percent / 100,
587     cb * percent / 100
588   );
589
590   return faded;
591 }
592
593 #define LERP(a,b,p) (a * p + b * (100 - p)) / 100
594
595 rxvt_color
596 rxvt_color::fade (rxvt_display *display, int percent, rxvt_color &fadeto)
597 {
598   percent = 100 - percent;
599
600   unsigned short cr, cg, cb;
601   unsigned short fcr, fcg, fcb;
602   rxvt_color faded;
603   
604   get (display, cr, cg, cb);
605   fadeto.get(display, fcr, fcg, fcb);
606
607   faded.set (
608     display,
609     LERP (cr, fcr, percent),
610     LERP (cg, fcg, percent),
611     LERP (cb, fcb, percent)
612   );
613
614   return faded;
615 }
616