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