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