1 /* display.c -- display handling
2    $Id$
3 
4    Copyright (C) 1999 John Harper <john@dcs.warwick.ac.uk>
5 
6    This file is part of sawfish.
7 
8    sawfish is free software; you can redistribute it and/or modify it
9    under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2, or (at your option)
11    any later version.
12 
13    sawfish is distributed in the hope that it will be useful, but
14    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 sawfish; see the file COPYING.   If not, write to
20    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 
22 #include "sawfish.h"
23 #include <X11/Xatom.h>
24 #include <X11/Xutil.h>
25 #include <X11/cursorfont.h>
26 #include <X11/keysym.h>
27 #include <X11/extensions/shape.h>
28 #include <X11/Xproto.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <stdarg.h>
32 #include <ctype.h>
33 #include <X11/XKBlib.h>         /* extensions/XKB.h */
34 
35 #ifdef HAVE_UNIX
36 # ifdef HAVE_FCNTL_H
37 #  include <fcntl.h>
38 # endif
39 # ifdef HAVE_UNISTD_H
40 #  include <unistd.h>
41 # endif
42 # ifdef HAVE_SYS_UTSNAME_H
43 #  include <sys/utsname.h>
44 # endif
45 # include <netdb.h>
46 #endif
47 
48 char *visual_name;
49 Display *dpy;
50 int screen_num, screen_width, screen_height;
51 Window root_window, no_focus_window;
52 int shape_event_base, shape_error_base;
53 
54 Visual *preferred_visual;
55 int preferred_depth;
56 
57 /* some atoms that may be useful.. */
58 Atom xa_wm_state, xa_wm_change_state, xa_wm_protocols, xa_wm_delete_window,
59     xa_wm_colormap_windows, xa_wm_take_focus, xa_compound_text,
60     xa_wm_net_name, xa_wm_net_icon_name, xa_utf8_string, xa_manager, xa_wm_sn;
61 
62 /* Time when the manager selection was acquired. */
63 Time startup_time;
64 
65 DEFSYM(display_name, "display-name");
66 DEFSYM(canonical_display_name, "canonical-display-name");
67 
68 int usexkb;                              /* bool */
69 int xkb_major_opcode = 0;
70 int xkb_event_base;
71 /* X error handlers */
72 
73 /* General error handler. Probably due to lag between windows being
74    killed and us receiving DestroyNotify events */
75 static int
error_handler(Display * dpy,XErrorEvent * ev)76 error_handler (Display *dpy, XErrorEvent *ev)
77 {
78     Lisp_Window *w;
79 
80     if (ev->resourceid != 0
81 	&& (ev->error_code == BadWindow || ev->error_code == BadDrawable))
82     {
83 	w = x_find_window_by_id (ev->resourceid);
84 
85 	if (w != NULL)
86 	{
87 	    DB(("error_handler (%s)\n", rep_STR(w->name)));
88 
89 	    if (!WINDOW_IS_GONE_P (w))
90            {
91                /* don't unmap a window that had send an X_ConfigureWindow request */
92                if(
93                    /*     ev->type == 0 what is the "type" ? but I've seen that type is always 0 */
94                    /*&&*/ ev->error_code==BadWindow /* the window is bad, because it is not configured yet */
95                      &&   ev->request_code==X_ConfigureWindow
96                      &&   ev->minor_code==0 /* X_ConfigureWindow is not in an Xlib extension, so it must be 0 */
97                )
98                {
99                    return 0;
100                } else
101                {
102                    remove_window (w, TRUE, TRUE);
103                }
104            }
105 
106 	    /* so we call emit_pending_destroys () at some point */
107 	    rep_mark_input_pending (ConnectionNumber (dpy));
108 	}
109     }
110 
111     return 0;
112 }
113 
114 /* Installed whilst trying to set the root window event mask */
115 static int
error_other_wm(Display * dpy,XErrorEvent * ev)116 error_other_wm (Display *dpy, XErrorEvent *ev)
117 {
118     fputs ("You may only run one window manager\n", stderr);
119     exit (1);
120 }
121 
122 
123 static void
replace_race_error()124 replace_race_error ()
125 {
126     fputs ("It is getting kind of crowded here.\n", stderr);
127     exit (1);
128 }
129 
130 
131 static char *
canonical_host(char * host)132 canonical_host (char *host)
133 {
134     static char buf[256];
135     char *ptr;
136 
137     /* check that the name is fully qualified */
138     if (!strchr (host, '.'))
139     {
140 	struct hostent *h = gethostbyname (host);
141 	if (h != 0)
142 	{
143 	    if (!strchr (h->h_name, '.'))
144 	    {
145 		char **aliases = h->h_aliases;
146 		while (*aliases && !strchr (*aliases, '.'))
147 		    aliases++;
148 		host = *aliases ? *aliases : h->h_name;
149 	    }
150 	    else
151 		host = h->h_name;
152 	}
153     }
154 
155     ptr = buf;
156     while (*host != 0)
157     {
158 	*ptr++ = tolower (*host);
159 	host++;
160     }
161     return buf;
162 }
163 
164 static char *
canonical_display(char * name)165 canonical_display (char *name)
166 {
167     static char buf[256];
168     char *ptr = buf;
169     if (strncmp ("unix:", name, 5) == 0)
170 	name += 4;
171     if (*name == ':')
172     {
173 	repv host = Fsystem_name ();
174 	if (host && rep_STRINGP(host))
175 	    strcpy (ptr, rep_STR(host));
176 	else
177 	    *ptr = 0;
178 	ptr += strlen (ptr);
179     }
180     else
181     {
182 	char *fq;
183 	while (*name && *name != ':')
184 	    *ptr++ = *name++;
185 	*ptr = 0;
186 	fq = canonical_host (buf);
187 	if (fq != buf)
188 	{
189 	    strcpy (buf, fq);
190 	    ptr = buf + strlen (buf);
191 	}
192     }
193     *ptr++ = *name++;
194     while (*name && *name != '.')
195 	*ptr++ = *name++;
196     if (*name == 0)
197 	strcpy (ptr, ".0");
198     else
199 	strcpy (ptr, name);
200     return buf;
201 }
202 
203 
204 static void
redisplay(void)205 redisplay (void)
206 {
207     commit_queued_reshapes ();
208 
209     /* round-trip requests swallow any pending events.. */
210     if (XPending (dpy) > 0)
211 	rep_mark_input_pending (ConnectionNumber (dpy));
212 }
213 
214 static void
beep(void)215 beep(void)
216 {
217     XBell(dpy, 0);
218     if (XPending (dpy) > 0)
219 	rep_mark_input_pending (ConnectionNumber (dpy));
220 }
221 
222 static void
choose_visual(void)223 choose_visual (void)
224 {
225     int id = 0;
226     if (visual_name != 0)
227     {
228 	if (!strcasecmp ("StaticGray", visual_name))
229 	    id = StaticGray;
230 	else if (!strcasecmp ("StaticColor", visual_name))
231 	    id = StaticColor;
232 	else if (!strcasecmp ("TrueColor", visual_name))
233 	    id = TrueColor;
234 	else if (!strcasecmp ("GrayScale", visual_name)
235 		 || !strcasecmp ("GreyScale", visual_name))
236 	    id = GrayScale;
237 	else if (!strcasecmp ("PseudoColor", visual_name))
238 	    id = PseudoColor;
239 	else if (!strcasecmp ("DirectColor", visual_name))
240 	    id = DirectColor;
241     }
242     if (id != 0 || preferred_depth != 0)
243     {
244 	XVisualInfo in, *out;
245 	int mask = VisualScreenMask, n_out;
246 	in.screen = screen_num;
247 	if (id != 0)
248 	{
249 	    in.class = id;
250 	    mask |= VisualClassMask;
251 	}
252 	out = XGetVisualInfo (dpy, mask, &in, &n_out);
253 	if (out != 0)
254 	{
255 	    int i, best = -1;
256 	    if (preferred_depth > 0)
257 	    {
258 		/* Look for a visual with the preferred depth. */
259 		for (i = 0; i < n_out; i++)
260 		{
261 		    if (out[i].depth == preferred_depth
262 			&& (best < 0
263 			    || out[i].colormap_size > out[best].colormap_size))
264 		    {
265 			best = i;
266 		    }
267 		}
268 	    }
269 	    if (best < 0)
270 	    {
271 		/* Else find the deepest visual of this type. */
272 		for (i = 0, best = 0; i < n_out; i++)
273 		{
274 		    if (out[i].depth > out[best].depth
275 			|| (out[i].depth == out[best].depth
276 			    && out[i].colormap_size > out[best].colormap_size))
277 		    {
278 			best = i;
279 		    }
280 		}
281 	    }
282 	    if (best >= 0 && best < n_out)
283 	    {
284 		preferred_visual = out[best].visual;
285 		preferred_depth = out[best].depth;
286 	    }
287 	    XFree (out);
288 	}
289     }
290     if (preferred_visual == 0)
291     {
292 	if (visual_name != 0)
293 	    fprintf (stderr, "warning: using default visual\n");
294 	preferred_visual = DefaultVisual (dpy, screen_num);
295 	preferred_depth = DefaultDepth (dpy, screen_num);
296     }
297 }
298 
299 
300 /* Acquire the manager selection, replacing its previous owner sel_owner
301    (can be None). */
302 static void
acquire_manager_selection(Window sel_owner)303 acquire_manager_selection(Window sel_owner)
304 {
305     XClientMessageEvent cm;
306     startup_time = get_server_timestamp();
307     if (sel_owner != None)
308     {
309         Window sel2;
310         XSelectInput (dpy, sel_owner, StructureNotifyMask);
311 	/* Make sure that sel_owner still owns the selection.  The window
312 	   might have been destroyed before the XSelectInput call above,
313 	   and we would never hear from it. */
314         sel2 = XGetSelectionOwner (dpy, xa_wm_sn);
315         if (sel2 == None)
316         {
317             /* Gone already! */
318             XSelectInput (dpy, sel_owner, 0);
319             sel_owner = None;
320         }
321         else if (sel2 != sel_owner)
322             /* Somebody else has taken over, so we quit. */
323             replace_race_error ();
324     }
325     XSetSelectionOwner (dpy, xa_wm_sn, no_focus_window, startup_time);
326     if (XGetSelectionOwner (dpy, xa_wm_sn) != no_focus_window)
327     {
328         fputs ("Could not acquire manager selection.\n", stderr);
329         exit (1);
330     }
331     if (sel_owner != None)
332     {
333         fputs ("Waiting for the previous manager to go away.\n", stderr);
334         /* This may hang. */
335         for (;;)
336         {
337             XEvent ev;
338             XWindowEvent (dpy,  sel_owner, StructureNotifyMask,
339                           &ev);
340             if (ev.type == DestroyNotify
341                 && ev.xdestroywindow.window == sel_owner)
342                 break;
343         }
344     }
345     cm.type = ClientMessage;
346     cm.window = root_window;
347     cm.message_type = xa_manager;
348     cm.format = 32;
349     cm.data.l[0] = startup_time;
350     cm.data.l[1] = xa_wm_sn;
351     cm.data.l[2] = no_focus_window;
352     XSendEvent (dpy, root_window, False, StructureNotifyMask, (XEvent *) &cm);
353 }
354 
355 
356 /* Called from main(). */
357 bool
sys_init(char * program_name)358 sys_init(char *program_name)
359 {
360     char *display_name = 0;
361     repv opt;
362 
363 #ifdef HAVE_UNIX
364     if (!batch_mode_p ())
365 	setpgid (0, 0);
366 #endif
367 
368     rep_INTERN_SPECIAL(display_name);
369     rep_INTERN_SPECIAL(canonical_display_name);
370     Fset (Qdisplay_name, rep_null_string ());
371     Fset (Qcanonical_display_name, rep_null_string ());
372 
373     if(!batch_mode_p ())
374     {
375 	if (rep_get_option ("--display", &opt))
376 	    display_name = strdup (rep_STR(opt));
377 	if (rep_get_option ("--visual", &opt))
378 	    visual_name = strdup (rep_STR(opt));
379 	if (rep_get_option ("--depth", &opt))
380 	    preferred_depth = atoi (rep_STR (opt));
381 
382 	if (display_name == 0)
383 	    display_name = getenv("DISPLAY");
384         {
385             int major=1;
386             int minor=0;
387             int error_rtrn;
388             int reason_rtrn;
389             dpy= XkbOpenDisplay (display_name, &xkb_event_base, &error_rtrn, &major, &minor, &reason_rtrn);
390             if (dpy == NULL)            /* or   reason_rtrn != XkbOD_Success. */
391             {
392                 switch (reason_rtrn) {
393                     case XkbOD_ConnectionRefused:
394                         /* nothing to do! */
395                         break;
396                     case XkbOD_NonXkbServer:
397                         fprintf(stderr, "sawfish: the server %s doesn't support XKB, trying without.\n",
398                                 display_name ? display_name : "");
399                     default:
400                         usexkb = 0;
401                         dpy = XOpenDisplay(display_name);
402                 };
403             } else {
404                 /* get xkb_major_opcode */
405                 XkbQueryExtension(dpy, &xkb_major_opcode, &xkb_event_base, &error_rtrn, &major, &minor);
406                 usexkb = 1;
407             }
408         }
409 	if(dpy != 0)
410 	{
411             Window sel_owner;
412 	    Fset (Qdisplay_name, rep_string_dup (display_name));
413 	    Fset (Qcanonical_display_name,
414 		  rep_string_dup (canonical_display (display_name)));
415 	    rep_register_input_fd (ConnectionNumber(dpy), handle_sync_input);
416 	    screen_num = DefaultScreen(dpy);
417 	    root_window = RootWindow(dpy, screen_num);
418 	    screen_width = DisplayWidth(dpy, screen_num);
419 	    screen_height = DisplayHeight(dpy, screen_num);
420 	    choose_visual ();
421 
422             {
423                 char buf[50];
424                 snprintf (buf, sizeof buf, "WM_S%d", screen_num);
425                 xa_wm_sn = XInternAtom (dpy, buf, False);
426             }
427 	    xa_wm_state = XInternAtom (dpy, "WM_STATE", False);
428 	    xa_wm_change_state = XInternAtom (dpy, "WM_CHANGE_STATE", False);
429 	    xa_wm_protocols = XInternAtom (dpy, "WM_PROTOCOLS", False);
430 	    xa_wm_delete_window = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
431 	    xa_wm_colormap_windows = XInternAtom (dpy, "WM_COLORMAP_WINDOWS",
432                                                   False);
433 	    xa_wm_take_focus = XInternAtom (dpy, "WM_TAKE_FOCUS", False);
434 	    xa_compound_text = XInternAtom (dpy, "COMPOUND_TEXT", False);
435 	    xa_wm_net_name = XInternAtom (dpy, "_NET_WM_NAME", False);
436 	    xa_wm_net_icon_name = XInternAtom (dpy, "_NET_WM_ICON_NAME",
437                                                False);
438 	    xa_utf8_string = XInternAtom (dpy, "UTF8_STRING", False);
439             xa_manager = XInternAtom (dpy, "MANAGER", False);
440 
441             sel_owner = XGetSelectionOwner (dpy, xa_wm_sn);
442 	    /* Order is significant: rep_get_option must get called. */
443             if (!rep_get_option ("--replace", 0) && sel_owner != None)
444             {
445                 fputs ("A window manager is already running but "
446                        "--replace was not given.\n", stderr);
447                 exit (1);
448             }
449 
450 	    if (!XShapeQueryExtension (dpy, &shape_event_base,
451 				       &shape_error_base))
452 	    {
453 		fprintf (stderr, "sawfish: your X server doesn't support "
454                          "the SHAPE extension; aborting\n");
455 		return FALSE;
456 	    }
457 
458 	    /* Error handler is used to prevent from two Sawfish running.
459 	     * (Not sure for other WMs.) */
460 	    {
461                 /* Create the mapped-but-invisible window that is given
462 		   the focus when no other window has it.  The window
463                    is also used for selection manipulation. */
464 		XSetWindowAttributes attr;
465 		attr.event_mask = NO_FOCUS_EVENTS;
466 		attr.override_redirect = True;
467 		no_focus_window = XCreateWindow (dpy, root_window,
468 						 -10, -10, 10, 10, 0, 0,
469 						 InputOnly, CopyFromParent,
470 						 CWEventMask
471 						 | CWOverrideRedirect,
472 						 &attr);
473 		XMapWindow (dpy, no_focus_window);
474 	    }
475 
476             acquire_manager_selection (sel_owner);
477 
478 	    XSync (dpy, False);
479 	    XSetErrorHandler (error_other_wm);
480 	    XSelectInput (dpy, root_window, ROOT_EVENTS);
481 	    XSync (dpy, False);
482 	    XSetErrorHandler (error_handler);
483 
484 	    /* This should _never_ be used in Real Life; only for
485 	       debugging. Sawfish tries to work out when the error
486 	       handle might be called (i.e. after any XGet, XQuery, XFetch
487 	       type function) and then call emit_pending_destroys ()
488 	       as soon as possible, so that there's as small as possible
489 	       delay between the window being destroyed and the hook
490 	       being called.. */
491 	    if (rep_get_option ("--sync", 0))
492 		XSynchronize (dpy, True);
493 
494 	    /* If I don't do this all the events that are created by
495 	       the window initialiation are ignored until the next
496 	       new event arrives (because of the XSync calls above) */
497 	    rep_mark_input_pending (ConnectionNumber(dpy));
498 
499 	    rep_redisplay_fun = redisplay;
500 	    rep_beep_fun = beep;
501 
502 	    return TRUE;
503 	}
504 	else
505 	{
506 	    fprintf(stderr, "sawfish: Can't open display: %s\n",
507 		    display_name ? display_name : "");
508 	    return FALSE;
509 	}
510     }
511     else
512 	return TRUE;
513 }
514 
515 void
sys_kill(void)516 sys_kill (void)
517 {
518     if(!batch_mode_p ())
519     {
520 	XSetInputFocus (dpy, PointerRoot, 0, last_event_time);
521 	XSelectInput (dpy, root_window, 0);
522 	XDestroyWindow (dpy, no_focus_window);
523 	XCloseDisplay (dpy);
524     }
525 }
526 
527 /* utilities */
528 
529 repv
x_atom_symbol(Atom atom)530 x_atom_symbol (Atom atom)
531 {
532     char *name = XGetAtomName (dpy, atom);
533     if (name != 0)
534     {
535 	repv sym = Fintern (rep_string_dup (name), rep_obarray);
536 	XFree (name);
537 	return sym;
538     }
539     else
540 	return Qnil;
541 }
542 
543 Window
x_win_from_arg(repv arg)544 x_win_from_arg (repv arg)
545 {
546     if (arg == Qroot)
547 	return root_window;
548     else if (WINDOWP(arg))
549 	return VWIN(arg)->id;
550     else if (rep_INTEGERP(arg))
551 	return rep_get_long_uint (arg);
552     else
553 	return 0;
554 }
555 
556 /***************************************************************************
557  *
558  * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
559  * client messages will have the following form:
560  *
561  *     event type       ClientMessage
562  *     message type     _XA_WM_PROTOCOLS
563  *     window           tmp->w
564  *     format           32
565  *     data[0]          message atom
566  *     data[1]          time stamp
567  *
568  ****************************************************************************/
569 void
send_client_message(Window w,Atom a,Time time)570 send_client_message (Window w, Atom a, Time time)
571 {
572   XClientMessageEvent ev;
573 
574   ev.type = ClientMessage;
575   ev.window = w;
576   ev.message_type = xa_wm_protocols;
577   ev.format = 32;
578   ev.data.l[0] = a;
579   ev.data.l[1] = time;
580   XSendEvent (dpy, w, False, 0L, (XEvent *) &ev);
581 }
582 
583 #if XlibSpecificationRelease < 6
584 Status
XGetAtomNames(Display * dpy,Atom * atoms,int count,char ** names_ret)585 XGetAtomNames (Display *dpy, Atom *atoms, int count, char **names_ret)
586 {
587     int i;
588     for (i = 0; i < count; i++)
589     {
590 	names_ret[i] = XGetAtomName (dpy, atoms[i]);
591 	if (names_ret[i] == 0)
592 	    break;
593     }
594     if (i == count)
595 	return 1;
596     for (i--; i >= 0; i--)
597 	XFree (names_ret[i]);
598     return 0;
599 }
600 #endif
601 
602 void
db_printf(char * fmt,...)603 db_printf(char *fmt, ...)
604 {
605     va_list args;
606     va_start(args, fmt);
607     rep_db_vprintf(rep_common_db, fmt, args);
608     va_end(args);
609 }
610