summaryrefslogtreecommitdiffstats
path: root/win/tkWinWm.c
diff options
context:
space:
mode:
authorandreas_kupries <akupries@shaw.ca>2001-01-02 19:13:01 (GMT)
committerandreas_kupries <akupries@shaw.ca>2001-01-02 19:13:01 (GMT)
commitc0b09e4b7f03aedca6f8753d549e82609548053f (patch)
treed3a7da2f8998914ebb2e96d440791a79c0911f2d /win/tkWinWm.c
parentcce7834230051d955c96f9a6c71ff86ec6037191 (diff)
downloadtk-c0b09e4b7f03aedca6f8753d549e82609548053f.zip
tk-c0b09e4b7f03aedca6f8753d549e82609548053f.tar.gz
tk-c0b09e4b7f03aedca6f8753d549e82609548053f.tar.bz2
2001-01-02 Andreas Kupries <a.kupries@westend.com>
* win/tkWinWm.c: * doc/wm.n: Applied patch #102833 (TIP #8).
Diffstat (limited to 'win/tkWinWm.c')
-rw-r--r--win/tkWinWm.c939
1 files changed, 903 insertions, 36 deletions
diff --git a/win/tkWinWm.c b/win/tkWinWm.c
index bb2ad54..a587f35 100644
--- a/win/tkWinWm.c
+++ b/win/tkWinWm.c
@@ -12,7 +12,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tkWinWm.c,v 1.27 2000/11/03 01:22:17 hobbs Exp $
+ * RCS: @(#) $Id: tkWinWm.c,v 1.28 2001/01/02 19:13:02 andreas_kupries Exp $
*/
#include "tkWinInt.h"
@@ -53,6 +53,80 @@ typedef struct ProtocolHandler {
#define HANDLER_SIZE(cmdLength) \
((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))
+/*
+ * This structure represents the contents of a icon, in terms of its
+ * image. The HICON is an internal Windows format. Most of these
+ * icon-specific-structures originated with the Winico extension.
+ * We stripped out unused parts of that code, and integrated the
+ * code more naturally with Tcl.
+ */
+typedef struct {
+ UINT Width, Height, Colors; /* Width, Height and bpp */
+ LPBYTE lpBits; /* ptr to DIB bits */
+ DWORD dwNumBytes; /* how many bytes? */
+ LPBITMAPINFO lpbi; /* ptr to header */
+ LPBYTE lpXOR; /* ptr to XOR image bits */
+ LPBYTE lpAND; /* ptr to AND image bits */
+ HICON hIcon; /* DAS ICON */
+} ICONIMAGE, *LPICONIMAGE;
+/*
+ * This structure is how we represent a block of the above
+ * items. We will reallocate these structures according to
+ * how many images they need to contain.
+ */
+typedef struct {
+ int nNumImages; /* How many images? */
+ ICONIMAGE IconImages[1]; /* Image entries */
+} BlockOfIconImages, *BlockOfIconImagesPtr;
+/*
+ * These two structures are used to read in icons from an
+ * 'icon directory' (i.e. the contents of a .icr file, say).
+ * We only use these structures temporarily, since we copy
+ * the information we want into a BlockOfIconImages.
+ */
+typedef struct {
+ BYTE bWidth; /* Width of the image */
+ BYTE bHeight; /* Height of the image (times 2) */
+ BYTE bColorCount; /* Number of colors in image (0 if >=8bpp) */
+ BYTE bReserved; /* Reserved */
+ WORD wPlanes; /* Color Planes */
+ WORD wBitCount; /* Bits per pixel */
+ DWORD dwBytesInRes; /* how many bytes in this resource? */
+ DWORD dwImageOffset; /* where in the file is this image */
+} ICONDIRENTRY, *LPICONDIRENTRY;
+typedef struct {
+ WORD idReserved; /* Reserved */
+ WORD idType; /* resource type (1 for icons) */
+ WORD idCount; /* how many images? */
+ ICONDIRENTRY idEntries[1]; /* the entries for each image */
+} ICONDIR, *LPICONDIR;
+
+/*
+ * A pointer to one of these strucutures is associated with each
+ * toplevel. This allows us to free up all memory associated with icon
+ * resources when a window is deleted or if the window's icon is
+ * changed. They are simply reference counted according to:
+ *
+ * (i) how many WmInfo structures point to this object
+ * (ii) whether the ThreadSpecificData defined in this file contains
+ * a pointer to this object.
+ *
+ * The former count is for windows whose icons are individually
+ * set, and the latter is for the global default icon choice.
+ *
+ * Icons loaded from .icr/.icr use the iconBlock field, icons
+ * loaded from .exe/.dll use the hIcon field.
+ */
+typedef struct WinIconInstance {
+ int refCount; /* Number of instances that share this
+ * data structure. */
+ BlockOfIconImagesPtr iconBlock;
+ /* Pointer to icon resource data for
+ * image. */
+} WinIconInstance;
+
+typedef struct WinIconInstance *WinIconPtr;
+
/*
* A data structure of the following type holds window-manager-related
* information for each top-level window in an application.
@@ -165,6 +239,8 @@ typedef struct TkWmInfo {
* property, or NULL. */
int flags; /* Miscellaneous flags, defined below. */
int numTransients; /* number of transients on this window */
+ WinIconPtr iconPtr; /* pointer to titlebar icon structure for
+ * this window, or NULL. */
struct TkWmInfo *nextPtr; /* Next in list of all top-level windows. */
} WmInfo;
@@ -256,6 +332,8 @@ typedef struct ThreadSpecificData {
* been initialized. */
int firstWindow; /* Flag, cleared when the first window
* is mapped in a non-iconic state. */
+ WinIconPtr iconPtr; /* IconPtr being used as default for all
+ * toplevels, or NULL. */
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
@@ -269,7 +347,6 @@ static int initialized; /* Flag indicating whether module has
* been initialized. */
TCL_DECLARE_MUTEX(winWmMutex)
-
/*
* Forward declarations for procedures defined in this file:
*/
@@ -307,11 +384,267 @@ static LRESULT CALLBACK WmProc _ANSI_ARGS_((HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam));
static void WmWaitVisibilityProc _ANSI_ARGS_((
ClientData clientData, XEvent *eventPtr));
+static BlockOfIconImagesPtr ReadIconFromICOFile _ANSI_ARGS_((
+ Tcl_Interp *interp, char* fileName));
+static WinIconPtr ReadIconFromFile _ANSI_ARGS_((
+ Tcl_Interp *interp, char *fileName));
+static int ReadICOHeader _ANSI_ARGS_((Tcl_Channel channel));
+static BOOL AdjustIconImagePointers _ANSI_ARGS_((LPICONIMAGE lpImage));
+static HICON MakeIconFromResource _ANSI_ARGS_((LPICONIMAGE lpIcon));
+static HICON GetIcon _ANSI_ARGS_((WinIconPtr titlebaricon,
+ int icon_size));
+static int WinSetIcon _ANSI_ARGS_((Tcl_Interp *interp,
+ WinIconPtr titlebaricon, Tk_Window tkw));
+static void FreeIconBlock _ANSI_ARGS_((BlockOfIconImagesPtr lpIR));
+static void DecrIconRefCount _ANSI_ARGS_((WinIconPtr titlebaricon));
+
+/* Used in BytesPerLine */
+#define WIDTHBYTES(bits) ((((bits) + 31)>>5)<<2)
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DIBNumColors --
+ *
+ * Calculates the number of entries in the color table, given by
+ * LPSTR lpbi - pointer to the CF_DIB memory block. Used by
+ * titlebar icon code.
+ *
+ * Results:
+ *
+ * WORD - Number of entries in the color table.
+ *
+ * Side effects: None.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static WORD DIBNumColors( LPSTR lpbi )
+{
+ WORD wBitCount;
+ DWORD dwClrUsed;
+
+ dwClrUsed = ((LPBITMAPINFOHEADER) lpbi)->biClrUsed;
+
+ if (dwClrUsed)
+ return (WORD) dwClrUsed;
+
+ wBitCount = ((LPBITMAPINFOHEADER) lpbi)->biBitCount;
+
+ switch (wBitCount)
+ {
+ case 1: return 2;
+ case 4: return 16;
+ case 8: return 256;
+ default:return 0;
+ }
+}
/*
*----------------------------------------------------------------------
*
- * InitWm --
+ * PaletteSize --
+ *
+ * Calculates the number of bytes in the color table, as given by
+ * LPSTR lpbi - pointer to the CF_DIB memory block. Used by
+ * titlebar icon code.
+ *
+ * Results:
+ * number of bytes in the color table
+ *
+ * Side effects: None.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static WORD PaletteSize( LPSTR lpbi )
+{
+ return ((WORD)( DIBNumColors( lpbi ) * sizeof( RGBQUAD )) );
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindDIBits --
+ *
+ * Locate the image bits in a CF_DIB format DIB, as given by
+ * LPSTR lpbi - pointer to the CF_DIB memory block. Used by
+ * titlebar icon code.
+ *
+ * Results:
+ * pointer to the image bits
+ *
+ * Side effects: None
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static LPSTR FindDIBBits( LPSTR lpbi )
+{
+ return ( lpbi + *(LPDWORD)lpbi + PaletteSize( lpbi ) );
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * BytesPerLine --
+ *
+ * Calculates the number of bytes in one scan line, as given by
+ * LPBITMAPINFOHEADER lpBMIH - pointer to the BITMAPINFOHEADER
+ * that begins the CF_DIB block. Used by titlebar icon code.
+ *
+ * Results:
+ * number of bytes in one scan line (DWORD aligned)
+ *
+ * Side effects: None
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static DWORD BytesPerLine( LPBITMAPINFOHEADER lpBMIH )
+{
+ return WIDTHBYTES(lpBMIH->biWidth * lpBMIH->biPlanes * lpBMIH->biBitCount);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AdjustIconImagePointers --
+ *
+ * Adjusts internal pointers in icon resource struct, as given
+ * by LPICONIMAGE lpImage - the resource to handle. Used by
+ * titlebar icon code.
+ *
+ * Results:
+ * BOOL - TRUE for success, FALSE for failure
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+BOOL AdjustIconImagePointers( LPICONIMAGE lpImage )
+{
+ /* Sanity check */
+ if (lpImage==NULL)
+ return FALSE;
+ /* BITMAPINFO is at beginning of bits */
+ lpImage->lpbi = (LPBITMAPINFO)lpImage->lpBits;
+ /* Width - simple enough */
+ lpImage->Width = lpImage->lpbi->bmiHeader.biWidth;
+ /* Icons are stored in funky format where height is doubled - account for it */
+ lpImage->Height = (lpImage->lpbi->bmiHeader.biHeight)/2;
+ /* How many colors? */
+ lpImage->Colors = lpImage->lpbi->bmiHeader.biPlanes * lpImage->lpbi->bmiHeader.biBitCount;
+ /* XOR bits follow the header and color table */
+ lpImage->lpXOR = (LPBYTE)FindDIBBits(((LPSTR)lpImage->lpbi));
+ /* AND bits follow the XOR bits */
+ lpImage->lpAND = lpImage->lpXOR + (lpImage->Height*BytesPerLine((LPBITMAPINFOHEADER)(lpImage->lpbi)));
+ return TRUE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MakeIconFromResource --
+ *
+ * Construct an actual HICON structure from the information
+ * in a resource.
+ *
+ * Results:
+ *
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static HICON MakeIconFromResource( LPICONIMAGE lpIcon ){
+ HICON hIcon ;
+ static FARPROC pfnCreateIconFromResourceEx=NULL;
+ static int initinfo=0;
+ /* Sanity Check */
+ if (lpIcon == NULL)
+ return NULL;
+ if (lpIcon->lpBits == NULL)
+ return NULL;
+ if (!initinfo) {
+ HMODULE hMod = GetModuleHandleA("USER32.DLL");
+ initinfo=1;
+ if(hMod){
+ pfnCreateIconFromResourceEx = GetProcAddress(hMod,"CreateIconFromResourceEx");
+ }
+ }
+ /* Let the OS do the real work :) */
+ if (pfnCreateIconFromResourceEx!=NULL) {
+ hIcon = (HICON) (pfnCreateIconFromResourceEx)
+ (lpIcon->lpBits, lpIcon->dwNumBytes, TRUE, 0x00030000,
+ (*(LPBITMAPINFOHEADER)(lpIcon->lpBits)).biWidth,
+ (*(LPBITMAPINFOHEADER)(lpIcon->lpBits)).biHeight/2, 0 );
+ } else {
+ hIcon = NULL;
+ }
+ /* It failed, odds are good we're on NT so try the non-Ex way */
+ if (hIcon == NULL) {
+ /* We would break on NT if we try with a 16bpp image */
+ if (lpIcon->lpbi->bmiHeader.biBitCount != 16) {
+ hIcon = CreateIconFromResource( lpIcon->lpBits, lpIcon->dwNumBytes, TRUE, 0x00030000 );
+ }
+ }
+ return hIcon;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadICOHeader --
+ *
+ * Reads the header from an ICO file, as specfied by channel.
+ *
+ * Results:
+ * UINT - Number of images in file, -1 for failure.
+ * If this succeeds, there is a decent chance this is a
+ * valid icon file.
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static int ReadICOHeader( Tcl_Channel channel )
+{
+ WORD Input;
+ DWORD dwBytesRead;
+
+ /* Read the 'reserved' WORD */
+ dwBytesRead = Tcl_Read( channel, (char*)&Input, sizeof( WORD ));
+ /* Did we get a WORD? */
+ if (dwBytesRead != sizeof( WORD ))
+ return -1;
+ /* Was it 'reserved' ? (ie 0) */
+ if (Input != 0)
+ return -1;
+ /* Read the type WORD */
+ dwBytesRead = Tcl_Read( channel, (char*)&Input, sizeof( WORD ));
+ /* Did we get a WORD? */
+ if (dwBytesRead != sizeof( WORD ))
+ return -1;
+ /* Was it type 1? */
+ if (Input != 1)
+ return -1;
+ /* Get the count of images */
+ dwBytesRead = Tcl_Read( channel, (char*)&Input, sizeof( WORD ));
+ /* Did we get a WORD? */
+ if (dwBytesRead != sizeof( WORD ))
+ return -1;
+ /* Return the count */
+ return (int)Input;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InitWindowClass --
*
* This routine creates the Wm toplevel decorative frame class.
*
@@ -323,38 +656,36 @@ static void WmWaitVisibilityProc _ANSI_ARGS_((
*
*----------------------------------------------------------------------
*/
-
-static void
-InitWm(void)
-{
+static int InitWindowClass(WinIconPtr titlebaricon) {
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
- Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
WNDCLASS * classPtr;
+ classPtr = &toplevelClass;
if (! tsdPtr->initialized) {
tsdPtr->initialized = 1;
tsdPtr->firstWindow = 1;
+ tsdPtr->iconPtr = NULL;
}
if (! initialized) {
Tcl_MutexLock(&winWmMutex);
if (! initialized) {
initialized = 1;
- classPtr = &toplevelClass;
- /*
- * When threads are enabled, we cannot use CLASSDC because
- * threads will then write into the same device context.
- *
- * This is a hack; we should add a subsystem that manages
- * device context on a per-thread basis. See also tkWinX.c,
- * which also initializes a WNDCLASS structure.
- */
+ /*
+ * When threads are enabled, we cannot use CLASSDC because
+ * threads will then write into the same device context.
+ *
+ * This is a hack; we should add a subsystem that manages
+ * device context on a per-thread basis. See also tkWinX.c,
+ * which also initializes a WNDCLASS structure.
+ */
-#ifdef TCL_THREADS
+ #ifdef TCL_THREADS
classPtr->style = CS_HREDRAW | CS_VREDRAW;
-#else
+ #else
classPtr->style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
-#endif
+ #endif
classPtr->cbClsExtra = 0;
classPtr->cbWndExtra = 0;
classPtr->hInstance = Tk_GetHINSTANCE();
@@ -362,7 +693,19 @@ InitWm(void)
classPtr->lpszMenuName = NULL;
classPtr->lpszClassName = TK_WIN_TOPLEVEL_CLASS_NAME;
classPtr->lpfnWndProc = WmProc;
- classPtr->hIcon = LoadIcon(Tk_GetHINSTANCE(), "tk");
+ if (titlebaricon == NULL) {
+ classPtr->hIcon = LoadIcon(Tk_GetHINSTANCE(), "tk");
+ } else {
+ classPtr->hIcon = GetIcon(titlebaricon, ICON_BIG);
+ if (classPtr->hIcon == NULL) {
+ return TCL_ERROR;
+ }
+ /*
+ * Store pointer to default icon so we know when
+ * we need to free that information
+ */
+ tsdPtr->iconPtr = titlebaricon;
+ }
classPtr->hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClass(classPtr)) {
@@ -371,6 +714,456 @@ InitWm(void)
}
Tcl_MutexUnlock(&winWmMutex);
}
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InitWm --
+ *
+ * This initialises the window manager
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Registers a new window class.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+InitWm(void)
+{
+ /* Ignore return result */
+ (void) InitWindowClass(NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WinSetIcon --
+ *
+ * Sets either the default toplevel titlebar icon, or the icon
+ * for a specific toplevel (if tkw is given, then only that
+ * window is used).
+ *
+ * The ref-count of the titlebaricon is NOT changed. If this
+ * function returns successfully, the caller should assume
+ * the icon was used (and therefore the ref-count should
+ * be adjusted to reflect that fact). If the function returned
+ * an error, the caller should assume the icon was not used
+ * (and may wish to free the memory associated with it).
+ *
+ * Results:
+ * A standard Tcl return code.
+ *
+ * Side effects:
+ * One or all windows may have their icon changed.
+ * The Tcl result may be modified.
+ * The window-manager will be initialised if it wasn't already.
+ * The given window will be forced into existence.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+WinSetIcon(interp, titlebaricon, tkw)
+ Tcl_Interp *interp;
+ WinIconPtr titlebaricon;
+ Tk_Window tkw;
+{
+ WmInfo *wmPtr;
+ HWND hwnd;
+ int application = 0;
+
+ if (tkw == NULL) {
+ tkw = Tk_MainWindow(interp);
+ application = 1;
+ }
+
+ if (!(Tk_IsTopLevel(tkw))) {
+ Tcl_AppendResult(interp, "window \"", Tk_PathName(tkw),
+ "\" isn't a top-level window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Tk_WindowId(tkw) == None) {
+ Tk_MakeWindowExist(tkw);
+ }
+ /* We must get the window's wrapper, not the window itself */
+ wmPtr = ((TkWindow*)tkw)->wmInfoPtr;
+ hwnd = wmPtr->wrapper;
+
+ if (application) {
+ if (hwnd == NULL) {
+ /*
+ * I don't actually think this is ever the correct thing, unless
+ * perhaps the window doesn't have a wrapper. But I believe all
+ * windows have wrappers.
+ */
+ hwnd = Tk_GetHWND(Tk_WindowId(tkw));
+ }
+ /*
+ * If we aren't initialised, then just initialise with the user's
+ * icon. Otherwise our icon choice will be ignored moments later
+ * when Tk finishes initialising.
+ */
+ if (!initialized) {
+ if (InitWindowClass(titlebaricon) != TCL_OK) {
+ Tcl_AppendResult(interp,"Unable to set icon", (char*)NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ ThreadSpecificData *tsdPtr;
+ if (!SetClassLong(hwnd, GCL_HICONSM, (LPARAM)GetIcon(titlebaricon, ICON_SMALL))) {
+ /*
+ * For some reason this triggers, even though it seems
+ * to be successful This is probably related to the
+ * WNDCLASS vs WNDCLASSEX difference. Anyway it seems
+ * we have to ignore errors returned here.
+ */
+
+ /*
+ * Tcl_AppendResult(interp,"Unable to set new small icon", (char*)NULL);
+ * return TCL_ERROR;
+ */
+ }
+ if (!SetClassLong(hwnd, GCL_HICON, (LPARAM)GetIcon(titlebaricon, ICON_BIG))) {
+ Tcl_AppendResult(interp,"Unable to set new icon", (char*)NULL);
+ return TCL_ERROR;
+ }
+ tsdPtr = (ThreadSpecificData *)
+ Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
+ if (tsdPtr->iconPtr != NULL) {
+ DecrIconRefCount(tsdPtr->iconPtr);
+ }
+ tsdPtr->iconPtr = titlebaricon;
+ }
+ } else {
+ if (!initialized) {
+ /*
+ * Need to initialise the wm otherwise we will fail on
+ * code which tries to set a toplevel's icon before that
+ * happens. Ignore return result.
+ */
+ (void)InitWindowClass(NULL);
+ }
+ /*
+ * The following code is exercised if you do
+ *
+ * toplevel .t ; wm titlebaricon .t foo.icr
+ *
+ * i.e. the wm hasn't had time to properly create
+ * the '.t' window before you set the icon.
+ */
+ if (hwnd == NULL) {
+ /*
+ * This little snippet is copied from the 'Map' function,
+ * and should probably be placed in one proper location
+ */
+ if (wmPtr->titleUid == NULL) {
+ wmPtr->titleUid = wmPtr->winPtr->nameUid;
+ }
+ UpdateWrapper(wmPtr->winPtr);
+ wmPtr = ((TkWindow*)tkw)->wmInfoPtr;
+ hwnd = wmPtr->wrapper;
+ if (hwnd == NULL) {
+ Tcl_AppendResult(interp,"Can't set icon; window has no wrapper.", (char*)NULL);
+ return TCL_ERROR;
+ }
+ }
+ SendMessage(hwnd,WM_SETICON,ICON_SMALL,(LPARAM)GetIcon(titlebaricon, ICON_SMALL));
+ SendMessage(hwnd,WM_SETICON,ICON_BIG,(LPARAM)GetIcon(titlebaricon, ICON_BIG));
+
+ /* Update the iconPtr we keep for each WmInfo structure. */
+ if (wmPtr->iconPtr != NULL) {
+ /* Free any old icon ptr which is associated with this window. */
+ DecrIconRefCount(wmPtr->iconPtr);
+ }
+ /*
+ * We do not need to increment the ref count for the
+ * titlebaricon, because it was already incremented when we
+ * retrieved it.
+ */
+ wmPtr->iconPtr = titlebaricon;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadIconFromFile --
+ *
+ * Read the contents of a file (usually .ico, .icr) and extract an
+ * icon resource, if possible, otherwise NULL is returned, and an
+ * error message will already be in the interpreter.
+ *
+ * Results:
+ * A WinIconPtr structure containing the icons in the file, with
+ * its ref count already incremented. The calling procedure should
+ * either place this structure inside a WmInfo structure, or it should
+ * pass it on to DecrIconRefCount() to ensure no memory leaks occur.
+ *
+ * If the given fileName did not contain a valid icon structure,
+ * return NULL.
+ *
+ * Side effects:
+ * Memory is allocated for the returned structure and the icons
+ * it contains. If the structure is not wanted, it should be
+ * passed to DecrIconRefCount, and in any case a valid ref count
+ * should be ensured to avoid memory leaks.
+ *
+ * Currently icon resources are not shared, so the ref count of
+ * one of these structures will always be 0 or 1. However all we
+ * need do is implement some sort of lookup function between
+ * filenames and WinIconPtr structures and no other code will need
+ * to be changed. The pseudo-code for this is implemented below
+ * in the 'if (0)' branch. It did not seem necessary to implement
+ * this optimisation here, since moving to icon<->image
+ * conversions will probably make it obsolete.
+ *
+ *----------------------------------------------------------------------
+ */
+static WinIconPtr
+ReadIconFromFile(interp, fileName)
+ Tcl_Interp *interp;
+ char *fileName;
+{
+ WinIconPtr titlebaricon = NULL;
+
+ if (0 /* If we already have an icon for this filename */) {
+ titlebaricon = NULL; /* Get the real value from a lookup */
+ titlebaricon->refCount++;
+ return titlebaricon;
+ } else {
+ BlockOfIconImagesPtr lpIR = ReadIconFromICOFile(interp, fileName);
+ if (lpIR != NULL) {
+ titlebaricon = (WinIconPtr) ckalloc(sizeof(WinIconInstance));
+ titlebaricon->iconBlock = lpIR;
+ titlebaricon->refCount = 1;
+ }
+ return titlebaricon;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DecrIconRefCount --
+ *
+ * Reduces the reference count.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If the ref count falls to zero, free the memory associated
+ * with the icon resource structures. In this case the pointer
+ * passed into this function is no longer valid.
+ *
+ *----------------------------------------------------------------------
+ */
+static void DecrIconRefCount(WinIconPtr titlebaricon) {
+ titlebaricon->refCount--;
+
+ if (titlebaricon->refCount <= 0) {
+ if (titlebaricon->iconBlock != NULL) {
+ FreeIconBlock(titlebaricon->iconBlock);
+ }
+ titlebaricon->iconBlock = NULL;
+
+ ckfree((char*)titlebaricon);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FreeIconBlock --
+ *
+ * Frees all memory associated with a previously loaded
+ * titlebaricon. The icon block pointer is no longer
+ * valid once this function returns.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static void FreeIconBlock(BlockOfIconImagesPtr lpIR) {
+ int i;
+
+ /* Free all the bits */
+ for (i=0; i< lpIR->nNumImages; i++) {
+ if (lpIR->IconImages[i].lpBits != NULL) {
+ ckfree((char*)lpIR->IconImages[i].lpBits);
+ }
+ if (lpIR->IconImages[i].hIcon != NULL) {
+ DestroyIcon(lpIR->IconImages[i].hIcon);
+ }
+ }
+ ckfree ((char*)lpIR);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetIcon --
+ *
+ * Extracts an icon of a given size from an icon resource
+ *
+ * Results:
+ * Returns the icon, if found, else NULL.
+ *
+ * Side effects:
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+static HICON GetIcon(WinIconPtr titlebaricon, int icon_size) {
+ BlockOfIconImagesPtr lpIR = titlebaricon->iconBlock;
+ if (lpIR == NULL) {
+ return NULL;
+ } else {
+ unsigned int size = (icon_size == 0 ? 16 : 32);
+ int i;
+
+ for (i = 0; i < lpIR->nNumImages; i++) {
+ /* Take the first or a 32x32 16 color icon*/
+ if((lpIR->IconImages[i].Height == size)
+ && (lpIR->IconImages[i].Width == size)
+ && (lpIR->IconImages[i].Colors >= 4)) {
+ return lpIR->IconImages[i].hIcon;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReadIconFromICOFile --
+ *
+ * Reads an Icon Resource from an ICO file, as given by
+ * char* fileName - Name of the ICO file. This name should
+ * be in Utf format.
+ *
+ * Results:
+ * Returns an icon resource, if found, else NULL.
+ *
+ * Side effects:
+ * May leave error messages in the Tcl interpreter.
+ *
+ *----------------------------------------------------------------------
+ */
+BlockOfIconImagesPtr ReadIconFromICOFile(Tcl_Interp* interp, char* fileName){
+ BlockOfIconImagesPtr lpIR , lpNew ;
+ Tcl_Channel channel;
+ int i;
+ DWORD dwBytesRead;
+ LPICONDIRENTRY lpIDE;
+
+ /* Open the file */
+ if ((channel = Tcl_OpenFileChannel(interp, fileName, "r", 0)) == NULL) {
+ Tcl_AppendResult(interp,"Error opening file \"", fileName,
+ "\" for reading",(char*)NULL);
+ return NULL;
+ }
+ if (Tcl_SetChannelOption(interp, channel, "-translation", "binary")
+ != TCL_OK) {
+ return NULL;
+ }
+ if (Tcl_SetChannelOption(interp, channel, "-encoding", "binary")
+ != TCL_OK) {
+ return NULL;
+ }
+ /* Allocate memory for the resource structure */
+ if ((lpIR = (BlockOfIconImagesPtr) ckalloc( sizeof(BlockOfIconImages) )) == NULL) {
+ Tcl_AppendResult(interp,"Error allocating memory",(char*)NULL);
+ Tcl_Close(NULL, channel);
+ return NULL;
+ }
+ /* Read in the header */
+ if ((lpIR->nNumImages = ReadICOHeader( channel )) == -1) {
+ Tcl_AppendResult(interp,"Invalid file header",(char*)NULL);
+ Tcl_Close(NULL, channel);
+ ckfree((char*) lpIR );
+ return NULL;
+ }
+ /* Adjust the size of the struct to account for the images */
+ if ((lpNew = (BlockOfIconImagesPtr) ckrealloc( (char*)lpIR, sizeof(BlockOfIconImages) + ((lpIR->nNumImages-1) * sizeof(ICONIMAGE)) )) == NULL) {
+ Tcl_AppendResult(interp,"Error allocating memory",(char*)NULL);
+ Tcl_Close(NULL, channel);
+ ckfree( (char*)lpIR );
+ return NULL;
+ }
+ lpIR = lpNew;
+ /* Allocate enough memory for the icon directory entries */
+ if ((lpIDE = (LPICONDIRENTRY) ckalloc( lpIR->nNumImages * sizeof( ICONDIRENTRY ) ) ) == NULL) {
+ Tcl_AppendResult(interp,"Error allocating memory",(char*)NULL);
+ Tcl_Close(NULL, channel);
+ ckfree( (char*)lpIR );
+ return NULL;
+ }
+ /* Read in the icon directory entries */
+ dwBytesRead = Tcl_Read( channel, (char*)lpIDE, lpIR->nNumImages * sizeof( ICONDIRENTRY ));
+ if (dwBytesRead != lpIR->nNumImages * sizeof( ICONDIRENTRY )) {
+ Tcl_AppendResult(interp,"Error reading file",(char*)NULL);
+ Tcl_Close(NULL, channel);
+ ckfree( (char*)lpIR );
+ return NULL;
+ }
+ /* Loop through and read in each image */
+ for( i = 0; i < lpIR->nNumImages; i++ ) {
+ /* Allocate memory for the resource */
+ if ((lpIR->IconImages[i].lpBits = (LPBYTE) ckalloc(lpIDE[i].dwBytesInRes)) == NULL)
+ {
+ Tcl_AppendResult(interp,"Error allocating memory",(char*)NULL);
+ Tcl_Close(NULL, channel);
+ ckfree( (char*)lpIR );
+ ckfree( (char*)lpIDE );
+ return NULL;
+ }
+ lpIR->IconImages[i].dwNumBytes = lpIDE[i].dwBytesInRes;
+ /* Seek to beginning of this image */
+ if (Tcl_Seek(channel, lpIDE[i].dwImageOffset, FILE_BEGIN) == -1) {
+ Tcl_AppendResult(interp,"Error seeking in file",(char*)NULL);
+ Tcl_Close(NULL, channel);
+ ckfree( (char*)lpIR );
+ ckfree( (char*)lpIDE );
+ return NULL;
+ }
+ /* Read it in */
+ dwBytesRead = Tcl_Read( channel, lpIR->IconImages[i].lpBits, lpIDE[i].dwBytesInRes);
+ if (dwBytesRead != lpIDE[i].dwBytesInRes) {
+ Tcl_AppendResult(interp,"Error reading file",(char*)NULL);
+ Tcl_Close(NULL, channel);
+ ckfree( (char*)lpIDE );
+ ckfree( (char*)lpIR );
+ return NULL;
+ }
+ /* Set the internal pointers appropriately */
+ if (!AdjustIconImagePointers( &(lpIR->IconImages[i]))) {
+ Tcl_AppendResult(interp,"Error converting to internal format",(char*)NULL);
+ Tcl_Close(NULL, channel);
+ ckfree( (char*)lpIDE );
+ ckfree( (char*)lpIR );
+ return NULL;
+ }
+ lpIR->IconImages[i].hIcon=MakeIconFromResource(&(lpIR->IconImages[i]));
+ }
+ /* Clean up */
+ ckfree((char*)lpIDE);
+ Tcl_Close(NULL, channel);
+ if (lpIR == NULL){
+ Tcl_AppendResult(interp,"Reading of ",fileName," failed!",(char*)NULL);
+ return NULL;
+ }
+ return lpIR;
}
/*
@@ -389,7 +1182,6 @@ InitWm(void)
*
*----------------------------------------------------------------------
*/
-
static TkWindow *
GetTopLevel(hwnd)
HWND hwnd;
@@ -625,6 +1417,7 @@ TkWmNewWindow(winPtr)
wmPtr->cmdArgv = NULL;
wmPtr->clientMachine = NULL;
wmPtr->flags = WM_NEVER_MAPPED;
+ wmPtr->iconPtr = NULL;
wmPtr->nextPtr = winPtr->dispPtr->firstWmPtr;
winPtr->dispPtr->firstWmPtr = wmPtr;
@@ -673,6 +1466,8 @@ UpdateWrapper(winPtr)
HWND child = TkWinGetHWND(winPtr->window);
int x, y, width, height, state;
WINDOWPLACEMENT place;
+ HICON hSmallIcon = NULL;
+ HICON hBigIcon = NULL;
Tcl_DString titleString;
int *childStateInfo = NULL;
ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
@@ -795,6 +1590,11 @@ UpdateWrapper(winPtr)
#endif
}
oldWrapper = SetParent(child, wmPtr->wrapper);
+ if (oldWrapper) {
+ hSmallIcon = (HICON) SendMessage(oldWrapper,WM_GETICON,ICON_SMALL,(LPARAM)NULL);
+ hBigIcon = (HICON) SendMessage(oldWrapper,WM_GETICON,ICON_BIG,(LPARAM)NULL);
+ }
+
if (oldWrapper && (oldWrapper != wmPtr->wrapper)
&& (oldWrapper != GetDesktopWindow())) {
#ifdef _WIN64
@@ -834,7 +1634,7 @@ UpdateWrapper(winPtr)
wmPtr->flags &= ~WM_NEVER_MAPPED;
SendMessage(wmPtr->wrapper, TK_ATTACHWINDOW, (WPARAM) child, 0);
-
+
/*
* Force an initial transition from withdrawn to the real
* initial state.
@@ -844,6 +1644,13 @@ UpdateWrapper(winPtr)
wmPtr->hints.initial_state = WithdrawnState;
TkpWmSetState(winPtr, state);
+ if (hSmallIcon != NULL) {
+ SendMessage(wmPtr->wrapper,WM_SETICON,ICON_SMALL,(LPARAM)hSmallIcon);
+ }
+ if (hBigIcon != NULL) {
+ SendMessage(wmPtr->wrapper,WM_SETICON,ICON_BIG,(LPARAM)hBigIcon);
+ }
+
/*
* If we are embedded then force a mapping of the window now,
* because we do not necessarily own the wrapper and may not
@@ -1158,6 +1965,15 @@ TkWmDeadWindow(winPtr)
DestroyWindow(Tk_GetHWND(winPtr->window));
}
}
+ if (wmPtr->iconPtr != NULL) {
+ /*
+ * This may delete the icon resource data. I believe we
+ * should do this after destroying the decorative frame,
+ * because the decorative frame is using this icon.
+ */
+ DecrIconRefCount(wmPtr->iconPtr);
+ }
+
ckfree((char *) wmPtr);
winPtr->wmInfoPtr = NULL;
}
@@ -1674,15 +2490,25 @@ Tk_WmCmd(clientData, interp, argc, argv)
}
} else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
&& (length >= 5)) {
- Pixmap pixmap;
-
- if ((argc != 3) && (argc != 4)) {
+ /* If true, then set for all windows. */
+ int isDefault = 0;
+
+ if ((argc < 3) || (argc > 5)) {
Tcl_AppendResult(interp, "wrong # arguments: must be \"",
- argv[0], " iconbitmap window ?bitmap?\"",
+ argv[0], " iconbitmap window ?-default? ?image?\"",
(char *) NULL);
return TCL_ERROR;
- }
- if (argc == 3) {
+ } else if (argc == 5) {
+ /* If we have 5 arguments, we must have a '-default' flag */
+ if (strcmp(argv[3],"-default")) {
+ Tcl_AppendResult(interp, "illegal option \"",
+ argv[3], " must be \"-default\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ isDefault = 1;
+ } else if (argc == 3) {
+ /* No arguments were given */
if (wmPtr->hints.flags & IconPixmapHint) {
Tcl_SetResult(interp,
Tk_NameOfBitmap(winPtr->display,
@@ -1690,19 +2516,60 @@ Tk_WmCmd(clientData, interp, argc, argv)
}
return TCL_OK;
}
- if (*argv[3] == '\0') {
+ if (*argv[argc-1] == '\0') {
if (wmPtr->hints.icon_pixmap != None) {
Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
}
wmPtr->hints.flags &= ~IconPixmapHint;
} else {
- pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr,
- Tk_GetUid(argv[3]));
- if (pixmap == None) {
- return TCL_ERROR;
+ /*
+ * In the future this block of code will use Tk's 'image'
+ * functionality to allow all supported image formats.
+ * However, this will require a change to the way icons are
+ * handled. We will need to add icon<->image conversions
+ * routines.
+ *
+ * Until that happens we simply try to find an icon in the
+ * given argument, and if that fails, we use the older
+ * bitmap code. We do things this way round (icon then
+ * bitmap), because the bitmap code actually seems to have
+ * no visible effect, so we want to give the icon code the
+ * first try at doing something.
+ */
+
+ /*
+ * Either return NULL, or return a valid titlebaricon with its
+ * ref count already incremented.
+ */
+ WinIconPtr titlebaricon = ReadIconFromFile(interp, argv[argc-1]);
+ if (titlebaricon != NULL) {
+ /*
+ * Try to set the icon for the window. If it is a '-default'
+ * icon, we must pass in NULL
+ */
+ if (WinSetIcon(interp, titlebaricon,
+ (isDefault ? NULL : (Tk_Window) winPtr)) != TCL_OK) {
+ /* We didn't use the titlebaricon after all */
+ DecrIconRefCount(titlebaricon);
+ titlebaricon = NULL;
+ }
+ }
+ if (titlebaricon == NULL) {
+ /*
+ * We didn't manage to handle the argument as a valid
+ * icon. Try as a bitmap. First we must clear the
+ * error message which was placed in the interpreter
+ */
+ Pixmap pixmap;
+ Tcl_ResetResult(interp);
+ pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr,
+ Tk_GetUid(argv[argc-1]));
+ if (pixmap == None) {
+ return TCL_ERROR;
+ }
+ wmPtr->hints.icon_pixmap = pixmap;
+ wmPtr->hints.flags |= IconPixmapHint;
}
- wmPtr->hints.icon_pixmap = pixmap;
- wmPtr->hints.flags |= IconPixmapHint;
}
} else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
&& (length >= 5)) {