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