/[dtapublic]/to_be_filed/sf_code/esrgpcpj/shared/tk_base/tkfocus.c
ViewVC logotype

Contents of /to_be_filed/sf_code/esrgpcpj/shared/tk_base/tkfocus.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 29 - (show annotations) (download)
Sat Oct 8 07:08:47 2016 UTC (7 years, 5 months ago) by dashley
File MIME type: text/plain
File size: 33783 byte(s)
Directories relocated.
1 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkfocus.c,v 1.1.1.1 2001/06/13 05:00:25 dtashley Exp $ */
2
3 /*
4 * tkFocus.c --
5 *
6 * This file contains procedures that manage the input
7 * focus for Tk.
8 *
9 * Copyright (c) 1990-1994 The Regents of the University of California.
10 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
11 *
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 *
15 * RCS: @(#) $Id: tkfocus.c,v 1.1.1.1 2001/06/13 05:00:25 dtashley Exp $
16 */
17
18 #include "tkInt.h"
19 #include "tkPort.h"
20
21
22 /*
23 * For each top-level window that has ever received the focus, there
24 * is a record of the following type:
25 */
26
27 typedef struct TkToplevelFocusInfo {
28 TkWindow *topLevelPtr; /* Information about top-level window. */
29 TkWindow *focusWinPtr; /* The next time the focus comes to this
30 * top-level, it will be given to this
31 * window. */
32 struct TkToplevelFocusInfo *nextPtr;
33 /* Next in list of all toplevel focus records
34 * for a given application. */
35 } ToplevelFocusInfo;
36
37 /*
38 * One of the following structures exists for each display used by
39 * each application. These are linked together from the TkMainInfo
40 * structure. These structures are needed because it isn't
41 * sufficient to store a single piece of focus information in each
42 * display or in each application: we need the cross-product.
43 * There needs to be separate information for each display, because
44 * it's possible to have multiple focus windows active simultaneously
45 * on different displays. There also needs to be separate information
46 * for each application, because of embedding: if an embedded
47 * application has the focus, its container application also has
48 * the focus. Thus we keep a list of structures for each application:
49 * the same display can appear in structures for several applications
50 * at once.
51 */
52
53 typedef struct TkDisplayFocusInfo {
54 TkDisplay *dispPtr; /* Display that this information pertains
55 * to. */
56 struct TkWindow *focusWinPtr;
57 /* Window that currently has the focus for
58 * this application on this display, or NULL
59 * if none. */
60 struct TkWindow *focusOnMapPtr;
61 /* This points to a toplevel window that is
62 * supposed to receive the X input focus as
63 * soon as it is mapped (needed to handle the
64 * fact that X won't allow the focus on an
65 * unmapped window). NULL means no delayed
66 * focus op in progress for this display. */
67 int forceFocus; /* Associated with focusOnMapPtr: non-zero
68 * means claim the focus even if some other
69 * application currently has it. */
70 unsigned long focusSerial; /* Serial number of last request this
71 * application made to change the focus on
72 * this display. Used to identify stale
73 * focus notifications coming from the
74 * X server. */
75 struct TkDisplayFocusInfo *nextPtr;
76 /* Next in list of all display focus
77 * records for a given application. */
78 } DisplayFocusInfo;
79
80 /*
81 * The following magic value is stored in the "send_event" field of
82 * FocusIn and FocusOut events that are generated in this file. This
83 * allows us to separate "real" events coming from the server from
84 * those that we generated.
85 */
86
87 #define GENERATED_EVENT_MAGIC ((Bool) 0x547321ac)
88
89 /*
90 * Forward declarations for procedures defined in this file:
91 */
92
93
94 static DisplayFocusInfo *FindDisplayFocusInfo _ANSI_ARGS_((TkMainInfo *mainPtr,
95 TkDisplay *dispPtr));
96 static void FocusMapProc _ANSI_ARGS_((ClientData clientData,
97 XEvent *eventPtr));
98 static void GenerateFocusEvents _ANSI_ARGS_((TkWindow *sourcePtr,
99 TkWindow *destPtr));
100
101 /*
102 *--------------------------------------------------------------
103 *
104 * Tk_FocusObjCmd --
105 *
106 * This procedure is invoked to process the "focus" Tcl command.
107 * See the user documentation for details on what it does.
108 *
109 * Results:
110 * A standard Tcl result.
111 *
112 * Side effects:
113 * See the user documentation.
114 *
115 *--------------------------------------------------------------
116 */
117
118 int
119 Tk_FocusObjCmd(clientData, interp, objc, objv)
120 ClientData clientData; /* Main window associated with
121 * interpreter. */
122 Tcl_Interp *interp; /* Current interpreter. */
123 int objc; /* Number of arguments. */
124 Tcl_Obj *CONST objv[]; /* Argument objects. */
125 {
126 static char *focusOptions[] = {"-displayof", "-force", "-lastfor",
127 (char *) NULL};
128 Tk_Window tkwin = (Tk_Window) clientData;
129 TkWindow *winPtr = (TkWindow *) clientData;
130 TkWindow *newPtr, *focusWinPtr, *topLevelPtr;
131 ToplevelFocusInfo *tlFocusPtr;
132 char *windowName;
133 int index;
134
135 /*
136 * If invoked with no arguments, just return the current focus window.
137 */
138
139 if (objc == 1) {
140 focusWinPtr = TkGetFocusWin(winPtr);
141 if (focusWinPtr != NULL) {
142 Tcl_SetResult(interp, focusWinPtr->pathName, TCL_STATIC);
143 }
144 return TCL_OK;
145 }
146
147 /*
148 * If invoked with a single argument beginning with "." then focus
149 * on that window.
150 */
151
152 if (objc == 2) {
153 windowName = Tcl_GetStringFromObj(objv[1], (int *) NULL);
154
155 /*
156 * The empty string case exists for backwards compatibility.
157 */
158
159 if (windowName[0] == '\0') {
160 return TCL_OK;
161 }
162 if (windowName[0] == '.') {
163 newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin);
164 if (newPtr == NULL) {
165 return TCL_ERROR;
166 }
167 if (!(newPtr->flags & TK_ALREADY_DEAD)) {
168 TkSetFocusWin(newPtr, 0);
169 }
170 return TCL_OK;
171 }
172 }
173
174 if (Tcl_GetIndexFromObj(interp, objv[1], focusOptions, "option", 0,
175 &index) != TCL_OK) {
176 return TCL_ERROR;
177 }
178 if (objc != 3) {
179 Tcl_WrongNumArgs(interp, 2, objv, "window");
180 return TCL_ERROR;
181 }
182 switch (index) {
183 case 0: { /* -displayof */
184 windowName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
185 newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin);
186 if (newPtr == NULL) {
187 return TCL_ERROR;
188 }
189 newPtr = TkGetFocusWin(newPtr);
190 if (newPtr != NULL) {
191 Tcl_SetResult(interp, newPtr->pathName, TCL_STATIC);
192 }
193 break;
194 }
195 case 1: { /* -force */
196 windowName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
197
198 /*
199 * The empty string case exists for backwards compatibility.
200 */
201
202 if (windowName[0] == '\0') {
203 return TCL_OK;
204 }
205 newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin);
206 if (newPtr == NULL) {
207 return TCL_ERROR;
208 }
209 TkSetFocusWin(newPtr, 1);
210 break;
211 }
212 case 2: { /* -lastfor */
213 windowName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
214 newPtr = (TkWindow *) Tk_NameToWindow(interp, windowName, tkwin);
215 if (newPtr == NULL) {
216 return TCL_ERROR;
217 }
218 for (topLevelPtr = newPtr; topLevelPtr != NULL;
219 topLevelPtr = topLevelPtr->parentPtr) {
220 if (topLevelPtr->flags & TK_TOP_LEVEL) {
221 for (tlFocusPtr = newPtr->mainPtr->tlFocusPtr;
222 tlFocusPtr != NULL;
223 tlFocusPtr = tlFocusPtr->nextPtr) {
224 if (tlFocusPtr->topLevelPtr == topLevelPtr) {
225 Tcl_SetResult(interp,
226 tlFocusPtr->focusWinPtr->pathName,
227 TCL_STATIC);
228 return TCL_OK;
229 }
230 }
231 Tcl_SetResult(interp, topLevelPtr->pathName, TCL_STATIC);
232 return TCL_OK;
233 }
234 }
235 break;
236 }
237 default: {
238 panic("bad const entries to focusOptions in focus command");
239 }
240 }
241 return TCL_OK;
242 }
243
244 /*
245 *--------------------------------------------------------------
246 *
247 * TkFocusFilterEvent --
248 *
249 * This procedure is invoked by Tk_HandleEvent when it encounters
250 * a FocusIn, FocusOut, Enter, or Leave event.
251 *
252 * Results:
253 * A return value of 1 means that Tk_HandleEvent should process
254 * the event normally (i.e. event handlers should be invoked).
255 * A return value of 0 means that this event should be ignored.
256 *
257 * Side effects:
258 * Additional events may be generated, and the focus may switch.
259 *
260 *--------------------------------------------------------------
261 */
262
263 int
264 TkFocusFilterEvent(winPtr, eventPtr)
265 TkWindow *winPtr; /* Window that focus event is directed to. */
266 XEvent *eventPtr; /* FocusIn, FocusOut, Enter, or Leave
267 * event. */
268 {
269 /*
270 * Design notes: the window manager and X server work together to
271 * transfer the focus among top-level windows. This procedure takes
272 * care of transferring the focus from a top-level or wrapper window
273 * to the actual window within that top-level that has the focus.
274 * We do this by synthesizing X events to move the focus around.
275 * None of the FocusIn and FocusOut events generated by X are ever
276 * used outside of this procedure; only the synthesized events get
277 * through to the rest of the application. At one point (e.g.
278 * Tk4.0b1) Tk used to call X to move the focus from a top-level to
279 * one of its descendants, then just pass through the events
280 * generated by X. This approach didn't work very well, for a
281 * variety of reasons. For example, if X generates the events they
282 * go at the back of the event queue, which could cause problems if
283 * other things have already happened, such as moving the focus to
284 * yet another window.
285 */
286
287 ToplevelFocusInfo *tlFocusPtr;
288 DisplayFocusInfo *displayFocusPtr;
289 TkDisplay *dispPtr = winPtr->dispPtr;
290 TkWindow *newFocusPtr;
291 int retValue, delta;
292
293 /*
294 * If this was a generated event, just turn off the generated
295 * flag and pass the event through to Tk bindings.
296 */
297
298 if (eventPtr->xfocus.send_event == GENERATED_EVENT_MAGIC) {
299 eventPtr->xfocus.send_event = 0;
300 return 1;
301 }
302
303 /*
304 * Check for special events generated by embedded applications to
305 * request the input focus. If this is one of those events, make
306 * the change in focus and return without any additional processing
307 * of the event (note: the "detail" field of the event indicates
308 * whether to claim the focus even if we don't already have it).
309 */
310
311 if ((eventPtr->xfocus.mode == EMBEDDED_APP_WANTS_FOCUS)
312 && (eventPtr->type == FocusIn)) {
313 TkSetFocusWin(winPtr, eventPtr->xfocus.detail);
314 return 0;
315 }
316
317 /*
318 * This was not a generated event. We'll return 1 (so that the
319 * event will be processed) if it's an Enter or Leave event, and
320 * 0 (so that the event won't be processed) if it's a FocusIn or
321 * FocusOut event.
322 */
323
324 retValue = 0;
325 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
326 if (eventPtr->type == FocusIn) {
327 /*
328 * Skip FocusIn events that cause confusion
329 * NotifyVirtual and NotifyNonlinearVirtual - Virtual events occur
330 * on windows in between the origin and destination of the
331 * focus change. For FocusIn we may see this when focus
332 * goes into an embedded child. We don't care about this,
333 * although we may end up getting a NotifyPointer later.
334 * NotifyInferior - focus is coming to us from an embedded child.
335 * When focus is on an embeded focus, we still think we have
336 * the focus, too, so this message doesn't change our state.
337 * NotifyPointerRoot - should never happen because this is sent
338 * to the root window.
339 *
340 * Interesting FocusIn events are
341 * NotifyAncestor - focus is coming from our parent, probably the root.
342 * NotifyNonlinear - focus is coming from a different branch, probably
343 * another toplevel.
344 * NotifyPointer - implicit focus because of the mouse position.
345 * This is only interesting on toplevels, when it means that the
346 * focus has been set to the root window but the mouse is over
347 * this toplevel. We take the focus implicitly (probably no
348 * window manager)
349 */
350
351 if ((eventPtr->xfocus.detail == NotifyVirtual)
352 || (eventPtr->xfocus.detail == NotifyNonlinearVirtual)
353 || (eventPtr->xfocus.detail == NotifyPointerRoot)
354 || (eventPtr->xfocus.detail == NotifyInferior)) {
355 return retValue;
356 }
357 } else if (eventPtr->type == FocusOut) {
358 /*
359 * Skip FocusOut events that cause confusion.
360 * NotifyPointer - the pointer is in us or a child, and we are losing
361 * focus because of an XSetInputFocus. Other focus events
362 * will set our state properly.
363 * NotifyPointerRoot - should never happen because this is sent
364 * to the root window.
365 * NotifyInferior - focus leaving us for an embedded child. We
366 * retain a notion of focus when an embedded child has focus.
367 *
368 * Interesting events are:
369 * NotifyAncestor - focus is going to root.
370 * NotifyNonlinear - focus is going to another branch, probably
371 * another toplevel.
372 * NotifyVirtual, NotifyNonlinearVirtual - focus is passing through,
373 * and we need to make sure we track this.
374 */
375
376 if ((eventPtr->xfocus.detail == NotifyPointer)
377 || (eventPtr->xfocus.detail == NotifyPointerRoot)
378 || (eventPtr->xfocus.detail == NotifyInferior)) {
379 return retValue;
380 }
381 } else {
382 retValue = 1;
383 if (eventPtr->xcrossing.detail == NotifyInferior) {
384 return retValue;
385 }
386 }
387
388 /*
389 * If winPtr isn't a top-level window than just ignore the event.
390 */
391
392 winPtr = TkWmFocusToplevel(winPtr);
393 if (winPtr == NULL) {
394 return retValue;
395 }
396
397 /*
398 * If there is a grab in effect and this window is outside the
399 * grabbed tree, then ignore the event.
400 */
401
402 if (TkGrabState(winPtr) == TK_GRAB_EXCLUDED) {
403 return retValue;
404 }
405
406 /*
407 * It is possible that there were outstanding FocusIn and FocusOut
408 * events on their way to us at the time the focus was changed
409 * internally with the "focus" command. If so, these events could
410 * potentially cause us to lose the focus (switch it to the window
411 * of the last FocusIn event) even though the focus change occurred
412 * after those events. The following code detects this and ignores
413 * the stale events.
414 *
415 * Note: the focusSerial is only generated by TkpChangeFocus,
416 * whereas in Tk 4.2 there was always a nop marker generated.
417 */
418
419 delta = eventPtr->xfocus.serial - displayFocusPtr->focusSerial;
420 if (delta < 0) {
421 return retValue;
422 }
423
424 /*
425 * Find the ToplevelFocusInfo structure for the window, and make a new one
426 * if there isn't one already.
427 */
428
429 for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL;
430 tlFocusPtr = tlFocusPtr->nextPtr) {
431 if (tlFocusPtr->topLevelPtr == winPtr) {
432 break;
433 }
434 }
435 if (tlFocusPtr == NULL) {
436 tlFocusPtr = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo));
437 tlFocusPtr->topLevelPtr = tlFocusPtr->focusWinPtr = winPtr;
438 tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr;
439 winPtr->mainPtr->tlFocusPtr = tlFocusPtr;
440 }
441 newFocusPtr = tlFocusPtr->focusWinPtr;
442
443 if (eventPtr->type == FocusIn) {
444 GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr);
445 displayFocusPtr->focusWinPtr = newFocusPtr;
446 dispPtr->focusPtr = newFocusPtr;
447
448 /*
449 * NotifyPointer gets set when the focus has been set to the root window
450 * but we have the pointer. We'll treat this like an implicit
451 * focus in event so that upon Leave events we release focus.
452 */
453
454 if (!(winPtr->flags & TK_EMBEDDED)) {
455 if (eventPtr->xfocus.detail == NotifyPointer) {
456 dispPtr->implicitWinPtr = winPtr;
457 } else {
458 dispPtr->implicitWinPtr = NULL;
459 }
460 }
461 } else if (eventPtr->type == FocusOut) {
462 GenerateFocusEvents(displayFocusPtr->focusWinPtr, (TkWindow *) NULL);
463
464 /*
465 * Reset dispPtr->focusPtr, but only if it currently is the same
466 * as this application's focusWinPtr: this check is needed to
467 * handle embedded applications in the same process.
468 */
469
470 if (dispPtr->focusPtr == displayFocusPtr->focusWinPtr) {
471 dispPtr->focusPtr = NULL;
472 }
473 displayFocusPtr->focusWinPtr = NULL;
474 } else if (eventPtr->type == EnterNotify) {
475 /*
476 * If there is no window manager, or if the window manager isn't
477 * moving the focus around (e.g. the disgusting "NoTitleFocus"
478 * option has been selected in twm), then we won't get FocusIn
479 * or FocusOut events. Instead, the "focus" field will be set
480 * in an Enter event to indicate that we've already got the focus
481 * when the mouse enters the window (even though we didn't get
482 * a FocusIn event). Watch for this and grab the focus when it
483 * happens. Note: if this is an embedded application then don't
484 * accept the focus implicitly like this; the container
485 * application will give us the focus explicitly if it wants us
486 * to have it.
487 */
488
489 if (eventPtr->xcrossing.focus &&
490 (displayFocusPtr->focusWinPtr == NULL)
491 && !(winPtr->flags & TK_EMBEDDED)) {
492 if (dispPtr->focusDebug) {
493 printf("Focussed implicitly on %s\n",
494 newFocusPtr->pathName);
495 }
496
497 GenerateFocusEvents(displayFocusPtr->focusWinPtr, newFocusPtr);
498 displayFocusPtr->focusWinPtr = newFocusPtr;
499 dispPtr->implicitWinPtr = winPtr;
500 dispPtr->focusPtr = newFocusPtr;
501 }
502 } else if (eventPtr->type == LeaveNotify) {
503 /*
504 * If the pointer just left a window for which we automatically
505 * claimed the focus on enter, move the focus back to the root
506 * window, where it was before we claimed it above. Note:
507 * dispPtr->implicitWinPtr may not be the same as
508 * displayFocusPtr->focusWinPtr (e.g. because the "focus"
509 * command was used to redirect the focus after it arrived at
510 * dispPtr->implicitWinPtr)!! In addition, we generate events
511 * because the window manager won't give us a FocusOut event when
512 * we focus on the root.
513 */
514
515 if ((dispPtr->implicitWinPtr != NULL)
516 && !(winPtr->flags & TK_EMBEDDED)) {
517 if (dispPtr->focusDebug) {
518 printf("Defocussed implicit Async\n");
519 }
520 GenerateFocusEvents(displayFocusPtr->focusWinPtr,
521 (TkWindow *) NULL);
522 XSetInputFocus(dispPtr->display, PointerRoot, RevertToPointerRoot,
523 CurrentTime);
524 displayFocusPtr->focusWinPtr = NULL;
525 dispPtr->implicitWinPtr = NULL;
526 }
527 }
528 return retValue;
529 }
530
531 /*
532 *----------------------------------------------------------------------
533 *
534 * TkSetFocusWin --
535 *
536 * This procedure is invoked to change the focus window for a
537 * given display in a given application.
538 *
539 * Results:
540 * None.
541 *
542 * Side effects:
543 * Event handlers may be invoked to process the change of
544 * focus.
545 *
546 *----------------------------------------------------------------------
547 */
548
549 void
550 TkSetFocusWin(winPtr, force)
551 TkWindow *winPtr; /* Window that is to be the new focus for
552 * its display and application. */
553 int force; /* If non-zero, set the X focus to this
554 * window even if the application doesn't
555 * currently have the X focus. */
556 {
557 ToplevelFocusInfo *tlFocusPtr;
558 DisplayFocusInfo *displayFocusPtr;
559 TkWindow *topLevelPtr;
560 int allMapped, serial;
561
562 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
563
564 /*
565 * If force is set, we should make sure we grab the focus regardless
566 * of the current focus window since under Windows, we may need to
567 * take control away from another application.
568 */
569
570 if (winPtr == displayFocusPtr->focusWinPtr && !force) {
571 return;
572 }
573
574 /*
575 * Find the top-level window for winPtr, then find (or create)
576 * a record for the top-level. Also see whether winPtr and all its
577 * ancestors are mapped.
578 */
579
580 allMapped = 1;
581 for (topLevelPtr = winPtr; ; topLevelPtr = topLevelPtr->parentPtr) {
582 if (topLevelPtr == NULL) {
583 /*
584 * The window is being deleted. No point in worrying about
585 * giving it the focus.
586 */
587 return;
588 }
589 if (!(topLevelPtr->flags & TK_MAPPED)) {
590 allMapped = 0;
591 }
592 if (topLevelPtr->flags & TK_TOP_LEVEL) {
593 break;
594 }
595 }
596
597 /*
598 * If the new focus window isn't mapped, then we can't focus on it
599 * (X will generate an error, for example). Instead, create an
600 * event handler that will set the focus to this window once it gets
601 * mapped. At the same time, delete any old handler that might be
602 * around; it's no longer relevant.
603 */
604
605 if (displayFocusPtr->focusOnMapPtr != NULL) {
606 Tk_DeleteEventHandler(
607 (Tk_Window) displayFocusPtr->focusOnMapPtr,
608 StructureNotifyMask, FocusMapProc,
609 (ClientData) displayFocusPtr->focusOnMapPtr);
610 displayFocusPtr->focusOnMapPtr = NULL;
611 }
612 if (!allMapped) {
613 Tk_CreateEventHandler((Tk_Window) winPtr,
614 VisibilityChangeMask, FocusMapProc,
615 (ClientData) winPtr);
616 displayFocusPtr->focusOnMapPtr = winPtr;
617 displayFocusPtr->forceFocus = force;
618 return;
619 }
620
621 for (tlFocusPtr = winPtr->mainPtr->tlFocusPtr; tlFocusPtr != NULL;
622 tlFocusPtr = tlFocusPtr->nextPtr) {
623 if (tlFocusPtr->topLevelPtr == topLevelPtr) {
624 break;
625 }
626 }
627 if (tlFocusPtr == NULL) {
628 tlFocusPtr = (ToplevelFocusInfo *) ckalloc(sizeof(ToplevelFocusInfo));
629 tlFocusPtr->topLevelPtr = topLevelPtr;
630 tlFocusPtr->nextPtr = winPtr->mainPtr->tlFocusPtr;
631 winPtr->mainPtr->tlFocusPtr = tlFocusPtr;
632 }
633 tlFocusPtr->focusWinPtr = winPtr;
634
635 /*
636 * Reset the window system's focus window and generate focus events,
637 * with two special cases:
638 *
639 * 1. If the application is embedded and doesn't currently have the
640 * focus, don't set the focus directly. Instead, see if the
641 * embedding code can claim the focus from the enclosing
642 * container.
643 * 2. Otherwise, if the application doesn't currently have the
644 * focus, don't change the window system's focus unless it was
645 * already in this application or "force" was specified.
646 */
647
648 if ((topLevelPtr->flags & TK_EMBEDDED)
649 && (displayFocusPtr->focusWinPtr == NULL)) {
650 TkpClaimFocus(topLevelPtr, force);
651 } else if ((displayFocusPtr->focusWinPtr != NULL) || force) {
652 /*
653 * Generate events to shift focus between Tk windows.
654 * We do this regardless of what TkpChangeFocus does with
655 * the real X focus so that Tk widgets track focus commands
656 * when there is no window manager. GenerateFocusEvents will
657 * set up a serial number marker so we discard focus events
658 * that are triggered by the ChangeFocus.
659 */
660
661 serial = TkpChangeFocus(TkpGetWrapperWindow(topLevelPtr), force);
662 if (serial != 0) {
663 displayFocusPtr->focusSerial = serial;
664 }
665 GenerateFocusEvents(displayFocusPtr->focusWinPtr, winPtr);
666 displayFocusPtr->focusWinPtr = winPtr;
667 winPtr->dispPtr->focusPtr = winPtr;
668 }
669 }
670
671 /*
672 *----------------------------------------------------------------------
673 *
674 * TkGetFocusWin --
675 *
676 * Given a window, this procedure returns the current focus
677 * window for its application and display.
678 *
679 * Results:
680 * The return value is a pointer to the window that currently
681 * has the input focus for the specified application and
682 * display, or NULL if none.
683 *
684 * Side effects:
685 * None.
686 *
687 *----------------------------------------------------------------------
688 */
689
690 TkWindow *
691 TkGetFocusWin(winPtr)
692 TkWindow *winPtr; /* Window that selects an application
693 * and a display. */
694 {
695 DisplayFocusInfo *displayFocusPtr;
696
697 if (winPtr == NULL) {
698 return (TkWindow *) NULL;
699 }
700
701 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
702 return displayFocusPtr->focusWinPtr;
703 }
704
705 /*
706 *----------------------------------------------------------------------
707 *
708 * TkFocusKeyEvent --
709 *
710 * Given a window and a key press or release event that arrived for
711 * the window, use information about the keyboard focus to compute
712 * which window should really get the event. In addition, update
713 * the event to refer to its new window.
714 *
715 * Results:
716 * The return value is a pointer to the window that has the input
717 * focus in winPtr's application, or NULL if winPtr's application
718 * doesn't have the input focus. If a non-NULL value is returned,
719 * eventPtr will be updated to refer properly to the focus window.
720 *
721 * Side effects:
722 * None.
723 *
724 *----------------------------------------------------------------------
725 */
726
727 TkWindow *
728 TkFocusKeyEvent(winPtr, eventPtr)
729 TkWindow *winPtr; /* Window that selects an application
730 * and a display. */
731 XEvent *eventPtr; /* X event to redirect (should be KeyPress
732 * or KeyRelease). */
733 {
734 DisplayFocusInfo *displayFocusPtr;
735 TkWindow *focusWinPtr;
736 int focusX, focusY, vRootX, vRootY, vRootWidth, vRootHeight;
737
738 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
739 focusWinPtr = displayFocusPtr->focusWinPtr;
740
741 /*
742 * The code below is a debugging aid to make sure that dispPtr->focusPtr
743 * is kept properly in sync with the "truth", which is the value in
744 * displayFocusPtr->focusWinPtr.
745 */
746
747 #ifdef TCL_MEM_DEBUG
748 if (focusWinPtr != winPtr->dispPtr->focusPtr) {
749 printf("TkFocusKeyEvent found dispPtr->focusPtr out of sync:\n");
750 printf("expected %s, got %s\n",
751 (focusWinPtr != NULL) ? focusWinPtr->pathName : "??",
752 (winPtr->dispPtr->focusPtr != NULL) ?
753 winPtr->dispPtr->focusPtr->pathName : "??");
754 }
755 #endif
756
757 if ((focusWinPtr != NULL) && (focusWinPtr->mainPtr == winPtr->mainPtr)) {
758 /*
759 * Map the x and y coordinates to make sense in the context of
760 * the focus window, if possible (make both -1 if the map-from
761 * and map-to windows don't share the same screen).
762 */
763
764 if ((focusWinPtr->display != winPtr->display)
765 || (focusWinPtr->screenNum != winPtr->screenNum)) {
766 eventPtr->xkey.x = -1;
767 eventPtr->xkey.y = -1;
768 } else {
769 Tk_GetVRootGeometry((Tk_Window) focusWinPtr, &vRootX, &vRootY,
770 &vRootWidth, &vRootHeight);
771 Tk_GetRootCoords((Tk_Window) focusWinPtr, &focusX, &focusY);
772 eventPtr->xkey.x = eventPtr->xkey.x_root - vRootX - focusX;
773 eventPtr->xkey.y = eventPtr->xkey.y_root - vRootY - focusY;
774 }
775 eventPtr->xkey.window = focusWinPtr->window;
776 return focusWinPtr;
777 }
778
779 /*
780 * The event doesn't belong to us. Perhaps, due to embedding, it
781 * really belongs to someone else. Give the embedding code a chance
782 * to redirect the event.
783 */
784
785 TkpRedirectKeyEvent(winPtr, eventPtr);
786 return (TkWindow *) NULL;
787 }
788
789 /*
790 *----------------------------------------------------------------------
791 *
792 * TkFocusDeadWindow --
793 *
794 * This procedure is invoked when it is determined that
795 * a window is dead. It cleans up focus-related information
796 * about the window.
797 *
798 * Results:
799 * None.
800 *
801 * Side effects:
802 * Various things get cleaned up and recycled.
803 *
804 *----------------------------------------------------------------------
805 */
806
807 void
808 TkFocusDeadWindow(winPtr)
809 register TkWindow *winPtr; /* Information about the window
810 * that is being deleted. */
811 {
812 ToplevelFocusInfo *tlFocusPtr, *prevPtr;
813 DisplayFocusInfo *displayFocusPtr;
814 TkDisplay *dispPtr = winPtr->dispPtr;
815
816 /*
817 * Search for focus records that refer to this window either as
818 * the top-level window or the current focus window.
819 */
820
821 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr, winPtr->dispPtr);
822 for (prevPtr = NULL, tlFocusPtr = winPtr->mainPtr->tlFocusPtr;
823 tlFocusPtr != NULL;
824 prevPtr = tlFocusPtr, tlFocusPtr = tlFocusPtr->nextPtr) {
825 if (winPtr == tlFocusPtr->topLevelPtr) {
826 /*
827 * The top-level window is the one being deleted: free
828 * the focus record and release the focus back to PointerRoot
829 * if we acquired it implicitly.
830 */
831
832 if (dispPtr->implicitWinPtr == winPtr) {
833 if (dispPtr->focusDebug) {
834 printf("releasing focus to root after %s died\n",
835 tlFocusPtr->topLevelPtr->pathName);
836 }
837 dispPtr->implicitWinPtr = NULL;
838 displayFocusPtr->focusWinPtr = NULL;
839 dispPtr->focusPtr = NULL;
840 }
841 if (displayFocusPtr->focusWinPtr == tlFocusPtr->focusWinPtr) {
842 displayFocusPtr->focusWinPtr = NULL;
843 dispPtr->focusPtr = NULL;
844 }
845 if (prevPtr == NULL) {
846 winPtr->mainPtr->tlFocusPtr = tlFocusPtr->nextPtr;
847 } else {
848 prevPtr->nextPtr = tlFocusPtr->nextPtr;
849 }
850 ckfree((char *) tlFocusPtr);
851 break;
852 } else if (winPtr == tlFocusPtr->focusWinPtr) {
853 /*
854 * The deleted window had the focus for its top-level:
855 * move the focus to the top-level itself.
856 */
857
858 tlFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr;
859 if ((displayFocusPtr->focusWinPtr == winPtr)
860 && !(tlFocusPtr->topLevelPtr->flags & TK_ALREADY_DEAD)) {
861 if (dispPtr->focusDebug) {
862 printf("forwarding focus to %s after %s died\n",
863 tlFocusPtr->topLevelPtr->pathName,
864 winPtr->pathName);
865 }
866 GenerateFocusEvents(displayFocusPtr->focusWinPtr,
867 tlFocusPtr->topLevelPtr);
868 displayFocusPtr->focusWinPtr = tlFocusPtr->topLevelPtr;
869 dispPtr->focusPtr = tlFocusPtr->topLevelPtr;
870 }
871 break;
872 }
873 }
874
875 if (displayFocusPtr->focusOnMapPtr == winPtr) {
876 displayFocusPtr->focusOnMapPtr = NULL;
877 }
878 }
879
880 /*
881 *----------------------------------------------------------------------
882 *
883 * GenerateFocusEvents --
884 *
885 * This procedure is called to create FocusIn and FocusOut events to
886 * move the input focus from one window to another.
887 *
888 * Results:
889 * None.
890 *
891 * Side effects:
892 * FocusIn and FocusOut events are generated.
893 *
894 *----------------------------------------------------------------------
895 */
896
897 static void
898 GenerateFocusEvents(sourcePtr, destPtr)
899 TkWindow *sourcePtr; /* Window that used to have the focus (may
900 * be NULL). */
901 TkWindow *destPtr; /* New window to have the focus (may be
902 * NULL). */
903
904 {
905 XEvent event;
906 TkWindow *winPtr;
907
908 winPtr = sourcePtr;
909 if (winPtr == NULL) {
910 winPtr = destPtr;
911 if (winPtr == NULL) {
912 return;
913 }
914 }
915
916 event.xfocus.serial = LastKnownRequestProcessed(winPtr->display);
917 event.xfocus.send_event = GENERATED_EVENT_MAGIC;
918 event.xfocus.display = winPtr->display;
919 event.xfocus.mode = NotifyNormal;
920 TkInOutEvents(&event, sourcePtr, destPtr, FocusOut, FocusIn,
921 TCL_QUEUE_MARK);
922 }
923
924 /*
925 *----------------------------------------------------------------------
926 *
927 * FocusMapProc --
928 *
929 * This procedure is called as an event handler for VisibilityNotify
930 * events, if a window receives the focus at a time when its
931 * toplevel isn't mapped. The procedure is needed because X
932 * won't allow the focus to be set to an unmapped window; we
933 * detect when the toplevel is mapped and set the focus to it then.
934 *
935 * Results:
936 * None.
937 *
938 * Side effects:
939 * If this is a map event, the focus gets set to the toplevel
940 * given by clientData.
941 *
942 *----------------------------------------------------------------------
943 */
944
945 static void
946 FocusMapProc(clientData, eventPtr)
947 ClientData clientData; /* Toplevel window. */
948 XEvent *eventPtr; /* Information about event. */
949 {
950 TkWindow *winPtr = (TkWindow *) clientData;
951 DisplayFocusInfo *displayFocusPtr;
952
953 if (eventPtr->type == VisibilityNotify) {
954 displayFocusPtr = FindDisplayFocusInfo(winPtr->mainPtr,
955 winPtr->dispPtr);
956 if (winPtr->dispPtr->focusDebug) {
957 printf("auto-focussing on %s, force %d\n", winPtr->pathName,
958 displayFocusPtr->forceFocus);
959 }
960 Tk_DeleteEventHandler((Tk_Window) winPtr, VisibilityChangeMask,
961 FocusMapProc, clientData);
962 displayFocusPtr->focusOnMapPtr = NULL;
963 TkSetFocusWin(winPtr, displayFocusPtr->forceFocus);
964 }
965 }
966
967 /*
968 *----------------------------------------------------------------------
969 *
970 * FindDisplayFocusInfo --
971 *
972 * Given an application and a display, this procedure locate the
973 * focus record for that combination. If no such record exists,
974 * it creates a new record and initializes it.
975 *
976 * Results:
977 * The return value is a pointer to the record.
978 *
979 * Side effects:
980 * A new record will be allocated if there wasn't one already.
981 *
982 *----------------------------------------------------------------------
983 */
984
985 static DisplayFocusInfo *
986 FindDisplayFocusInfo(mainPtr, dispPtr)
987 TkMainInfo *mainPtr; /* Record that identifies a particular
988 * application. */
989 TkDisplay *dispPtr; /* Display whose focus information is
990 * needed. */
991 {
992 DisplayFocusInfo *displayFocusPtr;
993
994 for (displayFocusPtr = mainPtr->displayFocusPtr;
995 displayFocusPtr != NULL;
996 displayFocusPtr = displayFocusPtr->nextPtr) {
997 if (displayFocusPtr->dispPtr == dispPtr) {
998 return displayFocusPtr;
999 }
1000 }
1001
1002 /*
1003 * The record doesn't exist yet. Make a new one.
1004 */
1005
1006 displayFocusPtr = (DisplayFocusInfo *) ckalloc(sizeof(DisplayFocusInfo));
1007 displayFocusPtr->dispPtr = dispPtr;
1008 displayFocusPtr->focusWinPtr = NULL;
1009 displayFocusPtr->focusOnMapPtr = NULL;
1010 displayFocusPtr->forceFocus = 0;
1011 displayFocusPtr->focusSerial = 0;
1012 displayFocusPtr->nextPtr = mainPtr->displayFocusPtr;
1013 mainPtr->displayFocusPtr = displayFocusPtr;
1014 return displayFocusPtr;
1015 }
1016
1017
1018 /* $History: tkFocus.c $
1019 *
1020 * ***************** Version 1 *****************
1021 * User: Dtashley Date: 1/02/01 Time: 2:49a
1022 * Created in $/IjuScripter, IjuConsole/Source/Tk Base
1023 * Initial check-in.
1024 */
1025
1026 /* End of TKFOCUS.C */

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25