session.c revision df1c27a6
1/* 2 * Copyright 1988 by Evans & Sutherland Computer Corporation, 3 * Salt Lake City, Utah 4 * Portions Copyright 1989 by the Massachusetts Institute of Technology 5 * Cambridge, Massachusetts 6 * 7 * Copyright 1992 Claude Lecommandeur. 8 */ 9 10/********************************************************************* 11 * 12 * This module has been modified by 13 * Matthew McNeill (21 Mar 1997) - University of Durham (UK) 14 * for the support of the X Session Management Protocol 15 * 16 ********************************************************************* 17 * 18 * Copyright (c) 1996-1997 The University of Durham, UK - 19 * Department of Computer Science. 20 * All rights reserved. 21 * 22 * Permission is hereby granted, without written agreement and without 23 * licence or royalty fees, to use, copy, modify, and distribute this 24 * software and its documentation, provided that usage, copying, modification 25 * or distribution is not for direct commercial advantage and that the 26 * above copyright notice and the following two paragraphs appear in 27 * all copies of this software. To use, copy, modify or distribute this 28 * software and its documentation otherwise requires a fee and/or specific 29 * permission. 30 * 31 * IN NO EVENT SHALL THE UNIVERSITY OF DURHAM BE LIABLE TO ANY PARTY FOR 32 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT 33 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF 34 * DURHAM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 * 36 * THE UNIVERSITY OF DURHAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, 37 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 38 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 39 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF DURHAM HAS NO OBLIGATION TO 40 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 41 * 42 ********************************************************************/ 43 44/********************************************************************** 45 * 46 * $XConsortium: session.c,v 1.18 95/01/04 22:28:37 mor Exp $ 47 * 48 * Session support for the X11R6 XSMP (X Session Management Protocol) 49 * 50 * 95/01/04 Ralph Mor, X Consortium Initial Version. 51 * 52 * Do the necessary modification to be integrated in ctwm. 53 * Can no longer be used for the standard twm. 54 * 55 * 21 Mar 1997 Matthew McNeill, Durham University Modified Version. 56 * 57 **********************************************************************/ 58 59#include "ctwm.h" 60 61#include <stdio.h> 62#include <stdlib.h> 63#include <unistd.h> 64#include <sys/stat.h> // For umask 65#include <limits.h> // PATH_MAX 66 67#include <X11/Xatom.h> 68 69#include "ctwm_atoms.h" 70#include "ctwm_shutdown.h" 71#include "icons.h" 72#include "list.h" 73#include "screen.h" 74#include "session.h" 75 76 77SmcConn smcConn = NULL; 78static XtInputId iceInputId; 79static char *twm_clientId; 80static TWMWinConfigEntry *winConfigHead = NULL; 81static bool sent_save_done = false; 82 83static void SaveYourselfCB(SmcConn smcCon, SmPointer clientData, 84 int saveType, Bool shutdown, int interactStyle, 85 Bool fast); 86static char *GetClientID(Window window); 87static char *GetWindowRole(Window window); 88static int WriteWinConfigEntry(FILE *configFile, TwmWindow *theWindow, 89 char *clientId, char *windowRole); 90static int ReadWinConfigEntry(FILE *configFile, unsigned short version, 91 TWMWinConfigEntry **pentry); 92static void SaveYourselfPhase2CB(SmcConn smcCon, SmPointer clientData); 93static void DieCB(SmcConn smcCon, SmPointer clientData); 94static void SaveCompleteCB(SmcConn smcCon, SmPointer clientData); 95static void ShutdownCancelledCB(SmcConn smcCon, SmPointer clientData); 96static void ProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id); 97 98#define SAVEFILE_VERSION 2 99 100 101/*===[ Get Client SM_CLIENT_ID ]=============================================*/ 102 103static char *GetClientID(Window window) 104/* This function returns the value of the session manager client ID property 105 * given a valid window handle. If no such property exists on a window then 106 * null is returned 107 */ 108{ 109 char *client_id = NULL; 110 Window client_leader; 111 XTextProperty tp; 112 Atom actual_type; 113 int actual_format; 114 unsigned long nitems; 115 unsigned long bytes_after; 116 Window *prop = NULL; 117 118 if(XGetWindowProperty(dpy, window, XA_WM_CLIENT_LEADER, 119 0L, 1L, False, AnyPropertyType, &actual_type, &actual_format, 120 &nitems, &bytes_after, (unsigned char **)&prop) == Success) { 121 if(actual_type == XA_WINDOW && actual_format == 32 && 122 nitems == 1 && bytes_after == 0) { 123 client_leader = *prop; 124 125 if(XGetTextProperty(dpy, client_leader, &tp, XA_SM_CLIENT_ID)) { 126 if(tp.encoding == XA_STRING && 127 tp.format == 8 && tp.nitems != 0) { 128 client_id = (char *) tp.value; 129 } 130 } 131 } 132 133 if(prop) { 134 XFree(prop); 135 } 136 } 137 138 return client_id; 139} 140 141/*===[ Get Window Role ]=====================================================*/ 142 143static char *GetWindowRole(Window window) 144/* this function returns the WM_WINDOW_ROLE property of a window 145 */ 146{ 147 XTextProperty tp; 148 149 if(XGetTextProperty(dpy, window, &tp, XA_WM_WINDOW_ROLE)) { 150 if(tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) { 151 return ((char *) tp.value); 152 } 153 } 154 155 return NULL; 156} 157 158/*===[ Various file write procedures ]=======================================*/ 159 160static int write_byte(FILE *file, unsigned char b) 161{ 162 if(fwrite((char *) &b, 1, 1, file) != 1) { 163 return 0; 164 } 165 return 1; 166} 167 168/*---------------------------------------------------------------------------*/ 169 170static int write_ushort(FILE *file, unsigned short s) 171{ 172 unsigned char file_short[2]; 173 174 file_short[0] = (s & (unsigned)0xff00) >> 8; 175 file_short[1] = s & 0xff; 176 if(fwrite((char *) file_short, (int) sizeof(file_short), 1, file) != 1) { 177 return 0; 178 } 179 return 1; 180} 181 182/*---------------------------------------------------------------------------*/ 183 184static int write_short(FILE *file, short s) 185{ 186 unsigned char file_short[2]; 187 188 file_short[0] = (s & (unsigned)0xff00) >> 8; 189 file_short[1] = s & 0xff; 190 if(fwrite((char *) file_short, (int) sizeof(file_short), 1, file) != 1) { 191 return 0; 192 } 193 return 1; 194} 195 196/*---------------------------------------------------------------------------* 197 * Matthew McNeill Feb 1997 - required to save the occupation state as an 198 * integer. 199 */ 200 201static int write_int(FILE *file, int i) 202{ 203 unsigned char file_int[4]; 204 205 file_int[0] = (i & (unsigned)0xff000000) >> 24; 206 file_int[1] = (i & (unsigned)0x00ff0000) >> 16; 207 file_int[2] = (i & (unsigned)0x0000ff00) >> 8; 208 file_int[3] = (i & (unsigned)0x000000ff); 209 if(fwrite((char *) file_int, (int) sizeof(file_int), 1, file) != 1) { 210 return 0; 211 } 212 return 1; 213} 214 215/*---------------------------------------------------------------------------*/ 216 217static int write_counted_string(FILE *file, char *string) 218{ 219 if(string) { 220 unsigned char count = strlen(string); 221 222 if(write_byte(file, count) == 0) { 223 return 0; 224 } 225 if(fwrite(string, (int) sizeof(char), (int) count, file) != count) { 226 return 0; 227 } 228 } 229 else { 230 if(write_byte(file, 0) == 0) { 231 return 0; 232 } 233 } 234 235 return 1; 236} 237 238/*===[ various file read procedures ]========================================*/ 239 240static int read_byte(FILE *file, unsigned char *bp) 241{ 242 if(fread((char *) bp, 1, 1, file) != 1) { 243 return 0; 244 } 245 return 1; 246} 247 248/*---------------------------------------------------------------------------*/ 249 250static int read_ushort(FILE *file, unsigned short *shortp) 251{ 252 unsigned char file_short[2]; 253 254 if(fread((char *) file_short, (int) sizeof(file_short), 1, file) != 1) { 255 return 0; 256 } 257 *shortp = file_short[0] * 256 + file_short[1]; 258 return 1; 259} 260 261/*---------------------------------------------------------------------------*/ 262 263static int read_short(FILE *file, short *shortp) 264{ 265 unsigned char file_short[2]; 266 267 if(fread((char *) file_short, (int) sizeof(file_short), 1, file) != 1) { 268 return 0; 269 } 270 *shortp = file_short[0] * 256 + file_short[1]; 271 return 1; 272} 273 274/*---------------------------------------------------------------------------* 275 * Matthew McNeill Feb 1997 - required to save the occupation state as an 276 * integer. 277 */ 278 279static int read_int(FILE *file, int *intp) 280{ 281 unsigned char file_int[4]; 282 283 if(fread((char *) file_int, (int) sizeof(file_int), 1, file) != 1) { 284 return 0; 285 } 286 *intp = (((int) file_int[0]) << 24) & 0xff000000; 287 *intp += (((int) file_int[1]) << 16) & 0x00ff0000; 288 *intp += (((int) file_int[2]) << 8) & 0x0000ff00; 289 *intp += ((int) file_int[3]); 290 return 1; 291} 292 293/*---------------------------------------------------------------------------*/ 294 295static int read_counted_string(FILE *file, char **stringp) 296{ 297 unsigned char len; 298 char *data; 299 300 if(read_byte(file, &len) == 0) { 301 return 0; 302 } 303 if(len == 0) { 304 data = 0; 305 } 306 else { 307 data = malloc((unsigned) len + 1); 308 if(!data) { 309 return 0; 310 } 311 if(fread(data, (int) sizeof(char), (int) len, file) != len) { 312 free(data); 313 return 0; 314 } 315 data[len] = '\0'; 316 } 317 *stringp = data; 318 return 1; 319} 320 321/*===[ Definition of a window config entry ]=================================== 322 * 323 * An entry in the saved window config file looks like this: 324 * 325 * FIELD BYTES 326 * ----- ---- 327 * SM_CLIENT_ID ID len 1 (may be 0) 328 * SM_CLIENT_ID LIST of bytes (may be NULL) 329 * 330 * WM_WINDOW_ROLE length 1 (may be 0) 331 * WM_WINDOW_ROLE LIST of bytes (may be NULL) 332 * 333 * if no WM_WINDOW_ROLE (length = 0) 334 * 335 * WM_CLASS "res name" length 1 336 * WM_CLASS "res name" LIST of bytes 337 * WM_CLASS "res class" length 1 338 * WM_CLASS "res class" LIST of bytes 339 * WM_NAME length 1 (0 if name changed) 340 * WM_NAME LIST of bytes 341 * WM_COMMAND arg count 1 (0 if no SM_CLIENT_ID) 342 * For each arg in WM_COMMAND 343 * arg length 1 344 * arg LIST of bytes 345 * 346 * Iconified bool 1 347 * Icon info present bool 1 348 * 349 * if icon info present 350 * icon x 2 351 * icon y 2 352 * 353 * Geom x 2 354 * Geom y 2 355 * Geom width 2 356 * Geom height 2 357 * 358 * Width ever changed by user 1 359 * Height ever changed by user 1 360 * 361 * ------------------[ Matthew McNeill Feb 1997 ]---------------------------- 362 * 363 * Workspace Occupation 4 364 * 365 */ 366 367 368/*===[ Write Window Config Entry to file ]===================================*/ 369 370static int WriteWinConfigEntry(FILE *configFile, TwmWindow *theWindow, 371 char *clientId, char *windowRole) 372/* this function writes a window configuration entry of a given window to 373 * the given configuration file 374 */ 375{ 376 char **wm_command; 377 int wm_command_count, i; 378 379 /* ...unless the config file says otherwise. */ 380 if(LookInList(Scr == NULL ? ScreenList [0]->DontSave : Scr->DontSave, 381 theWindow->name, &theWindow->class)) { 382 return 1; 383 } 384 385 if(!write_counted_string(configFile, clientId)) { 386 return 0; 387 } 388 389 if(!write_counted_string(configFile, windowRole)) { 390 return 0; 391 } 392 393 if(!windowRole) { 394 if(!write_counted_string(configFile, theWindow->class.res_name)) { 395 return 0; 396 } 397 if(!write_counted_string(configFile, theWindow->class.res_class)) { 398 return 0; 399 } 400 if(theWindow->nameChanged) { 401 /* 402 * If WM_NAME changed on this window, we can't use it as 403 * a criteria for looking up window configurations. See the 404 * longer explanation in the GetWindowConfig() function below. 405 */ 406 407 if(!write_counted_string(configFile, NULL)) { 408 return 0; 409 } 410 } 411 else { 412 if(!write_counted_string(configFile, theWindow->name)) { 413 return 0; 414 } 415 } 416 417 wm_command = NULL; 418 wm_command_count = 0; 419 XGetCommand(dpy, theWindow->w, &wm_command, &wm_command_count); 420 421 if(clientId || !wm_command || wm_command_count == 0) { 422 if(!write_byte(configFile, 0)) { 423 return 0; 424 } 425 } 426 else { 427 if(!write_byte(configFile, (char) wm_command_count)) { 428 return 0; 429 } 430 for(i = 0; i < wm_command_count; i++) 431 if(!write_counted_string(configFile, wm_command[i])) { 432 return 0; 433 } 434 XFreeStringList(wm_command); 435 } 436 } 437 438 /* ===================[ Matthew McNeill Feb 1997 ]========================= * 439 * there has been a structural change to TwmWindow in ctwm. The Icon information 440 * is in a sub-structure now. The presence of icon information is not indicative 441 * of its current state. There is a new boolean condition for this (isicon) 442 */ 443 444 if(!write_byte(configFile, theWindow->isicon ? 1 : 0)) { 445 return 0; /* iconified */ 446 } 447 448 /* ===================[ Matthew McNeill Feb 1997 ]========================= * 449 * there has been a structural change to TwmWindow in ctwm. The Icon information 450 * is in a sub-structure now, if there is no icon, this sub-structure does 451 * not exist and the attempted access (below) causes a core dump. 452 * we need to check that the structure exists before trying to access it 453 */ 454 if(theWindow->icon) { 455 if(!write_byte(configFile, theWindow->icon->w ? 1 : 0)) { 456 return 0; /* icon info exists */ 457 } 458 if(theWindow->icon->w) { 459 int icon_x, icon_y; 460 XGetGeometry(dpy, theWindow->icon->w, &JunkRoot, &icon_x, 461 &icon_y, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth); 462 if(!write_short(configFile, (short) icon_x)) { 463 return 0; 464 } 465 if(!write_short(configFile, (short) icon_y)) { 466 return 0; 467 } 468 } 469 } 470 else { 471 if(!write_byte(configFile, 0)) { 472 return 0; /* icon does not have any information */ 473 } 474 } 475 /* ======================================================================= */ 476 477 if(!write_short(configFile, (short) theWindow->frame_x)) { 478 return 0; 479 } 480 if(!write_short(configFile, (short) theWindow->frame_y)) { 481 return 0; 482 } 483 if(!write_ushort(configFile, (unsigned short) theWindow->attr.width)) { 484 return 0; 485 } 486 if(!write_ushort(configFile, (unsigned short) theWindow->attr.height)) { 487 return 0; 488 } 489 if(!write_byte(configFile, theWindow->widthEverChangedByUser ? 1 : 0)) { 490 return 0; 491 } 492 if(!write_byte(configFile, theWindow->heightEverChangedByUser ? 1 : 0)) { 493 return 0; 494 } 495 496 /* ===================[ Matthew McNeill Feb 1997 ]=======================* 497 * write an extra piece of information to the file, this is the occupation 498 * number and is a bit field of the workspaces occupied by the client. 499 */ 500 501 if(!write_int(configFile, theWindow->occupation)) { 502 return 0; 503 } 504 505 /* ======================================================================*/ 506 507 return 1; 508} 509 510/*===[ Read Window Configuration Entry ]=====================================*/ 511 512static int ReadWinConfigEntry(FILE *configFile, unsigned short version, 513 TWMWinConfigEntry **pentry) 514/* this function reads the next window configuration entry from the given file 515 * else it returns 0 if none exists or there is a problem 516 */ 517{ 518 TWMWinConfigEntry *entry; 519 unsigned char byte; 520 int i; 521 522 *pentry = entry = calloc(1, sizeof(TWMWinConfigEntry)); 523 if(!*pentry) { 524 return 0; 525 } 526 527 if(!read_counted_string(configFile, &entry->client_id)) { 528 goto give_up; 529 } 530 531 if(!read_counted_string(configFile, &entry->window_role)) { 532 goto give_up; 533 } 534 535 if(!entry->window_role) { 536 if(!read_counted_string(configFile, &entry->class.res_name)) { 537 goto give_up; 538 } 539 if(!read_counted_string(configFile, &entry->class.res_class)) { 540 goto give_up; 541 } 542 if(!read_counted_string(configFile, &entry->wm_name)) { 543 goto give_up; 544 } 545 546 if(!read_byte(configFile, &byte)) { 547 goto give_up; 548 } 549 entry->wm_command_count = byte; 550 551 if(entry->wm_command_count == 0) { 552 entry->wm_command = NULL; 553 } 554 else { 555 entry->wm_command = calloc(entry->wm_command_count, sizeof(char *)); 556 557 if(!entry->wm_command) { 558 goto give_up; 559 } 560 561 for(i = 0; i < entry->wm_command_count; i++) 562 if(!read_counted_string(configFile, &entry->wm_command[i])) { 563 goto give_up; 564 } 565 } 566 } 567 568 if(!read_byte(configFile, &byte)) { 569 goto give_up; 570 } 571 572 entry->iconified = byte; 573 574 if(!read_byte(configFile, &byte)) { 575 goto give_up; 576 } 577 578 entry->icon_info_present = byte; 579 580 if(entry->icon_info_present) { 581 if(!read_short(configFile, (short *) &entry->icon_x)) { 582 goto give_up; 583 } 584 if(!read_short(configFile, (short *) &entry->icon_y)) { 585 goto give_up; 586 } 587 } 588 589 if(!read_short(configFile, (short *) &entry->x)) { 590 goto give_up; 591 } 592 if(!read_short(configFile, (short *) &entry->y)) { 593 goto give_up; 594 } 595 if(!read_ushort(configFile, &entry->width)) { 596 goto give_up; 597 } 598 if(!read_ushort(configFile, &entry->height)) { 599 goto give_up; 600 } 601 602 if(version > 1) { 603 if(!read_byte(configFile, &byte)) { 604 goto give_up; 605 } 606 entry->width_ever_changed_by_user = byte; 607 608 if(!read_byte(configFile, &byte)) { 609 goto give_up; 610 } 611 entry->height_ever_changed_by_user = byte; 612 } 613 else { 614 entry->width_ever_changed_by_user = False; 615 entry->height_ever_changed_by_user = False; 616 } 617 618 /* ===================[ Matthew McNeill Feb 1997 ]======================= * 619 * read in the occupation information to restore the windows to the 620 * correct workspaces. 621 */ 622 623 if(!read_int(configFile, &entry->occupation)) { 624 goto give_up; 625 } 626 627 /* ====================================================================== */ 628 629 return 1; 630 631give_up: 632 633 if(entry->client_id) { 634 free(entry->client_id); 635 } 636 if(entry->window_role) { 637 free(entry->window_role); 638 } 639 if(entry->class.res_name) { 640 free(entry->class.res_name); 641 } 642 if(entry->class.res_class) { 643 free(entry->class.res_class); 644 } 645 if(entry->wm_name) { 646 free(entry->wm_name); 647 } 648 if(entry->wm_command_count && entry->wm_command) { 649 for(i = 0; i < entry->wm_command_count; i++) 650 if(entry->wm_command[i]) { 651 free(entry->wm_command[i]); 652 } 653 } 654 if(entry->wm_command) { 655 free(entry->wm_command); 656 } 657 658 free(entry); 659 *pentry = NULL; 660 661 return 0; 662} 663 664/*===[ Read In Win Config File ]=============================================*/ 665 666void ReadWinConfigFile(char *filename) 667/* this function reads the window configuration file and stores the information 668 * in a data structure which is returned 669 */ 670{ 671 FILE *configFile; 672 TWMWinConfigEntry *entry; 673 int done = 0; 674 unsigned short version; 675 676 configFile = fopen(filename, "rb"); 677 if(!configFile) { 678 return; 679 } 680 681 if(!read_ushort(configFile, &version) || 682 version > SAVEFILE_VERSION) { 683 done = 1; 684 } 685 686 while(!done) { 687 if(ReadWinConfigEntry(configFile, version, &entry)) { 688 entry->next = winConfigHead; 689 winConfigHead = entry; 690 } 691 else { 692 done = 1; 693 } 694 } 695 696 fclose(configFile); 697} 698 699/*===[ Get Window Configuration ]============================================* 700 * Matthew McNeill Feb 1997 - added extra parameter (occupation) to return 701 * restored occupation of the window 702 */ 703 704int GetWindowConfig(TwmWindow *theWindow, short *x, short *y, 705 unsigned short *width, unsigned short *height, 706 bool *iconified, bool *icon_info_present, 707 short *icon_x, short *icon_y, 708 bool *width_ever_changed_by_user, 709 bool *height_ever_changed_by_user, 710 int *occupation) /* <== [ Matthew McNeill Feb 1997 ] == */ 711/* This function attempts to extract all the relevant information from the 712 * given window and return values via the rest of the parameters to the 713 * function 714 */ 715{ 716 char *clientId, *windowRole; 717 TWMWinConfigEntry *ptr; 718 int found = 0; 719 720 ptr = winConfigHead; 721 722 if(!ptr) { 723 return 0; 724 } 725 726 clientId = GetClientID(theWindow->w); 727 windowRole = GetWindowRole(theWindow->w); 728 729 while(ptr && !found) { 730 int client_id_match = (!clientId && !ptr->client_id) || 731 (clientId && ptr->client_id && 732 strcmp(clientId, ptr->client_id) == 0); 733 734 if(!ptr->tag && client_id_match) { 735 if(windowRole || ptr->window_role) { 736 found = (windowRole && ptr->window_role && 737 strcmp(windowRole, ptr->window_role) == 0); 738 } 739 else { 740 /* 741 * Compare WM_CLASS + only compare WM_NAME if the 742 * WM_NAME in the saved file is non-NULL. If the 743 * WM_NAME in the saved file is NULL, this means that 744 * the client changed the value of WM_NAME during the 745 * session, and we can not use it as a criteria for 746 * our search. For example, with xmh, at save time 747 * the window name might be "xmh: folderY". However, 748 * if xmh does not properly restore state when it is 749 * restarted, the initial window name might be 750 * "xmh: folderX". This would cause the window manager 751 * to fail in finding the saved window configuration. 752 * The best we can do is ignore WM_NAME if its value 753 * changed in the previous session. 754 */ 755 756 if(strcmp(theWindow->class.res_name, 757 ptr->class.res_name) == 0 && 758 strcmp(theWindow->class.res_class, 759 ptr->class.res_class) == 0 && 760 (ptr->wm_name == NULL || 761 strcmp(theWindow->name, ptr->wm_name) == 0)) { 762 if(clientId) { 763 /* 764 * If a client ID was present, we should not check 765 * WM_COMMAND because Xt will put a -xtsessionID arg 766 * on the command line. 767 */ 768 769 found = 1; 770 } 771 else { 772 /* 773 * For non-XSMP clients, also check WM_COMMAND. 774 */ 775 776 char **wm_command = NULL; 777 int wm_command_count = 0, i; 778 779 XGetCommand(dpy, theWindow->w, 780 &wm_command, &wm_command_count); 781 782 if(wm_command_count == ptr->wm_command_count) { 783 for(i = 0; i < wm_command_count; i++) 784 if(strcmp(wm_command[i], 785 ptr->wm_command[i]) != 0) { 786 break; 787 } 788 789 if(i == wm_command_count) { 790 found = 1; 791 } 792 } 793 } 794 } 795 } 796 } 797 798 if(!found) { 799 ptr = ptr->next; 800 } 801 } 802 803 if(found) { 804 *x = ptr->x; 805 *y = ptr->y; 806 *width = ptr->width; 807 *height = ptr->height; 808 *iconified = ptr->iconified; 809 *icon_info_present = ptr->icon_info_present; 810 *width_ever_changed_by_user = ptr->width_ever_changed_by_user; 811 *height_ever_changed_by_user = ptr->height_ever_changed_by_user; 812 813 if(*icon_info_present) { 814 *icon_x = ptr->icon_x; 815 *icon_y = ptr->icon_y; 816 } 817 818 *occupation = ptr->occupation; /* <== [ Matthew McNeill Feb 1997 ] == */ 819 820 ptr->tag = 1; 821 } 822 else { 823 *iconified = 0; 824 } 825 826 if(clientId) { 827 XFree(clientId); 828 } 829 830 if(windowRole) { 831 XFree(windowRole); 832 } 833 834 return found; 835} 836 837/*===[ Unique Filename Generator ]===========================================*/ 838 839static char *unique_filename(char *path, char *prefix, int *fd) 840/* this function attempts to allocate a temporary filename to store the 841 * information of the windows 842 */ 843{ 844#ifdef MISSING_MKSTEMP 845 char *name; 846 847 while(1) { 848 name = (char *) tempnam(path, prefix); 849 *fd = open(name, O_CREAT | O_EXCL); 850 if(*fd >= 0) { 851 return name; 852 } 853 free(name); 854 } 855#else 856 char *tempFile; 857 mode_t prev_umask; 858 859 asprintf(&tempFile, "%s/%sXXXXXX", path, prefix); 860 prev_umask = umask(077); 861 *fd = mkstemp(tempFile); 862 umask(prev_umask); 863 if(*fd >= 0) { 864 return tempFile; 865 } 866 else { 867 free(tempFile); 868 return NULL; 869 } 870#endif 871} 872 873/*===[ SAVE WINDOW INFORMATION ]=============================================*/ 874 875#ifndef PATH_MAX 876# define PATH_MAX 1023 877#endif 878 879static void SaveYourselfPhase2CB(SmcConn smcCon, SmPointer clientData) 880/* this is where all the work is done in saving the state of the windows. 881 * it is not done in Phase One because phase one is used for the other clients 882 * to make sure that all the property information on their windows is correct 883 * and up to date 884 */ 885{ 886 int scrnum; 887 ScreenInfo *theScreen; 888 TwmWindow *theWindow; 889 char *clientId, *windowRole; 890 FILE *configFile = NULL; 891 char *path; 892 char *filename = NULL; 893 Bool success = False; 894 SmProp prop1, prop2, prop3, *props[3]; 895 SmPropValue prop1val, prop2val, prop3val; 896 char discardCommand[PATH_MAX + 4]; 897 int numVals, i; 898 static int first_time = 1; 899 int configFd; 900 901 if(first_time) { 902 char userId[20]; 903 char hint = SmRestartIfRunning; 904 905 prop1.name = SmProgram; 906 prop1.type = SmARRAY8; 907 prop1.num_vals = 1; 908 prop1.vals = &prop1val; 909 prop1val.value = Argv[0]; 910 prop1val.length = strlen(Argv[0]); 911 912 sprintf(userId, "%d", getuid()); 913 prop2.name = SmUserID; 914 prop2.type = SmARRAY8; 915 prop2.num_vals = 1; 916 prop2.vals = &prop2val; 917 prop2val.value = (SmPointer) userId; 918 prop2val.length = strlen(userId); 919 920 prop3.name = SmRestartStyleHint; 921 prop3.type = SmCARD8; 922 prop3.num_vals = 1; 923 prop3.vals = &prop3val; 924 prop3val.value = (SmPointer) &hint; 925 prop3val.length = 1; 926 927 props[0] = &prop1; 928 props[1] = &prop2; 929 props[2] = &prop3; 930 931 SmcSetProperties(smcCon, 3, props); 932 933 first_time = 0; 934 } 935 936 path = getenv("SM_SAVE_DIR"); 937 if(!path) { 938 path = getenv("HOME"); 939 if(!path) { 940 path = "."; 941 } 942 } 943 /*==============[ Matthew McNeill Feb 1997 ]==============* 944 * changed the unique name to CTWM rather than TWM 945 * this is tidier and more functional and prevents 946 * TWM picking up CTWM config files. The format is 947 * no longer the same since the new format supports 948 * virtaul workspaces. 949 *========================================================*/ 950 if((filename = unique_filename(path, ".ctwm", &configFd)) == NULL) { 951 goto bad; 952 } 953 954 if(!(configFile = fdopen(configFd, "wb"))) { /* wb = write binary */ 955 goto bad; 956 } 957 958 if(!write_ushort(configFile, SAVEFILE_VERSION)) { 959 goto bad; 960 } 961 962 success = True; 963 964 for(scrnum = 0; scrnum < NumScreens && success; scrnum++) { 965 if(ScreenList[scrnum] != NULL) { 966 theScreen = ScreenList[scrnum]; 967 theWindow = theScreen->FirstWindow; 968 969 while(theWindow && success) { 970 clientId = GetClientID(theWindow->w); 971 windowRole = GetWindowRole(theWindow->w); 972 973 if(!WriteWinConfigEntry(configFile, theWindow, 974 clientId, windowRole)) { 975 success = False; 976 } 977 978 if(clientId) { 979 XFree(clientId); 980 } 981 982 if(windowRole) { 983 XFree(windowRole); 984 } 985 986 theWindow = theWindow->next; 987 } 988 } 989 } 990 991 prop1.name = SmRestartCommand; 992 prop1.type = SmLISTofARRAY8; 993 994 prop1.vals = calloc((Argc + 4), sizeof(SmPropValue)); 995 996 if(!prop1.vals) { 997 success = False; 998 goto bad; 999 } 1000 1001 numVals = 0; 1002 1003 for(i = 0; i < Argc; i++) { 1004 if(strcmp(Argv[i], "-clientId") == 0 || 1005 strcmp(Argv[i], "-restore") == 0) { 1006 i++; 1007 } 1008 else { 1009 prop1.vals[numVals].value = (SmPointer) Argv[i]; 1010 prop1.vals[numVals++].length = strlen(Argv[i]); 1011 } 1012 } 1013 1014 prop1.vals[numVals].value = (SmPointer) "-clientId"; 1015 prop1.vals[numVals++].length = 9; 1016 1017 prop1.vals[numVals].value = (SmPointer) twm_clientId; 1018 prop1.vals[numVals++].length = strlen(twm_clientId); 1019 1020 prop1.vals[numVals].value = (SmPointer) "-restore"; 1021 prop1.vals[numVals++].length = 8; 1022 1023 prop1.vals[numVals].value = (SmPointer) filename; 1024 prop1.vals[numVals++].length = strlen(filename); 1025 1026 prop1.num_vals = numVals; 1027 1028 sprintf(discardCommand, "rm %s", filename); 1029 prop2.name = SmDiscardCommand; 1030 prop2.type = SmARRAY8; 1031 prop2.num_vals = 1; 1032 prop2.vals = &prop2val; 1033 prop2val.value = (SmPointer) discardCommand; 1034 prop2val.length = strlen(discardCommand); 1035 1036 props[0] = &prop1; 1037 props[1] = &prop2; 1038 1039 SmcSetProperties(smcCon, 2, props); 1040 free(prop1.vals); 1041 1042bad: 1043 SmcSaveYourselfDone(smcCon, success); 1044 sent_save_done = true; 1045 1046 if(configFile) { 1047 fclose(configFile); 1048 } 1049 1050 if(filename) { 1051 free(filename); 1052 } 1053} 1054 1055/*===[ Save Yourself SM CallBack ]===========================================*/ 1056 1057static void SaveYourselfCB(SmcConn smcCon, SmPointer clientData, 1058 int saveType, Bool shutdown, int interactStyle, 1059 Bool fast) 1060/* this procedure is called by the session manager when requesting the 1061 * window manager to save its status, ie all the window configurations 1062 */ 1063{ 1064 if(!SmcRequestSaveYourselfPhase2(smcCon, SaveYourselfPhase2CB, NULL)) { 1065 SmcSaveYourselfDone(smcCon, False); 1066 sent_save_done = true; 1067 } 1068 else { 1069 sent_save_done = false; 1070 } 1071} 1072 1073/*===[ Die SM Call Back ]====================================================*/ 1074 1075static void DieCB(SmcConn smcCon, SmPointer clientData) 1076/* this procedure is called by the session manager when requesting that the 1077 * application shut istelf down 1078 */ 1079{ 1080 SmcCloseConnection(smcCon, 0, NULL); 1081 XtRemoveInput(iceInputId); 1082 DoShutdown(); 1083} 1084 1085/*===[ Save Complete SM Call Back ]==========================================*/ 1086 1087static void SaveCompleteCB(SmcConn smcCon, SmPointer clientData) 1088/* This function is called to say that the save has been completed and that 1089 * the program can continue its operation 1090 */ 1091{ 1092 ; 1093} 1094 1095/*===[ Shutdown Cancelled SM Call Back ]=====================================*/ 1096 1097static void ShutdownCancelledCB(SmcConn smcCon, SmPointer clientData) 1098 1099{ 1100 if(!sent_save_done) { 1101 SmcSaveYourselfDone(smcCon, False); 1102 sent_save_done = true; 1103 } 1104} 1105 1106/*===[ Process ICE Message ]=================================================*/ 1107 1108static void ProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id) 1109 1110{ 1111 IceConn ice_conn = (IceConn) client_data; 1112 IceProcessMessages(ice_conn, NULL, NULL); 1113} 1114 1115 1116/*===[ Connect To Session Manager ]==========================================*/ 1117 1118void ConnectToSessionManager(char *previous_id) 1119/* This procedure attempts to connect to the session manager and setup the 1120 * interclent exchange via the Call Backs 1121 */ 1122{ 1123 char errorMsg[256]; 1124 unsigned long mask; 1125 SmcCallbacks callbacks; 1126 IceConn iceConn; 1127 1128 mask = SmcSaveYourselfProcMask | SmcDieProcMask | 1129 SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; 1130 1131 callbacks.save_yourself.callback = SaveYourselfCB; 1132 callbacks.save_yourself.client_data = (SmPointer) NULL; 1133 1134 callbacks.die.callback = DieCB; 1135 callbacks.die.client_data = (SmPointer) NULL; 1136 1137 callbacks.save_complete.callback = SaveCompleteCB; 1138 callbacks.save_complete.client_data = (SmPointer) NULL; 1139 1140 callbacks.shutdown_cancelled.callback = ShutdownCancelledCB; 1141 callbacks.shutdown_cancelled.client_data = (SmPointer) NULL; 1142 1143 smcConn = SmcOpenConnection( 1144 NULL, /* use SESSION_MANAGER env */ 1145 (SmPointer) appContext, 1146 SmProtoMajor, 1147 SmProtoMinor, 1148 mask, 1149 &callbacks, 1150 previous_id, 1151 &twm_clientId, 1152 256, errorMsg); 1153 1154 if(smcConn == NULL) { 1155#ifdef DEBUG 1156 /* == [ Matthew McNeill Feb 1997 ] == */ 1157 fprintf(stderr, "%s : Connection to session manager failed. (%s)", ProgramName, 1158 errorMsg); 1159#endif 1160 return; 1161 } 1162 1163 iceConn = SmcGetIceConnection(smcConn); 1164 1165 iceInputId = XtAppAddInput( 1166 appContext, 1167 IceConnectionNumber(iceConn), 1168 (XtPointer) XtInputReadMask, 1169 ProcessIceMsgProc, 1170 (XtPointer) iceConn); 1171} 1172 1173 1174void shutdown_session(void) 1175{ 1176 if(smcConn) { 1177 SmcCloseConnection(smcConn, 0, NULL); 1178 } 1179} 1180