1/* $Xorg: choose.c,v 1.4 2001/02/09 02:05:59 xorgcvs Exp $ */
2/******************************************************************************
3
4Copyright 1993, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25******************************************************************************/
26/* $XFree86: xc/programs/xsm/choose.c,v 1.6tsi Exp $ */
27
28#include "xsm.h"
29#include "saveutil.h"
30#include "lock.h"
31#include "choose.h"
32#include <sys/types.h>
33
34#include <X11/Shell.h>
35#include <X11/Xaw/Form.h>
36#include <X11/Xaw/List.h>
37#include <X11/Xaw/Command.h>
38
39#ifndef X_NOT_POSIX
40#include <dirent.h>
41#else
42#include <sys/dir.h>
43#ifndef dirent
44#define dirent direct
45#endif
46#endif
47
48static Pixel save_message_foreground;
49static Pixel save_message_background;
50
51static int delete_session_phase = 0;
52static int break_lock_phase = 0;
53
54static Widget chooseSessionPopup;
55static Widget chooseSessionForm;
56static Widget chooseSessionLabel;
57static Widget chooseSessionListWidget;
58static Widget chooseSessionMessageLabel;
59static Widget chooseSessionLoadButton;
60static Widget chooseSessionDeleteButton;
61static Widget chooseSessionBreakLockButton;
62static Widget chooseSessionFailSafeButton;
63static Widget chooseSessionCancelButton;
64
65
66
67int
68GetSessionNames(int *count_ret, String **short_names_ret,
69		String **long_names_ret, Bool **locked_ret)
70{
71    DIR *dir;
72    struct dirent *entry;
73    const char *path;
74    int count;
75
76    path = getenv ("SM_SAVE_DIR");
77    if (!path)
78    {
79	path = getenv ("HOME");
80	if (!path)
81	    path = ".";
82    }
83
84    *count_ret = 0;
85    *short_names_ret = NULL;
86    *locked_ret = NULL;
87    if (long_names_ret)
88	*long_names_ret = NULL;
89
90    if ((dir = opendir (path)) == NULL)
91	return 0;
92
93    fcntl(dirfd(dir), F_SETFD, FD_CLOEXEC);
94
95    count = 0;
96
97    while ((entry = readdir (dir)) != NULL)
98    {
99	if (strncmp (entry->d_name, ".XSM-", 5) == 0)
100	    count++;
101    }
102
103    if (count == 0 ||
104       ((*short_names_ret = (String *) XtMalloc (
105           count * sizeof (String))) == NULL) ||
106       (long_names_ret && (*long_names_ret =
107           (String *) XtMalloc (count * sizeof (String))) == NULL) ||
108       ((*locked_ret = (Bool *) XtMalloc (count * sizeof (Bool))) == NULL))
109    {
110	closedir (dir);
111	if (*short_names_ret)
112	    XtFree ((char *) *short_names_ret);
113	if (long_names_ret && *long_names_ret)
114	    XtFree ((char *) *long_names_ret);
115	return 0;
116    }
117
118    rewinddir (dir);
119
120    while ((entry = readdir (dir)) != NULL && *count_ret < count)
121    {
122	if (strncmp (entry->d_name, ".XSM-", 5) == 0)
123	{
124	    char *name = (char *) entry->d_name + 5;
125	    char *id = NULL;
126	    Bool locked = CheckSessionLocked (name, long_names_ret!=NULL, &id);
127
128	    (*short_names_ret)[*count_ret] = XtNewString (name);
129	    (*locked_ret)[*count_ret] = locked;
130
131	    if (long_names_ret)
132	    {
133		if (!locked)
134		{
135		    (*long_names_ret)[*count_ret] =
136			(*short_names_ret)[*count_ret];
137		}
138		else
139		{
140		    char *host = ((char *) strchr (id, '/')) + 1;
141		    char *colon = (char *) strrchr (host, ':');
142		    char *lockmsg;
143
144		    /* backtrack over previous colon if there are 2 (DECnet),
145		       but not three (IPv6) */
146		    if ((*(colon - 1) == ':') && (*(colon - 2) != ':'))
147			colon--;
148
149		    *colon = '\0';
150
151		    XtAsprintf (&lockmsg, "%s (locked at %s)", name, host);
152		    (*long_names_ret)[*count_ret] = lockmsg;
153		    *colon = ':';
154
155		    XtFree (id);
156		}
157	    }
158
159	    (*count_ret)++;
160	}
161    }
162
163    closedir (dir);
164
165    return 1;
166}
167
168
169
170void
171FreeSessionNames(int count, String *namesShort, String *namesLong,
172		 Bool *lockFlags)
173{
174    int i;
175
176    for (i = 0; i < count; i++)
177	XtFree ((char *) namesShort[i]);
178    XtFree ((char *) namesShort);
179
180    if (namesLong)
181    {
182	for (i = 0; i < count; i++)
183	    if (lockFlags[i])
184		XtFree ((char *) namesLong[i]);
185	XtFree ((char *) namesLong);
186    }
187
188    XtFree ((char *) lockFlags);
189}
190
191
192
193static void
194SessionSelected(int number, Bool highlight)
195{
196    if (number >= 0)
197    {
198	Bool locked = sessionsLocked[number];
199
200	if (highlight)
201	    XawListHighlight (chooseSessionListWidget, number);
202
203	XtSetSensitive (chooseSessionLoadButton, !locked);
204	XtSetSensitive (chooseSessionDeleteButton, !locked);
205	XtSetSensitive (chooseSessionBreakLockButton, locked);
206    }
207    else
208    {
209	XtSetSensitive (chooseSessionLoadButton, False);
210	XtSetSensitive (chooseSessionDeleteButton, False);
211	XtSetSensitive (chooseSessionBreakLockButton, False);
212    }
213}
214
215
216
217static void
218AddSessionNames(int count, String *names)
219{
220    int i;
221
222    XawListChange (chooseSessionListWidget, names, count, 0, True);
223
224    /*
225     * Highlight the first unlocked session, if any.
226     */
227
228    for (i = 0; i < sessionNameCount; i++)
229	if (!sessionsLocked[i])
230	    break;
231
232    SessionSelected (i < sessionNameCount ? i : -1, True);
233}
234
235
236
237void
238ChooseWindowStructureNotifyXtHandler(Widget w, XtPointer closure,
239				     XEvent *event,
240				     Boolean *continue_to_dispatch)
241{
242    if (event->type == MapNotify)
243    {
244	/*
245	 * Set the input focus to the choose window and direct all keyboard
246	 * events to the list widget.  This way, the user can make selections
247	 * using the keyboard.
248	 */
249
250	XtSetKeyboardFocus (chooseSessionPopup, chooseSessionListWidget);
251
252	XSetInputFocus (XtDisplay (topLevel), XtWindow (chooseSessionPopup),
253	    RevertToPointerRoot, CurrentTime);
254
255	XSync (XtDisplay (topLevel), 0);
256
257	XtRemoveEventHandler (chooseSessionPopup, StructureNotifyMask, False,
258	    ChooseWindowStructureNotifyXtHandler, NULL);
259    }
260}
261
262
263void
264ChooseSession(void)
265{
266    Dimension   width, height;
267    Position	x, y;
268
269
270    /*
271     * Add the session names to the list
272     */
273
274    AddSessionNames (sessionNameCount, sessionNamesLong);
275
276
277    /*
278     * Center popup containing choice of sessions
279     */
280
281    XtRealizeWidget (chooseSessionPopup);
282
283    XtVaGetValues (chooseSessionPopup,
284	XtNwidth, &width,
285	XtNheight, &height,
286	NULL);
287
288    x = (Position)(WidthOfScreen (XtScreen (topLevel)) - width) / 2;
289    y = (Position)(HeightOfScreen (XtScreen (topLevel)) - height) / 3;
290
291    XtVaSetValues (chooseSessionPopup,
292	XtNx, x,
293	XtNy, y,
294	NULL);
295
296    XtVaSetValues (chooseSessionListWidget,
297	XtNlongest, width,
298	NULL);
299
300    XtVaSetValues (chooseSessionLabel,
301	XtNwidth, width,
302	NULL);
303
304    XtVaGetValues (chooseSessionMessageLabel,
305	XtNforeground, &save_message_foreground,
306	XtNbackground, &save_message_background,
307	NULL);
308
309    XtVaSetValues (chooseSessionMessageLabel,
310	XtNwidth, width,
311	XtNforeground, save_message_background,
312	NULL);
313
314    /*
315     * Wait for a map notify on the popup, then set input focus.
316     */
317
318    XtAddEventHandler (chooseSessionPopup, StructureNotifyMask, False,
319	ChooseWindowStructureNotifyXtHandler, NULL);
320
321    XtPopup (chooseSessionPopup, XtGrabNone);
322}
323
324
325
326static void
327CheckDeleteCancel (void)
328{
329    if (delete_session_phase > 0)
330    {
331	XtVaSetValues (chooseSessionMessageLabel,
332	    XtNforeground, save_message_background,
333            NULL);
334
335	delete_session_phase = 0;
336    }
337}
338
339
340static void
341CheckBreakLockCancel(void)
342{
343    if (break_lock_phase > 0)
344    {
345	XtVaSetValues (chooseSessionMessageLabel,
346	    XtNforeground, save_message_background,
347            NULL);
348
349	break_lock_phase = 0;
350    }
351}
352
353
354
355static void
356ChooseSessionUp(Widget w, XEvent *event, String *params, Cardinal *numParams)
357{
358    XawListReturnStruct *current;
359
360    CheckDeleteCancel ();
361    CheckBreakLockCancel ();
362
363    current = XawListShowCurrent (chooseSessionListWidget);
364    if (current->list_index > 0)
365	SessionSelected (current->list_index - 1, True);
366    XtFree ((char *) current);
367}
368
369
370static void
371ChooseSessionDown(Widget w, XEvent *event, String *params, Cardinal *numParams)
372{
373    XawListReturnStruct *current;
374
375    CheckDeleteCancel ();
376    CheckBreakLockCancel ();
377
378    current = XawListShowCurrent (chooseSessionListWidget);
379    if (current->list_index < sessionNameCount - 1)
380	SessionSelected (current->list_index + 1, True);
381    XtFree ((char *) current);
382}
383
384
385
386static void
387ChooseSessionBtn1Down(Widget w, XEvent *event, String *params,
388		      Cardinal *numParams)
389{
390    XawListReturnStruct *current;
391
392    CheckDeleteCancel ();
393    CheckBreakLockCancel ();
394
395    current = XawListShowCurrent (chooseSessionListWidget);
396    SessionSelected (current->list_index, False /* already highlighted */);
397    XtFree ((char *) current);
398}
399
400
401
402static void
403ChooseSessionLoadXtProc(Widget w, XtPointer client_data, XtPointer callData)
404{
405    XawListReturnStruct *current;
406
407    CheckDeleteCancel ();
408    CheckBreakLockCancel ();
409
410    current = XawListShowCurrent (chooseSessionListWidget);
411
412    if (!current || !current->string || *(current->string) == '\0')
413    {
414	if (current)
415	    XtFree ((char *) current);
416#ifdef XKB
417	XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_BadValue);
418#else
419	XBell (XtDisplay (topLevel), 0);
420#endif
421	return;
422    }
423
424    /*
425     * Pop down choice of sessions and start the specified session.
426     */
427
428    XtPopdown (chooseSessionPopup);
429
430    if (session_name)
431	XtFree (session_name);
432
433    session_name = XtNewString (current->string);
434
435    XtFree ((char *) current);
436
437    FreeSessionNames (sessionNameCount,
438	sessionNamesShort, sessionNamesLong, sessionsLocked);
439
440
441    /*
442     * Start the session, looking for .XSM-<session name> startup file.
443     */
444
445    if (!StartSession (session_name, False))
446	UnableToLockSession (session_name);
447}
448
449
450
451static void
452ChooseSessionDeleteXtProc(Widget w, XtPointer client_data, XtPointer callData)
453{
454    XawListReturnStruct *current;
455    int longest;
456    String name;
457
458    CheckBreakLockCancel ();
459
460    current = XawListShowCurrent (chooseSessionListWidget);
461
462    if (!current || !(name = current->string) || *name == '\0')
463    {
464	if (current)
465	    XtFree ((char *) current);
466#ifdef XKB
467	XkbStdBell(XtDisplay(w),XtWindow(w),0,XkbBI_BadValue);
468#else
469	XBell (XtDisplay (topLevel), 0);
470#endif
471	return;
472    }
473
474    delete_session_phase++;
475
476    if (delete_session_phase == 1)
477    {
478	XtVaSetValues (chooseSessionMessageLabel,
479	    XtNforeground, save_message_foreground,
480            NULL);
481
482#ifdef XKB
483	XkbStdBell(XtDisplay(w),XtWindow(w),0,XkbBI_BadValue);
484#else
485	XBell (XtDisplay (topLevel), 0);
486#endif
487    }
488    else
489    {
490	XtVaSetValues (chooseSessionMessageLabel,
491	    XtNforeground, save_message_background,
492            NULL);
493
494	if (DeleteSession (name))
495	{
496	    int i, j;
497
498	    for (i = 0; i < sessionNameCount; i++)
499	    {
500		if (strcmp (sessionNamesLong[i], name) == 0)
501		{
502		    XtFree ((char *) sessionNamesShort[i]);
503
504		    if (sessionsLocked[i])
505			XtFree ((char *) sessionNamesLong[i]);
506
507		    for (j = i; j < sessionNameCount - 1; j++)
508		    {
509			sessionNamesLong[j] = sessionNamesLong[j + 1];
510			sessionNamesShort[j] = sessionNamesShort[j + 1];
511			sessionsLocked[j] = sessionsLocked[j + 1];
512		    }
513		    sessionNameCount--;
514		    break;
515		}
516	    }
517
518	    if (sessionNameCount == 0)
519	    {
520		XtSetSensitive (chooseSessionLoadButton, 0);
521		XtSetSensitive (chooseSessionDeleteButton, 0);
522		XtUnmanageChild (chooseSessionListWidget);
523	    }
524	    else
525	    {
526		XtVaGetValues (chooseSessionListWidget,
527		    XtNlongest, &longest,
528		    NULL);
529
530		XawListChange (chooseSessionListWidget,
531		    sessionNamesLong, sessionNameCount, longest, True);
532
533		SessionSelected (-1, False);
534	    }
535	}
536
537	delete_session_phase = 0;
538    }
539
540    XtFree ((char *) current);
541}
542
543
544
545static void
546ChooseSessionBreakLockXtProc(Widget w, XtPointer client_data,
547			     XtPointer callData)
548{
549    XawListReturnStruct *current;
550    String name;
551
552    CheckDeleteCancel ();
553
554    current = XawListShowCurrent (chooseSessionListWidget);
555
556    if (!current || !(name = current->string) || *name == '\0')
557    {
558	if (current)
559	    XtFree ((char *) current);
560#ifdef XKB
561	XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_BadValue);
562#else
563	XBell (XtDisplay (topLevel), 0);
564#endif
565	return;
566    }
567
568    break_lock_phase++;
569
570    if (break_lock_phase == 1)
571    {
572	XtVaSetValues (chooseSessionMessageLabel,
573	    XtNforeground, save_message_foreground,
574            NULL);
575
576#ifdef XKB
577	XkbStdBell(XtDisplay(topLevel),XtWindow(topLevel),0,XkbBI_BadValue);
578#else
579	XBell (XtDisplay (topLevel), 0);
580#endif
581    }
582    else
583    {
584	int longest;
585
586	XtVaSetValues (chooseSessionMessageLabel,
587	    XtNforeground, save_message_background,
588            NULL);
589
590	name = sessionNamesShort[current->list_index];
591
592	(void) GetLockId (name);
593
594	UnlockSession (name);
595
596	sessionsLocked[current->list_index] = False;
597	XtFree ((char *) sessionNamesLong[current->list_index]);
598	sessionNamesLong[current->list_index] =
599	    sessionNamesShort[current->list_index];
600
601	XtVaGetValues (chooseSessionListWidget,
602	    XtNlongest, &longest,
603	    NULL);
604
605	XawListChange (chooseSessionListWidget,
606	    sessionNamesLong, sessionNameCount, longest, True);
607
608	SessionSelected (current->list_index, True);
609
610	break_lock_phase = 0;
611    }
612
613    XtFree ((char *) current);
614}
615
616
617
618static void
619ChooseSessionFailSafeXtProc(Widget w, XtPointer client_data,
620			    XtPointer callData)
621{
622    /*
623     * Pop down choice of sessions, and start the fail safe session.
624     */
625
626    CheckDeleteCancel ();
627    CheckBreakLockCancel ();
628
629    XtPopdown (chooseSessionPopup);
630
631    if (session_name)
632	XtFree (session_name);
633
634    session_name = XtNewString (FAILSAFE_SESSION_NAME);
635
636    FreeSessionNames (sessionNameCount,
637	sessionNamesShort, sessionNamesLong, sessionsLocked);
638
639
640    /*
641     * We don't need to check return value of StartSession in this case,
642     * because we are using the default session, and StartSession will
643     * not try to lock the session at this time.  It will try to lock
644     * it as soon as the user gives the session a name.
645     */
646
647    StartSession (session_name,
648	True /* Use ~/.xsmstartup if found, else system.xsm */);
649}
650
651
652
653static void
654ChooseSessionCancelXtProc(Widget w, XtPointer client_data, XtPointer callData)
655{
656    if (delete_session_phase > 0 || break_lock_phase > 0)
657    {
658	XtVaSetValues (chooseSessionMessageLabel,
659	    XtNforeground, save_message_background,
660            NULL);
661
662	delete_session_phase = 0;
663	break_lock_phase = 0;
664    }
665    else
666	EndSession (2);
667}
668
669
670
671void
672create_choose_session_popup(void)
673
674{
675    static XtActionsRec choose_actions[] = {
676        {"ChooseSessionUp", ChooseSessionUp},
677        {"ChooseSessionDown", ChooseSessionDown},
678        {"ChooseSessionBtn1Down", ChooseSessionBtn1Down}
679    };
680
681    /*
682     * Pop up for choosing session at startup
683     */
684
685    chooseSessionPopup = XtVaCreatePopupShell (
686	"chooseSessionPopup", transientShellWidgetClass, topLevel,
687	XtNallowShellResize, True,
688	NULL);
689
690
691    chooseSessionForm = XtVaCreateManagedWidget (
692	"chooseSessionForm", formWidgetClass, chooseSessionPopup,
693	NULL);
694
695
696    chooseSessionLabel = XtVaCreateManagedWidget (
697	"chooseSessionLabel", labelWidgetClass, chooseSessionForm,
698        XtNfromHoriz, NULL,
699        XtNfromVert, NULL,
700        XtNborderWidth, 0,
701	XtNresizable, True,
702	XtNjustify, XtJustifyCenter,
703	NULL);
704
705    chooseSessionListWidget = XtVaCreateManagedWidget (
706	"chooseSessionListWidget", listWidgetClass, chooseSessionForm,
707	XtNresizable, True,
708        XtNdefaultColumns, 1,
709	XtNforceColumns, True,
710        XtNfromHoriz, NULL,
711        XtNfromVert, chooseSessionLabel,
712	XtNvertDistance, 25,
713	NULL);
714
715    chooseSessionMessageLabel = XtVaCreateManagedWidget (
716	"chooseSessionMessageLabel", labelWidgetClass, chooseSessionForm,
717        XtNfromHoriz, NULL,
718        XtNfromVert, chooseSessionListWidget,
719        XtNborderWidth, 0,
720	XtNresizable, True,
721	XtNjustify, XtJustifyCenter,
722	NULL);
723
724    chooseSessionLoadButton = XtVaCreateManagedWidget (
725	"chooseSessionLoadButton", commandWidgetClass, chooseSessionForm,
726        XtNfromHoriz, NULL,
727        XtNfromVert, chooseSessionMessageLabel,
728        NULL);
729
730    XtAddCallback (chooseSessionLoadButton, XtNcallback,
731	ChooseSessionLoadXtProc, NULL);
732
733    chooseSessionDeleteButton = XtVaCreateManagedWidget (
734	"chooseSessionDeleteButton", commandWidgetClass, chooseSessionForm,
735        XtNfromHoriz, chooseSessionLoadButton,
736        XtNfromVert, chooseSessionMessageLabel,
737        NULL);
738
739    XtAddCallback (chooseSessionDeleteButton, XtNcallback,
740	ChooseSessionDeleteXtProc, NULL);
741
742    chooseSessionBreakLockButton = XtVaCreateManagedWidget (
743	"chooseSessionBreakLockButton",
744	commandWidgetClass, chooseSessionForm,
745        XtNfromHoriz, chooseSessionDeleteButton,
746        XtNfromVert, chooseSessionMessageLabel,
747        NULL);
748
749    XtAddCallback (chooseSessionBreakLockButton, XtNcallback,
750	ChooseSessionBreakLockXtProc, NULL);
751
752    chooseSessionFailSafeButton = XtVaCreateManagedWidget (
753	"chooseSessionFailSafeButton", commandWidgetClass, chooseSessionForm,
754        XtNfromHoriz, chooseSessionBreakLockButton,
755        XtNfromVert, chooseSessionMessageLabel,
756        NULL);
757
758    XtAddCallback (chooseSessionFailSafeButton, XtNcallback,
759	ChooseSessionFailSafeXtProc, NULL);
760
761
762    chooseSessionCancelButton = XtVaCreateManagedWidget (
763	"chooseSessionCancelButton", commandWidgetClass, chooseSessionForm,
764        XtNfromHoriz, chooseSessionFailSafeButton,
765        XtNfromVert, chooseSessionMessageLabel,
766        NULL);
767
768    XtAddCallback (chooseSessionCancelButton, XtNcallback,
769	ChooseSessionCancelXtProc, NULL);
770
771    XtAppAddActions (appContext, choose_actions, XtNumber (choose_actions));
772
773    XtInstallAllAccelerators (chooseSessionListWidget, chooseSessionPopup);
774}
775