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