1/* 2 * tkUnixFocus.c -- 3 * 4 * This file contains platform specific procedures that manage 5 * focus for Tk. 6 * 7 * Copyright (c) 1997 Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution 10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id: tkUnixFocus.c,v 1.3 1999/04/16 01:51:46 stanton Exp $ 13 */ 14 15#include "tkInt.h" 16#include "tkPort.h" 17#include "tkUnixInt.h" 18 19 20/* 21 *---------------------------------------------------------------------- 22 * 23 * TkpChangeFocus -- 24 * 25 * This procedure is invoked to move the official X focus from 26 * one window to another. 27 * 28 * Results: 29 * The return value is the serial number of the command that 30 * changed the focus. It may be needed by the caller to filter 31 * out focus change events that were queued before the command. 32 * If the procedure doesn't actually change the focus then 33 * it returns 0. 34 * 35 * Side effects: 36 * The official X focus window changes; the application's focus 37 * window isn't changed by this procedure. 38 * 39 *---------------------------------------------------------------------- 40 */ 41 42int 43TkpChangeFocus(winPtr, force) 44 TkWindow *winPtr; /* Window that is to receive the X focus. */ 45 int force; /* Non-zero means claim the focus even 46 * if it didn't originally belong to 47 * topLevelPtr's application. */ 48{ 49 TkDisplay *dispPtr = winPtr->dispPtr; 50 Tk_ErrorHandler errHandler; 51 Window window, root, parent, *children; 52 unsigned int numChildren, serial; 53 TkWindow *winPtr2; 54 int dummy; 55 56 /* 57 * Don't set the X focus to a window that's marked 58 * override-redirect. This is a hack to avoid problems with menus 59 * under olvwm: if we move the focus then the focus can get lost 60 * during keyboard traversal. Fortunately, we don't really need to 61 * move the focus for menus: events will still find their way to the 62 * focus window, and menus aren't decorated anyway so the window 63 * manager doesn't need to hear about the focus change in order to 64 * redecorate the menu. 65 */ 66 67 serial = 0; 68 if (winPtr->atts.override_redirect) { 69 return serial; 70 } 71 72 /* 73 * Check to make sure that the focus is still in one of the windows 74 * of this application or one of their descendants. Furthermore, 75 * grab the server to make sure that the focus doesn't change in the 76 * middle of this operation. 77 */ 78 79 XGrabServer(dispPtr->display); 80 if (!force) { 81 /* 82 * Find the focus window, then see if it or one of its ancestors 83 * is a window in our application (it's possible that the focus 84 * window is in an embedded application, which may or may not be 85 * in the same process. 86 */ 87 88 XGetInputFocus(dispPtr->display, &window, &dummy); 89 while (1) { 90 winPtr2 = (TkWindow *) Tk_IdToWindow(dispPtr->display, window); 91 if ((winPtr2 != NULL) && (winPtr2->mainPtr == winPtr->mainPtr)) { 92 break; 93 } 94 if ((window == PointerRoot) || (window == None)) { 95 goto done; 96 } 97 XQueryTree(dispPtr->display, window, &root, &parent, &children, 98 &numChildren); 99 if (children != NULL) { 100 XFree((void *) children); 101 } 102 if (parent == root) { 103 goto done; 104 } 105 window = parent; 106 } 107 } 108 109 /* 110 * Tell X to change the focus. Ignore errors that occur when changing 111 * the focus: it is still possible that the window we're focussing 112 * to could have gotten unmapped, which will generate an error. 113 */ 114 115 errHandler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1, 116 (Tk_ErrorProc *) NULL, (ClientData) NULL); 117 if (winPtr->window == None) { 118 panic("ChangeXFocus got null X window"); 119 } 120 XSetInputFocus(dispPtr->display, winPtr->window, RevertToParent, 121 CurrentTime); 122 Tk_DeleteErrorHandler(errHandler); 123 124 /* 125 * Remember the current serial number for the X server and issue 126 * a dummy server request. This marks the position at which we 127 * changed the focus, so we can distinguish FocusIn and FocusOut 128 * events on either side of the mark. 129 */ 130 131 serial = NextRequest(winPtr->display); 132 XNoOp(winPtr->display); 133 134 done: 135 XUngrabServer(dispPtr->display); 136 137 /* 138 * After ungrabbing the server, it's important to flush the output 139 * immediately so that the server sees the ungrab command. Otherwise 140 * we might do something else that needs to communicate with the 141 * server (such as invoking a subprocess that needs to do I/O to 142 * the screen); if the ungrab command is still sitting in our 143 * output buffer, we could deadlock. 144 */ 145 146 XFlush(dispPtr->display); 147 return serial; 148} 149