2 * Copyright (c) 1993, 2015, Oracle and/or its affiliates. All rights reserved.
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
25 /************************************************************
26 Basic boilerplate extension.
28 This file also contains the code to make the priocntl on behalf
31 Note that ChangePriority also sets the last client with focus
32 to the normal priority.
34 If there are knobs to be added to the system, for say the nice
35 values for the IA class, they would be added here.
36 ********************************************************/
38 /* THIS IS NOT AN X CONSORTIUM STANDARD */
40 #ifdef HAVE_DIX_CONFIG_H
41 #include <dix-config.h>
45 #include <sys/types.h>
48 #include <sys/priocntl.h>
49 #include <sys/iapriocntl.h>
54 #include <X11/Xproto.h>
56 #include "dixstruct.h"
57 #include "windowstr.h"
59 #include "extnsionst.h"
61 #include <X11/extensions/interactive.h>
62 #include <X11/Xfuncproto.h>
66 #include "interactive_srv.h"
68 static int ProcIADispatch(ClientPtr client
);
69 static int SProcIADispatch(ClientPtr client
);
70 static int ProcIASetProcessInfo(ClientPtr client
);
71 static int SProcIASetProcessInfo(ClientPtr client
);
72 static int ProcIAGetProcessInfo(ClientPtr client
);
73 static int SProcIAGetProcessInfo(ClientPtr client
);
74 static int ProcIAQueryVersion(ClientPtr client
);
75 static int SProcIAQueryVersion(ClientPtr client
);
76 static void IACloseDown(ExtensionEntry
*ext
);
77 static void IAClientStateChange(CallbackListPtr
*pcbl
, void *nulldata
,
80 static int InitializeClass(void);
81 static void SetIAPrivate(int *);
82 static void ChangeInteractive(ClientPtr
);
83 static int SetPriority(const ClientProcessPtr
, int);
84 static void ChangePriority(register ClientPtr client
);
86 static int SetClientPrivate(ClientPtr client
, ConnectionPidPtr stuff
,
88 static void FreeProcessList(IAClientPrivatePtr priv
);
89 /* static int LocalConnection(OsCommPtr); */
90 static int PidSetEqual(ClientProcessPtr
, ClientProcessPtr
);
92 static int IAWrapProcVectors(void);
93 static int IAUnwrapProcVectors(void);
95 static CARD32
IAInitTimerCall(OsTimerPtr timer
, CARD32 now
, void *arg
);
97 static iaclass_t IAClass
;
99 static ClientProcessPtr LastPids
= NULL
;
100 static int ia_nice
= IA_BOOST
;
101 static Bool InteractiveOS
= TRUE
;
102 static unsigned long IAExtensionGeneration
= 0;
103 static OsTimerPtr IAInitTimer
= NULL
;
104 static int (* IASavedProcVector
[256]) (ClientPtr client
);
106 static DevPrivateKeyRec IAPrivKeyRec
;
107 #define IAPrivKey (&IAPrivKeyRec)
109 #define GetIAClient(pClient) \
110 ((IAClientPrivatePtr) dixLookupPrivate(&(pClient)->devPrivates, IAPrivKey))
112 static inline ClientProcessPtr
113 GetConnectionPids(ClientPtr pClient
)
115 IAClientPrivatePtr priv
= GetIAClient(pClient
);
120 return priv
->process
;
124 /* Set via xorg.conf option in loadable module */
125 int IADebugLevel
= 0;
127 #define IA_DEBUG(level, func) \
128 if (IADebugLevel >= level) { func; } else (void)(0)
130 #define IA_DEBUG_BASIC 1
131 #define IA_DEBUG_PRIOCNTL 3
134 IAExtensionInit(void)
136 ConnectionPidRec myPid
= P_MYID
;
137 ClientProcessRec myProc
= { 1, &myPid
, FALSE
};
139 IA_DEBUG(IA_DEBUG_BASIC
,
140 LogMessage(X_INFO
, "SolarisIA: Initializing (generation %ld)\n",
141 IAExtensionGeneration
));
143 if (IAExtensionGeneration
== serverGeneration
)
146 InteractiveOS
= FALSE
;
148 if (InitializeClass() != Success
)
151 if (SetPriority(&myProc
, SET_INTERACTIVE
) != Success
)
154 if (SetPriority(&myProc
, SET_PRIORITY
) != Success
)
157 if (!dixRegisterPrivateKey(&IAPrivKeyRec
, PRIVATE_CLIENT
, 0))
160 if (!AddCallback(&ClientStateCallback
, IAClientStateChange
, NULL
))
163 if (IAWrapProcVectors() != 0)
166 if (!AddExtension(IANAME
, IANumberEvents
, IANumberErrors
,
167 ProcIADispatch
, SProcIADispatch
,
168 IACloseDown
, StandardMinorOpcode
))
171 /* InitExtensions is called before InitClientPrivates(serverClient)
172 so we set this timer to callback as soon as we hit WaitForSomething
173 to initialize the serverClient */
174 IAInitTimer
= TimerSet(IAInitTimer
, 0, 1, IAInitTimerCall
, NULL
);
176 InteractiveOS
= TRUE
;
177 IAExtensionGeneration
= serverGeneration
;
179 IA_DEBUG(IA_DEBUG_BASIC
,
181 "SolarisIA: Finished initializing (generation %ld)\n",
182 IAExtensionGeneration
));
185 /* Allocate client private structure for this client */
187 IAInitClientPrivate(ClientPtr pClient
)
189 IAClientPrivatePtr priv
;
191 priv
= GetIAClient(pClient
);
196 priv
= malloc(sizeof(IAClientPrivateRec
));
201 priv
->process
= NULL
;
204 dixSetPrivate(&(pClient
)->devPrivates
, IAPrivKey
, priv
);
209 /* Called when we first hit WaitForSomething to initialize serverClient */
211 IAInitTimerCall(OsTimerPtr timer
, CARD32 now
, void *arg
)
213 ConnectionPidRec serverPid
;
215 if (InteractiveOS
!= TRUE
)
218 IAInitClientPrivate(serverClient
);
220 serverPid
= getpid();
221 SetClientPrivate(serverClient
, &serverPid
, 1);
223 ChangePriority(serverClient
);
227 /* Called when new client connects or existing client disconnects */
229 IAClientStateChange(CallbackListPtr
*pcbl
, void *nulldata
, void *calldata
)
231 NewClientInfoRec
*pci
= (NewClientInfoRec
*)calldata
;
232 ClientPtr pClient
= pci
->client
;
233 ClientProcessPtr CurrentPids
;
234 IAClientPrivatePtr priv
;
235 LocalClientCredRec
*lcc
;
237 switch (pClient
->clientState
) {
238 case ClientStateGone
:
239 case ClientStateRetained
:
240 priv
= GetIAClient(pClient
);
244 CurrentPids
= priv
->process
;
247 IA_DEBUG(IA_DEBUG_BASIC
,
249 "SolarisIA: WindowManager closed (pid %d)\n",
250 (CurrentPids
&& CurrentPids
->pids
) ?
251 CurrentPids
->pids
[0] : -1));
254 if (CurrentPids
&& CurrentPids
->boosted
) {
255 SetPriority(CurrentPids
, UNSET_PRIORITY
);
258 if (CurrentPids
&& LastPids
&& PidSetEqual(CurrentPids
, LastPids
)) {
262 FreeProcessList(priv
);
264 dixSetPrivate(&(pClient
)->devPrivates
, IAPrivKey
, NULL
);
267 case ClientStateInitial
:
268 IAInitClientPrivate(pClient
);
270 ConnectionPidRec clientPid
= GetClientPid(pClient
);
271 if (clientPid
!= -1) {
272 SetClientPrivate(pClient
, &clientPid
, 1);
273 ChangeInteractive(pClient
);
285 ProcIADispatch (ClientPtr client
)
290 case X_IAQueryVersion
:
291 return ProcIAQueryVersion(client
);
292 case X_IASetProcessInfo
:
293 return ProcIASetProcessInfo(client
);
294 case X_IAGetProcessInfo
:
295 return ProcIAGetProcessInfo(client
);
302 ProcIAQueryVersion(ClientPtr client
)
304 REQUEST(xIAQueryVersionReq
);
305 xIAQueryVersionReply rep
;
307 REQUEST_SIZE_MATCH(xIAQueryVersionReq
);
310 rep
.sequenceNumber
= client
->sequence
;
311 rep
.majorVersion
= IA_MAJOR_VERSION
;
312 rep
.minorVersion
= IA_MINOR_VERSION
;
313 WriteToClient(client
, sizeof(xIAQueryVersionReply
), (char *)&rep
);
314 return (client
->noClientException
);
318 ProcIASetProcessInfo(ClientPtr client
)
321 REQUEST(xIASetProcessInfoReq
);
323 static uid_t ServerUid
= (uid_t
)-1;
325 REQUEST_AT_LEAST_SIZE(xIASetProcessInfoReq
);
327 if (ServerUid
== (uid_t
)-1)
330 if ((stuff
->flags
& INTERACTIVE_INFO
) &&
331 (stuff
->uid
==ServerUid
|| ServerUid
==0 || stuff
->uid
==0) &&
333 length
= stuff
->length
- (sizeof(xIASetProcessInfoReq
)>>2);
334 SetClientPrivate(client
, (ConnectionPidPtr
)&stuff
[1], length
);
335 ChangeInteractive(client
);
338 if ((stuff
->flags
& INTERACTIVE_SETTING
) &&
339 (stuff
->uid
==ServerUid
|| ServerUid
==0) &&
341 SetIAPrivate((int *)&stuff
[1]);
344 return (client
->noClientException
);
348 ProcIAGetProcessInfo(ClientPtr client
)
350 IAClientPrivatePtr priv
;
351 ClientProcessPtr CurrentPids
;
352 REQUEST(xIAGetProcessInfoReq
);
353 xIAGetProcessInfoReply rep
;
354 register int length
= 0;
356 int32_t *write_back
= NULL
;
358 REQUEST_SIZE_MATCH(xIAGetProcessInfoReq
);
361 rep
.sequenceNumber
= client
->sequence
;
363 if (stuff
->flags
& INTERACTIVE_INFO
) {
364 priv
= GetIAClient(client
);
365 if ( (priv
== NULL
) || (priv
->process
== NULL
) ) {
368 CurrentPids
= priv
->process
;
369 rep
.count
= CurrentPids
->count
;
370 length
= rep
.count
<< 2;
371 rep
.length
= rep
.count
;
372 write_back
= malloc((size_t) rep
.count
* sizeof(int32_t));
373 if (write_back
== NULL
)
376 int32_t *tmp
= write_back
;
378 for (i
= 0; i
< CurrentPids
->count
; ++i
)
379 *tmp
++ = *CurrentPids
->pids
++;
383 if (stuff
->flags
& INTERACTIVE_SETTING
) {
385 length
=rep
.count
<< 2;
386 rep
.length
= rep
.count
;
388 write_back
= malloc(sizeof(int32_t));
389 if (write_back
== NULL
)
392 *write_back
= (int32_t) ia_nice
;
395 WriteToClient(client
, sizeof(xIAGetProcessInfoReply
), (char *)&rep
);
397 if (rep
.length
> 0) {
398 (void) WriteToClient(client
, (int) sizeof(int32_t) * rep
.count
,
399 (char *) write_back
);
403 return (client
->noClientException
);
407 IACloseDown(ExtensionEntry
*ext
)
409 InteractiveOS
= FALSE
;
411 IAUnwrapProcVectors();
413 DeleteCallback(&ClientStateCallback
, IAClientStateChange
, NULL
);
417 The SProc* functions are here for completeness. They should never get
418 called. But since they do the server has to eat the request and
419 return thanks for sharing.
424 SProcIADispatch (ClientPtr client
)
429 case X_IAQueryVersion
:
430 return SProcIAQueryVersion(client
);
431 case X_IASetProcessInfo
:
432 return SProcIASetProcessInfo(client
);
433 case X_IAGetProcessInfo
:
434 return SProcIAGetProcessInfo(client
);
442 SProcIAQueryVersion(ClientPtr client
)
444 REQUEST_SIZE_MATCH(xIAQueryVersionReq
);
445 return (client
->noClientException
);
450 SProcIASetProcessInfo(ClientPtr client
)
452 REQUEST(xIASetProcessInfoReq
);
453 REQUEST_AT_LEAST_SIZE(xIASetProcessInfoReq
);
455 return (client
->noClientException
);
460 SProcIAGetProcessInfo(ClientPtr client
)
462 REQUEST(xIAGetProcessInfoReq
);
463 REQUEST_SIZE_MATCH(xIAGetProcessInfoReq
);
465 return (client
->noClientException
);
469 ChangeInteractive(ClientPtr client
)
471 ClientProcessPtr CurrentPids
= GetConnectionPids(client
);
473 if (InteractiveOS
==FALSE
)
476 if (!CurrentPids
|| !CurrentPids
->pids
)
479 SetPriority(CurrentPids
, SET_INTERACTIVE
);
483 * Loop through pids associated with client. Magically make last focus
484 * group go non-interactive -IA_BOOST.
487 ChangePriority(register ClientPtr client
)
489 IAClientPrivatePtr priv
= GetIAClient(client
);
490 ClientProcessPtr CurrentPids
= (priv
== NULL
? NULL
: priv
->process
);
492 if (CurrentPids
&& LastPids
&& PidSetEqual(CurrentPids
, LastPids
)) {
493 /* Shortcut. Focus changed between two windows with same pid */
497 /* Remove priority boost for last focus group */
499 SetPriority(LastPids
, UNSET_PRIORITY
);
503 /* If no pid info for current client, then we're done here.
504 * This can happen if we have a remote client with focus or if the client
505 * is statically linked or if it is using a down rev version of libX11.
507 if ( (CurrentPids
== NULL
) || (CurrentPids
->count
== 0) ||
508 (CurrentPids
->pids
== NULL
) ) {
512 /* Set the priority boost if it isn't already active */
513 if (!CurrentPids
->boosted
) {
514 SetPriority(CurrentPids
, SET_PRIORITY
);
517 /* Make sure server or wmgr isn't unset by testing for them, so
518 * that LastPids is never set to point to the server or wmgr pid.
520 if ((client
->index
!= serverClient
->index
) && (priv
->wmgr
!= TRUE
)) {
521 LastPids
= CurrentPids
;
526 InitializeClass(void)
530 /* Get TS class information */
531 strcpy (pcinfo
.pc_clname
, "TS");
532 priocntl(0, 0, PC_GETCID
, (caddr_t
)&pcinfo
);
533 TScid
= pcinfo
.pc_cid
;
535 /* Get IA class information */
536 strcpy (pcinfo
.pc_clname
, "IA");
537 if ((priocntl(0, 0, PC_GETCID
, (caddr_t
)&pcinfo
)) == -1)
540 IAClass
.pc_cid
= pcinfo
.pc_cid
;
541 ((iaparms_t
*)IAClass
.pc_clparms
)->ia_uprilim
= IA_NOCHANGE
;
542 ((iaparms_t
*)IAClass
.pc_clparms
)->ia_upri
= IA_NOCHANGE
;
548 SetPriority(const ClientProcessPtr cpp
, int cmd
)
552 gid_t usr_egid
= getegid();
555 if ( (cpp
== NULL
) || (cpp
->pids
== NULL
) || (cpp
->count
== 0) ) {
559 if ( setegid(0) < 0 ) {
560 ErrorF("Error in setting egid to 0: %s\n", strerror(errno
));
563 for (i
= 0; i
< cpp
->count
; i
++) {
564 id_t pid
= cpp
->pids
[i
];
566 pcinfo
.pc_cid
= PC_CLNULL
;
567 if ((priocntl(P_PID
, pid
, PC_GETPARMS
, (caddr_t
)&pcinfo
)) < 0) {
568 if ( setegid(usr_egid
) < 0 ) {
569 ErrorF("Error in resetting egid: %s\n", strerror(errno
));
571 return ~Success
; /* Scary time; punt */
574 /* If process is in TS or IA class we can safely set parameters */
575 if ((pcinfo
.pc_cid
== IAClass
.pc_cid
) || (pcinfo
.pc_cid
== TScid
)) {
579 ((iaparms_t
*)IAClass
.pc_clparms
)->ia_mode
=IA_INTERACTIVE_OFF
;
582 ((iaparms_t
*)IAClass
.pc_clparms
)->ia_mode
=IA_SET_INTERACTIVE
;
584 case SET_INTERACTIVE
:
585 /* If this returns true, the process is already in the */
586 /* IA class, so we don't need to update it. */
587 if ( pcinfo
.pc_cid
== IAClass
.pc_cid
)
590 ((iaparms_t
*)IAClass
.pc_clparms
)->ia_mode
=IA_INTERACTIVE_OFF
;
594 if (priocntl(P_PID
, pid
, PC_SETPARMS
, (caddr_t
)&IAClass
) == -1)
599 IA_DEBUG(IA_DEBUG_PRIOCNTL
,
604 case UNSET_PRIORITY
: cmdmsg
= "UNSET_PRIORITY"; break;
605 case SET_PRIORITY
: cmdmsg
= "SET_PRIORITY"; break;
606 case SET_INTERACTIVE
: cmdmsg
= "SET_INTERACTIVE"; break;
607 default: cmdmsg
= "UNKNOWN_CMD!!!"; break;
609 LogMessage(X_INFO
, "SolarisIA: SetPriority(%d, %s): %s\n",
611 (ret
== Success
) ? "succeeeded" : "failed");
616 if (setegid(usr_egid
) < 0)
617 ErrorF("Error in resetting egid: %s\n", strerror(errno
));
619 if (ret
== Success
) {
620 if (cmd
== SET_PRIORITY
) {
622 } else if (cmd
== UNSET_PRIORITY
) {
623 cpp
->boosted
= FALSE
;
631 SetIAPrivate(int *value
)
636 /*****************************************************************************
637 * Various utility functions - in Xsun these lived in Xserver/os/process.c
640 /* In Xsun we used the osPrivate in OsCommPtr, so this was SetOsPrivate. */
642 SetClientPrivate(ClientPtr client
, ConnectionPidPtr stuff
, int length
)
644 ClientProcessPtr cpp
;
645 IAClientPrivatePtr priv
;
647 priv
= GetIAClient(client
);
649 IAInitClientPrivate(client
);
651 FreeProcessList(priv
);
654 cpp
= malloc(sizeof(ClientProcessRec
));
659 cpp
->pids
= malloc(sizeof(ConnectionPidRec
)*length
);
661 if (cpp
->pids
== NULL
) {
667 memcpy(cpp
->pids
, stuff
, sizeof(ConnectionPidRec
)*length
);
668 cpp
->boosted
= FALSE
;
675 FreeProcessList(IAClientPrivatePtr priv
)
677 ClientProcessPtr cpp
= priv
->process
;
681 priv
->process
= NULL
;
686 if (cpp
->pids
!= NULL
)
693 Check to see that all in current (a) are in last (b).
694 And that a and b have the same number of members in the set.
697 PidSetEqual(ClientProcessPtr a
, ClientProcessPtr b
)
700 int count
= a
->count
;
703 if (a
->count
!= b
->count
) {
704 return 0; /* definately NOT the same set */
707 for (aN
= 0; aN
< count
; aN
++) {
709 for (bN
= 0; bN
< count
; bN
++) {
710 if (a
->pids
[aN
] == b
->pids
[bN
]) {
723 /*****************************************************************************
724 * Wrappers for normal procs - in Xsun we modified the original procs directly
725 * in dix, but here we wrap them for a small performance loss but a large
726 * increase in maintainability and ease of porting to new releases.
730 IAProcSetInputFocus(ClientPtr client
)
735 REQUEST(xSetInputFocusReq
);
737 res
= (*IASavedProcVector
[X_SetInputFocus
])(client
);
738 if ((res
!= Success
) || (InteractiveOS
!= TRUE
))
741 focusID
= stuff
->focus
;
745 focusWin
= NullWindow
;
748 focusWin
= PointerRootWin
;
751 res
= dixLookupWindow(&focusWin
, focusID
, client
, DixReadAccess
);
756 if ((focusWin
!= NullWindow
) && (focusWin
!= PointerRootWin
)) {
757 register ClientPtr requestee
= wClient(focusWin
);
758 ChangePriority(requestee
);
765 IAProcSendEvent(ClientPtr client
)
768 REQUEST(xSendEventReq
);
770 res
= (*IASavedProcVector
[X_SendEvent
])(client
);
771 if ((res
!= Success
) || (InteractiveOS
!= TRUE
))
774 if ((InteractiveOS
==TRUE
) &&
775 (GetIAClient(client
)->wmgr
== TRUE
) &&
776 (stuff
->event
.u
.u
.type
== ClientMessage
) &&
777 (stuff
->event
.u
.u
.detail
== 32) ) {
779 register ClientPtr requestee
;
780 WindowPtr pWin
= NULL
;
781 DeviceIntPtr pPtr
= PickPointer(client
);
783 if (stuff
->destination
== PointerWindow
)
784 pWin
= GetSpriteWindow(pPtr
);
785 else if (stuff
->destination
== InputFocus
)
787 WindowPtr inputFocus
= inputInfo
.keyboard
->focus
->win
;
789 if (inputFocus
== NoneWin
)
792 /* If the input focus is PointerRootWin, send the event to where
793 the pointer is if possible, then perhaps propogate up to root. */
794 if (inputFocus
== PointerRootWin
)
795 inputFocus
= GetCurrentRootWindow(pPtr
);
797 if (IsParent(inputFocus
, GetSpriteWindow(pPtr
)))
798 pWin
= GetSpriteWindow(pPtr
);
804 res
= dixLookupWindow(&pWin
, stuff
->destination
, client
,
814 requestee
= wClient(pWin
);
815 ChangePriority(requestee
);
821 IAProcChangeWindowAttributes(ClientPtr client
)
823 REQUEST(xChangeWindowAttributesReq
);
825 if ((InteractiveOS
==TRUE
) && (stuff
->valueMask
& CWEventMask
) &&
826 (GetIAClient(client
)->wmgr
== FALSE
) ) {
828 register XID
*pVlist
= (XID
*) &stuff
[1];
829 register Mask tmask
= stuff
->valueMask
;
830 register Mask index2
= 0;
833 index2
= (Mask
) lowbit (tmask
);
835 if (index2
== CWEventMask
) {
841 if ((index2
== CWEventMask
) && (*pVlist
& SubstructureRedirectMask
)) {
842 IA_DEBUG(IA_DEBUG_BASIC
,
843 ClientProcessPtr CurrentPids
=GetConnectionPids(client
);
846 "SolarisIA: WindowManager detected (pid %d)\n",
847 (CurrentPids
&& CurrentPids
->pids
) ?
848 CurrentPids
->pids
[0] : -1));
850 GetIAClient(client
)->wmgr
= TRUE
;
851 ChangePriority(client
);
856 return (*IASavedProcVector
[X_ChangeWindowAttributes
])(client
);
861 IAWrapProcVectors(void)
863 IASavedProcVector
[X_SetInputFocus
] = ProcVector
[X_SetInputFocus
];
864 ProcVector
[X_SetInputFocus
] = IAProcSetInputFocus
;
866 IASavedProcVector
[X_SendEvent
] = ProcVector
[X_SendEvent
];
867 ProcVector
[X_SendEvent
] = IAProcSendEvent
;
869 IASavedProcVector
[X_ChangeWindowAttributes
]
870 = ProcVector
[X_ChangeWindowAttributes
];
871 ProcVector
[X_ChangeWindowAttributes
] = IAProcChangeWindowAttributes
;
877 IAUnwrapProcVectors(void)
879 ProcVector
[X_SetInputFocus
] = IASavedProcVector
[X_SetInputFocus
];
880 ProcVector
[X_SendEvent
] = IASavedProcVector
[X_SendEvent
];
881 ProcVector
[X_ChangeWindowAttributes
] = IASavedProcVector
[X_ChangeWindowAttributes
];