Home | History | Annotate | Line # | Download | only in src
      1 /******************************************************************************
      2 
      3 Copyright 1994, 1998  The Open Group
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included in
     12 all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20 
     21 Except as contained in this notice, the name of The Open Group shall not be
     22 used in advertising or otherwise to promote the sale, use or other dealings
     23 in this Software without prior written authorization from The Open Group.
     24 
     25 Author:  Ralph Mor, X Consortium
     26 ******************************************************************************/
     27 
     28 #ifdef HAVE_CONFIG_H
     29 #include "config.h"
     30 #endif
     31 
     32 #include <X11/Xos.h>
     33 
     34 #ifndef X_NOT_POSIX
     35 #ifdef _POSIX_SOURCE
     36 #include <limits.h>
     37 #else
     38 #define _POSIX_SOURCE
     39 #include <limits.h>
     40 #undef _POSIX_SOURCE
     41 #endif
     42 #endif                          /* X_NOT_POSIX */
     43 #ifndef PATH_MAX
     44 #include <sys/param.h>
     45 #ifndef PATH_MAX
     46 #ifdef MAXPATHLEN
     47 #define PATH_MAX MAXPATHLEN
     48 #else
     49 #define PATH_MAX 1024
     50 #endif
     51 #endif
     52 #endif                          /* PATH_MAX */
     53 #ifdef HAVE_MKSTEMP
     54 #include <unistd.h>
     55 #endif
     56 
     57 #include <X11/Xlib.h>
     58 #include <X11/SM/SMlib.h>
     59 #include <X11/Xatom.h>
     60 #include <stdio.h>
     61 #include "twm.h"
     62 #include "screen.h"
     63 #include "session.h"
     64 
     65 SmcConn smcConn = NULL;
     66 static XtInputId iceInputId;
     67 static char *twm_clientId;
     68 static TWMWinConfigEntry *winConfigHead = NULL;
     69 static Bool sent_save_done = 0;
     70 static SmProp *props[5];
     71 
     72 #define SAVEFILE_VERSION 2
     73 
     74 #ifndef HAVE_MKSTEMP
     75 static char *unique_filename(const char *path, const char *prefix);
     76 #else
     77 static char *unique_filename(const char *path, const char *prefix, int *pFd);
     78 #endif
     79 
     80 static char *
     81 GetClientID(Window window)
     82 {
     83     char *client_id = NULL;
     84     Window client_leader;
     85     XTextProperty tp;
     86     Atom actual_type;
     87     int actual_format;
     88     unsigned long nitems;
     89     unsigned long bytes_after;
     90     unsigned char *prop = NULL;
     91 
     92     if (XGetWindowProperty(dpy, window, _XA_WM_CLIENT_LEADER,
     93                            0L, 1L, False, AnyPropertyType, &actual_type,
     94                            &actual_format, &nitems, &bytes_after,
     95                            &prop) == Success) {
     96         if (actual_type == XA_WINDOW && actual_format == 32 && nitems == 1 &&
     97             bytes_after == 0) {
     98             client_leader = *((Window *) prop);
     99 
    100             if (XGetTextProperty(dpy, client_leader, &tp, _XA_SM_CLIENT_ID)) {
    101                 if (tp.encoding == XA_STRING &&
    102                     tp.format == 8 && tp.nitems != 0)
    103                     client_id = (char *) tp.value;
    104             }
    105         }
    106 
    107         if (prop)
    108             XFree(prop);
    109     }
    110 
    111     return client_id;
    112 }
    113 
    114 static char *
    115 GetWindowRole(Window window)
    116 {
    117     XTextProperty tp;
    118 
    119     if (XGetTextProperty(dpy, window, &tp, _XA_WM_WINDOW_ROLE)) {
    120         if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
    121             return ((char *) tp.value);
    122     }
    123 
    124     return NULL;
    125 }
    126 
    127 static int
    128 write_byte(FILE *file, unsigned char b)
    129 {
    130     if (fwrite((char *) &b, 1, 1, file) != 1)
    131         return 0;
    132     return 1;
    133 }
    134 
    135 static int
    136 write_ushort(FILE *file, unsigned short s)
    137 {
    138     unsigned char file_short[2];
    139 
    140     file_short[0] = (unsigned char) ((s & (unsigned) 0xff00) >> 8);
    141     file_short[1] = (unsigned char) (s & 0xff);
    142     if (fwrite((char *) file_short, (int) sizeof(file_short), 1, file) != 1)
    143         return 0;
    144     return 1;
    145 }
    146 
    147 static int
    148 write_short(FILE *file, short s)
    149 {
    150     unsigned char file_short[2];
    151 
    152     file_short[0] = (unsigned char) (((unsigned) s & (unsigned) 0xff00) >> 8);
    153     file_short[1] = (unsigned char) (s & 0xff);
    154     if (fwrite((char *) file_short, (int) sizeof(file_short), 1, file) != 1)
    155         return 0;
    156     return 1;
    157 }
    158 
    159 static int
    160 write_counted_string(FILE *file, char *string)
    161 {
    162     if (string) {
    163         unsigned char count = (unsigned char) strlen(string);
    164 
    165         if (write_byte(file, count) == 0)
    166             return 0;
    167         if (fwrite(string, (int) sizeof(char), (int) count, file) != count)
    168             return 0;
    169     }
    170     else {
    171         if (write_byte(file, 0) == 0)
    172             return 0;
    173     }
    174 
    175     return 1;
    176 }
    177 
    178 static int
    179 read_byte(FILE *file, unsigned char *bp)
    180 {
    181     if (fread((char *) bp, 1, 1, file) != 1)
    182         return 0;
    183     return 1;
    184 }
    185 
    186 static int
    187 read_ushort(FILE *file, unsigned short *shortp)
    188 {
    189     unsigned char file_short[2];
    190 
    191     if (fread((char *) file_short, (int) sizeof(file_short), 1, file) != 1)
    192         return 0;
    193     *shortp = (unsigned short) (file_short[0] * 256 + file_short[1]);
    194     return 1;
    195 }
    196 
    197 static int
    198 read_short(FILE *file, short *shortp)
    199 {
    200     unsigned char file_short[2];
    201 
    202     if (fread((char *) file_short, (int) sizeof(file_short), 1, file) != 1)
    203         return 0;
    204     *shortp = (short) (file_short[0] * 256 + file_short[1]);
    205     return 1;
    206 }
    207 
    208 static int
    209 read_counted_string(FILE *file, char **stringp)
    210 {
    211     unsigned char len;
    212     char *data;
    213 
    214     if (read_byte(file, &len) == 0)
    215         return 0;
    216     if (len == 0) {
    217         data = NULL;
    218     }
    219     else {
    220         data = (char *) malloc((unsigned) len + 1);
    221         if (!data)
    222             return 0;
    223         if (fread(data, (int) sizeof(char), (int) len, file) != len) {
    224             free(data);
    225             return 0;
    226         }
    227         data[len] = '\0';
    228     }
    229     *stringp = data;
    230     return 1;
    231 }
    232 
    233 /*
    234  * An entry in the saved window config file looks like this:
    235  *
    236  * FIELD                                BYTES
    237  * -----                                ----
    238  * SM_CLIENT_ID ID len                  1              (may be 0)
    239  * SM_CLIENT_ID                         LIST of bytes  (may be NULL)
    240  *
    241  * WM_WINDOW_ROLE length                1              (may be 0)
    242  * WM_WINDOW_ROLE                       LIST of bytes  (may be NULL)
    243  *
    244  * if no WM_WINDOW_ROLE (length = 0)
    245  *
    246  *   WM_CLASS "res name" length         1
    247  *   WM_CLASS "res name"                LIST of bytes
    248  *   WM_CLASS "res class" length        1
    249  *   WM_CLASS "res class"               LIST of bytes
    250  *   WM_NAME length                     1               (0 if name changed)
    251  *   WM_NAME                            LIST of bytes
    252  *   WM_COMMAND arg count               1               (0 if no SM_CLIENT_ID)
    253  *   For each arg in WM_COMMAND
    254  *      arg length                      1
    255  *      arg                             LIST of bytes
    256  *
    257  * Iconified bool                       1
    258  * Icon info present bool               1
    259  *
    260  * if icon info present
    261  *      icon x                          2
    262  *      icon y                          2
    263  *
    264  * Geom x                               2
    265  * Geom y                               2
    266  * Geom width                           2
    267  * Geom height                          2
    268  *
    269  * Width ever changed by user           1
    270  * Height ever changed by user          1
    271  */
    272 
    273 static int
    274 WriteWinConfigEntry(FILE *configFile, TwmWindow *theWindow,
    275                     char *clientId, char *windowRole)
    276 {
    277     char **wm_command;
    278     int wm_command_count;
    279     unsigned udummy = 0;
    280 
    281     if (!write_counted_string(configFile, clientId))
    282         return 0;
    283 
    284     if (!write_counted_string(configFile, windowRole))
    285         return 0;
    286 
    287     if (!windowRole) {
    288         if (!write_counted_string(configFile, theWindow->xclass.res_name))
    289             return 0;
    290         if (!write_counted_string(configFile, theWindow->xclass.res_class))
    291             return 0;
    292         if (theWindow->nameChanged) {
    293             /*
    294              * If WM_NAME changed on this window, we can't use it as
    295              * a criteria for looking up window configurations.  See the
    296              * longer explanation in the GetWindowConfig() function below.
    297              */
    298 
    299             if (!write_counted_string(configFile, NULL))
    300                 return 0;
    301         }
    302         else {
    303             if (!write_counted_string(configFile, theWindow->name))
    304                 return 0;
    305         }
    306 
    307         wm_command = NULL;
    308         wm_command_count = 0;
    309         XGetCommand(dpy, theWindow->w, &wm_command, &wm_command_count);
    310 
    311         if (clientId || !wm_command || wm_command_count == 0) {
    312             if (!write_byte(configFile, 0))
    313                 return 0;
    314         }
    315         else {
    316             int i;
    317 
    318             if (!write_byte(configFile, (unsigned char) wm_command_count))
    319                 return 0;
    320             for (i = 0; i < wm_command_count; i++)
    321                 if (!write_counted_string(configFile, wm_command[i]))
    322                     return 0;
    323             XFreeStringList(wm_command);
    324         }
    325     }
    326 
    327     if (!write_byte(configFile, theWindow->icon ? 1 : 0))       /* iconified */
    328         return 0;
    329 
    330     if (!write_byte(configFile, theWindow->icon_w ? 1 : 0))     /* icon exists */
    331         return 0;
    332 
    333     if (theWindow->icon_w) {
    334         int icon_x, icon_y;
    335         Window wdummy = None;
    336 
    337         XGetGeometry(dpy, theWindow->icon_w, &wdummy, &icon_x,
    338                      &icon_y, &udummy, &udummy, &udummy, &udummy);
    339 
    340         if (!write_short(configFile, (short) icon_x))
    341             return 0;
    342         if (!write_short(configFile, (short) icon_y))
    343             return 0;
    344     }
    345 
    346     if (!write_short(configFile, (short) theWindow->frame_x))
    347         return 0;
    348     if (!write_short(configFile, (short) theWindow->frame_y))
    349         return 0;
    350     if (!write_ushort(configFile, (unsigned short) theWindow->attr.width))
    351         return 0;
    352     if (!write_ushort(configFile, (unsigned short) theWindow->attr.height))
    353         return 0;
    354 
    355     if (!write_byte(configFile, theWindow->widthEverChangedByUser ? 1 : 0))
    356         return 0;
    357 
    358     if (!write_byte(configFile, theWindow->heightEverChangedByUser ? 1 : 0))
    359         return 0;
    360 
    361     return 1;
    362 }
    363 
    364 static int
    365 ReadWinConfigEntry(FILE *configFile, unsigned short version,
    366                    TWMWinConfigEntry **pentry)
    367 {
    368     TWMWinConfigEntry *entry;
    369     unsigned char byte;
    370     int i;
    371 
    372     *pentry = entry = (TWMWinConfigEntry *) malloc(sizeof(TWMWinConfigEntry));
    373     if (!*pentry)
    374         return 0;
    375 
    376     entry->tag = 0;
    377     entry->client_id = NULL;
    378     entry->window_role = NULL;
    379     entry->xclass.res_name = NULL;
    380     entry->xclass.res_class = NULL;
    381     entry->wm_name = NULL;
    382     entry->wm_command = NULL;
    383     entry->wm_command_count = 0;
    384 
    385     if (!read_counted_string(configFile, &entry->client_id))
    386         goto give_up;
    387 
    388     if (!read_counted_string(configFile, &entry->window_role))
    389         goto give_up;
    390 
    391     if (!entry->window_role) {
    392         if (!read_counted_string(configFile, &entry->xclass.res_name))
    393             goto give_up;
    394         if (!read_counted_string(configFile, &entry->xclass.res_class))
    395             goto give_up;
    396         if (!read_counted_string(configFile, &entry->wm_name))
    397             goto give_up;
    398 
    399         if (!read_byte(configFile, &byte))
    400             goto give_up;
    401         entry->wm_command_count = byte;
    402 
    403         if (entry->wm_command_count == 0)
    404             entry->wm_command = NULL;
    405         else {
    406             entry->wm_command = (char **)
    407                 malloc((size_t) entry->wm_command_count * sizeof(char *));
    408 
    409             if (!entry->wm_command)
    410                 goto give_up;
    411 
    412             for (i = 0; i < entry->wm_command_count; i++)
    413                 if (!read_counted_string(configFile, &entry->wm_command[i]))
    414                     goto give_up;
    415         }
    416     }
    417 
    418     if (!read_byte(configFile, &byte))
    419         goto give_up;
    420     entry->iconified = byte;
    421 
    422     if (!read_byte(configFile, &byte))
    423         goto give_up;
    424     entry->icon_info_present = byte;
    425 
    426     if (entry->icon_info_present) {
    427         if (!read_short(configFile, (short *) &entry->icon_x))
    428             goto give_up;
    429         if (!read_short(configFile, (short *) &entry->icon_y))
    430             goto give_up;
    431     }
    432 
    433     if (!read_short(configFile, (short *) &entry->x))
    434         goto give_up;
    435     if (!read_short(configFile, (short *) &entry->y))
    436         goto give_up;
    437     if (!read_ushort(configFile, &entry->width))
    438         goto give_up;
    439     if (!read_ushort(configFile, &entry->height))
    440         goto give_up;
    441 
    442     if (version > 1) {
    443         if (!read_byte(configFile, &byte))
    444             goto give_up;
    445         entry->width_ever_changed_by_user = byte;
    446 
    447         if (!read_byte(configFile, &byte))
    448             goto give_up;
    449         entry->height_ever_changed_by_user = byte;
    450     }
    451     else {
    452         entry->width_ever_changed_by_user = False;
    453         entry->height_ever_changed_by_user = False;
    454     }
    455 
    456     return 1;
    457 
    458  give_up:
    459 
    460     if (entry->client_id)
    461         free(entry->client_id);
    462     if (entry->window_role)
    463         free(entry->window_role);
    464     if (entry->xclass.res_name)
    465         free(entry->xclass.res_name);
    466     if (entry->xclass.res_class)
    467         free(entry->xclass.res_class);
    468     if (entry->wm_name)
    469         free(entry->wm_name);
    470     if (entry->wm_command_count && entry->wm_command) {
    471         for (i = 0; i < entry->wm_command_count; i++)
    472             if (entry->wm_command[i])
    473                 free(entry->wm_command[i]);
    474     }
    475     if (entry->wm_command)
    476         free(entry->wm_command);
    477 
    478     free(entry);
    479     *pentry = NULL;
    480 
    481     return 0;
    482 }
    483 
    484 void
    485 ReadWinConfigFile(char *filename)
    486 {
    487     FILE *configFile;
    488     TWMWinConfigEntry *entry;
    489     int done = 0;
    490     unsigned short version = 0;
    491 
    492     configFile = fopen(filename, "rb");
    493     if (!configFile)
    494         return;
    495 
    496     if (!read_ushort(configFile, &version) || version > SAVEFILE_VERSION) {
    497         done = 1;
    498     }
    499 
    500     while (!done) {
    501         if (ReadWinConfigEntry(configFile, version, &entry)) {
    502             entry->next = winConfigHead;
    503             winConfigHead = entry;
    504         }
    505         else
    506             done = 1;
    507     }
    508 
    509     fclose(configFile);
    510 }
    511 
    512 int
    513 GetWindowConfig(TwmWindow *theWindow,
    514                 short *x, short *y,
    515                 unsigned short *width, unsigned short *height,
    516                 Bool *iconified,
    517                 Bool *icon_info_present,
    518                 short *icon_x, short *icon_y,
    519                 Bool *width_ever_changed_by_user,
    520                 Bool *height_ever_changed_by_user)
    521 {
    522     char *clientId, *windowRole;
    523     TWMWinConfigEntry *ptr;
    524     int found = 0;
    525 
    526     ptr = winConfigHead;
    527 
    528     if (!ptr)
    529         return 0;
    530 
    531     clientId = GetClientID(theWindow->w);
    532     windowRole = GetWindowRole(theWindow->w);
    533 
    534     while (ptr && !found) {
    535         int client_id_match = (!clientId && !ptr->client_id) ||
    536             (clientId && ptr->client_id &&
    537              strcmp(clientId, ptr->client_id) == 0);
    538 
    539         if (!ptr->tag && client_id_match) {
    540             if (windowRole || ptr->window_role) {
    541                 found = (windowRole && ptr->window_role &&
    542                          strcmp(windowRole, ptr->window_role) == 0);
    543             }
    544             else {
    545                 /*
    546                  * Compare WM_CLASS + only compare WM_NAME if the
    547                  * WM_NAME in the saved file is non-NULL.  If the
    548                  * WM_NAME in the saved file is NULL, this means that
    549                  * the client changed the value of WM_NAME during the
    550                  * session, and we can not use it as a criteria for
    551                  * our search.  For example, with xmh, at save time
    552                  * the window name might be "xmh: folderY".  However,
    553                  * if xmh does not properly restore state when it is
    554                  * restarted, the initial window name might be
    555                  * "xmh: folderX".  This would cause the window manager
    556                  * to fail in finding the saved window configuration.
    557                  * The best we can do is ignore WM_NAME if its value
    558                  * changed in the previous session.
    559                  */
    560 
    561                 if (strcmp(theWindow->xclass.res_name,
    562                            ptr->xclass.res_name) == 0 &&
    563                     strcmp(theWindow->xclass.res_class,
    564                            ptr->xclass.res_class) == 0 &&
    565                     (ptr->wm_name == NULL ||
    566                      strcmp(theWindow->name, ptr->wm_name) == 0)) {
    567                     if (clientId) {
    568                         /*
    569                          * If a client ID was present, we should not check
    570                          * WM_COMMAND because Xt will put a -xtsessionID arg
    571                          * on the command line.
    572                          */
    573 
    574                         found = 1;
    575                     }
    576                     else {
    577                         /*
    578                          * For non-XSMP clients, also check WM_COMMAND.
    579                          */
    580 
    581                         char **wm_command = NULL;
    582                         int wm_command_count = 0;
    583 
    584                         XGetCommand(dpy, theWindow->w,
    585                                     &wm_command, &wm_command_count);
    586 
    587                         if (wm_command_count == ptr->wm_command_count) {
    588                             int i;
    589 
    590                             for (i = 0; i < wm_command_count; i++)
    591                                 if (strcmp(wm_command[i],
    592                                            ptr->wm_command[i]) != 0)
    593                                     break;
    594 
    595                             if (i == wm_command_count)
    596                                 found = 1;
    597                         }
    598                     }
    599                 }
    600             }
    601         }
    602 
    603         if (!found)
    604             ptr = ptr->next;
    605     }
    606 
    607     if (found) {
    608         *x = ptr->x;
    609         *y = ptr->y;
    610         *width = ptr->width;
    611         *height = ptr->height;
    612         *iconified = ptr->iconified;
    613         *icon_info_present = ptr->icon_info_present;
    614         *width_ever_changed_by_user = ptr->width_ever_changed_by_user;
    615         *height_ever_changed_by_user = ptr->height_ever_changed_by_user;
    616 
    617         if (*icon_info_present) {
    618             *icon_x = ptr->icon_x;
    619             *icon_y = ptr->icon_y;
    620         }
    621         ptr->tag = 1;
    622     }
    623     else
    624         *iconified = 0;
    625 
    626     if (clientId)
    627         XFree(clientId);
    628 
    629     if (windowRole)
    630         XFree(windowRole);
    631 
    632     return found;
    633 }
    634 
    635 #ifndef HAVE_MKSTEMP
    636 static char *
    637 unique_filename(const char *path, const char *prefix)
    638 #else
    639 static char *
    640 unique_filename(const char *path, const char *prefix, int *pFd)
    641 #endif
    642 {
    643 #ifndef HAVE_MKSTEMP
    644 #ifndef X_NOT_POSIX
    645     return ((char *) tempnam(path, prefix));
    646 #else
    647     char tempFile[PATH_MAX];
    648     char *tmp;
    649 
    650     snprintf(tempFile, sizeof(tempFile), "%s/%sXXXXXX", path, prefix);
    651     tmp = (char *) mktemp(tempFile);
    652     if (tmp)
    653         return strdup(tmp);
    654     else
    655         return (NULL);
    656 #endif
    657 #else
    658     char tempFile[PATH_MAX];
    659     char *ptr;
    660 
    661     snprintf(tempFile, sizeof(tempFile), "%s/%sXXXXXX", path, prefix);
    662     ptr = strdup(tempFile);
    663     if (ptr != NULL)
    664         *pFd = mkstemp(ptr);
    665     return ptr;
    666 #endif
    667 }
    668 
    669 static void
    670 SaveYourselfPhase2CB(SmcConn smcConn2, SmPointer clientData _X_UNUSED)
    671 {
    672     int scrnum;
    673     ScreenInfo *theScreen;
    674     TwmWindow *theWindow;
    675     char *clientId, *windowRole;
    676     FILE *configFile = NULL;
    677     const char *path;
    678     char *filename = NULL;
    679     Bool success = False;
    680     char discardCommand[80];
    681     int numVals, i;
    682     static int first_time = 1;
    683 
    684 #ifdef HAVE_MKSTEMP
    685     int fd;
    686 #endif
    687 
    688     if (first_time) {
    689         char userId[20];
    690         char *hint;
    691 
    692         props[0] = (SmProp *) calloc(1, sizeof(SmProp));
    693         props[0]->name = strdup(SmProgram);
    694         props[0]->type = strdup(SmARRAY8);
    695         props[0]->num_vals = 1;
    696         props[0]->vals = (SmPropValue *) calloc(1, sizeof(SmPropValue));
    697         props[0]->vals[0].value = strdup(Argv[0]);
    698         props[0]->vals[0].length = (int) strlen(Argv[0]);
    699 
    700         snprintf(userId, sizeof(userId), "%ld", (long) getuid());
    701 
    702         props[1] = (SmProp *) calloc(1, sizeof(SmProp));
    703         props[1]->name = strdup(SmUserID);
    704         props[1]->type = strdup(SmARRAY8);
    705         props[1]->num_vals = 1;
    706         props[1]->vals = (SmPropValue *) calloc(1, sizeof(SmPropValue));
    707         props[1]->vals[0].value = strdup(userId);
    708         props[1]->vals[0].length = (int) strlen(userId);
    709 
    710         hint = (char *) malloc(1);
    711         hint[0] = SmRestartIfRunning;
    712 
    713         props[2] = (SmProp *) calloc(1, sizeof(SmProp));
    714         props[2]->name = strdup(SmRestartStyleHint);
    715         props[2]->type = strdup(SmCARD8);
    716         props[2]->num_vals = 1;
    717         props[2]->vals = (SmPropValue *) calloc(1, sizeof(SmPropValue));
    718         props[2]->vals[0].value = hint;
    719         props[2]->vals[0].length = 1;
    720 
    721         SmcSetProperties(smcConn2, 3, props);
    722         first_time = 0;
    723     }
    724 
    725     path = getenv("SM_SAVE_DIR");
    726     if (!path) {
    727         path = getenv("HOME");
    728         if (!path)
    729             path = ".";
    730     }
    731 #ifndef HAVE_MKSTEMP
    732     if ((filename = unique_filename(path, ".twm")) == NULL)
    733         goto bad;
    734 
    735     if (!(configFile = fopen(filename, "wb")))
    736         goto bad;
    737 #else
    738     if ((filename = unique_filename(path, ".twm", &fd)) == NULL)
    739         goto bad;
    740 
    741     if (!(configFile = fdopen(fd, "wb")))
    742         goto bad;
    743 #endif
    744 
    745     if (!write_ushort(configFile, SAVEFILE_VERSION))
    746         goto bad;
    747 
    748     success = True;
    749 
    750     for (scrnum = 0; scrnum < NumScreens && success; scrnum++) {
    751         if (ScreenList[scrnum] != NULL) {
    752             theScreen = ScreenList[scrnum];
    753             theWindow = theScreen->TwmRoot.next;
    754 
    755             while (theWindow && success) {
    756                 clientId = GetClientID(theWindow->w);
    757                 windowRole = GetWindowRole(theWindow->w);
    758 
    759                 if (!WriteWinConfigEntry(configFile, theWindow,
    760                                          clientId, windowRole))
    761                     success = False;
    762 
    763                 if (clientId)
    764                     XFree(clientId);
    765 
    766                 if (windowRole)
    767                     XFree(windowRole);
    768 
    769                 theWindow = theWindow->next;
    770             }
    771         }
    772     }
    773 
    774     props[3] = (SmProp *) calloc(1, sizeof(SmProp));
    775     props[3]->name = strdup(SmRestartCommand);
    776     props[3]->type = strdup(SmLISTofARRAY8);
    777     props[3]->vals = (SmPropValue *) calloc((size_t)(Argc + 4), sizeof(SmPropValue));
    778 
    779     if (!props[3]->vals) {
    780         success = False;
    781         goto bad;
    782     }
    783 
    784     numVals = 0;
    785 
    786     for (i = 0; i < Argc; i++) {
    787         if (strcmp(Argv[i], "-clientId") == 0 ||
    788             strcmp(Argv[i], "-restore") == 0) {
    789             i++;
    790         }
    791         else {
    792             props[3]->vals[numVals].value = (SmPointer) strdup(Argv[i]);
    793             props[3]->vals[numVals++].length = (int) strlen(Argv[i]);
    794         }
    795     }
    796 
    797     props[3]->vals[numVals].value = strdup("-clientId");
    798     props[3]->vals[numVals++].length = 9;
    799 
    800     props[3]->vals[numVals].value = strdup(twm_clientId);
    801     props[3]->vals[numVals++].length = (int) strlen(twm_clientId);
    802 
    803     props[3]->vals[numVals].value = strdup("-restore");
    804     props[3]->vals[numVals++].length = 8;
    805 
    806     props[3]->vals[numVals].value = strdup(filename);
    807     props[3]->vals[numVals++].length = (int) strlen(filename);
    808 
    809     props[3]->num_vals = numVals;
    810 
    811     snprintf(discardCommand, sizeof(discardCommand), "rm %s", filename);
    812 
    813     props[4] = (SmProp *) calloc(1, sizeof(SmProp));
    814     props[4]->name = strdup(SmDiscardCommand);
    815     props[4]->type = strdup(SmARRAY8);
    816     props[4]->num_vals = 1;
    817     props[4]->vals = (SmPropValue *) calloc(1, sizeof(SmPropValue));
    818     props[4]->vals[0].value = strdup(discardCommand);
    819     props[4]->vals[0].length = (int) strlen(discardCommand);
    820 
    821     SmcSetProperties(smcConn2, 2, props + 3);
    822 
    823  bad:
    824     SmcSaveYourselfDone(smcConn2, success);
    825     sent_save_done = 1;
    826 
    827     if (configFile)
    828         fclose(configFile);
    829 
    830     if (filename)
    831         free(filename);
    832 }
    833 
    834 static void
    835 SaveYourselfCB(SmcConn smcConn2,
    836                SmPointer clientData _X_UNUSED,
    837                int saveType _X_UNUSED,
    838                Bool shutdown _X_UNUSED,
    839                int interactStyle _X_UNUSED, Bool fast _X_UNUSED)
    840 {
    841     if (!SmcRequestSaveYourselfPhase2(smcConn2, SaveYourselfPhase2CB, NULL)) {
    842         SmcSaveYourselfDone(smcConn2, False);
    843         sent_save_done = 1;
    844     }
    845     else
    846         sent_save_done = 0;
    847 }
    848 
    849 static _X_NORETURN void
    850 DieCB(SmcConn smcConn2, SmPointer clientData _X_UNUSED)
    851 {
    852     SmcCloseConnection(smcConn2, 0, NULL);
    853     XtRemoveInput(iceInputId);
    854     Done(NULL, NULL);
    855 }
    856 
    857 static void
    858 SaveCompleteCB(SmcConn smcConnm _X_UNUSED, SmPointer clientData _X_UNUSED)
    859 {
    860     ;
    861 }
    862 
    863 static void
    864 ShutdownCancelledCB(SmcConn smcConn2, SmPointer clientData _X_UNUSED)
    865 {
    866     if (!sent_save_done) {
    867         SmcSaveYourselfDone(smcConn2, False);
    868         sent_save_done = 1;
    869     }
    870 }
    871 
    872 static void
    873 ProcessIceMsgProc(XtPointer client_data, int *source _X_UNUSED,
    874                   XtInputId *id _X_UNUSED)
    875 {
    876     IceConn ice_conn = (IceConn) client_data;
    877 
    878     IceProcessMessages(ice_conn, NULL, NULL);
    879 }
    880 
    881 void
    882 ConnectToSessionManager(char *previous_id, XtAppContext appContext)
    883 {
    884     char errorMsg[256];
    885     unsigned long mask;
    886     SmcCallbacks callbacks;
    887     IceConn iceConn;
    888 
    889     mask = SmcSaveYourselfProcMask | SmcDieProcMask |
    890         SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
    891 
    892     callbacks.save_yourself.callback = SaveYourselfCB;
    893     callbacks.save_yourself.client_data = (SmPointer) NULL;
    894 
    895     callbacks.die.callback = DieCB;
    896     callbacks.die.client_data = (SmPointer) NULL;
    897 
    898     callbacks.save_complete.callback = SaveCompleteCB;
    899     callbacks.save_complete.client_data = (SmPointer) NULL;
    900 
    901     callbacks.shutdown_cancelled.callback = ShutdownCancelledCB;
    902     callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;
    903 
    904     smcConn = SmcOpenConnection(NULL,   /* use SESSION_MANAGER env */
    905                                 (SmPointer) appContext,
    906                                 SmProtoMajor,
    907                                 SmProtoMinor,
    908                                 mask,
    909                                 &callbacks,
    910                                 previous_id, &twm_clientId, 256, errorMsg);
    911 
    912     if (smcConn == NULL)
    913         return;
    914 
    915     iceConn = SmcGetIceConnection(smcConn);
    916 
    917     iceInputId = XtAppAddInput(appContext,
    918                                IceConnectionNumber(iceConn),
    919                                (XtPointer) XtInputReadMask,
    920                                ProcessIceMsgProc, (XtPointer) iceConn);
    921 }
    922 
    923 void
    924 DestroySession(void)
    925 {
    926     int i;
    927     for (i = 0; i < 5; ++i) {
    928         if (props[i]) {
    929             SmFreeProperty(props[i]);
    930         }
    931     }
    932 }
    933