114c0a534Smrg/****************************************************************************** 214c0a534Smrg 314c0a534SmrgCopyright 1994, 1998 The Open Group 414c0a534Smrg 514c0a534SmrgPermission to use, copy, modify, distribute, and sell this software and its 614c0a534Smrgdocumentation for any purpose is hereby granted without fee, provided that 714c0a534Smrgthe above copyright notice appear in all copies and that both that 814c0a534Smrgcopyright notice and this permission notice appear in supporting 914c0a534Smrgdocumentation. 1014c0a534Smrg 1114c0a534SmrgThe above copyright notice and this permission notice shall be included in 1214c0a534Smrgall copies or substantial portions of the Software. 1314c0a534Smrg 1414c0a534SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1514c0a534SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1614c0a534SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1714c0a534SmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 1814c0a534SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 1914c0a534SmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2014c0a534Smrg 2114c0a534SmrgExcept as contained in this notice, the name of The Open Group shall not be 2214c0a534Smrgused in advertising or otherwise to promote the sale, use or other dealings 2314c0a534Smrgin this Software without prior written authorization from The Open Group. 2414c0a534Smrg 2514c0a534SmrgAuthor: Ralph Mor, X Consortium 2614c0a534Smrg******************************************************************************/ 2714c0a534Smrg 2814c0a534Smrg#include "smproxy.h" 2914c0a534Smrg#include <unistd.h> 3014c0a534Smrg#include <X11/Xmu/WinUtil.h> 31bdc460c5Smrg#include <limits.h> 32bdc460c5Smrg 33bdc460c5Smrg#ifndef HOST_NAME_MAX 34bdc460c5Smrg#define HOST_NAME_MAX 256 35bdc460c5Smrg#endif 3614c0a534Smrg 37bf2eeab3Smrgstatic XtAppContext appContext; 38bf2eeab3Smrgstatic Display *disp; 3914c0a534Smrg 40bf2eeab3Smrgstatic Atom wmProtocolsAtom; 41bf2eeab3Smrgstatic Atom wmSaveYourselfAtom; 42bf2eeab3Smrgstatic Atom wmStateAtom; 43bf2eeab3Smrgstatic Atom smClientIdAtom; 44bf2eeab3Smrgstatic Atom wmClientLeaderAtom; 4514c0a534Smrg 46bf2eeab3Smrgstatic Bool debug = 0; 4714c0a534Smrg 48bf2eeab3Smrgstatic SmcConn proxy_smcConn; 49bf2eeab3Smrgstatic XtInputId proxy_iceInputId; 50bf2eeab3Smrgstatic char *proxy_clientId = NULL; 5114c0a534Smrg 5214c0a534SmrgWinInfo *win_head = NULL; 5314c0a534Smrg 54bf2eeab3Smrgstatic int proxy_count = 0; 55bf2eeab3Smrgstatic int die_count = 0; 5614c0a534Smrg 57bf2eeab3Smrgstatic Bool ok_to_die = 0; 5814c0a534Smrg 59bf2eeab3Smrgstatic Bool caught_error = 0; 6014c0a534Smrg 61bf2eeab3Smrgstatic Bool sent_save_done = 0; 6214c0a534Smrg 63bf2eeab3Smrgstatic int Argc; 64bf2eeab3Smrgstatic char **Argv; 6514c0a534Smrg 66bf2eeab3Smrgstatic Bool HasSaveYourself ( Window window ); 67bf2eeab3Smrgstatic Bool HasXSMPsupport ( Window window ); 68bf2eeab3Smrgstatic WinInfo * GetClientLeader ( WinInfo *winptr ); 69bf2eeab3Smrgstatic char * CheckFullyQuantifiedName ( char *name, int *newstring ); 70bf2eeab3Smrgstatic void FinishSaveYourself ( WinInfo *winInfo, Bool has_WM_SAVEYOURSELF ); 71bf2eeab3Smrgstatic void SaveYourselfCB ( SmcConn smcConn, SmPointer clientData, int saveType, 72bf2eeab3Smrg Bool shutdown, int interactStyle, Bool fast ); 73bf2eeab3Smrgstatic void DieCB ( SmcConn smcConn, SmPointer clientData ); 74bf2eeab3Smrgstatic void SaveCompleteCB ( SmcConn smcConn, SmPointer clientData ); 75bf2eeab3Smrgstatic void ShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData ); 76bf2eeab3Smrgstatic void ProcessIceMsgProc ( XtPointer client_data, int *source, XtInputId *id ); 77bf2eeab3Smrgstatic void NullIceErrorHandler ( IceConn iceConn, Bool swap, 78bdc460c5Smrg int offendingMinorOpcode, 7914c0a534Smrg unsigned long offendingSequence, 8014c0a534Smrg int errorClass, int severity, IcePointer values ); 81bf2eeab3Smrgstatic void ConnectClientToSM ( WinInfo *winInfo ); 82bf2eeab3Smrgstatic int MyErrorHandler ( Display *display, XErrorEvent *event ); 83bf2eeab3Smrgstatic Bool LookupWindow ( Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret ); 84bf2eeab3Smrgstatic WinInfo * AddNewWindow ( Window window ); 85bf2eeab3Smrgstatic void RemoveWindow ( WinInfo *winptr ); 86bf2eeab3Smrgstatic void Got_WM_STATE ( WinInfo *winptr ); 87bf2eeab3Smrgstatic void HandleCreate ( XCreateWindowEvent *event ); 88bf2eeab3Smrgstatic void HandleDestroy ( XDestroyWindowEvent *event ); 89bf2eeab3Smrgstatic void HandleUpdate ( XPropertyEvent *event ); 90bf2eeab3Smrgstatic void ProxySaveYourselfPhase2CB ( SmcConn smcConn, SmPointer clientData ); 91bf2eeab3Smrgstatic void ProxySaveYourselfCB ( SmcConn smcConn, SmPointer clientData, 9214c0a534Smrg int saveType, Bool shutdown, int interactStyle, 9314c0a534Smrg Bool fast ); 94bf2eeab3Smrgstatic void ProxyDieCB ( SmcConn smcConn, SmPointer clientData ); 95bf2eeab3Smrgstatic void ProxySaveCompleteCB ( SmcConn smcConn, SmPointer clientData ); 96bf2eeab3Smrgstatic void ProxyShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData ); 97bf2eeab3Smrgstatic Status ConnectProxyToSM ( char *previous_id ); 98bf2eeab3Smrgstatic void CheckForExistingWindows ( Window root ); 9914c0a534Smrg 10014c0a534Smrg 101bf2eeab3Smrgstatic Bool 102bf2eeab3SmrgHasSaveYourself(Window window) 10314c0a534Smrg{ 10414c0a534Smrg Atom *protocols; 10514c0a534Smrg int numProtocols; 106bdc460c5Smrg int found; 10714c0a534Smrg 10814c0a534Smrg protocols = NULL; 10914c0a534Smrg 11014c0a534Smrg if (XGetWMProtocols (disp, window, &protocols, &numProtocols) != True) 11114c0a534Smrg return (False); 11214c0a534Smrg 11314c0a534Smrg found = 0; 11414c0a534Smrg 11514c0a534Smrg if (protocols != NULL) 11614c0a534Smrg { 117bdc460c5Smrg for (int i = 0; i < numProtocols; i++) { 11814c0a534Smrg if (protocols[i] == wmSaveYourselfAtom) 11914c0a534Smrg found = 1; 120bdc460c5Smrg } 12114c0a534Smrg 12214c0a534Smrg XFree (protocols); 12314c0a534Smrg } 12414c0a534Smrg 12514c0a534Smrg return (found); 12614c0a534Smrg} 12714c0a534Smrg 12814c0a534Smrg 12914c0a534Smrg 130bf2eeab3Smrgstatic Bool 131bf2eeab3SmrgHasXSMPsupport(Window window) 13214c0a534Smrg{ 13314c0a534Smrg XTextProperty tp; 13414c0a534Smrg Bool hasIt = 0; 13514c0a534Smrg 13614c0a534Smrg if (XGetTextProperty (disp, window, &tp, smClientIdAtom)) 13714c0a534Smrg { 13814c0a534Smrg if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) 13914c0a534Smrg hasIt = 1; 14014c0a534Smrg 14114c0a534Smrg if (tp.value) 14214c0a534Smrg XFree ((char *) tp.value); 14314c0a534Smrg } 14414c0a534Smrg 14514c0a534Smrg return (hasIt); 14614c0a534Smrg} 14714c0a534Smrg 14814c0a534Smrg 14914c0a534Smrg 150bf2eeab3Smrgstatic WinInfo * 151bf2eeab3SmrgGetClientLeader(WinInfo *winptr) 15214c0a534Smrg{ 15314c0a534Smrg Atom actual_type; 15414c0a534Smrg int actual_format; 15514c0a534Smrg unsigned long nitems, bytesafter; 15614c0a534Smrg unsigned long *datap = NULL; 15714c0a534Smrg WinInfo *leader_winptr = NULL; 15814c0a534Smrg Bool failure = 0; 15914c0a534Smrg 16014c0a534Smrg if (XGetWindowProperty (disp, winptr->window, wmClientLeaderAtom, 16114c0a534Smrg 0L, 1L, False, AnyPropertyType, &actual_type, &actual_format, 16214c0a534Smrg &nitems, &bytesafter, (unsigned char **) &datap) == Success) 16314c0a534Smrg { 16414c0a534Smrg if (actual_type == XA_WINDOW && actual_format == 32 && 16514c0a534Smrg nitems == 1 && bytesafter == 0) 16614c0a534Smrg { 16714c0a534Smrg Window leader_win = *((Window *) datap); 16814c0a534Smrg 16914c0a534Smrg if (!LookupWindow (leader_win, &leader_winptr, NULL)) 17014c0a534Smrg failure = 1; 17114c0a534Smrg } 17214c0a534Smrg 17314c0a534Smrg if (datap) 17414c0a534Smrg XFree (datap); 17514c0a534Smrg } 17614c0a534Smrg 17714c0a534Smrg if (failure) 17814c0a534Smrg { 17914c0a534Smrg /* The client leader was defined, but we couldn't find the window */ 18014c0a534Smrg 18114c0a534Smrg return (NULL); 18214c0a534Smrg } 18314c0a534Smrg else if (leader_winptr) 18414c0a534Smrg { 18514c0a534Smrg /* We found the real client leader */ 18614c0a534Smrg 18714c0a534Smrg return (leader_winptr); 18814c0a534Smrg } 18914c0a534Smrg else 19014c0a534Smrg { 19114c0a534Smrg /* There is no client leader defined, return this window */ 19214c0a534Smrg 19314c0a534Smrg return (winptr); 19414c0a534Smrg } 19514c0a534Smrg} 19614c0a534Smrg 19714c0a534Smrg 19814c0a534Smrg 199bf2eeab3Smrgstatic char * 200bf2eeab3SmrgCheckFullyQuantifiedName(char *name, int *newstring) 20114c0a534Smrg{ 20214c0a534Smrg /* 20314c0a534Smrg * Due to a bug in Xlib (for hpux in particular), some clients 20414c0a534Smrg * will have a WM_CLIENT_MACHINE that is not fully quantified. 20514c0a534Smrg * For example, we might get "excon" instead of "excon.x.org". 20614c0a534Smrg * This really stinks. The best we can do is tag on our own 207bdc460c5Smrg * domain name, if we can - if not, the unquantified name is 208bdc460c5Smrg * better than nothing. 20914c0a534Smrg */ 21014c0a534Smrg 21114c0a534Smrg if (strchr (name, '.') != NULL) 21214c0a534Smrg { 21314c0a534Smrg *newstring = 0; 21414c0a534Smrg return (name); 21514c0a534Smrg } 21614c0a534Smrg else 21714c0a534Smrg { 218bdc460c5Smrg char hostnamebuf[HOST_NAME_MAX + 1] = { 0 }; 21914c0a534Smrg char *firstDot; 22014c0a534Smrg 221bdc460c5Smrg if (gethostname (hostnamebuf, sizeof hostnamebuf) == 0) 222bdc460c5Smrg firstDot = strchr (hostnamebuf, '.'); 223bdc460c5Smrg else 224bdc460c5Smrg firstDot = NULL; 22514c0a534Smrg 22614c0a534Smrg if (!firstDot) 22714c0a534Smrg { 22814c0a534Smrg *newstring = 0; 22914c0a534Smrg return (name); 23014c0a534Smrg } 23114c0a534Smrg else 23214c0a534Smrg { 23314c0a534Smrg char *newptr; 23414c0a534Smrg 23524047306Smrg if (asprintf (&newptr, "%s.%s", name, firstDot + 1) == -1) { 23624047306Smrg *newstring = 0; 237bdc460c5Smrg return (name); 23824047306Smrg } 23914c0a534Smrg *newstring = 1; 24014c0a534Smrg return (newptr); 24114c0a534Smrg } 24214c0a534Smrg } 24314c0a534Smrg} 24414c0a534Smrg 24514c0a534Smrg 24614c0a534Smrg 247bf2eeab3Smrgstatic void 248bf2eeab3SmrgFinishSaveYourself(WinInfo *winInfo, Bool has_WM_SAVEYOURSELF) 24914c0a534Smrg{ 25014c0a534Smrg SmProp prop1, prop2, prop3, *props[3]; 25114c0a534Smrg SmPropValue prop1val, prop2val, prop3val; 25214c0a534Smrg 25314c0a534Smrg if (!winInfo->got_first_save_yourself) 25414c0a534Smrg { 25514c0a534Smrg char userId[20], restartService[80]; 25614c0a534Smrg char *fullyQuantifiedName; 25714c0a534Smrg int newstring; 25814c0a534Smrg 25914c0a534Smrg prop1.name = SmProgram; 26014c0a534Smrg prop1.type = SmARRAY8; 26114c0a534Smrg prop1.num_vals = 1; 26214c0a534Smrg prop1.vals = &prop1val; 26314c0a534Smrg prop1val.value = (SmPointer) winInfo->wm_command[0]; 26414c0a534Smrg prop1val.length = strlen (winInfo->wm_command[0]); 26514c0a534Smrg 26624047306Smrg snprintf (userId, sizeof(userId), "%ld", (long)getuid()); 26714c0a534Smrg prop2.name = SmUserID; 26814c0a534Smrg prop2.type = SmARRAY8; 26914c0a534Smrg prop2.num_vals = 1; 27014c0a534Smrg prop2.vals = &prop2val; 27114c0a534Smrg prop2val.value = (SmPointer) userId; 27214c0a534Smrg prop2val.length = strlen (userId); 27314c0a534Smrg 27414c0a534Smrg fullyQuantifiedName = CheckFullyQuantifiedName ( 27514c0a534Smrg (char *) winInfo->wm_client_machine.value, &newstring); 27624047306Smrg snprintf (restartService, sizeof(restartService), 27724047306Smrg "rstart-rsh/%s", fullyQuantifiedName); 27814c0a534Smrg if (newstring) 27914c0a534Smrg free (fullyQuantifiedName); 28014c0a534Smrg 28114c0a534Smrg prop3.name = "_XC_RestartService"; 28214c0a534Smrg prop3.type = SmLISTofARRAY8; 28314c0a534Smrg prop3.num_vals = 1; 28414c0a534Smrg prop3.vals = &prop3val; 28514c0a534Smrg prop3val.value = (SmPointer) restartService; 28614c0a534Smrg prop3val.length = strlen (restartService); 28714c0a534Smrg 28814c0a534Smrg props[0] = &prop1; 28914c0a534Smrg props[1] = &prop2; 29014c0a534Smrg props[2] = &prop3; 29114c0a534Smrg 29214c0a534Smrg SmcSetProperties (winInfo->smc_conn, 3, props); 29314c0a534Smrg 29414c0a534Smrg winInfo->got_first_save_yourself = 1; 29514c0a534Smrg } 29614c0a534Smrg 29714c0a534Smrg prop1.name = SmRestartCommand; 29814c0a534Smrg prop1.type = SmLISTofARRAY8; 29914c0a534Smrg prop1.num_vals = winInfo->wm_command_count; 30014c0a534Smrg 301bdc460c5Smrg prop1.vals = calloc (winInfo->wm_command_count, sizeof (SmPropValue)); 30214c0a534Smrg 30314c0a534Smrg if (!prop1.vals) 30414c0a534Smrg { 30514c0a534Smrg SmcSaveYourselfDone (winInfo->smc_conn, False); 30614c0a534Smrg return; 30714c0a534Smrg } 30814c0a534Smrg 309bdc460c5Smrg for (int i = 0; i < winInfo->wm_command_count; i++) 31014c0a534Smrg { 31114c0a534Smrg prop1.vals[i].value = (SmPointer) winInfo->wm_command[i]; 31214c0a534Smrg prop1.vals[i].length = strlen (winInfo->wm_command[i]); 31314c0a534Smrg } 31414c0a534Smrg 31514c0a534Smrg prop2.name = SmCloneCommand; 31614c0a534Smrg prop2.type = SmLISTofARRAY8; 31714c0a534Smrg prop2.num_vals = winInfo->wm_command_count; 31814c0a534Smrg prop2.vals = prop1.vals; 31914c0a534Smrg 32014c0a534Smrg props[0] = &prop1; 32114c0a534Smrg props[1] = &prop2; 32214c0a534Smrg 32314c0a534Smrg SmcSetProperties (winInfo->smc_conn, 2, props); 32414c0a534Smrg 325bdc460c5Smrg free (prop1.vals); 32614c0a534Smrg 32714c0a534Smrg /* 32814c0a534Smrg * If the client doesn't support WM_SAVE_YOURSELF, we should 32914c0a534Smrg * return failure for the save, since we really don't know if 33014c0a534Smrg * the application needed to save state. 33114c0a534Smrg */ 33214c0a534Smrg 33314c0a534Smrg SmcSaveYourselfDone (winInfo->smc_conn, has_WM_SAVEYOURSELF); 33414c0a534Smrg} 33514c0a534Smrg 33614c0a534Smrg 33714c0a534Smrg 338bf2eeab3Smrgstatic void 339bf2eeab3SmrgSaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType, 340bf2eeab3Smrg Bool shutdown, int interactStyle, Bool fast) 34114c0a534Smrg{ 34214c0a534Smrg WinInfo *winInfo = (WinInfo *) clientData; 34314c0a534Smrg 34414c0a534Smrg if (!winInfo->has_save_yourself) 34514c0a534Smrg { 34614c0a534Smrg FinishSaveYourself (winInfo, False); 34714c0a534Smrg } 34814c0a534Smrg else 34914c0a534Smrg { 35014c0a534Smrg XClientMessageEvent saveYourselfMessage; 35114c0a534Smrg 35214c0a534Smrg 35314c0a534Smrg /* Send WM_SAVE_YOURSELF */ 35414c0a534Smrg 35514c0a534Smrg saveYourselfMessage.type = ClientMessage; 35614c0a534Smrg saveYourselfMessage.window = winInfo->window; 35714c0a534Smrg saveYourselfMessage.message_type = wmProtocolsAtom; 35814c0a534Smrg saveYourselfMessage.format = 32; 35914c0a534Smrg saveYourselfMessage.data.l[0] = wmSaveYourselfAtom; 36014c0a534Smrg saveYourselfMessage.data.l[1] = CurrentTime; 36114c0a534Smrg 36214c0a534Smrg if (XSendEvent (disp, winInfo->window, False, NoEventMask, 36314c0a534Smrg (XEvent *) &saveYourselfMessage)) 36414c0a534Smrg { 36514c0a534Smrg winInfo->waiting_for_update = 1; 36614c0a534Smrg 36714c0a534Smrg if (debug) 36814c0a534Smrg { 36914c0a534Smrg printf ("Sent SAVE YOURSELF to 0x%x\n", 37014c0a534Smrg (unsigned int)winInfo->window); 37114c0a534Smrg printf ("\n"); 37214c0a534Smrg } 37314c0a534Smrg } 37414c0a534Smrg else 37514c0a534Smrg { 37614c0a534Smrg if (debug) 37714c0a534Smrg { 37814c0a534Smrg printf ("Failed to send SAVE YOURSELF to 0x%x\n", 37914c0a534Smrg (unsigned int)winInfo->window); 38014c0a534Smrg printf ("\n"); 38114c0a534Smrg } 38214c0a534Smrg } 38314c0a534Smrg } 38414c0a534Smrg} 38514c0a534Smrg 38614c0a534Smrg 38714c0a534Smrg 388bf2eeab3Smrgstatic void 389bf2eeab3SmrgDieCB(SmcConn smcConn, SmPointer clientData) 39014c0a534Smrg{ 39114c0a534Smrg WinInfo *winInfo = (WinInfo *) clientData; 39214c0a534Smrg 39314c0a534Smrg SmcCloseConnection (winInfo->smc_conn, 0, NULL); 39414c0a534Smrg winInfo->smc_conn = NULL; 39514c0a534Smrg XtRemoveInput (winInfo->input_id); 39614c0a534Smrg 39714c0a534Smrg /* Now tell the client to die */ 39814c0a534Smrg 39914c0a534Smrg if (debug) 40014c0a534Smrg printf ("Trying to kill 0x%x\n", (unsigned int)winInfo->window); 40114c0a534Smrg 40214c0a534Smrg XSync (disp, 0); 40314c0a534Smrg XKillClient (disp, winInfo->window); 40414c0a534Smrg XSync (disp, 0); 40514c0a534Smrg 40614c0a534Smrg 40714c0a534Smrg /* 40814c0a534Smrg * Proxy must exit when all clients die, and the proxy itself 40914c0a534Smrg * must have received a Die. 41014c0a534Smrg */ 41114c0a534Smrg 41214c0a534Smrg die_count++; 41314c0a534Smrg 41414c0a534Smrg if (die_count == proxy_count && ok_to_die) 41514c0a534Smrg { 41614c0a534Smrg exit (0); 41714c0a534Smrg } 41814c0a534Smrg} 41914c0a534Smrg 42014c0a534Smrg 42114c0a534Smrg 422bf2eeab3Smrgstatic void 423bf2eeab3SmrgSaveCompleteCB(SmcConn smcConn, SmPointer clientData) 42414c0a534Smrg{ 42514c0a534Smrg /* 42614c0a534Smrg * Nothing to do here. 42714c0a534Smrg */ 42814c0a534Smrg} 42914c0a534Smrg 43014c0a534Smrg 43114c0a534Smrg 432bf2eeab3Smrgstatic void 433bf2eeab3SmrgShutdownCancelledCB(SmcConn smcConn, SmPointer clientData) 43414c0a534Smrg{ 43514c0a534Smrg /* 43614c0a534Smrg * Since we did not request to interact or request save yourself 43714c0a534Smrg * phase 2, we know we already sent the save yourself done, so 43814c0a534Smrg * there is nothing to do here. 43914c0a534Smrg */ 44014c0a534Smrg} 44114c0a534Smrg 44214c0a534Smrg 44314c0a534Smrg 444bf2eeab3Smrgstatic void 445bf2eeab3SmrgProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id) 44614c0a534Smrg{ 44714c0a534Smrg IceConn ice_conn = (IceConn) client_data; 44814c0a534Smrg 44914c0a534Smrg IceProcessMessages (ice_conn, NULL, NULL); 45014c0a534Smrg} 45114c0a534Smrg 45214c0a534Smrg 45314c0a534Smrg 454bf2eeab3Smrgstatic void 455bf2eeab3SmrgNullIceErrorHandler(IceConn iceConn, Bool swap, int offendingMinorOpcode, 456bf2eeab3Smrg unsigned long offendingSequence, int errorClass, 457bf2eeab3Smrg int severity, IcePointer values) 45814c0a534Smrg{ 45914c0a534Smrg return; 46014c0a534Smrg} 46114c0a534Smrg 46214c0a534Smrg 463bf2eeab3Smrgstatic void 464bf2eeab3SmrgConnectClientToSM(WinInfo *winInfo) 46514c0a534Smrg{ 46614c0a534Smrg char errorMsg[256]; 46714c0a534Smrg unsigned long mask; 46814c0a534Smrg SmcCallbacks callbacks; 46914c0a534Smrg IceConn ice_conn; 47014c0a534Smrg char *prevId; 47114c0a534Smrg 47214c0a534Smrg mask = SmcSaveYourselfProcMask | SmcDieProcMask | 47314c0a534Smrg SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; 47414c0a534Smrg 47514c0a534Smrg callbacks.save_yourself.callback = SaveYourselfCB; 47614c0a534Smrg callbacks.save_yourself.client_data = (SmPointer) winInfo; 47714c0a534Smrg 47814c0a534Smrg callbacks.die.callback = DieCB; 47914c0a534Smrg callbacks.die.client_data = (SmPointer) winInfo; 48014c0a534Smrg 48114c0a534Smrg callbacks.save_complete.callback = SaveCompleteCB; 48214c0a534Smrg callbacks.save_complete.client_data = (SmPointer) winInfo; 48314c0a534Smrg 48414c0a534Smrg callbacks.shutdown_cancelled.callback = ShutdownCancelledCB; 48514c0a534Smrg callbacks.shutdown_cancelled.client_data = (SmPointer) winInfo; 48614c0a534Smrg 48714c0a534Smrg prevId = LookupClientID (winInfo); 48814c0a534Smrg 48914c0a534Smrg /* 49014c0a534Smrg * In case a protocol error occurs when opening the connection, 49114c0a534Smrg * (e.g. an authentication error), we set a null error handler 49214c0a534Smrg * before the open, then restore the default handler after the open. 49314c0a534Smrg */ 49414c0a534Smrg 49514c0a534Smrg IceSetErrorHandler (NullIceErrorHandler); 49614c0a534Smrg 49714c0a534Smrg winInfo->smc_conn = SmcOpenConnection ( 49814c0a534Smrg NULL, /* use SESSION_MANAGER env */ 49914c0a534Smrg (SmPointer) winInfo, /* force a new connection */ 50014c0a534Smrg SmProtoMajor, 50114c0a534Smrg SmProtoMinor, 50214c0a534Smrg mask, 50314c0a534Smrg &callbacks, 50414c0a534Smrg prevId, 50514c0a534Smrg &winInfo->client_id, 50614c0a534Smrg 256, errorMsg); 50714c0a534Smrg 50814c0a534Smrg IceSetErrorHandler (NULL); 50914c0a534Smrg 51014c0a534Smrg if (winInfo->smc_conn == NULL) 51114c0a534Smrg return; 51214c0a534Smrg 51314c0a534Smrg ice_conn = SmcGetIceConnection (winInfo->smc_conn); 51414c0a534Smrg 51514c0a534Smrg winInfo->input_id = XtAppAddInput ( 51614c0a534Smrg appContext, 51714c0a534Smrg IceConnectionNumber (ice_conn), 51814c0a534Smrg (XtPointer) XtInputReadMask, 51914c0a534Smrg ProcessIceMsgProc, 52014c0a534Smrg (XtPointer) ice_conn); 52114c0a534Smrg 52214c0a534Smrg if (debug) 52314c0a534Smrg { 52414c0a534Smrg printf ("Connected to SM, window = 0x%x\n", 52514c0a534Smrg (unsigned int)winInfo->window); 52614c0a534Smrg printf ("\n"); 52714c0a534Smrg } 52814c0a534Smrg 52914c0a534Smrg proxy_count++; 53014c0a534Smrg} 53114c0a534Smrg 53214c0a534Smrg 53314c0a534Smrg 534bf2eeab3Smrgstatic int 535bf2eeab3SmrgMyErrorHandler(Display *display, XErrorEvent *event) 53614c0a534Smrg{ 53714c0a534Smrg caught_error = 1; 53814c0a534Smrg return 0; 53914c0a534Smrg} 54014c0a534Smrg 54114c0a534Smrg 54214c0a534Smrg 543bf2eeab3Smrgstatic Bool 544bf2eeab3SmrgLookupWindow(Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret) 54514c0a534Smrg{ 54614c0a534Smrg WinInfo *ptr, *prev; 54714c0a534Smrg 54814c0a534Smrg ptr = win_head; 54914c0a534Smrg prev = NULL; 55014c0a534Smrg 55114c0a534Smrg while (ptr) 55214c0a534Smrg { 55314c0a534Smrg if (ptr->window == window) 55414c0a534Smrg break; 55514c0a534Smrg else 55614c0a534Smrg { 55714c0a534Smrg prev = ptr; 55814c0a534Smrg ptr = ptr->next; 55914c0a534Smrg } 56014c0a534Smrg } 56114c0a534Smrg 56214c0a534Smrg if (ptr) 56314c0a534Smrg { 56414c0a534Smrg if (ptr_ret) 56514c0a534Smrg *ptr_ret = ptr; 56614c0a534Smrg if (prev_ptr_ret) 56714c0a534Smrg *prev_ptr_ret = prev; 56814c0a534Smrg return (1); 56914c0a534Smrg } 57014c0a534Smrg else 57114c0a534Smrg return (0); 57214c0a534Smrg} 57314c0a534Smrg 57414c0a534Smrg 57514c0a534Smrg 576bf2eeab3Smrgstatic WinInfo * 577bf2eeab3SmrgAddNewWindow(Window window) 57814c0a534Smrg{ 57914c0a534Smrg WinInfo *newptr; 58014c0a534Smrg 58114c0a534Smrg if (LookupWindow (window, NULL, NULL)) 58214c0a534Smrg return (NULL); 58314c0a534Smrg 584bdc460c5Smrg newptr = malloc (sizeof (WinInfo)); 58514c0a534Smrg 58614c0a534Smrg if (newptr == NULL) 58714c0a534Smrg return (NULL); 58814c0a534Smrg 58914c0a534Smrg newptr->next = win_head; 59014c0a534Smrg win_head = newptr; 59114c0a534Smrg 59214c0a534Smrg newptr->window = window; 59314c0a534Smrg newptr->smc_conn = NULL; 59414c0a534Smrg newptr->tested_for_sm_client_id = 0; 59514c0a534Smrg newptr->client_id = NULL; 59614c0a534Smrg newptr->wm_command = NULL; 59714c0a534Smrg newptr->wm_command_count = 0; 59814c0a534Smrg newptr->class.res_name = NULL; 59914c0a534Smrg newptr->class.res_class = NULL; 60014c0a534Smrg newptr->wm_name = NULL; 60114c0a534Smrg newptr->wm_client_machine.value = NULL; 60214c0a534Smrg newptr->wm_client_machine.nitems = 0; 60314c0a534Smrg newptr->has_save_yourself = 0; 60414c0a534Smrg newptr->waiting_for_update = 0; 60514c0a534Smrg newptr->got_first_save_yourself = 0; 60614c0a534Smrg 60714c0a534Smrg return (newptr); 60814c0a534Smrg} 60914c0a534Smrg 61014c0a534Smrg 61114c0a534Smrg 612bf2eeab3Smrgstatic void 613bf2eeab3SmrgRemoveWindow(WinInfo *winptr) 61414c0a534Smrg{ 61514c0a534Smrg WinInfo *ptr, *prev; 61614c0a534Smrg 61714c0a534Smrg if (LookupWindow (winptr->window, &ptr, &prev)) 61814c0a534Smrg { 61914c0a534Smrg if (prev == NULL) 62014c0a534Smrg win_head = ptr->next; 62114c0a534Smrg else 62214c0a534Smrg prev->next = ptr->next; 62314c0a534Smrg 62414c0a534Smrg if (ptr->client_id) 62514c0a534Smrg free (ptr->client_id); 62614c0a534Smrg 62714c0a534Smrg if (ptr->wm_command) 62814c0a534Smrg XFreeStringList (ptr->wm_command); 62914c0a534Smrg 63014c0a534Smrg if (ptr->wm_name) 63114c0a534Smrg XFree (ptr->wm_name); 63214c0a534Smrg 63314c0a534Smrg if (ptr->wm_client_machine.value) 63414c0a534Smrg XFree (ptr->wm_client_machine.value); 63514c0a534Smrg 63614c0a534Smrg if (ptr->class.res_name) 63714c0a534Smrg XFree (ptr->class.res_name); 63814c0a534Smrg 63914c0a534Smrg if (ptr->class.res_class) 64014c0a534Smrg XFree (ptr->class.res_class); 64114c0a534Smrg 64214c0a534Smrg free ((char *) ptr); 64314c0a534Smrg } 64414c0a534Smrg} 64514c0a534Smrg 64614c0a534Smrg 64714c0a534Smrg 648bf2eeab3Smrgstatic void 649bf2eeab3SmrgGot_WM_STATE(WinInfo *winptr) 65014c0a534Smrg{ 65114c0a534Smrg WinInfo *leader_winptr; 65214c0a534Smrg 65314c0a534Smrg /* 65414c0a534Smrg * If we already got WM_STATE and tested for SM_CLIENT_ID, we 65514c0a534Smrg * shouldn't do it again. 65614c0a534Smrg */ 65714c0a534Smrg 65814c0a534Smrg if (winptr->tested_for_sm_client_id) 65914c0a534Smrg { 66014c0a534Smrg return; 66114c0a534Smrg } 66214c0a534Smrg 66314c0a534Smrg 66414c0a534Smrg /* 66514c0a534Smrg * Set a null error handler, in case this window goes away 66614c0a534Smrg * behind our back. 66714c0a534Smrg */ 66814c0a534Smrg 66914c0a534Smrg caught_error = 0; 67014c0a534Smrg XSetErrorHandler (MyErrorHandler); 67114c0a534Smrg 67214c0a534Smrg 67314c0a534Smrg /* 67414c0a534Smrg * Get the client leader window. 67514c0a534Smrg */ 67614c0a534Smrg 67714c0a534Smrg leader_winptr = GetClientLeader (winptr); 67814c0a534Smrg 67914c0a534Smrg if (caught_error) 68014c0a534Smrg { 68114c0a534Smrg caught_error = 0; 68214c0a534Smrg RemoveWindow (winptr); 68314c0a534Smrg XSetErrorHandler (NULL); 68414c0a534Smrg return; 68514c0a534Smrg } 68614c0a534Smrg 68714c0a534Smrg 68814c0a534Smrg /* 68914c0a534Smrg * If we already checked for SM_CLIENT_ID on the client leader 69014c0a534Smrg * window, don't do it again. 69114c0a534Smrg */ 69214c0a534Smrg 69314c0a534Smrg if (!leader_winptr || leader_winptr->tested_for_sm_client_id) 69414c0a534Smrg { 69514c0a534Smrg caught_error = 0; 69614c0a534Smrg XSetErrorHandler (NULL); 69714c0a534Smrg return; 69814c0a534Smrg } 69914c0a534Smrg 70014c0a534Smrg leader_winptr->tested_for_sm_client_id = 1; 70114c0a534Smrg 70214c0a534Smrg if (!HasXSMPsupport (leader_winptr->window)) 70314c0a534Smrg { 70414c0a534Smrg XFetchName (disp, leader_winptr->window, &leader_winptr->wm_name); 70514c0a534Smrg 70614c0a534Smrg XGetCommand (disp, leader_winptr->window, 70714c0a534Smrg &leader_winptr->wm_command, 70814c0a534Smrg &leader_winptr->wm_command_count); 70914c0a534Smrg 71014c0a534Smrg XGetClassHint (disp, leader_winptr->window, &leader_winptr->class); 71114c0a534Smrg 71214c0a534Smrg XGetWMClientMachine (disp, leader_winptr->window, 71314c0a534Smrg &leader_winptr->wm_client_machine); 71414c0a534Smrg 71514c0a534Smrg if (leader_winptr->wm_name != NULL && 71614c0a534Smrg leader_winptr->wm_command != NULL && 71714c0a534Smrg leader_winptr->wm_command_count > 0 && 71814c0a534Smrg leader_winptr->class.res_name != NULL && 71914c0a534Smrg leader_winptr->class.res_class != NULL && 72014c0a534Smrg leader_winptr->wm_client_machine.value != NULL && 72114c0a534Smrg leader_winptr->wm_client_machine.nitems != 0) 72214c0a534Smrg { 72314c0a534Smrg leader_winptr->has_save_yourself = 72414c0a534Smrg HasSaveYourself (leader_winptr->window); 72514c0a534Smrg 72614c0a534Smrg ConnectClientToSM (leader_winptr); 72714c0a534Smrg } 72814c0a534Smrg } 72914c0a534Smrg 73014c0a534Smrg XSync (disp, 0); 73114c0a534Smrg XSetErrorHandler (NULL); 73214c0a534Smrg 73314c0a534Smrg if (caught_error) 73414c0a534Smrg { 73514c0a534Smrg caught_error = 0; 73614c0a534Smrg RemoveWindow (leader_winptr); 73714c0a534Smrg } 73814c0a534Smrg} 73914c0a534Smrg 74014c0a534Smrg 74114c0a534Smrg 742bf2eeab3Smrgstatic void 743bf2eeab3SmrgHandleCreate(XCreateWindowEvent *event) 74414c0a534Smrg{ 74514c0a534Smrg Atom actual_type; 74614c0a534Smrg int actual_format; 74714c0a534Smrg unsigned long nitems, bytesafter; 74814c0a534Smrg unsigned long *datap = NULL; 74914c0a534Smrg WinInfo *winptr; 75014c0a534Smrg Bool got_wm_state = 0; 75114c0a534Smrg 75214c0a534Smrg /* 75314c0a534Smrg * We are waiting for all proxy connections to close so we can die. 75414c0a534Smrg * Don't handle new connections. 75514c0a534Smrg */ 75614c0a534Smrg 75714c0a534Smrg if (ok_to_die) 75814c0a534Smrg return; 75914c0a534Smrg 76014c0a534Smrg 76114c0a534Smrg /* 76214c0a534Smrg * Add the new window 76314c0a534Smrg */ 76414c0a534Smrg 76514c0a534Smrg if ((winptr = AddNewWindow (event->window)) == NULL) 76614c0a534Smrg return; 76714c0a534Smrg 76814c0a534Smrg 76914c0a534Smrg /* 77014c0a534Smrg * Right after the window was created, it might have been destroyed, 77114c0a534Smrg * so the following Xlib calls might fail. Need to catch the error 77214c0a534Smrg * by installing an error handler. 77314c0a534Smrg */ 77414c0a534Smrg 77514c0a534Smrg caught_error = 0; 77614c0a534Smrg XSetErrorHandler (MyErrorHandler); 77714c0a534Smrg 77814c0a534Smrg 77914c0a534Smrg /* 78014c0a534Smrg * Select for Property Notify on the window so we can determine 78114c0a534Smrg * when WM_STATE is defined. To avoid a race condition, we must 78214c0a534Smrg * do this _before_ we check for WM_STATE right now. 78314c0a534Smrg * 78414c0a534Smrg * Select for Substructure Notify so we can determine when the 78514c0a534Smrg * window is destroyed. 78614c0a534Smrg */ 78714c0a534Smrg 78814c0a534Smrg XSelectInput (disp, event->window, 78914c0a534Smrg SubstructureNotifyMask | PropertyChangeMask); 79014c0a534Smrg 79114c0a534Smrg 79214c0a534Smrg /* 79314c0a534Smrg * WM_STATE may already be there. Check now. 79414c0a534Smrg */ 79514c0a534Smrg 79614c0a534Smrg if (XGetWindowProperty (disp, event->window, wmStateAtom, 79714c0a534Smrg 0L, 2L, False, AnyPropertyType, 79814c0a534Smrg &actual_type, &actual_format, &nitems, &bytesafter, 79914c0a534Smrg (unsigned char **) &datap) == Success && datap) 80014c0a534Smrg { 80114c0a534Smrg if (nitems > 0) 80214c0a534Smrg got_wm_state = 1; 80314c0a534Smrg 80414c0a534Smrg if (datap) 80514c0a534Smrg XFree ((char *) datap); 80614c0a534Smrg } 80714c0a534Smrg 80814c0a534Smrg XSync (disp, 0); 80914c0a534Smrg XSetErrorHandler (NULL); 81014c0a534Smrg 81114c0a534Smrg if (caught_error) 81214c0a534Smrg { 81314c0a534Smrg caught_error = 0; 81414c0a534Smrg RemoveWindow (winptr); 81514c0a534Smrg } 81614c0a534Smrg else if (got_wm_state) 81714c0a534Smrg { 81814c0a534Smrg Got_WM_STATE (winptr); 81914c0a534Smrg } 82014c0a534Smrg} 82114c0a534Smrg 82214c0a534Smrg 82314c0a534Smrg 824bf2eeab3Smrgstatic void 825bf2eeab3SmrgHandleDestroy(XDestroyWindowEvent *event) 82614c0a534Smrg{ 82714c0a534Smrg WinInfo *winptr; 82814c0a534Smrg 82914c0a534Smrg if (LookupWindow (event->window, &winptr, NULL)) 83014c0a534Smrg { 83114c0a534Smrg if (winptr->smc_conn) 83214c0a534Smrg { 83314c0a534Smrg SmcCloseConnection (winptr->smc_conn, 0, NULL); 83414c0a534Smrg XtRemoveInput (winptr->input_id); 83514c0a534Smrg proxy_count--; 83614c0a534Smrg } 83714c0a534Smrg 83814c0a534Smrg if (debug) 83914c0a534Smrg { 84014c0a534Smrg printf ("Removed window (window = 0x%x)\n", 84114c0a534Smrg (unsigned int)winptr->window); 84214c0a534Smrg printf ("\n"); 84314c0a534Smrg } 84414c0a534Smrg 84514c0a534Smrg RemoveWindow (winptr); 84614c0a534Smrg } 84714c0a534Smrg} 84814c0a534Smrg 84914c0a534Smrg 85014c0a534Smrg 851bf2eeab3Smrgstatic void 852bf2eeab3SmrgHandleUpdate(XPropertyEvent *event) 85314c0a534Smrg{ 85414c0a534Smrg Window window = event->window; 85514c0a534Smrg WinInfo *winptr; 85614c0a534Smrg 85714c0a534Smrg if (!LookupWindow (window, &winptr, NULL)) 85814c0a534Smrg return; 85914c0a534Smrg 86014c0a534Smrg if (event->atom == wmStateAtom) 86114c0a534Smrg { 86214c0a534Smrg Got_WM_STATE (winptr); 86314c0a534Smrg } 86414c0a534Smrg else if (event->atom == XA_WM_COMMAND && winptr->waiting_for_update) 86514c0a534Smrg { 86614c0a534Smrg /* Finish off the Save Yourself */ 86714c0a534Smrg 86814c0a534Smrg if (winptr->wm_command) 86914c0a534Smrg { 87014c0a534Smrg XFreeStringList (winptr->wm_command); 87114c0a534Smrg winptr->wm_command = NULL; 87214c0a534Smrg winptr->wm_command_count = 0; 87314c0a534Smrg } 87414c0a534Smrg 87514c0a534Smrg XGetCommand (disp, window, 87614c0a534Smrg &winptr->wm_command, 87714c0a534Smrg &winptr->wm_command_count); 87814c0a534Smrg 87914c0a534Smrg winptr->waiting_for_update = 0; 88014c0a534Smrg FinishSaveYourself (winptr, True); 88114c0a534Smrg } 88214c0a534Smrg} 88314c0a534Smrg 88414c0a534Smrg 88514c0a534Smrg 886bf2eeab3Smrgstatic void 887bf2eeab3SmrgProxySaveYourselfPhase2CB(SmcConn smcConn, SmPointer clientData) 88814c0a534Smrg{ 88914c0a534Smrg char *filename; 89014c0a534Smrg Bool success = True; 89114c0a534Smrg SmProp prop1, prop2, prop3, *props[3]; 89214c0a534Smrg SmPropValue prop1val, prop2val, prop3val; 89324047306Smrg char *discardCommand; 894bdc460c5Smrg int numVals; 89514c0a534Smrg static int first_time = 1; 89614c0a534Smrg 89714c0a534Smrg if (first_time) 89814c0a534Smrg { 89914c0a534Smrg char userId[20]; 90014c0a534Smrg char hint = SmRestartIfRunning; 90114c0a534Smrg 90214c0a534Smrg prop1.name = SmProgram; 90314c0a534Smrg prop1.type = SmARRAY8; 90414c0a534Smrg prop1.num_vals = 1; 90514c0a534Smrg prop1.vals = &prop1val; 90614c0a534Smrg prop1val.value = Argv[0]; 90714c0a534Smrg prop1val.length = strlen (Argv[0]); 90814c0a534Smrg 90924047306Smrg snprintf (userId, sizeof(userId), "%ld", (long)getuid()); 91014c0a534Smrg prop2.name = SmUserID; 91114c0a534Smrg prop2.type = SmARRAY8; 91214c0a534Smrg prop2.num_vals = 1; 91314c0a534Smrg prop2.vals = &prop2val; 91414c0a534Smrg prop2val.value = (SmPointer) userId; 91514c0a534Smrg prop2val.length = strlen (userId); 91614c0a534Smrg 91714c0a534Smrg prop3.name = SmRestartStyleHint; 91814c0a534Smrg prop3.type = SmCARD8; 91914c0a534Smrg prop3.num_vals = 1; 92014c0a534Smrg prop3.vals = &prop3val; 92114c0a534Smrg prop3val.value = (SmPointer) &hint; 92214c0a534Smrg prop3val.length = 1; 92314c0a534Smrg 92414c0a534Smrg props[0] = &prop1; 92514c0a534Smrg props[1] = &prop2; 92614c0a534Smrg props[2] = &prop3; 92714c0a534Smrg 92814c0a534Smrg SmcSetProperties (smcConn, 3, props); 92914c0a534Smrg 93014c0a534Smrg first_time = 0; 93114c0a534Smrg } 93214c0a534Smrg 93314c0a534Smrg if ((filename = WriteProxyFile ()) == NULL) 93414c0a534Smrg { 93514c0a534Smrg success = False; 93614c0a534Smrg goto finishUp; 93714c0a534Smrg } 93814c0a534Smrg 93914c0a534Smrg prop1.name = SmRestartCommand; 94014c0a534Smrg prop1.type = SmLISTofARRAY8; 94114c0a534Smrg 942bdc460c5Smrg prop1.vals = calloc ((Argc + 4), sizeof (SmPropValue)); 94314c0a534Smrg 94414c0a534Smrg if (!prop1.vals) 94514c0a534Smrg { 94614c0a534Smrg success = False; 94714c0a534Smrg goto finishUp; 94814c0a534Smrg } 94914c0a534Smrg 95014c0a534Smrg numVals = 0; 95114c0a534Smrg 952bdc460c5Smrg for (int i = 0; i < Argc; i++) 95314c0a534Smrg { 95414c0a534Smrg if (strcmp (Argv[i], "-clientId") == 0 || 95514c0a534Smrg strcmp (Argv[i], "-restore") == 0) 95614c0a534Smrg { 95714c0a534Smrg i++; 95814c0a534Smrg } 95914c0a534Smrg else 96014c0a534Smrg { 96114c0a534Smrg prop1.vals[numVals].value = (SmPointer) Argv[i]; 96214c0a534Smrg prop1.vals[numVals++].length = strlen (Argv[i]); 96314c0a534Smrg } 96414c0a534Smrg } 96514c0a534Smrg 96614c0a534Smrg prop1.vals[numVals].value = (SmPointer) "-clientId"; 96714c0a534Smrg prop1.vals[numVals++].length = 9; 96814c0a534Smrg 96914c0a534Smrg prop1.vals[numVals].value = (SmPointer) proxy_clientId; 97014c0a534Smrg prop1.vals[numVals++].length = strlen (proxy_clientId); 97114c0a534Smrg 97214c0a534Smrg prop1.vals[numVals].value = (SmPointer) "-restore"; 97314c0a534Smrg prop1.vals[numVals++].length = 8; 97414c0a534Smrg 97514c0a534Smrg prop1.vals[numVals].value = (SmPointer) filename; 97614c0a534Smrg prop1.vals[numVals++].length = strlen (filename); 97714c0a534Smrg 97814c0a534Smrg prop1.num_vals = numVals; 97914c0a534Smrg 98014c0a534Smrg 98124047306Smrg if (asprintf (&discardCommand, "rm %s", filename) == -1) { 98224047306Smrg success = False; 98324047306Smrg goto finishUp; 98424047306Smrg } 98514c0a534Smrg prop2.name = SmDiscardCommand; 98614c0a534Smrg prop2.type = SmARRAY8; 98714c0a534Smrg prop2.num_vals = 1; 98814c0a534Smrg prop2.vals = &prop2val; 98914c0a534Smrg prop2val.value = (SmPointer) discardCommand; 99014c0a534Smrg prop2val.length = strlen (discardCommand); 99114c0a534Smrg 99214c0a534Smrg props[0] = &prop1; 99314c0a534Smrg props[1] = &prop2; 99414c0a534Smrg 99514c0a534Smrg SmcSetProperties (smcConn, 2, props); 996bdc460c5Smrg free (prop1.vals); 99724047306Smrg free (discardCommand); 99814c0a534Smrg 99914c0a534Smrg finishUp: 100014c0a534Smrg 100114c0a534Smrg SmcSaveYourselfDone (smcConn, success); 100214c0a534Smrg sent_save_done = 1; 100314c0a534Smrg 100414c0a534Smrg if (filename) 100514c0a534Smrg free (filename); 100614c0a534Smrg} 100714c0a534Smrg 100814c0a534Smrg 100914c0a534Smrg 1010bf2eeab3Smrgstatic void 1011bf2eeab3SmrgProxySaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType, 1012bf2eeab3Smrg Bool shutdown, int interactStyle, Bool fast) 101314c0a534Smrg{ 101414c0a534Smrg /* 101514c0a534Smrg * We want the proxy to respond to the Save Yourself after all 101614c0a534Smrg * the regular XSMP clients have finished with the save (and possibly 101714c0a534Smrg * interacted with the user). 101814c0a534Smrg */ 101914c0a534Smrg 102014c0a534Smrg if (!SmcRequestSaveYourselfPhase2 (smcConn, 102114c0a534Smrg ProxySaveYourselfPhase2CB, NULL)) 102214c0a534Smrg { 102314c0a534Smrg SmcSaveYourselfDone (smcConn, False); 102414c0a534Smrg sent_save_done = 1; 102514c0a534Smrg } 102614c0a534Smrg else 102714c0a534Smrg sent_save_done = 0; 102814c0a534Smrg} 102914c0a534Smrg 103014c0a534Smrg 103114c0a534Smrg 1032bf2eeab3Smrgstatic void 1033bf2eeab3SmrgProxyDieCB(SmcConn smcConn, SmPointer clientData) 103414c0a534Smrg{ 103514c0a534Smrg SmcCloseConnection (proxy_smcConn, 0, NULL); 103614c0a534Smrg XtRemoveInput (proxy_iceInputId); 103714c0a534Smrg 103814c0a534Smrg if (die_count == proxy_count) 103914c0a534Smrg exit (0); 104014c0a534Smrg else 104114c0a534Smrg ok_to_die = 1; 104214c0a534Smrg} 104314c0a534Smrg 104414c0a534Smrg 104514c0a534Smrg 1046bf2eeab3Smrgstatic void 1047bf2eeab3SmrgProxySaveCompleteCB(SmcConn smcConn, SmPointer clientData) 104814c0a534Smrg{ 104914c0a534Smrg ; 105014c0a534Smrg} 105114c0a534Smrg 105214c0a534Smrg 105314c0a534Smrg 1054bf2eeab3Smrgstatic void 1055bf2eeab3SmrgProxyShutdownCancelledCB(SmcConn smcConn, SmPointer clientData) 105614c0a534Smrg{ 105714c0a534Smrg if (!sent_save_done) 105814c0a534Smrg { 105914c0a534Smrg SmcSaveYourselfDone (smcConn, False); 106014c0a534Smrg sent_save_done = 1; 106114c0a534Smrg } 106214c0a534Smrg} 106314c0a534Smrg 106414c0a534Smrg 106514c0a534Smrg 1066bf2eeab3Smrgstatic Status 1067bf2eeab3SmrgConnectProxyToSM(char *previous_id) 106814c0a534Smrg{ 106914c0a534Smrg char errorMsg[256]; 107014c0a534Smrg unsigned long mask; 107114c0a534Smrg SmcCallbacks callbacks; 107214c0a534Smrg IceConn iceConn; 107314c0a534Smrg 107414c0a534Smrg mask = SmcSaveYourselfProcMask | SmcDieProcMask | 107514c0a534Smrg SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; 107614c0a534Smrg 107714c0a534Smrg callbacks.save_yourself.callback = ProxySaveYourselfCB; 107814c0a534Smrg callbacks.save_yourself.client_data = (SmPointer) NULL; 107914c0a534Smrg 108014c0a534Smrg callbacks.die.callback = ProxyDieCB; 108114c0a534Smrg callbacks.die.client_data = (SmPointer) NULL; 108214c0a534Smrg 108314c0a534Smrg callbacks.save_complete.callback = ProxySaveCompleteCB; 108414c0a534Smrg callbacks.save_complete.client_data = (SmPointer) NULL; 108514c0a534Smrg 108614c0a534Smrg callbacks.shutdown_cancelled.callback = ProxyShutdownCancelledCB; 108714c0a534Smrg callbacks.shutdown_cancelled.client_data = (SmPointer) NULL; 108814c0a534Smrg 108914c0a534Smrg proxy_smcConn = SmcOpenConnection ( 109014c0a534Smrg NULL, /* use SESSION_MANAGER env */ 109114c0a534Smrg (SmPointer) appContext, 109214c0a534Smrg SmProtoMajor, 109314c0a534Smrg SmProtoMinor, 109414c0a534Smrg mask, 109514c0a534Smrg &callbacks, 109614c0a534Smrg previous_id, 109714c0a534Smrg &proxy_clientId, 109814c0a534Smrg 256, errorMsg); 109914c0a534Smrg 110014c0a534Smrg if (proxy_smcConn == NULL) 110114c0a534Smrg return (0); 110214c0a534Smrg 110314c0a534Smrg iceConn = SmcGetIceConnection (proxy_smcConn); 110414c0a534Smrg 110514c0a534Smrg proxy_iceInputId = XtAppAddInput ( 110614c0a534Smrg appContext, 110714c0a534Smrg IceConnectionNumber (iceConn), 110814c0a534Smrg (XtPointer) XtInputReadMask, 110914c0a534Smrg ProcessIceMsgProc, 111014c0a534Smrg (XtPointer) iceConn); 111114c0a534Smrg 111214c0a534Smrg return (1); 111314c0a534Smrg} 111414c0a534Smrg 111514c0a534Smrg 111614c0a534Smrg 1117bf2eeab3Smrgstatic void 1118bf2eeab3SmrgCheckForExistingWindows(Window root) 111914c0a534Smrg{ 1120bdc460c5Smrg Window dontCare1, dontCare2, *children; 112114c0a534Smrg unsigned int nchildren, i; 112214c0a534Smrg 112314c0a534Smrg /* 112414c0a534Smrg * We query the root tree for all windows created thus far. 112514c0a534Smrg * Note that at any moment after XQueryTree is called, a 112614c0a534Smrg * window may be deleted. So we must take extra care to make 112714c0a534Smrg * sure a window really exists. 112814c0a534Smrg */ 112914c0a534Smrg 113014c0a534Smrg XQueryTree (disp, root, &dontCare1, &dontCare2, &children, &nchildren); 113114c0a534Smrg 113214c0a534Smrg for (i = 0; i < nchildren; i++) 113314c0a534Smrg { 1134bdc460c5Smrg Window client_window; 1135bdc460c5Smrg XCreateWindowEvent event = { .window = children[i] }; 113614c0a534Smrg 113714c0a534Smrg HandleCreate (&event); 113814c0a534Smrg 113914c0a534Smrg caught_error = 0; 114014c0a534Smrg XSetErrorHandler (MyErrorHandler); 114114c0a534Smrg 114214c0a534Smrg client_window = XmuClientWindow (disp, children[i]); 114314c0a534Smrg 114414c0a534Smrg XSetErrorHandler (NULL); 114514c0a534Smrg 114614c0a534Smrg if (!caught_error && client_window != children[i]) 114714c0a534Smrg { 114814c0a534Smrg event.window = client_window; 114914c0a534Smrg HandleCreate (&event); 115014c0a534Smrg } 115114c0a534Smrg } 115214c0a534Smrg} 115314c0a534Smrg 115414c0a534Smrg 115514c0a534Smrg 115614c0a534Smrgint 115714c0a534Smrgmain (int argc, char *argv[]) 115814c0a534Smrg{ 115914c0a534Smrg char *restore_filename = NULL; 116014c0a534Smrg char *client_id = NULL; 116114c0a534Smrg int i, zero = 0; 11627015785aSmrg const char * const usage = 11637015785aSmrg "usage: %s [-clientId id] [-restore file] [-debug] [-version]\n"; 116414c0a534Smrg 116514c0a534Smrg Argc = argc; 116614c0a534Smrg Argv = argv; 116714c0a534Smrg 116814c0a534Smrg for (i = 1; i < argc; i++) 116914c0a534Smrg { 117014c0a534Smrg if (argv[i][0] == '-') 117114c0a534Smrg { 117214c0a534Smrg switch (argv[i][1]) 117314c0a534Smrg { 117414c0a534Smrg case 'd': /* -debug */ 117514c0a534Smrg debug = 1; 117614c0a534Smrg continue; 117714c0a534Smrg 117814c0a534Smrg case 'c': /* -clientId */ 117924047306Smrg if (++i >= argc) { 118024047306Smrg fprintf (stderr, "%s: -clientId requires an argument\n", 118124047306Smrg argv[0]); 118224047306Smrg goto usage; 118324047306Smrg } 118414c0a534Smrg client_id = argv[i]; 118514c0a534Smrg continue; 118614c0a534Smrg 118714c0a534Smrg case 'r': /* -restore */ 118824047306Smrg if (++i >= argc) { 118924047306Smrg fprintf (stderr, "%s: -restore requires an argument\n", 119024047306Smrg argv[0]); 119124047306Smrg goto usage; 119224047306Smrg } 119314c0a534Smrg restore_filename = argv[i]; 119414c0a534Smrg continue; 119524047306Smrg 119624047306Smrg case 'v': 119724047306Smrg puts (PACKAGE_STRING); 119824047306Smrg exit (0); 11997015785aSmrg 12007015785aSmrg case '-': 12017015785aSmrg if (strcmp(argv[i], "--help") == 0) { 12027015785aSmrg fprintf (stdout, usage, argv[0]); 12037015785aSmrg exit (0); 12047015785aSmrg } 12057015785aSmrg if (strcmp(argv[i], "--version") == 0) { 12067015785aSmrg puts (PACKAGE_STRING); 12077015785aSmrg exit (0); 12087015785aSmrg } 12097015785aSmrg break; 121014c0a534Smrg } 121114c0a534Smrg } 121214c0a534Smrg 121324047306Smrg fprintf (stderr, "%s: unrecognized argument: %s\n", argv[0], argv[i]); 121424047306Smrg 121514c0a534Smrg usage: 121614c0a534Smrg 12177015785aSmrg fprintf (stderr, usage, argv[0]); 121814c0a534Smrg exit (1); 121914c0a534Smrg } 122014c0a534Smrg 122114c0a534Smrg 122214c0a534Smrg XtToolkitInitialize (); 122314c0a534Smrg appContext = XtCreateApplicationContext (); 122414c0a534Smrg 122514c0a534Smrg if (!(disp = XtOpenDisplay (appContext, NULL, "SM-PROXY", "SM-PROXY", 122614c0a534Smrg NULL, 0, &zero, NULL))) 122714c0a534Smrg { 122814c0a534Smrg fprintf (stderr, "smproxy: unable to open display\n"); 122914c0a534Smrg exit (1); 123014c0a534Smrg } 123114c0a534Smrg 123214c0a534Smrg if (restore_filename) 123314c0a534Smrg ReadProxyFile (restore_filename); 123414c0a534Smrg 123514c0a534Smrg if (!ConnectProxyToSM (client_id)) 123614c0a534Smrg { 123714c0a534Smrg fprintf (stderr, "smproxy: unable to connect to session manager\n"); 123814c0a534Smrg exit (1); 123914c0a534Smrg } 124014c0a534Smrg 124114c0a534Smrg wmProtocolsAtom = XInternAtom (disp, "WM_PROTOCOLS", False); 124214c0a534Smrg wmSaveYourselfAtom = XInternAtom (disp, "WM_SAVE_YOURSELF", False); 124314c0a534Smrg wmStateAtom = XInternAtom (disp, "WM_STATE", False); 124414c0a534Smrg smClientIdAtom = XInternAtom (disp, "SM_CLIENT_ID", False); 124514c0a534Smrg wmClientLeaderAtom = XInternAtom (disp, "WM_CLIENT_LEADER", False); 124614c0a534Smrg 124714c0a534Smrg for (i = 0; i < ScreenCount (disp); i++) 124814c0a534Smrg { 124914c0a534Smrg Window root = RootWindow (disp, i); 125014c0a534Smrg XSelectInput (disp, root, SubstructureNotifyMask | PropertyChangeMask); 125114c0a534Smrg CheckForExistingWindows (root); 125214c0a534Smrg } 125314c0a534Smrg 125414c0a534Smrg while (1) 125514c0a534Smrg { 125614c0a534Smrg XEvent event; 125714c0a534Smrg 125814c0a534Smrg XtAppNextEvent (appContext, &event); 125914c0a534Smrg 126014c0a534Smrg switch (event.type) 126114c0a534Smrg { 126214c0a534Smrg case CreateNotify: 126314c0a534Smrg HandleCreate (&event.xcreatewindow); 126414c0a534Smrg break; 126514c0a534Smrg 126614c0a534Smrg case DestroyNotify: 126714c0a534Smrg HandleDestroy (&event.xdestroywindow); 126814c0a534Smrg break; 126914c0a534Smrg 127014c0a534Smrg case PropertyNotify: 127114c0a534Smrg HandleUpdate (&event.xproperty); 127214c0a534Smrg break; 127314c0a534Smrg 127414c0a534Smrg default: 127514c0a534Smrg XtDispatchEvent (&event); 127614c0a534Smrg break; 127714c0a534Smrg } 127814c0a534Smrg } 127914c0a534Smrg exit(0); 128014c0a534Smrg} 1281