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