1 /*
2  * tkUnixEvent.c --
3  *
4  *	This file implements an event source for X displays for the UNIX
5  *	version of Tk.
6  *
7  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
8  *
9  * See the file "license.terms" for information on usage and redistribution of
10  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11  */
12 
13 #include "tkUnixInt.h"
14 #include <signal.h>
15 #ifdef HAVE_XKBKEYCODETOKEYSYM
16 #  include <X11/XKBlib.h>
17 #else
18 #  define XkbOpenDisplay(D,V,E,M,m,R) (((void)D),((void)V),((void)E),((void)M),((void)m),((void)R),(NULL))
19 #endif
20 
21 /*
22  * The following static indicates whether this module has been initialized in
23  * the current thread.
24  */
25 
26 typedef struct {
27     int initialized;
28 } ThreadSpecificData;
29 static Tcl_ThreadDataKey dataKey;
30 
31 /*
32  * Prototypes for functions that are referenced only in this file:
33  */
34 
35 static void		DisplayCheckProc(ClientData clientData, int flags);
36 static void		DisplayExitHandler(ClientData clientData);
37 static void		DisplayFileProc(ClientData clientData, int flags);
38 static void		DisplaySetupProc(ClientData clientData, int flags);
39 static void		TransferXEventsToTcl(Display *display);
40 #ifdef TK_USE_INPUT_METHODS
41 static void		InstantiateIMCallback(Display *, XPointer client_data, XPointer call_data);
42 static void		DestroyIMCallback(XIM im, XPointer client_data, XPointer call_data);
43 static void		OpenIM(TkDisplay *dispPtr);
44 #endif
45 
46 /*
47  *----------------------------------------------------------------------
48  *
49  * TkCreateXEventSource --
50  *
51  *	This function is called during Tk initialization to create the event
52  *	source for X Window events.
53  *
54  * Results:
55  *	None.
56  *
57  * Side effects:
58  *	A new event source is created.
59  *
60  *----------------------------------------------------------------------
61  */
62 
63 void
TkCreateXEventSource(void)64 TkCreateXEventSource(void)
65 {
66     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
67 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
68 
69     if (!tsdPtr->initialized) {
70 	tsdPtr->initialized = 1;
71 	Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
72 	TkCreateExitHandler(DisplayExitHandler, NULL);
73     }
74 }
75 
76 /*
77  *----------------------------------------------------------------------
78  *
79  * DisplayExitHandler --
80  *
81  *	This function is called during finalization to clean up the display
82  *	module.
83  *
84  * Results:
85  *	None.
86  *
87  * Side effects:
88  *	None.
89  *
90  *----------------------------------------------------------------------
91  */
92 
93 static void
DisplayExitHandler(ClientData clientData)94 DisplayExitHandler(
95     ClientData clientData)	/* Not used. */
96 {
97     ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
98 	    Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
99 
100     Tcl_DeleteEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
101     tsdPtr->initialized = 0;
102 }
103 
104 /*
105  *----------------------------------------------------------------------
106  *
107  * TkpOpenDisplay --
108  *
109  *	Allocates a new TkDisplay, opens the X display, and establishes the
110  *	file handler for the connection.
111  *
112  * Results:
113  *	A pointer to a Tk display structure.
114  *
115  * Side effects:
116  *	Opens a display.
117  *
118  *----------------------------------------------------------------------
119  */
120 
121 TkDisplay *
TkpOpenDisplay(const char * displayNameStr)122 TkpOpenDisplay(
123     const char *displayNameStr)
124 {
125     TkDisplay *dispPtr;
126     Display *display;
127     int event = 0;
128     int error = 0;
129     int major = 1;
130     int minor = 0;
131     int reason = 0;
132     unsigned int use_xkb = 0;
133     /* Disabled, until we have a better test. See [Bug 3613668] */
134 #if 0 && defined(XKEYCODETOKEYSYM_IS_DEPRECATED) && defined(TCL_THREADS)
135     static int xinited = 0;
136     static Tcl_Mutex xinitMutex = NULL;
137 
138     if (!xinited) {
139 	Tcl_MutexLock(&xinitMutex);
140 	if (!xinited) {
141 	    /* Necessary for threaded apps, of no consequence otherwise  */
142 	    /* need only be called once, but must be called before *any* */
143 	    /* Xlib call is made. If xinitMutex is still NULL after the  */
144 	    /* Tcl_MutexLock call, Tcl was compiled without threads so   */
145 	    /* we cannot use XInitThreads() either.                      */
146 	    if (xinitMutex != NULL){
147 		XInitThreads();
148 	    }
149 	    xinited = 1;
150 	}
151 	Tcl_MutexUnlock(&xinitMutex);
152     }
153 #endif
154 
155     /*
156     ** Bug [3607830]: Before using Xkb, it must be initialized and confirmed
157     **                that the serve supports it.  The XkbOpenDisplay call
158     **                will perform this check and return NULL if the extension
159     **                is not supported.
160     **
161     ** Work around un-const-ified Xkb headers using (char *) cast.
162     */
163     display = XkbOpenDisplay((char *)displayNameStr, &event, &error, &major,
164 	    &minor, &reason);
165 
166     if (display == NULL) {
167 	/*fprintf(stderr,"event=%d error=%d major=%d minor=%d reason=%d\nDisabling xkb\n",
168 	event, error, major, minor, reason);*/
169 	display = XOpenDisplay(displayNameStr);
170     } else {
171 	use_xkb = TK_DISPLAY_USE_XKB;
172 	/*fprintf(stderr, "Using xkb %d.%d\n", major, minor);*/
173     }
174 
175     if (display == NULL) {
176 	return NULL;
177     }
178     dispPtr = ckalloc(sizeof(TkDisplay));
179     memset(dispPtr, 0, sizeof(TkDisplay));
180     dispPtr->display = display;
181     dispPtr->flags |= use_xkb;
182 #ifdef TK_USE_INPUT_METHODS
183     OpenIM(dispPtr);
184     XRegisterIMInstantiateCallback(dispPtr->display, NULL, NULL, NULL,
185 	    InstantiateIMCallback, (XPointer) dispPtr);
186 #endif
187     Tcl_CreateFileHandler(ConnectionNumber(display), TCL_READABLE,
188 	    DisplayFileProc, dispPtr);
189 
190     /*
191      * Observed weird WidthMMOfScreen() in X on Wayland on a
192      * Fedora 30/i386 running in a VM. Fallback to 75 dpi,
193      * otherwise many other strange things may happen later.
194      * See: [https://core.tcl-lang.org/tk/tktview?name=a01b6f7227]
195      */
196     if (WidthMMOfScreen(DefaultScreenOfDisplay(display)) <= 0) {
197 	int mm;
198 
199 	mm = WidthOfScreen(DefaultScreenOfDisplay(display)) * (25.4 / 75.0);
200 	WidthMMOfScreen(DefaultScreenOfDisplay(display)) = mm;
201     }
202     if (HeightMMOfScreen(DefaultScreenOfDisplay(display)) <= 0) {
203 	int mm;
204 
205 	mm = HeightOfScreen(DefaultScreenOfDisplay(display)) * (25.4 / 75.0);
206 	HeightMMOfScreen(DefaultScreenOfDisplay(display)) = mm;
207     }
208 
209     /*
210      * Key map info must be available immediately, because of "send event".
211      */
212     TkpInitKeymapInfo(dispPtr);
213 
214     return dispPtr;
215 }
216 
217 /*
218  *----------------------------------------------------------------------
219  *
220  * TkpCloseDisplay --
221  *
222  *	Cancels notifier callbacks and closes a display.
223  *
224  * Results:
225  *	None.
226  *
227  * Side effects:
228  *	Deallocates the displayPtr and unix-specific resources.
229  *
230  *----------------------------------------------------------------------
231  */
232 
233 void
TkpCloseDisplay(TkDisplay * dispPtr)234 TkpCloseDisplay(
235     TkDisplay *dispPtr)
236 {
237     TkSendCleanup(dispPtr);
238 
239     TkWmCleanup(dispPtr);
240 
241 #ifdef TK_USE_INPUT_METHODS
242     if (dispPtr->inputXfs) {
243 	XFreeFontSet(dispPtr->display, dispPtr->inputXfs);
244     }
245     if (dispPtr->inputMethod) {
246 	XCloseIM(dispPtr->inputMethod);
247     }
248 #endif
249 
250     if (dispPtr->display != 0) {
251 	Tcl_DeleteFileHandler(ConnectionNumber(dispPtr->display));
252 	(void) XSync(dispPtr->display, False);
253 	(void) XCloseDisplay(dispPtr->display);
254     }
255 }
256 
257 /*
258  *----------------------------------------------------------------------
259  *
260  * TkClipCleanup --
261  *
262  *	This function is called to cleanup resources associated with claiming
263  *	clipboard ownership and for receiving selection get results. This
264  *	function is called in tkWindow.c. This has to be called by the display
265  *	cleanup function because we still need the access display elements.
266  *
267  * Results:
268  *	None.
269  *
270  * Side effects:
271  *	Resources are freed - the clipboard may no longer be used.
272  *
273  *----------------------------------------------------------------------
274  */
275 
276 void
TkClipCleanup(TkDisplay * dispPtr)277 TkClipCleanup(
278     TkDisplay *dispPtr)		/* Display associated with clipboard */
279 {
280     if (dispPtr->clipWindow != NULL) {
281 	Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom,
282 		dispPtr->applicationAtom);
283 	Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom,
284 		dispPtr->windowAtom);
285 
286 	Tk_DestroyWindow(dispPtr->clipWindow);
287 	Tcl_Release(dispPtr->clipWindow);
288 	dispPtr->clipWindow = NULL;
289     }
290 }
291 
292 /*
293  *----------------------------------------------------------------------
294  *
295  * DisplaySetupProc --
296  *
297  *	This function implements the setup part of the UNIX X display event
298  *	source. It is invoked by Tcl_DoOneEvent before entering the notifier
299  *	to check for events on all displays.
300  *
301  * Results:
302  *	None.
303  *
304  * Side effects:
305  *	If data is queued on a display inside Xlib, then the maximum block
306  *	time will be set to 0 to ensure that the notifier returns control to
307  *	Tcl even if there is no more data on the X connection.
308  *
309  *----------------------------------------------------------------------
310  */
311 
312 static void
DisplaySetupProc(ClientData clientData,int flags)313 DisplaySetupProc(
314     ClientData clientData,	/* Not used. */
315     int flags)
316 {
317     TkDisplay *dispPtr;
318     static Tcl_Time blockTime = { 0, 0 };
319 
320     if (!(flags & TCL_WINDOW_EVENTS)) {
321 	return;
322     }
323 
324     for (dispPtr = TkGetDisplayList(); dispPtr != NULL;
325 	    dispPtr = dispPtr->nextPtr) {
326 	/*
327 	 * Flush the display. If data is pending on the X queue, set the block
328 	 * time to zero. This ensures that we won't block in the notifier if
329 	 * there is data in the X queue, but not on the server socket.
330 	 */
331 
332 	XFlush(dispPtr->display);
333 	if (QLength(dispPtr->display) > 0) {
334 	    Tcl_SetMaxBlockTime(&blockTime);
335 	}
336     }
337 }
338 
339 /*
340  *----------------------------------------------------------------------
341  *
342  * TransferXEventsToTcl --
343  *
344  *	Transfer events from the X event queue to the Tk event queue.
345  *
346  * Results:
347  *	None.
348  *
349  * Side effects:
350  *	Moves queued X events onto the Tcl event queue.
351  *
352  *----------------------------------------------------------------------
353  */
354 
355 static void
TransferXEventsToTcl(Display * display)356 TransferXEventsToTcl(
357     Display *display)
358 {
359     union {
360 	int type;
361 	XEvent x;
362 	TkKeyEvent k;
363     } event;
364     Window w;
365     TkDisplay *dispPtr = NULL;
366 
367     /*
368      * Transfer events from the X event queue to the Tk event queue after XIM
369      * event filtering. KeyPress and KeyRelease events need special treatment
370      * so that they get directed according to Tk's focus rules during XIM
371      * handling. Theoretically they can go to the wrong place still (if
372      * there's a focus change in the queue) but if we push the handling off
373      * until Tk_HandleEvent then many input methods actually cease to work
374      * correctly. Most of the time, Tk processes its event queue fast enough
375      * for this to not be an issue anyway. [Bug 1924761]
376      */
377 
378     while (QLength(display) > 0) {
379 	XNextEvent(display, &event.x);
380 	if (event.type > MappingNotify) {
381 	    continue;
382 	}
383 	w = None;
384 	if (event.type == KeyPress || event.type == KeyRelease) {
385 	    for (dispPtr = TkGetDisplayList(); ; dispPtr = dispPtr->nextPtr) {
386 		if (dispPtr == NULL) {
387 		    break;
388 		} else if (dispPtr->display == event.x.xany.display) {
389 		    if (dispPtr->focusPtr != NULL) {
390 			w = dispPtr->focusPtr->window;
391 		    }
392 		    break;
393 		}
394 	    }
395 	}
396 	if (XFilterEvent(&event.x, w)) {
397 	    continue;
398 	}
399 	if (event.type == KeyPress || event.type == KeyRelease) {
400 	    event.k.charValuePtr = NULL;
401 	    event.k.charValueLen = 0;
402 	    event.k.keysym = NoSymbol;
403 
404 	    /*
405 	     * Force the calling of the input method engine now. The results
406 	     * from it will be cached in the event so that they don't get lost
407 	     * (to a race condition with other XIM-handled key events) between
408 	     * entering the event queue and getting serviced. [Bug 1924761]
409 	     */
410 
411 #ifdef TK_USE_INPUT_METHODS
412 	    if (event.type == KeyPress && dispPtr &&
413 		    (dispPtr->flags & TK_DISPLAY_USE_IM)) {
414 		if (dispPtr->focusPtr && dispPtr->focusPtr->inputContext) {
415 		    Tcl_DString ds;
416 
417 		    Tcl_DStringInit(&ds);
418 		    (void) TkpGetString(dispPtr->focusPtr, &event.x, &ds);
419 		    Tcl_DStringFree(&ds);
420 		}
421 	    }
422 #endif
423 	}
424 	Tk_QueueWindowEvent(&event.x, TCL_QUEUE_TAIL);
425     }
426 }
427 
428 /*
429  *----------------------------------------------------------------------
430  *
431  * DisplayCheckProc --
432  *
433  *	This function checks for events sitting in the X event queue.
434  *
435  * Results:
436  *	None.
437  *
438  * Side effects:
439  *	Moves queued events onto the Tcl event queue.
440  *
441  *----------------------------------------------------------------------
442  */
443 
444 static void
DisplayCheckProc(ClientData clientData,int flags)445 DisplayCheckProc(
446     ClientData clientData,	/* Not used. */
447     int flags)
448 {
449     TkDisplay *dispPtr;
450 
451     if (!(flags & TCL_WINDOW_EVENTS)) {
452 	return;
453     }
454 
455     for (dispPtr = TkGetDisplayList(); dispPtr != NULL;
456 	    dispPtr = dispPtr->nextPtr) {
457 	XFlush(dispPtr->display);
458 	TransferXEventsToTcl(dispPtr->display);
459     }
460 }
461 
462 /*
463  *----------------------------------------------------------------------
464  *
465  * DisplayFileProc --
466  *
467  *	This function implements the file handler for the X connection.
468  *
469  * Results:
470  *	None.
471  *
472  * Side effects:
473  *	Makes entries on the Tcl event queue for all the events available from
474  *	all the displays.
475  *
476  *----------------------------------------------------------------------
477  */
478 
479 static void
DisplayFileProc(ClientData clientData,int flags)480 DisplayFileProc(
481     ClientData clientData,	/* The display pointer. */
482     int flags)			/* Should be TCL_READABLE. */
483 {
484     TkDisplay *dispPtr = (TkDisplay *) clientData;
485     Display *display = dispPtr->display;
486     int numFound;
487 
488     XFlush(display);
489     numFound = XEventsQueued(display, QueuedAfterReading);
490     if (numFound == 0) {
491 	/*
492 	 * Things are very tricky if there aren't any events readable at this
493 	 * point (after all, there was supposedly data available on the
494 	 * connection). A couple of things could have occurred:
495 	 *
496 	 * One possibility is that there were only error events in the input
497 	 * from the server. If this happens, we should return (we don't want
498 	 * to go to sleep in XNextEvent below, since this would block out
499 	 * other sources of input to the process).
500 	 *
501 	 * Another possibility is that our connection to the server has been
502 	 * closed. This will not necessarily be detected in XEventsQueued (!!)
503 	 * so if we just return then there will be an infinite loop. To detect
504 	 * such an error, generate a NoOp protocol request to exercise the
505 	 * connection to the server, then return. However, must disable
506 	 * SIGPIPE while sending the request, or else the process will die
507 	 * from the signal and won't invoke the X error function to print a
508 	 * nice (?!) message.
509 	 */
510 
511 	void (*oldHandler)(int);
512 
513 	oldHandler = (void (*)(int)) signal(SIGPIPE, SIG_IGN);
514 	XNoOp(display);
515 	XFlush(display);
516 	(void) signal(SIGPIPE, oldHandler);
517     }
518 
519     TransferXEventsToTcl(display);
520 }
521 
522 /*
523  *----------------------------------------------------------------------
524  *
525  * TkUnixDoOneXEvent --
526  *
527  *	This routine waits for an X event to be processed or for a timeout to
528  *	occur. The timeout is specified as an absolute time. This routine is
529  *	called when Tk needs to wait for a particular X event without letting
530  *	arbitrary events be processed. The caller will typically call
531  *	Tk_RestrictEvents to set up an event filter before calling this
532  *	routine. This routine will service at most one event per invocation.
533  *
534  * Results:
535  *	Returns 0 if the timeout has expired, otherwise returns 1.
536  *
537  * Side effects:
538  *	Can invoke arbitrary Tcl scripts.
539  *
540  *----------------------------------------------------------------------
541  */
542 
543 int
TkUnixDoOneXEvent(Tcl_Time * timePtr)544 TkUnixDoOneXEvent(
545     Tcl_Time *timePtr)		/* Specifies the absolute time when the call
546 				 * should time out. */
547 {
548     TkDisplay *dispPtr;
549     static fd_mask readMask[MASK_SIZE];
550     struct timeval blockTime, *timeoutPtr;
551     Tcl_Time now;
552     int fd, index, numFound, numFdBits = 0;
553     fd_mask bit, *readMaskPtr = readMask;
554 
555     /*
556      * Look for queued events first.
557      */
558 
559     if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
560 	return 1;
561     }
562 
563     /*
564      * Compute the next block time and check to see if we have timed out. Note
565      * that HP-UX defines tv_sec to be unsigned so we have to be careful in
566      * our arithmetic.
567      */
568 
569     if (timePtr) {
570 	Tcl_GetTime(&now);
571 	blockTime.tv_sec = timePtr->sec;
572 	blockTime.tv_usec = timePtr->usec - now.usec;
573 	if (blockTime.tv_usec < 0) {
574 	    now.sec += 1;
575 	    blockTime.tv_usec += 1000000;
576 	}
577 	if (blockTime.tv_sec < now.sec) {
578 	    blockTime.tv_sec = 0;
579 	    blockTime.tv_usec = 0;
580 	} else {
581 	    blockTime.tv_sec -= now.sec;
582 	}
583 	timeoutPtr = &blockTime;
584     } else {
585 	timeoutPtr = NULL;
586     }
587 
588     /*
589      * Set up the select mask for all of the displays. If a display has data
590      * pending, then we want to poll instead of blocking.
591      */
592 
593     memset(readMask, 0, MASK_SIZE*sizeof(fd_mask));
594     for (dispPtr = TkGetDisplayList(); dispPtr != NULL;
595 	    dispPtr = dispPtr->nextPtr) {
596 	XFlush(dispPtr->display);
597 	if (QLength(dispPtr->display) > 0) {
598 	    blockTime.tv_sec = 0;
599 	    blockTime.tv_usec = 0;
600 	}
601 	fd = ConnectionNumber(dispPtr->display);
602 	index = fd/(NBBY*sizeof(fd_mask));
603 	bit = ((fd_mask)1) << (fd%(NBBY*sizeof(fd_mask)));
604 	readMask[index] |= bit;
605 	if (numFdBits <= fd) {
606 	    numFdBits = fd+1;
607 	}
608     }
609 
610     numFound = select(numFdBits, (SELECT_MASK *) readMaskPtr, NULL, NULL,
611 	    timeoutPtr);
612     if (numFound <= 0) {
613 	/*
614 	 * Some systems don't clear the masks after an error, so we have to do
615 	 * it here.
616 	 */
617 
618 	memset(readMask, 0, MASK_SIZE*sizeof(fd_mask));
619     }
620 
621     /*
622      * Process any new events on the display connections.
623      */
624 
625     for (dispPtr = TkGetDisplayList(); dispPtr != NULL;
626 	    dispPtr = dispPtr->nextPtr) {
627 	fd = ConnectionNumber(dispPtr->display);
628 	index = fd/(NBBY*sizeof(fd_mask));
629 	bit = ((fd_mask)1) << (fd%(NBBY*sizeof(fd_mask)));
630 	if ((readMask[index] & bit) || (QLength(dispPtr->display) > 0)) {
631 	    DisplayFileProc(dispPtr, TCL_READABLE);
632 	}
633     }
634     if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
635 	return 1;
636     }
637 
638     /*
639      * Check to see if we timed out.
640      */
641 
642     if (timePtr) {
643 	Tcl_GetTime(&now);
644 	if ((now.sec > timePtr->sec) || ((now.sec == timePtr->sec)
645 		&& (now.usec > timePtr->usec))) {
646 	    return 0;
647 	}
648     }
649 
650     /*
651      * We had an event but we did not generate a Tcl event from it. Behave as
652      * though we dealt with it. (JYL&SS)
653      */
654 
655     return 1;
656 }
657 
658 /*
659  *----------------------------------------------------------------------
660  *
661  * TkpSync --
662  *
663  *	This routine ensures that all pending X requests have been seen by the
664  *	server, and that any pending X events have been moved onto the Tk
665  *	event queue.
666  *
667  * Results:
668  *	None.
669  *
670  * Side effects:
671  *	Places new events on the Tk event queue.
672  *
673  *----------------------------------------------------------------------
674  */
675 
676 void
TkpSync(Display * display)677 TkpSync(
678     Display *display)		/* Display to sync. */
679 {
680     XSync(display, False);
681 
682     /*
683      * Transfer events from the X event queue to the Tk event queue.
684      */
685 
686     TransferXEventsToTcl(display);
687 }
688 #ifdef TK_USE_INPUT_METHODS
689 
690 static void
InstantiateIMCallback(Display * display,XPointer client_data,XPointer call_data)691 InstantiateIMCallback(
692     Display      *display,
693     XPointer     client_data,
694     XPointer     call_data)
695 {
696     TkDisplay    *dispPtr;
697 
698     dispPtr = (TkDisplay *) client_data;
699     OpenIM(dispPtr);
700     XUnregisterIMInstantiateCallback(dispPtr->display, NULL, NULL, NULL,
701 	    InstantiateIMCallback, (XPointer) dispPtr);
702 }
703 
704 static void
DestroyIMCallback(XIM im,XPointer client_data,XPointer call_data)705 DestroyIMCallback(
706     XIM         im,
707     XPointer    client_data,
708     XPointer    call_data)
709 {
710     TkDisplay   *dispPtr;
711 
712     dispPtr = (TkDisplay *) client_data;
713     dispPtr->inputMethod = NULL;
714     ++dispPtr->ximGeneration;
715     XRegisterIMInstantiateCallback(dispPtr->display, NULL, NULL, NULL,
716 	    InstantiateIMCallback, (XPointer) dispPtr);
717 }
718 
719 /*
720  *--------------------------------------------------------------
721  *
722  * OpenIM --
723  *
724  *	Tries to open an X input method associated with the given display.
725  *
726  * Results:
727  *	Stores the input method in dispPtr->inputMethod; if there isn't a
728  *	suitable input method, then NULL is stored in dispPtr->inputMethod.
729  *
730  * Side effects:
731  *	An input method gets opened.
732  *
733  *--------------------------------------------------------------
734  */
735 
736 static void
OpenIM(TkDisplay * dispPtr)737 OpenIM(
738     TkDisplay *dispPtr)		/* Tk's structure for the display. */
739 {
740     int i;
741     XIMStyles *stylePtr;
742     XIMStyle bestStyle = 0;
743 
744     if (XSetLocaleModifiers("") == NULL) {
745 	return;
746     }
747 
748     ++dispPtr->ximGeneration;
749     dispPtr->inputMethod = XOpenIM(dispPtr->display, NULL, NULL, NULL);
750     if (dispPtr->inputMethod == NULL) {
751 	return;
752     }
753 
754     /* Require X11R6 */
755     {
756 	XIMCallback destroy_cb;
757 
758 	destroy_cb.callback = DestroyIMCallback;
759 	destroy_cb.client_data = (XPointer) dispPtr;
760 	if (XSetIMValues(dispPtr->inputMethod, XNDestroyCallback,
761 		&destroy_cb, NULL))
762 	    goto error;
763     }
764 
765     if ((XGetIMValues(dispPtr->inputMethod, XNQueryInputStyle, &stylePtr,
766 	    NULL) != NULL) || (stylePtr == NULL)) {
767 	goto error;
768     }
769 
770     /*
771      * Select the best input style supported by both the IM and Tk.
772      */
773     for (i = 0; i < stylePtr->count_styles; i++) {
774 	XIMStyle thisStyle = stylePtr->supported_styles[i];
775 	if (thisStyle == (XIMPreeditPosition | XIMStatusNothing)) {
776 	    bestStyle = thisStyle;
777 	    break;
778 	} else if (thisStyle == (XIMPreeditNothing | XIMStatusNothing)) {
779 	    bestStyle = thisStyle;
780 	}
781     }
782     XFree(stylePtr);
783     if (bestStyle == 0) {
784 	goto error;
785     }
786 
787     dispPtr->inputStyle = bestStyle;
788 
789     /*
790      * Create an XFontSet for preedit area.
791      */
792     if (dispPtr->inputStyle & XIMPreeditPosition) {
793 	char **missing_list;
794 	int missing_count;
795 	char *def_string;
796 
797 	dispPtr->inputXfs = XCreateFontSet(dispPtr->display,
798 		"-*-*-*-R-Normal--14-130-75-75-*-*",
799 		&missing_list, &missing_count, &def_string);
800 	if (missing_count > 0) {
801 	    XFreeStringList(missing_list);
802 	}
803     }
804 
805     return;
806 
807 error:
808     if (dispPtr->inputMethod) {
809 	XCloseIM(dispPtr->inputMethod);
810 	dispPtr->inputMethod = NULL;
811 	++dispPtr->ximGeneration;
812     }
813 }
814 #endif /* TK_USE_INPUT_METHODS */
815 
816 void
TkpWarpPointer(TkDisplay * dispPtr)817 TkpWarpPointer(
818     TkDisplay *dispPtr)
819 {
820     Window w;			/* Which window to warp relative to. */
821 
822     if (dispPtr->warpWindow != NULL) {
823 	w = Tk_WindowId(dispPtr->warpWindow);
824     } else {
825 	w = RootWindow(dispPtr->display,
826 		Tk_ScreenNumber(dispPtr->warpMainwin));
827     }
828     XWarpPointer(dispPtr->display, None, w, 0, 0, 0, 0,
829 	    (int) dispPtr->warpX, (int) dispPtr->warpY);
830 }
831 
832 /*
833  * Local Variables:
834  * mode: c
835  * c-basic-offset: 4
836  * fill-column: 78
837  * End:
838  */
839