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