Exemple #1
0
void
SecureDisplay (struct display *d, Display *dpy)
{
    Debug ("SecureDisplay %s\n", d->name);
    (void) Signal (SIGALRM, syncTimeout);
    if (Setjmp (syncJump)) {
	LogError ("WARNING: display %s could not be secured\n",
		   d->name);
	SessionExit (d, RESERVER_DISPLAY, FALSE);
    }
    (void) alarm ((unsigned) d->grabTimeout);
    Debug ("Before XGrabServer %s\n", d->name);
    XGrabServer (dpy);
    if (XGrabKeyboard (dpy, DefaultRootWindow (dpy), True, GrabModeAsync,
		       GrabModeAsync, CurrentTime) != GrabSuccess)
    {
	(void) alarm (0);
	(void) Signal (SIGALRM, SIG_DFL);
	LogError ("WARNING: keyboard on display %s could not be secured\n",
		  d->name);
	SessionExit (d, RESERVER_DISPLAY, FALSE);
    }
    Debug ("XGrabKeyboard succeeded %s\n", d->name);
    (void) alarm (0);
    (void) Signal (SIGALRM, SIG_DFL);
    pseudoReset (dpy);
    if (!d->grabServer)
    {
	XUngrabServer (dpy);
	XSync (dpy, 0);
    }
    Debug ("done secure %s\n", d->name);
}
Exemple #2
0
void SessionPingFailed(struct display *d)
{
	if (clientPid > 1) {
		AbortClient(clientPid);
		source(verify.systemEnviron, d->reset);
	}
	SessionExit(d, RESERVER_DISPLAY, TRUE);
}
Exemple #3
0
static void
callback_die(SmcConn smc_conn __UNUSED__, SmPointer client_data __UNUSED__)
{
   if (EDebug(EDBUG_TYPE_SESSION))
      Eprintf("callback_die\n");

   SessionExit(EEXIT_EXIT, NULL);
}
Exemple #4
0
__dead void
ManageSession (struct display *d)
{
    pid_t	pid = 0;
    greet_user_rtn	greet_stat;

    Debug ("ManageSession %s\n", d->name);
    (void)XSetIOErrorHandler(IOErrorHandler);
    (void)XSetErrorHandler(ErrorHandler);
    setproctitle("%s", d->name);


    if (d->autoLogin == NULL || d->autoLogin[0] == '\0') {
        /* Load system default Resources */
        LoadXloginResources (d);
        greet_stat = GreetUser(d, &verify, &greet);
    } else
        greet_stat = AutoLogin(d, &verify, &greet);

    if (greet_stat == Greet_Success) {
	clientPid = 0;
	(void) signal (SIGTERM, catchTerm);
	/*
	 * Start the clients, changing uid/groups
	 *	   setting up environment and running the session
	 */
	if (StartClient (&verify, d, &clientPid, greet.name)) {
	  Debug ("Client Started\n");
	  /* Wait for session to end, */
	  pid = waitpid(clientPid, NULL, 0);
	  if (pid <= 0 && abortSession) {
	    /*
	     * when terminating the session, nuke
	     * the child and then run the reset script
	     */
	    AbortClient (clientPid);
	  }
	} else {
	  LogError ("session start failed\n");
	}
    }
    /*
     * run system-wide reset file
     */
    if (d->windowPath != NULL)
        login_fbtab(d->windowPath, 0, 0);
    Debug ("Source reset program %s\n", d->reset);
    source (verify.systemEnviron, d->reset);
    SessionExit (d, OBEYSESS_DISPLAY, TRUE);
}
Exemple #5
0
void
SecureDisplay (struct display *d, Display *dpy)
{
    Debug ("SecureDisplay %s\n", d->name);
    Debug ("Before XGrabServer %s\n", d->name);
    XGrabServer (dpy);
    if (XGrabKeyboard (dpy, DefaultRootWindow (dpy), True, GrabModeAsync,
		       GrabModeAsync, CurrentTime) != GrabSuccess) {
	LogError ("WARNING: keyboard on display %s could not be secured\n",
		  d->name);
	SessionExit (d, RESERVER_DISPLAY, FALSE);
    }
    Debug ("XGrabKeyboard succeeded %s\n", d->name);
    pseudoReset (dpy);
    if (!d->grabServer) {
	XUngrabServer (dpy);
	XSync (dpy, 0);
    }
    Debug ("done secure %s\n", d->name);
}
Exemple #6
0
static void guaranteed_read(int fd, char *buf, size_t count)
{
	int bytes_read;

	if (count == 0) {
		return;
	}

	while ((bytes_read = read(fd, buf, count)) > 0) {
		count -= bytes_read;
		buf += bytes_read;

		if (count == 0) {
			return;
		}
	}

	WDMError("Greet: guarenteed_read error, UNMANAGE DISPLAY\n");
	WDMError("Greet: pipe read error with %s\n", wdmLogin);
	SessionExit(Save_d, RESERVER_DISPLAY, FALSE);	/* this exits */
	exit(UNMANAGE_DISPLAY);		/* should not happen */
}
Exemple #7
0
int
main(int argc, char **argv)
{
    int                 ch, i, loop;
    struct utsname      ubuf;
    const char         *str, *dstr;

    /* This function runs all the setup for startup, and then
     * proceeds into the primary event loop at the end.
     */

    /* Init state variable struct */
    memset(&Mode, 0, sizeof(EMode));

    Mode.wm.master = 1;
    Mode.wm.pid = getpid();
    Mode.wm.exec_name = argv[0];
    Mode.wm.startup = 1;

    Mode.mode = MODE_NONE;

    EXInit();
    Dpy.screen = -1;

    str = getenv("EDEBUG");
    if (str)
        EDebugInit(str);
    str = getenv("EDEBUG_COREDUMP");
    if (str)
        Mode.wm.coredump = 1;
    str = getenv("EDEBUG_EXIT");
    if (str)
        Mode.debug_exit = atoi(str);

    str = getenv("ECONFNAME");
    if (str)
        EConfNameSet(str);
    str = getenv("ECONFDIR");
    if (str)
        EDirUserSet(str);
    str = getenv("ECACHEDIR");
    if (str)
        EDirUserCacheSet(str);

    srand((unsigned int)time(NULL));

    if (!uname(&ubuf))
        Mode.wm.machine_name = Estrdup(ubuf.nodename);
    if (!Mode.wm.machine_name)
        Mode.wm.machine_name = Estrdup("localhost");

    /* Now we're going to interpret any of the commandline parameters
     * that are passed to it -- Well, at least the ones that we
     * understand.
     */

    Mode.theme.path = NULL;
    dstr = NULL;

    for (loop = 1; loop;)
    {
        ch = EoptGet(argc, argv);
        if (ch <= 0)
            break;
#if 0
        Eprintf("Opt: %c: %d - %s\n", ch, eoptind, eoptarg);
#endif
        switch (ch)
        {
        default:
        case '?':
            printf("e16: Ignoring: ");
            for (i = eoptind; i < argc; i++)
                printf("%s ", argv[i]);
            printf("\n");
            loop = 0;
            break;
        case 'h':
            EoptHelp();
            exit(0);
            break;
        case 'd':
            dstr = eoptarg;
            break;
        case 'f':
            Mode.wm.restart = 1;
            break;
        case 'p':
            EConfNameSet(eoptarg);
            break;
        case 'P':
            EDirUserSet(eoptarg);
            break;
        case 'Q':
            EDirUserCacheSet(eoptarg);
            break;
        case 's':
            Mode.wm.single = 1;
            Dpy.screen = strtoul(eoptarg, NULL, 10);
            break;
        case 'S':
            SetSMID(eoptarg);
            break;
        case 't':
            Mode.theme.path = Estrdup(eoptarg);
            break;
        case 'V':
            printf("%s %s\n", e_wm_name, e_wm_version);
            exit(0);
            break;
        case 'v':
            EDebugSet(EDBUG_TYPE_VERBOSE, 1);
            break;
        case 'w':
            sscanf(eoptarg, "%dx%d", &Mode.wm.win_w, &Mode.wm.win_h);
            Mode.wm.window = 1;
            Mode.wm.single = 1;
            Mode.wm.master = 0;
            break;
#ifdef USE_EXT_INIT_WIN
        case 'X':
            ExtInitWinSet(strtoul(eoptarg, NULL, 0));
            Mode.wm.restart = 1;
            break;
#endif
        case 'm':
            Mode.wm.master = 0;
            Mode.wm.master_screen = strtoul(eoptarg, NULL, 10);
            break;
        }
    }

    SignalsSetup();		/* Install signal handlers */

    EDirsSetup();
    ECheckEprog("epp");
    ECheckEprog("eesh");

    SetupX(dstr);		/* This is where the we fork per screen */
    /* X is now running, and we have forked per screen */

    ESavePrefixSetup();

    /* So far nothing should rely on a selected settings or theme. */
    ConfigurationLoad();		/* Load settings */

    /* Initialise internationalisation */
    LangInit();

    /* The theme path must now be available for config file loading. */
    ThemePathFind();

    /* Set the Environment variables */
    Esetenv("EVERSION", e_wm_version);
    Esetenv("EROOT", EDirRoot());
    Esetenv("EBIN", EDirBin());
    Esetenv("ECONFDIR", EDirUser());
    Esetenv("ECACHEDIR", EDirUserCache());
    Esetenv("ETHEME", Mode.theme.path);

    /* Move elsewhere? */
    EImageInit();
    HintsInit();
    CommsInit();
    SessionInit();
    SnapshotsLoad();

#if USE_DBUS
    DbusInit();
#endif

    if (Mode.wm.window)
        EMapWindow(VROOT);

    ModulesSignal(ESIGNAL_INIT, NULL);

    /* Load the theme */
    ThemeConfigLoad();

    if (Mode.debug_exit)
        return 0;

    /* Do initial configuration */
    ModulesSignal(ESIGNAL_CONFIGURE, NULL);

    /* Set root window cursor */
    ECsrApply(ECSR_ROOT, WinGetXwin(VROOT));

#ifdef USE_EXT_INIT_WIN
    /* Kill the E process owning the "init window" */
    ExtInitWinKill();
#endif

    /* let's make sure we set this up and go to our desk anyways */
    DeskGoto(DesksGetCurrent());
    ESync(ESYNC_MAIN);

#ifdef SIGCONT
    for (i = 0; i < Mode.wm.child_count; i++)
        kill(Mode.wm.children[i], SIGCONT);
#endif

    ModulesSignal(ESIGNAL_START, NULL);
#if ENABLE_DIALOGS
    DialogsInit();
#endif
    EwinsManage();

    RunInitPrograms();
    SnapshotsSpawn();

    if (!Mode.wm.restart)
        StartupWindowsOpen();

    Conf.startup.firsttime = 0;
    Mode.wm.save_ok = Conf.autosave;
    Mode.wm.startup = 0;
    autosave();

    /* The primary event loop */
    EventsMain();

    SessionExit(EEXIT_QUIT, NULL);

    return 0;
}
Exemple #8
0
void
ManageSession (struct display *d)
{
    static int		pid = 0;
    Display		*dpy;
    greet_user_rtn	greet_stat; 
    static GreetUserProc greet_user_proc = NULL;
#ifndef GREET_USER_STATIC
    void		*greet_lib_handle;
#endif

    Debug ("ManageSession %s\n", d->name);
    (void)XSetIOErrorHandler(IOErrorHandler);
    (void)XSetErrorHandler(ErrorHandler);
#ifndef HAS_SETPROCTITLE
    SetTitle(d->name, (char *) 0);
#else
    setproctitle("%s", d->name);
#endif
    /*
     * Load system default Resources
     */
    LoadXloginResources (d);

#ifdef GREET_USER_STATIC
    greet_user_proc = GreetUser;
#else
    Debug("ManageSession: loading greeter library %s\n", greeterLib);
    greet_lib_handle = dlopen(greeterLib, RTLD_NOW);
    if (greet_lib_handle != NULL)
	greet_user_proc = (GreetUserProc)dlsym(greet_lib_handle, "GreetUser");
    if (greet_user_proc == NULL)
	{
	LogError("%s while loading %s\n", dlerror(), greeterLib);
	exit(UNMANAGE_DISPLAY);
	}
#endif

    /* tell the possibly dynamically loaded greeter function
     * what data structure formats to expect.
     * These version numbers are registered with The Open Group. */
    verify.version = 1;
    greet.version = 1;
    greet_stat = (*greet_user_proc)(d, &dpy, &verify, &greet, &dlfuncs);

    if (greet_stat == Greet_Success)
    {
	clientPid = 0;
	if (!Setjmp (abortSession)) {
	    (void) Signal (SIGTERM, catchTerm);
	    /*
	     * Start the clients, changing uid/groups
	     *	   setting up environment and running the session
	     */
	    if (StartClient (&verify, d, &clientPid, greet.name, greet.password)) {
		Debug ("Client Started\n");

#ifndef GREET_USER_STATIC
                /* Save memory; close library */
                dlclose(greet_lib_handle);
#endif
 
		/*
		 * Wait for session to end,
		 */
		for (;;) {
		    if (d->pingInterval)
		    {
			if (!Setjmp (pingTime))
			{
			    (void) Signal (SIGALRM, catchAlrm);
			    (void) alarm (d->pingInterval * 60);
			    pid = wait ((waitType *) 0);
			    (void) alarm (0);
			}
			else
			{
			    (void) alarm (0);
			    if (!PingServer (d, (Display *) NULL))
				SessionPingFailed (d);
			}
		    }
		    else
		    {
			pid = wait ((waitType *) 0);
		    }
		    if (pid == clientPid)
			break;
		}
	    } else {
		LogError ("session start failed\n");
	    }
	} else {
	    /*
	     * when terminating the session, nuke
	     * the child and then run the reset script
	     */
	    AbortClient (clientPid);
	}
    }
    /*
     * run system-wide reset file
     */
    Debug ("Source reset program %s\n", d->reset);
    source (verify.systemEnviron, d->reset);
    SessionExit (d, OBEYSESS_DISPLAY, TRUE);
}
Exemple #9
0
static void
HandleXIOError(void)
{
   SessionExit(EEXIT_ERROR, NULL);
}
Exemple #10
0
void ManageSession(struct display *d)
{
	static int pid = 0;
	Display *dpy;
	greet_user_rtn greet_stat;
#ifdef WITH_CONSOLE_KIT
	char *ck_session_cookie = NULL;
#endif

	WDMDebug("ManageSession %s\n", d->name);
	(void)XSetIOErrorHandler(IOErrorHandler);
	(void)XSetErrorHandler(ErrorHandler);
#ifndef HAS_SETPROCTITLE
	SetTitle(d->name, (char *)0);
#else
	setproctitle("%s", d->name);
#endif
	/*
	 * Load system default Resources
	 */
	LoadXloginResources(d);

	verify.version = 1;
	greet.version = 1;
	greet_stat = GreetUser(d, &dpy, &verify, &greet);

	if (greet_stat == Greet_Success) {
		clientPid = 0;
		if (!Setjmp(abortSession)) {
			(void)Signal(SIGTERM, catchTerm);
			/*
			 * Start the clients, changing uid/groups
			 *     setting up environment and running the session
			 */
#ifdef WITH_CONSOLE_KIT
			ck_session_cookie = open_ck_session(getpwnam(greet.name), d);
#endif
			if (StartClient(&verify, d, &clientPid, greet.name, greet.password
#ifdef WITH_CONSOLE_KIT
							, ck_session_cookie
#endif
				)) {
				WDMDebug("Client Started\n");

				/*
				 * Wait for session to end,
				 */
				for (;;) {
					if (d->pingInterval) {
						if (!Setjmp(pingTime)) {
							(void)Signal(SIGALRM, catchAlrm);
							(void)alarm(d->pingInterval * 60);
							pid = wait((waitType *) 0);
							(void)alarm(0);
						} else {
							(void)alarm(0);
							if (!PingServer(d, (Display *) NULL))
								SessionPingFailed(d);
						}
					} else {
						pid = wait((waitType *) 0);
					}
					if (pid == clientPid)
						break;
				}
			} else {
				WDMError("session start failed\n");
			}
		} else {
			/*
			 * when terminating the session, nuke
			 * the child and then run the reset script
			 */
			AbortClient(clientPid);
		}
	}
#ifdef WITH_CONSOLE_KIT
	if (ck_session_cookie != NULL) {
		close_ck_session(ck_session_cookie);
		free(ck_session_cookie);
	}
#endif

	/*
	 * run system-wide reset file
	 */
	WDMDebug("Source reset program %s\n", d->reset);
	source(verify.systemEnviron, d->reset);
	SessionExit(d, OBEYSESS_DISPLAY, TRUE);
}
Exemple #11
0
greet_user_rtn GreetUser(struct display * d, Display ** dpy, struct verify_info * verify, struct greet_info * greet)
{
	int flag;
	int pid;
	int code;

	Save_d = d;					/* hopefully, this is OK */

	*dpy = XOpenDisplay(d->name);	/* make sure we have the display */
	/*
	 * Run the setup script - note this usually will not work when
	 * the server is grabbed, so we don't even bother trying.
	 */
	if (!d->grabServer)
		SetupDisplay(d);
	if (!*dpy) {
		WDMError("Cannot reopen display %s for greet window\n", d->name);
		exit(RESERVER_DISPLAY);
	}

	pid = InitGreet(d);			/* fork and exec the external program */

	for (;;) {
		/*
		 * Greet user, requesting name/password
		 */
		code = Greet(d, greet);
		WDMDebug("Greet greet done: %s, pwlen=%i\n", name, strlen(password));

		if (code != 0) {
			WDMDebug("Greet: exit code=%i, %s\n", code, exitArg);
			if (wdmVerify || wdmRoot) {
				flag = False;
				if (Verify(d, greet, verify))
					flag = True;
				if (wdmRoot && (strcmp(greet->name, "root") != 0))
					flag = False;
			} else
				flag = True;
			if (flag == True) {
				switch (code) {
				case 2:		/* reboot */
					CloseGreet(pid);
					WDMInfo("reboot(%s) by %s\n", exitArg, name);
					system(wdmReboot);
					SessionExit(d, UNMANAGE_DISPLAY, FALSE);
					break;
				case 3:		/* halt */
					CloseGreet(pid);
					WDMInfo("halt(%s) by %s\n", exitArg, name);
					system(wdmHalt);
					SessionExit(d, UNMANAGE_DISPLAY, FALSE);
					break;
				case 4:		/* exit */
					CloseGreet(pid);
					WDMDebug("UNMANAGE_DISPLAY\n");
					WDMInfo("%s exit(%s) by %s\n", PACKAGE_NAME, exitArg, name);
#if 0
					SessionExit(d, UNMANAGE_DISPLAY, FALSE);
#else
					WDMDebug("Killing parent process %d\n", getppid());
					kill(getppid(), SIGINT);
#endif
					break;
				}
			} else {
				kill(pid, SIGUSR1);	/* Verify failed */
			}
		} else {
			/*
			 * Verify user
			 */
			if ((!*greet->name) && *wdmDefaultUser) {
				greet->name = wdmDefaultUser;
				greet->password = wdmDefaultPasswd;
			}
			if (Verify(d, greet, verify))
				break;
			else
				kill(pid, SIGUSR1);	/* Verify failed */
		}
	}
	DeleteXloginResources(d, *dpy);
	CloseGreet(pid);
	WDMDebug("Greet loop finished\n");
	/*
	 * Run system-wide initialization file
	 */
	if (source(verify->systemEnviron, d->startup) != 0) {
		WDMDebug("Startup program %s exited with non-zero status\n", d->startup);
		SessionExit(d, OBEYSESS_DISPLAY, FALSE);
	}

	return Greet_Success;
}
Exemple #12
0
static void
ShowAlert(const char *title,
	  const char *ignore, const char *restart, const char *quit,
	  const char *fmt, va_list args)
{
   char                text[4096], buf1[64], buf2[64], buf3[64];
   Window              win, b1 = 0, b2 = 0, b3 = 0, root;
   Display            *dd;
   int                 wid, hih, w, h, i, k, mask;
   XGCValues           gcv;
   GC                  gc;
   unsigned int        len;
   XEvent              ev;
   XSetWindowAttributes att;
   XRectangle          rect1, rect2;
   char                colorful;
   unsigned long       cols[5];
   XColor              xcl;
   Colormap            cmap;
   int                 cnum, fh, x, y, ww, hh, bw, bh;
   char               *str1, *str2, *str3, *p;
   KeyCode             keycode;
   int                 button;
   char              **missing_charset_list_return, *def_string_return;
   int                 missing_charset_count_return;
   XFontStruct       **font_struct_list_return;
   char              **font_name_list_return;

#if 0
   /* Don't play sound here (maybe if not forked/in signal handler - later) */
   SoundPlay(SOUND_ALERT);
#endif

   if (!fmt)
      return;

   Evsnprintf(text, sizeof(text), fmt, args);

   /*
    * We may get here from obscure places like an X-error or signal handler
    * and things seem to work properly only if we do a new XOpenDisplay().
    */
   dd = XOpenDisplay(NULL);
   if (!dd)
     {
	fprintf(stderr, "%s\n", text);
	fflush(stderr);
	return;
     }

   button = 0;

   if (!title)
      title = _("Enlightenment Error");
   str1 = AlertButtonText(1, buf1, sizeof(buf1), ignore);
   str2 = AlertButtonText(2, buf2, sizeof(buf2), restart);
   str3 = AlertButtonText(3, buf3, sizeof(buf3), quit);

   cnum = 0;
   colorful = 0;
   cols[0] = cols[1] = cols[2] = cols[3] = cols[4] = 0;
   cmap = DefaultColormap(dd, DefaultScreen(dd));
   if (DefaultDepth(dd, DefaultScreen(dd)) > 4)
     {
	ExSetColor(&xcl, 220, 220, 220);
	if (!XAllocColor(dd, cmap, &xcl))
	   goto CN;
	cols[cnum++] = xcl.pixel;
	ExSetColor(&xcl, 160, 160, 160);
	if (!XAllocColor(dd, cmap, &xcl))
	   goto CN;
	cols[cnum++] = xcl.pixel;
	ExSetColor(&xcl, 100, 100, 100);
	if (!XAllocColor(dd, cmap, &xcl))
	   goto CN;
	cols[cnum++] = xcl.pixel;
	ExSetColor(&xcl, 0, 0, 0);
	if (!XAllocColor(dd, cmap, &xcl))
	   goto CN;
	cols[cnum++] = xcl.pixel;
	ExSetColor(&xcl, 255, 255, 255);
	if (!XAllocColor(dd, cmap, &xcl))
	   goto CN;
	cols[cnum++] = xcl.pixel;
	colorful = 1;
     }
 CN:

   if (colorful)
      att.background_pixel = cols[1];
   else
      att.background_pixel = BlackPixel(dd, DefaultScreen(dd));
   if (colorful)
      att.border_pixel = cols[3];
   else
      att.border_pixel = WhitePixel(dd, DefaultScreen(dd));
   att.backing_store = Always;
   att.save_under = True;
   att.override_redirect = True;
   mask = CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWSaveUnder |
      CWBackingStore;

#if USE_COMPOSITE_OVERLAY_WINDOW
   /*
    * Intended workings:
    * Composite extension not enabled (or COW not available?)
    * - fall back to root
    * Composite extension enabled
    * - use COW whether or not compositing is enabled, window mode too
    */
   root = XCompositeGetOverlayWindow(dd, DefaultRootWindow(dd));
   if (root == None)
#endif
     {
	root = DefaultRootWindow(dd);
     }
   win = XCreateWindow(dd, root, -100, -100, 1, 1, 0,
		       CopyFromParent, InputOutput, CopyFromParent, mask, &att);

   gc = XCreateGC(dd, win, 0, &gcv);
   if (colorful)
      XSetForeground(dd, gc, cols[3]);
   else
      XSetForeground(dd, gc, att.border_pixel);

   xfs = XCreateFontSet(dd, "fixed",
			&missing_charset_list_return,
			&missing_charset_count_return, &def_string_return);
   if (!xfs)
      goto done;

   if (missing_charset_list_return)
      XFreeStringList(missing_charset_list_return);

   k = XFontsOfFontSet(xfs, &font_struct_list_return, &font_name_list_return);
   fh = 0;
   for (i = 0; i < k; i++)
     {
	h = font_struct_list_return[i]->ascent +
	   font_struct_list_return[i]->descent;
	if (fh < h)
	   fh = h;
     }

   XSelectInput(dd, win, ExposureMask);
   XMapWindow(dd, win);

   XGrabServer(dd);

   XGrabPointer(dd, win, False, ButtonPressMask | ButtonReleaseMask,
		GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
   XGrabKeyboard(dd, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
   XSetInputFocus(dd, win, RevertToPointerRoot, CurrentTime);

   XSync(dd, False);

   wid = DisplayWidth(dd, DefaultScreen(dd));
   hih = DisplayHeight(dd, DefaultScreen(dd));
   ww = (wid >= 600) ? 600 : (wid / 40) * 40;
   hh = (hih >= 440) ? 440 : (hih / 40) * 40;

   for (i = 40; i < ww; i += 40)
     {
	w = i;
	h = (i * hh) / ww;
	x = (wid - w) >> 1;
	y = (hih - h) >> 1;
	XMoveResizeWindow(dd, win, x, y, w, h);
	DRAW_BOX_OUT(dd, gc, win, 0, 0, w, h);
	XSync(dd, False);
	SleepUs(20000);
     }
   x = (wid - ww) >> 1;
   y = (hih - hh) >> 1;
   XMoveResizeWindow(dd, win, x, y, ww, hh);
   XSync(dd, False);

   bw = 0;
   if (str1)
     {
	ExTextExtents(xfs, str1, strlen(str1), &rect1, &rect2);
	bw = (rect2.width > bw) ? rect2.width : bw;
     }
   if (str2)
     {
	ExTextExtents(xfs, str2, strlen(str2), &rect1, &rect2);
	bw = (rect2.width > bw) ? rect2.width : bw;
     }
   if (str3)
     {
	ExTextExtents(xfs, str3, strlen(str3), &rect1, &rect2);
	bw = (rect2.width > bw) ? rect2.width : bw;
     }
   bw += 20;
   bh = fh + 10;

#define BX(i) (5 + (((ww - bw - 10) * (i)) / 2))
#define BY    (hh - bh - 5)

   if (str1)
     {
	b1 = XCreateWindow(dd, win, BX(0), BY, bw, bh, 0, CopyFromParent,
			   InputOutput, CopyFromParent, mask, &att);
	XMapWindow(dd, b1);
     }
   if (str2)
     {
	b2 = XCreateWindow(dd, win, BX(1), BY, bw, bh, 0, CopyFromParent,
			   InputOutput, CopyFromParent, mask, &att);
	XMapWindow(dd, b2);
     }
   if (str3)
     {
	b3 = XCreateWindow(dd, win, BX(2), BY, bw, bh, 0, CopyFromParent,
			   InputOutput, CopyFromParent, mask, &att);
	XMapWindow(dd, b3);
     }
   XSync(dd, False);

   button = 0;
   for (; button == 0;)
     {
	XNextEvent(dd, &ev);
	switch (ev.type)
	  {
	  case KeyPress:
	     keycode = XKeysymToKeycode(dd, XK_F1);
	     if (keycode == ev.xkey.keycode)
	       {
		  DRAW_BOX_IN(dd, gc, b1, 0, 0, bw, bh);
		  XSync(dd, False);
		  SleepUs(500000);
		  DRAW_BOX_OUT(dd, gc, b1, 0, 0, bw, bh);
		  button = 1;
		  goto do_sync;
	       }
	     keycode = XKeysymToKeycode(dd, XK_F2);
	     if (keycode == ev.xkey.keycode)
	       {
		  DRAW_BOX_IN(dd, gc, b2, 0, 0, bw, bh);
		  XSync(dd, False);
		  SleepUs(500000);
		  DRAW_BOX_OUT(dd, gc, b2, 0, 0, bw, bh);
		  button = 2;
		  goto do_sync;
	       }
	     keycode = XKeysymToKeycode(dd, XK_F3);
	     if (keycode == ev.xkey.keycode)
	       {
		  DRAW_BOX_IN(dd, gc, b3, 0, 0, bw, bh);
		  XSync(dd, False);
		  SleepUs(500000);
		  DRAW_BOX_OUT(dd, gc, b3, 0, 0, bw, bh);
		  button = 3;
		  goto do_sync;
	       }
	     break;

	  case ButtonPress:
	     if (!(ev.xbutton.y >= BY && ev.xbutton.y < BY + bh))
		break;

	     x = BX(0);
	     if (b1 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
	       {
		  DRAW_BOX_IN(dd, gc, b1, 0, 0, bw, bh);
		  goto do_sync;
	       }
	     x = BX(1);
	     if (b2 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
	       {
		  DRAW_BOX_IN(dd, gc, b2, 0, 0, bw, bh);
		  goto do_sync;
	       }
	     x = BX(2);
	     if (b3 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
	       {
		  DRAW_BOX_IN(dd, gc, b3, 0, 0, bw, bh);
		  goto do_sync;
	       }
	     break;

	  case ButtonRelease:
	     if (!(ev.xbutton.y >= BY && ev.xbutton.y < BY + bh))
		break;

	     x = BX(0);
	     if (b1 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
	       {
		  DRAW_BOX_OUT(dd, gc, b1, 0, 0, bw, bh);
		  button = 1;
		  goto do_sync;
	       }
	     x = BX(1);
	     if (b2 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
	       {
		  DRAW_BOX_OUT(dd, gc, b2, 0, 0, bw, bh);
		  button = 2;
		  goto do_sync;
	       }
	     x = BX(2);
	     if (b3 && ev.xbutton.x >= x && ev.xbutton.x < x + bw)
	       {
		  DRAW_BOX_OUT(dd, gc, b3, 0, 0, bw, bh);
		  button = 3;
		  goto do_sync;
	       }
	     break;

	  case Expose:
	     /* Flush all other Expose events */
	     while (XCheckTypedWindowEvent(dd, ev.xexpose.window, Expose, &ev))
		;

	     ExTextExtents(xfs, title, strlen(title), &rect1, &rect2);
	     w = rect2.width;

	     DRAW_HEADER(dd, gc, win, (ww - w) / 2, 5 - rect2.y, title);
	     DRAW_BOX_OUT(dd, gc, win, 0, 0, ww, bh);
	     DRAW_BOX_OUT(dd, gc, win, 0, bh - 1, ww, hh - fh - fh - 30 + 2);
	     DRAW_BOX_OUT(dd, gc, win, 0, hh - fh - 20, ww, fh + 20);
	     k = bh;
	     for (p = text;; p += len + 1)
	       {
		  len = strcspn(p, "\n");
		  DRAW_STRING(dd, gc, win, 6, 6 + k + fh, p, len);
		  k += fh + 2;
		  if (p[len] == '\0')
		     break;
	       }
	     if (str1)
	       {
		  ExTextExtents(xfs, str1, strlen(str1), &rect1, &rect2);
		  w = rect2.width;
		  DRAW_HEADER(dd, gc, b1, (bw - w) / 2, 5 - rect2.y, str1);
		  DRAW_BOX_OUT(dd, gc, b1, 0, 0, bw, bh);
		  DRAW_THIN_BOX_IN(dd, gc, win,
				   BX(0) - 2, BY - 2, bw + 4, bh + 4);
	       }
	     if (str2)
	       {
		  ExTextExtents(xfs, str2, strlen(str2), &rect1, &rect2);
		  w = rect2.width;
		  DRAW_HEADER(dd, gc, b2, (bw - w) / 2, 5 - rect2.y, str2);
		  DRAW_BOX_OUT(dd, gc, b2, 0, 0, bw, bh);
		  DRAW_THIN_BOX_IN(dd, gc, win,
				   BX(1) - 2, BY - 2, bw + 4, bh + 4);
	       }
	     if (str3)
	       {
		  ExTextExtents(xfs, str3, strlen(str3), &rect1, &rect2);
		  w = rect2.width;
		  DRAW_HEADER(dd, gc, b3, (bw - w) / 2, 5 - rect2.y, str3);
		  DRAW_BOX_OUT(dd, gc, b3, 0, 0, bw, bh);
		  DRAW_THIN_BOX_IN(dd, gc, win,
				   BX(2) - 2, BY - 2, bw + 4, bh + 4);
	       }
	   do_sync:
	     XSync(dd, False);
	     break;

	  default:
	     break;
	  }
     }

   XFreeFontSet(dd, xfs);
 done:
   XUngrabServer(dd);
   XDestroyWindow(dd, win);
   XFreeGC(dd, gc);
   if (cnum > 0)
      XFreeColors(dd, cmap, cols, cnum, 0);
   XCloseDisplay(dd);

   switch (button)
     {
     default:
     case 1:
	break;
     case 2:
	SessionExit(EEXIT_RESTART, NULL);
	break;
     case 3:
	SessionExit(EEXIT_EXIT, NULL);
	break;
     }
}