1/*
2 * tkWinWindow.c --
3 *
4 *	Xlib emulation routines for Windows related to creating,
5 *	displaying and destroying windows.
6 *
7 * Copyright (c) 1995-1997 Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tkWinWindow.c,v 1.10 2002/06/14 22:25:12 jenglish Exp $
13 */
14
15#include "tkWinInt.h"
16
17typedef struct ThreadSpecificData {
18    int initialized;            /* 0 means table below needs initializing. */
19    Tcl_HashTable windowTable;  /* The windowTable maps from HWND to
20				 * Tk_Window handles. */
21} ThreadSpecificData;
22static Tcl_ThreadDataKey dataKey;
23
24/*
25 * Forward declarations for procedures defined in this file:
26 */
27
28static void		NotifyVisibility _ANSI_ARGS_((XEvent *eventPtr,
29			    TkWindow *winPtr));
30
31/*
32 *----------------------------------------------------------------------
33 *
34 * Tk_AttachHWND --
35 *
36 *	This function binds an HWND and a reflection procedure to
37 *	the specified Tk_Window.
38 *
39 * Results:
40 *	Returns an X Window that encapsulates the HWND.
41 *
42 * Side effects:
43 *	May allocate a new X Window.  Also enters the HWND into the
44 *	global window table.
45 *
46 *----------------------------------------------------------------------
47 */
48
49Window
50Tk_AttachHWND(tkwin, hwnd)
51    Tk_Window tkwin;
52    HWND hwnd;
53{
54    int new;
55    Tcl_HashEntry *entryPtr;
56    TkWinDrawable *twdPtr = (TkWinDrawable *) Tk_WindowId(tkwin);
57    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
58            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
59
60    if (!tsdPtr->initialized) {
61	Tcl_InitHashTable(&tsdPtr->windowTable, TCL_ONE_WORD_KEYS);
62	tsdPtr->initialized = 1;
63    }
64
65    /*
66     * Allocate a new drawable if necessary.  Otherwise, remove the
67     * previous HWND from from the window table.
68     */
69
70    if (twdPtr == NULL) {
71	twdPtr = (TkWinDrawable*) ckalloc(sizeof(TkWinDrawable));
72	twdPtr->type = TWD_WINDOW;
73	twdPtr->window.winPtr = (TkWindow *) tkwin;
74    } else if (twdPtr->window.handle != NULL) {
75	entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable,
76		(char *)twdPtr->window.handle);
77	Tcl_DeleteHashEntry(entryPtr);
78    }
79
80    /*
81     * Insert the new HWND into the window table.
82     */
83
84    twdPtr->window.handle = hwnd;
85    entryPtr = Tcl_CreateHashEntry(&tsdPtr->windowTable, (char *)hwnd, &new);
86    Tcl_SetHashValue(entryPtr, (ClientData)tkwin);
87
88    return (Window)twdPtr;
89}
90
91/*
92 *----------------------------------------------------------------------
93 *
94 * Tk_HWNDToWindow --
95 *
96 *	This function retrieves a Tk_Window from the window table
97 *	given an HWND.
98 *
99 * Results:
100 *	Returns the matching Tk_Window.
101 *
102 * Side effects:
103 *	None.
104 *
105 *----------------------------------------------------------------------
106 */
107
108Tk_Window
109Tk_HWNDToWindow(hwnd)
110    HWND hwnd;
111{
112    Tcl_HashEntry *entryPtr;
113    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
114            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
115
116    if (!tsdPtr->initialized) {
117	Tcl_InitHashTable(&tsdPtr->windowTable, TCL_ONE_WORD_KEYS);
118	tsdPtr->initialized = 1;
119    }
120    entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, (char*)hwnd);
121    if (entryPtr != NULL) {
122	return (Tk_Window) Tcl_GetHashValue(entryPtr);
123    }
124    return NULL;
125}
126
127/*
128 *----------------------------------------------------------------------
129 *
130 * Tk_GetHWND --
131 *
132 *	This function extracts the HWND from an X Window.
133 *
134 * Results:
135 *	Returns the HWND associated with the Window.
136 *
137 * Side effects:
138 *	None.
139 *
140 *----------------------------------------------------------------------
141 */
142
143HWND
144Tk_GetHWND(window)
145    Window window;
146{
147    return ((TkWinDrawable *) window)->window.handle;
148}
149
150/*
151 *----------------------------------------------------------------------
152 *
153 * TkpPrintWindowId --
154 *
155 *	This routine stores the string representation of the
156 *	platform dependent window handle for an X Window in the
157 *	given buffer.
158 *
159 * Results:
160 *	Returns the result in the specified buffer.
161 *
162 * Side effects:
163 *	None.
164 *
165 *----------------------------------------------------------------------
166 */
167
168void
169TkpPrintWindowId(buf, window)
170    char *buf;			/* Pointer to string large enough to hold
171				 * the hex representation of a pointer. */
172    Window window;		/* Window to be printed into buffer. */
173{
174    HWND hwnd = (window) ? Tk_GetHWND(window) : 0;
175    /*
176     * Use pointer representation, because Win64 is P64 (*not* LP64).
177     * Windows doesn't print the 0x for %p, so we do it.
178     */
179    sprintf(buf, "0x%p", hwnd);
180}
181
182/*
183 *----------------------------------------------------------------------
184 *
185 * TkpScanWindowId --
186 *
187 *	Given a string which represents the platform dependent window
188 *	handle, produce the X Window id for the window.
189 *
190 * Results:
191 *	The return value is normally TCL_OK;  in this case *idPtr
192 *	will be set to the X Window id equivalent to string.  If
193 *	string is improperly formed then TCL_ERROR is returned and
194 *	an error message will be left in the interp's result.  If the
195 *	number does not correspond to a Tk Window, then *idPtr will
196 *	be set to None.
197 *
198 * Side effects:
199 *	None.
200 *
201 *----------------------------------------------------------------------
202 */
203
204int
205TkpScanWindowId(interp, string, idPtr)
206    Tcl_Interp *interp;		/* Interpreter to use for error reporting. */
207    CONST char *string;		/* String containing a (possibly signed)
208				 * integer in a form acceptable to strtol. */
209    Window *idPtr;		/* Place to store converted result. */
210{
211    Tk_Window tkwin;
212    Window number;
213
214    /*
215     * We want sscanf for the 64-bit check, but if that doesn't work,
216     * then Tcl_GetInt manages the error correctly.
217     */
218    if (
219#ifdef _WIN64
220	(sscanf(string, "0x%p", &number) != 1) &&
221#endif
222	Tcl_GetInt(interp, string, (int *)&number) != TCL_OK) {
223	return TCL_ERROR;
224    }
225
226    tkwin = Tk_HWNDToWindow((HWND)number);
227    if (tkwin) {
228	*idPtr = Tk_WindowId(tkwin);
229    } else {
230	*idPtr = None;
231    }
232    return TCL_OK;
233}
234
235/*
236 *----------------------------------------------------------------------
237 *
238 * TkpMakeWindow --
239 *
240 *	Creates a Windows window object based on the current attributes
241 *	of the specified TkWindow.
242 *
243 * Results:
244 *	Returns a pointer to a new TkWinDrawable cast to a Window.
245 *
246 * Side effects:
247 *	Creates a new window.
248 *
249 *----------------------------------------------------------------------
250 */
251
252Window
253TkpMakeWindow(winPtr, parent)
254    TkWindow *winPtr;
255    Window parent;
256{
257    HWND parentWin;
258    int style;
259    HWND hwnd;
260
261    if (parent != None) {
262	parentWin = Tk_GetHWND(parent);
263	style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
264    } else {
265	parentWin = NULL;
266	style = WS_POPUP | WS_CLIPCHILDREN;
267    }
268
269    /*
270     * Create the window, then ensure that it is at the top of the
271     * stacking order.
272     */
273
274    hwnd = CreateWindowEx(WS_EX_NOPARENTNOTIFY, TK_WIN_CHILD_CLASS_NAME, NULL,
275	    style, Tk_X(winPtr), Tk_Y(winPtr), Tk_Width(winPtr),
276	    Tk_Height(winPtr), parentWin, NULL, Tk_GetHINSTANCE(), NULL);
277    SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
278		    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
279    return Tk_AttachHWND((Tk_Window)winPtr, hwnd);
280}
281
282/*
283 *----------------------------------------------------------------------
284 *
285 * XDestroyWindow --
286 *
287 *	Destroys the given window.
288 *
289 * Results:
290 *	None.
291 *
292 * Side effects:
293 *	Sends the WM_DESTROY message to the window and then destroys
294 *	it the Win32 resources associated with the window.
295 *
296 *----------------------------------------------------------------------
297 */
298
299void
300XDestroyWindow(display, w)
301    Display* display;
302    Window w;
303{
304    Tcl_HashEntry *entryPtr;
305    TkWinDrawable *twdPtr = (TkWinDrawable *)w;
306    TkWindow *winPtr = TkWinGetWinPtr(w);
307    HWND hwnd = Tk_GetHWND(w);
308    ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
309            Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
310
311    display->request++;
312
313    /*
314     * Remove references to the window in the pointer module then
315     * release the drawable.
316     */
317
318    TkPointerDeadWindow(winPtr);
319
320    entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, (char*)hwnd);
321    if (entryPtr != NULL) {
322	Tcl_DeleteHashEntry(entryPtr);
323    }
324
325    ckfree((char *)twdPtr);
326
327    /*
328     * Don't bother destroying the window if we are going to destroy
329     * the parent later.
330     */
331
332    if (hwnd != NULL && !(winPtr->flags & TK_DONT_DESTROY_WINDOW)) {
333	DestroyWindow(hwnd);
334    }
335}
336
337/*
338 *----------------------------------------------------------------------
339 *
340 * XMapWindow --
341 *
342 *	Cause the given window to become visible.
343 *
344 * Results:
345 *	None
346 *
347 * Side effects:
348 *	Causes the window state to change, and generates a MapNotify
349 *	event.
350 *
351 *----------------------------------------------------------------------
352 */
353
354void
355XMapWindow(display, w)
356    Display* display;
357    Window w;
358{
359    XEvent event;
360    TkWindow *parentPtr;
361    TkWindow *winPtr = TkWinGetWinPtr(w);
362
363    display->request++;
364
365    ShowWindow(Tk_GetHWND(w), SW_SHOWNORMAL);
366    winPtr->flags |= TK_MAPPED;
367
368    /*
369     * Check to see if this window is visible now.  If all of the parent
370     * windows up to the first toplevel are mapped, then this window and
371     * its mapped children have just become visible.
372     */
373
374    if (!(winPtr->flags & TK_TOP_HIERARCHY)) {
375	for (parentPtr = winPtr->parentPtr; ;
376	        parentPtr = parentPtr->parentPtr) {
377	    if ((parentPtr == NULL) || !(parentPtr->flags & TK_MAPPED)) {
378		return;
379	    }
380	    if (parentPtr->flags & TK_TOP_HIERARCHY) {
381		break;
382	    }
383	}
384    } else {
385	event.type = MapNotify;
386	event.xmap.serial = display->request;
387	event.xmap.send_event = False;
388	event.xmap.display = display;
389	event.xmap.event = winPtr->window;
390	event.xmap.window = winPtr->window;
391	event.xmap.override_redirect = winPtr->atts.override_redirect;
392	Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
393    }
394
395    /*
396     * Generate VisibilityNotify events for this window and its mapped
397     * children.
398     */
399
400    event.type = VisibilityNotify;
401    event.xvisibility.serial = display->request;
402    event.xvisibility.send_event = False;
403    event.xvisibility.display = display;
404    event.xvisibility.window = winPtr->window;
405    event.xvisibility.state = VisibilityUnobscured;
406    NotifyVisibility(&event, winPtr);
407}
408
409/*
410 *----------------------------------------------------------------------
411 *
412 * NotifyVisibility --
413 *
414 *	This function recursively notifies the mapped children of the
415 *	specified window of a change in visibility.  Note that we don't
416 *	properly report the visibility state, since Windows does not
417 *	provide that info.  The eventPtr argument must point to an event
418 *	that has been completely initialized except for the window slot.
419 *
420 * Results:
421 *	None.
422 *
423 * Side effects:
424 *	Generates lots of events.
425 *
426 *----------------------------------------------------------------------
427 */
428
429static void
430NotifyVisibility(eventPtr, winPtr)
431    XEvent *eventPtr;		/* Initialized VisibilityNotify event. */
432    TkWindow *winPtr;		/* Window to notify. */
433{
434    if (winPtr->atts.event_mask & VisibilityChangeMask) {
435	eventPtr->xvisibility.window = winPtr->window;
436	Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL);
437    }
438    for (winPtr = winPtr->childList; winPtr != NULL;
439	    winPtr = winPtr->nextPtr) {
440	if (winPtr->flags & TK_MAPPED) {
441	    NotifyVisibility(eventPtr, winPtr);
442	}
443    }
444}
445
446/*
447 *----------------------------------------------------------------------
448 *
449 * XUnmapWindow --
450 *
451 *	Cause the given window to become invisible.
452 *
453 * Results:
454 *	None
455 *
456 * Side effects:
457 *	Causes the window state to change, and generates an UnmapNotify
458 *	event.
459 *
460 *----------------------------------------------------------------------
461 */
462
463void
464XUnmapWindow(display, w)
465    Display* display;
466    Window w;
467{
468    XEvent event;
469    TkWindow *winPtr = TkWinGetWinPtr(w);
470
471    display->request++;
472
473    /*
474     * Bug fix: Don't short circuit this routine based on TK_MAPPED because
475     * it will be cleared before XUnmapWindow is called.
476     */
477
478    ShowWindow(Tk_GetHWND(w), SW_HIDE);
479    winPtr->flags &= ~TK_MAPPED;
480
481    if (winPtr->flags & TK_WIN_MANAGED) {
482	event.type = UnmapNotify;
483	event.xunmap.serial = display->request;
484	event.xunmap.send_event = False;
485	event.xunmap.display = display;
486	event.xunmap.event = winPtr->window;
487	event.xunmap.window = winPtr->window;
488	event.xunmap.from_configure = False;
489	Tk_HandleEvent(&event);
490    }
491}
492
493/*
494 *----------------------------------------------------------------------
495 *
496 * XMoveResizeWindow --
497 *
498 *	Move and resize a window relative to its parent.
499 *
500 * Results:
501 *	None.
502 *
503 * Side effects:
504 *	Repositions and resizes the specified window.
505 *
506 *----------------------------------------------------------------------
507 */
508
509void
510XMoveResizeWindow(display, w, x, y, width, height)
511    Display* display;
512    Window w;
513    int x;			/* Position relative to parent. */
514    int y;
515    unsigned int width;
516    unsigned int height;
517{
518    display->request++;
519    MoveWindow(Tk_GetHWND(w), x, y, width, height, TRUE);
520}
521
522/*
523 *----------------------------------------------------------------------
524 *
525 * XMoveWindow --
526 *
527 *	Move a window relative to its parent.
528 *
529 * Results:
530 *	None.
531 *
532 * Side effects:
533 *	Repositions the specified window.
534 *
535 *----------------------------------------------------------------------
536 */
537
538void
539XMoveWindow(display, w, x, y)
540    Display* display;
541    Window w;
542    int x;
543    int y;
544{
545    TkWindow *winPtr = TkWinGetWinPtr(w);
546
547    display->request++;
548
549    MoveWindow(Tk_GetHWND(w), x, y, winPtr->changes.width,
550	    winPtr->changes.height, TRUE);
551}
552
553/*
554 *----------------------------------------------------------------------
555 *
556 * XResizeWindow --
557 *
558 *	Resize a window.
559 *
560 * Results:
561 *	None.
562 *
563 * Side effects:
564 *	Resizes the specified window.
565 *
566 *----------------------------------------------------------------------
567 */
568
569void
570XResizeWindow(display, w, width, height)
571    Display* display;
572    Window w;
573    unsigned int width;
574    unsigned int height;
575{
576    TkWindow *winPtr = TkWinGetWinPtr(w);
577
578    display->request++;
579
580    MoveWindow(Tk_GetHWND(w), winPtr->changes.x, winPtr->changes.y, width,
581	    height, TRUE);
582}
583
584/*
585 *----------------------------------------------------------------------
586 *
587 * XRaiseWindow --
588 *
589 *	Change the stacking order of a window.
590 *
591 * Results:
592 *	None.
593 *
594 * Side effects:
595 *	Changes the stacking order of the specified window.
596 *
597 *----------------------------------------------------------------------
598 */
599
600void
601XRaiseWindow(display, w)
602    Display* display;
603    Window w;
604{
605    HWND window = Tk_GetHWND(w);
606
607    display->request++;
608    SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0,
609	    SWP_NOMOVE | SWP_NOSIZE);
610}
611
612/*
613 *----------------------------------------------------------------------
614 *
615 * XConfigureWindow --
616 *
617 *	Change the size, position, stacking, or border of the specified
618 *	window.
619 *
620 * Results:
621 *	None.
622 *
623 * Side effects:
624 *	Changes the attributes of the specified window.  Note that we
625 *	ignore the passed in values and use the values stored in the
626 *	TkWindow data structure.
627 *
628 *----------------------------------------------------------------------
629 */
630
631void
632XConfigureWindow(display, w, value_mask, values)
633    Display* display;
634    Window w;
635    unsigned int value_mask;
636    XWindowChanges* values;
637{
638    TkWindow *winPtr = TkWinGetWinPtr(w);
639    HWND hwnd = Tk_GetHWND(w);
640
641    display->request++;
642
643    /*
644     * Change the shape and/or position of the window.
645     */
646
647    if (value_mask & (CWX|CWY|CWWidth|CWHeight)) {
648	MoveWindow(hwnd, winPtr->changes.x, winPtr->changes.y,
649		winPtr->changes.width, winPtr->changes.height, TRUE);
650    }
651
652    /*
653     * Change the stacking order of the window.
654     */
655
656    if (value_mask & CWStackMode) {
657	HWND sibling;
658	if ((value_mask & CWSibling) && (values->sibling != None)) {
659	    sibling = Tk_GetHWND(values->sibling);
660	} else {
661	    sibling = NULL;
662	}
663	TkWinSetWindowPos(hwnd, sibling, values->stack_mode);
664    }
665}
666
667/*
668 *----------------------------------------------------------------------
669 *
670 * XClearWindow --
671 *
672 *	Clears the entire window to the current background color.
673 *
674 * Results:
675 *	None.
676 *
677 * Side effects:
678 *	Erases the current contents of the window.
679 *
680 *----------------------------------------------------------------------
681 */
682
683void
684XClearWindow(display, w)
685    Display* display;
686    Window w;
687{
688    RECT rc;
689    HBRUSH brush;
690    HPALETTE oldPalette, palette;
691    TkWindow *winPtr;
692    HWND hwnd = Tk_GetHWND(w);
693    HDC dc = GetDC(hwnd);
694
695    palette = TkWinGetPalette(display->screens[0].cmap);
696    oldPalette = SelectPalette(dc, palette, FALSE);
697
698    display->request++;
699
700    winPtr = TkWinGetWinPtr(w);
701    brush = CreateSolidBrush(winPtr->atts.background_pixel);
702    GetWindowRect(hwnd, &rc);
703    rc.right = rc.right - rc.left;
704    rc.bottom = rc.bottom - rc.top;
705    rc.left = rc.top = 0;
706    FillRect(dc, &rc, brush);
707
708    DeleteObject(brush);
709    SelectPalette(dc, oldPalette, TRUE);
710    ReleaseDC(hwnd, dc);
711}
712
713/*
714 *----------------------------------------------------------------------
715 *
716 * XChangeWindowAttributes --
717 *
718 *	This function is called when the attributes on a window are
719 *	updated.  Since Tk maintains all of the window state, the only
720 *	relevant value is the cursor.
721 *
722 * Results:
723 *	None.
724 *
725 * Side effects:
726 *	May cause the mouse position to be updated.
727 *
728 *----------------------------------------------------------------------
729 */
730
731void
732XChangeWindowAttributes(display, w, valueMask, attributes)
733    Display* display;
734    Window w;
735    unsigned long valueMask;
736    XSetWindowAttributes* attributes;
737{
738    if (valueMask & CWCursor) {
739	XDefineCursor(display, w, attributes->cursor);
740    }
741}
742
743/*
744 *----------------------------------------------------------------------
745 *
746 * TkWinSetWindowPos --
747 *
748 *	Adjust the stacking order of a window relative to a second
749 *	window (or NULL).
750 *
751 * Results:
752 *	None.
753 *
754 * Side effects:
755 *	Moves the specified window in the stacking order.
756 *
757 *----------------------------------------------------------------------
758 */
759
760void
761TkWinSetWindowPos(hwnd, siblingHwnd, pos)
762    HWND hwnd;			/* Window to restack. */
763    HWND siblingHwnd;		/* Sibling window. */
764    int pos;			/* One of Above or Below. */
765{
766    HWND temp;
767
768    /*
769     * Since Windows does not support Above mode, we place the
770     * specified window below the sibling and then swap them.
771     */
772
773    if (siblingHwnd) {
774	if (pos == Above) {
775	    SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0,
776		    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
777	    temp = hwnd;
778	    hwnd = siblingHwnd;
779	    siblingHwnd = temp;
780	}
781    } else {
782	siblingHwnd = (pos == Above) ? HWND_TOP : HWND_BOTTOM;
783    }
784
785    SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0,
786	    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
787}
788
789/*
790 *----------------------------------------------------------------------
791 *
792 * TkpWindowWasRecentlyDeleted --
793 *
794 *	Determines whether we know if the window given as argument was
795 *	recently deleted. Called by the generic code error handler to
796 *	handle BadWindow events.
797 *
798 * Results:
799 *	Always 0. We do not keep this information on Windows.
800 *
801 * Side effects:
802 *	None.
803 *
804 *----------------------------------------------------------------------
805 */
806
807int
808TkpWindowWasRecentlyDeleted(win, dispPtr)
809    Window win;
810    TkDisplay *dispPtr;
811{
812    return 0;
813}
814