/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ /* Copyright (c) 2008-2014 Oleksandr Shneyder */ /* Copyright (c) 2011-2016 Mike Gabriel */ /* Copyright (c) 2014-2016 Mihai Moldovan */ /* Copyright (c) 2014-2016 Ulrich Sibiller */ /* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ /* */ /* NXAGENT, NX protocol compression and NX extensions to this software */ /* are copyright of the aforementioned persons and companies. */ /* */ /* Redistribution and use of the present software is allowed according */ /* to terms specified in the file LICENSE which comes in the source */ /* distribution. */ /* */ /* All rights reserved. */ /* */ /* NOTE: This software has received contributions from various other */ /* contributors, only the core maintainers and supporters are listed as */ /* copyright holders. Please contact us, if you feel you should be listed */ /* as copyright holder, as well. */ /* */ /**************************************************************************/ /* * Used in handling of karma on lost focus. */ #include #include #include #include #include "Xatom.h" #include "dixstruct.h" #include "scrnintstr.h" #include "windowstr.h" #include "osdep.h" /* * NX specific includes and definitions. */ #include "Agent.h" #include "Args.h" #include "Display.h" #include "Client.h" #include "Dialog.h" #include "Handlers.h" #include "Events.h" #include "Drawable.h" #include "Utils.h" /* * Need to include this after the stub * definition of GC in Agent.h. */ #include "compext/Compext.h" /* * Set here the required log level. */ #define PANIC #define WARNING #undef TEST #undef DEBUG /* * Returns the last signal delivered * to the process. */ extern int _X11TransSocketCheckSignal(void); /* * Time in milliseconds of first iteration * through the dispatcher. */ unsigned long nxagentStartTime = -1; /* * If defined, add a function checking if we * need a null timeout after a client wakeup. */ #undef CHECK_RESTARTED_CLIENTS #ifdef CHECK_RESTARTED_CLIENTS void nxagentCheckRestartedClients(struct timeval **timeout); #endif /* * Allow attaching private members to the client. */ int nxagentClientPrivateIndex; /* * The master nxagent holds in nxagentShadowCounter * the number of shadow nxagents connected to itself. */ int nxagentShadowCounter = 0; void nxagentInitClientPrivates(ClientPtr client) { if (nxagentClientPriv(client)) { nxagentClientPriv(client) -> clientState = 0; nxagentClientPriv(client) -> clientBytes = 0; nxagentClientPriv(client) -> clientHint = UNKNOWN; } } /* * Guess the running application based on the * properties attached to its main window. */ void nxagentGuessClientHint(ClientPtr client, Atom property, char *data) { #ifdef TEST fprintf(stderr, "++++++nxagentGuessClientHint: Client [%d] setting property [%s] as [%s].\n", client -> index, validateString(NameForAtom(property)), validateString(data)); #endif if (nxagentClientPriv(client) -> clientHint == UNKNOWN) { if (property == XA_WM_CLASS) { if (strcmp(data, "nxclient") == 0) { #ifdef TEST fprintf(stderr, "++++++nxagentGuessClientHint: Detected nxclient as [%d].\n", client -> index); #endif nxagentClientHint(client) = NXCLIENT_WINDOW; } else if (strstr(data, "java")) { #ifdef TEST fprintf(stderr, "++++++nxagentGuessClientHint: Detected java as [%d].\n", client -> index); #endif nxagentClientHint(client) = JAVA_WINDOW; } } } if (nxagentClientPriv(client) -> clientHint == NXCLIENT_WINDOW) { if (property == MakeAtom("WM_WINDOW_ROLE", 14, True) && strncmp(data, "msgBox", 6) == 0) { #ifdef TEST fprintf(stderr, "++++++nxagentGuessClientHint: Detected nxclient dialog as [%d].\n", client -> index); #endif nxagentClientHint(client) = NXCLIENT_DIALOG; } } } void nxagentGuessShadowHint(ClientPtr client, Atom property) { #ifdef DEBUG fprintf(stderr, "nxagentGuessShadowHint: Client [%d] setting property [%s].\n", client -> index, validateString(NameForAtom(property))); #endif if (nxagentClientPriv(client) -> clientHint == UNKNOWN) { if (strcmp(validateString(NameForAtom(property)), "_NX_SHADOW") == 0) { #ifdef TEST fprintf(stderr, "nxagentGuessShadowHint: nxagentShadowCounter [%d].\n", nxagentShadowCounter); fprintf(stderr, "nxagentGuessShadowHint: Detected shadow nxagent as client [%d].\n", client -> index); #endif nxagentClientHint(client) = NXAGENT_SHADOW; nxagentShadowCounter++; #ifdef TEST fprintf(stderr, "nxagentGuessShadowHint: nxagentShadowCounter [%d].\n", nxagentShadowCounter); #endif /* * From this moment on we ignore the visibility * checks to keep the windows updated. */ nxagentChangeOption(IgnoreVisibility, 1); } } } void nxagentCheckIfShadowAgent(ClientPtr client) { if (nxagentClientPriv(client) -> clientHint == NXAGENT_SHADOW) { #ifdef TEST fprintf(stderr, "nxagentCheckIfShadowAgent: nxagentShadowCounter [%d].\n", nxagentShadowCounter); fprintf(stderr, "nxagentCheckIfShadowAgent: Shadow nxagent as client [%d] detached.\n", client -> index); fprintf(stderr, "nxagentCheckIfShadowAgent: Decreasing nxagentShadowCounter.\n"); #endif /* * We decrease nxagentShadowCounter. */ nxagentShadowCounter--; #ifdef TEST fprintf(stderr, "nxagentCheckIfShadowAgent: nxagentShadowCounter [%d].\n", nxagentShadowCounter); #endif if (nxagentShadowCounter == 0) { /* * The last shadow nxagent has been detached * from master nxagent. * The master nxagent could do some action * here. */ #ifdef TEST fprintf(stderr, "nxagentCheckIfShadowAgent: The last shadow nxagent has been detached.\n"); #endif nxagentChangeOption(IgnoreVisibility, 0); } } } void nxagentWakeupByReconnect(void) { int i; #ifdef TEST fprintf(stderr, "++++++nxagentWakeupByReconnect: Going to wakeup all clients.\n"); #endif for (i = 1; i < currentMaxClients; i++) { if (clients[i] != NULL) { nxagentWakeupByReset(clients[i]); } } } void nxagentWakeupByReset(ClientPtr client) { #ifdef TEST fprintf(stderr, "++++++nxagentWakeupByReset: Going to check client id [%d].\n", client -> index); #endif if (nxagentNeedWakeup(client)) { #ifdef TEST fprintf(stderr, "++++++nxagentWakeupByReset: Going to wakeup client id [%d].\n", client -> index); #endif if (client -> index < MAX_CONNECTIONS) { if (nxagentNeedWakeupBySplit(client)) { nxagentWakeupBySplit(client); } } } if (client -> index < MAX_CONNECTIONS) { #ifdef TEST fprintf(stderr, "++++++nxagentWakeupByReset: Going to reset bytes received for client id [%d].\n", client -> index); #endif nxagentClientBytes(client) = 0; } } /* * Wait for any event. */ #define WAIT_ALL_EVENTS #ifndef WAIT_ALL_EVENTS static Bool nxagentWaitWakeupBySplitPredicate(Display *display, XEvent *event, XPointer ptr) { return (event -> type == ClientMessage && (event -> xclient.data.l[0] == NXNoSplitNotify || event -> xclient.data.l[0] == NXStartSplitNotify || event -> xclient.data.l[0] == NXCommitSplitNotify || event -> xclient.data.l[0] == NXEndSplitNotify || event -> xclient.data.l[0] == NXEmptySplitNotify) && event -> xclient.window == 0 && event -> xclient.message_type == 0 && event -> xclient.format == 32); } #endif #define USE_FINISH_SPLIT void nxagentWaitWakeupBySplit(ClientPtr client) { #ifdef TEST if (nxagentNeedWakeupBySplit(client) == 0) { fprintf(stderr, "++++++nxagentWaitWakeupBySplit: WARNING! The client [%d] is already awake.\n", client -> index); } fprintf(stderr, "++++++nxagentWaitWakeupBySplit: Going to wait for the client [%d].\n", client -> index); #endif /* * Be sure we intercept an I/O error * as well as an interrupt. */ #ifdef USE_FINISH_SPLIT NXFinishSplit(nxagentDisplay, client -> index); #endif NXFlushDisplay(nxagentDisplay, NXFlushBuffer); for (;;) { /* * Can we handle all the possible events here * or we need to select only the split events? * Handling all the possible events would pre- * empt the queue and make a better use of the * link. */ #ifdef WAIT_ALL_EVENTS nxagentDispatchEvents(NULL); #else nxagentDispatchEvents(nxagentWaitWakeupBySplitPredicate); #endif if (nxagentNeedWakeupBySplit(client) == 0 || NXDisplayError(nxagentDisplay) == 1) { #ifdef TEST if (nxagentNeedWakeupBySplit(client) == 0) { fprintf(stderr, "++++++nxagentWaitWakeupBySplit: Client [%d] can now run.\n", client -> index); } else { fprintf(stderr, "++++++nxagentWaitWakeupBySplit: WARNING! Display error " "detected waiting for restart.\n"); } #endif return; } #ifdef TEST fprintf(stderr, "++++++nxagentWaitWakeupBySplit: Yielding control to the NX transport.\n"); #endif nxagentWaitEvents(nxagentDisplay, NULL); } } int nxagentSuspendBySplit(ClientPtr client) { /* FIXME: Should record a serial number for the client, so that the client is not restarted because of an end of split of a previous client with the same index. */ if (client -> index < MAX_CONNECTIONS) { if (nxagentNeedWakeup(client) == 0) { #ifdef TEST fprintf(stderr, "++++++nxagentSuspendBySplit: Suspending client [%d] with agent sequence [%ld].\n", client -> index, NextRequest(nxagentDisplay) - 1); #endif if (client -> clientGone == 0) { #ifdef TEST fprintf(stderr, "++++++nxagentSuspendBySplit: Client [%d] suspended.\n", client -> index); #endif IgnoreClient(client); } } #ifdef TEST else { fprintf(stderr, "++++++nxagentSuspendBySplit: WARNING! Client [%d] already ignored with state [%x].\n", client -> index, nxagentClientPriv(client) -> clientState); } #endif nxagentClientPriv(client) -> clientState |= SleepingBySplit; return 1; } #ifdef WARNING fprintf(stderr, "++++++nxagentSuspendBySplit: WARNING! Invalid client [%d] provided to function.\n", client -> index); #endif return -1; } int nxagentWakeupBySplit(ClientPtr client) { /* FIXME: Should record a serial number for the client, so that the client is not restarted because of the end of the split for a previous client with the same index. */ if (client -> index < MAX_CONNECTIONS) { nxagentClientPriv(client) -> clientState &= ~SleepingBySplit; if (nxagentNeedWakeup(client) == 0) { #ifdef TEST fprintf(stderr, "++++++nxagentWakeupBySplit: Resuming client [%d] with agent sequence [%ld].\n", client -> index, NextRequest(nxagentDisplay) - 1); #endif if (client -> clientGone == 0) { AttendClient(client); } } #ifdef TEST else { fprintf(stderr, "++++++nxagentWakeupBySplit: WARNING! Client [%d] still suspended with state [%x].\n", client -> index, nxagentClientPriv(client) -> clientState); } #endif return 1; } #ifdef WARNING fprintf(stderr, "++++++nxagentWakeupBySplit: WARNING! Invalid client [%d] provided to function.\n", client -> index); #endif return -1; } #ifdef CHECK_RESTARTED_CLIENTS void nxagentCheckRestartedClients(struct timeval **timeout) { static struct timeval zero; int i; /* * If any of the restarted clients had requests * in input we'll need to enter the select with * a null timeout, or we will block until any * other client becomes available. */ for (i = 1; i < currentMaxClients; i++) { if (clients[i] != NULL && clients[i] -> osPrivate != NULL && nxagentNeedWakeup(clients[i]) == 0) { int fd = ((OsCommPtr) clients[i] -> osPrivate) -> fd; if (FD_ISSET(fd, &ClientsWithInput)) { #ifdef WARNING fprintf(stderr, "nxagentCheckRestartedClients: WARNING! Client [%d] with fd [%d] has input.\n", clients[i] -> index, fd); #endif #ifdef DEBUG fprintf(stderr, "nxagentCheckRestartedClients: Setting a null timeout with former timeout [%ld] Ms.\n", (*timeout) -> tv_sec * 1000 + (*timeout) -> tv_usec / 1000); #endif if (*timeout != NULL) { (*timeout) -> tv_sec = 0; (*timeout) -> tv_usec = 0; } else { zero.tv_sec = 0; zero.tv_usec = 0; *timeout = &zero; } } } } } #endif