/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkfocus.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkfocus.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 69 - (hide annotations) (download)
Sat Nov 5 10:54:17 2016 UTC (7 years, 8 months ago) by dashley
File MIME type: text/plain
File size: 33463 byte(s)
License and property (keyword) changes.
1 dashley 69 /* $Header$ */
2 dashley 25
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 dashley 69 /* End of tkfocus.c */

Properties

Name Value
svn:keywords Header

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25