Posted to tcl by patthoyts at Mon Dec 07 15:27:53 GMT 2009view pretty

Fix extended window manager hints for Tk menus
Also add a 'wm attributes . -type' option to apply a type hint to any
Tk window from script. This is needed for tooltip windows, combobox
dropdown windows and splash screens and probably others.

Not quite complete yet. Reading the hints back from the window
need to do the reverse lookup still.

diff --git a/unix/tkUnixWm.c b/unix/tkUnixWm.c
index c4ec9af..fcdcac1 100644
--- a/unix/tkUnixWm.c
+++ b/unix/tkUnixWm.c
@@ -52,12 +52,12 @@ typedef struct {
 
 typedef enum {
     WMATT_ALPHA, WMATT_TOPMOST, WMATT_ZOOMED, WMATT_FULLSCREEN,
-    _WMATT_LAST_ATTRIBUTE
+    WMATT_TYPE, _WMATT_LAST_ATTRIBUTE
 } WmAttribute;
 
 static const char *WmAttributeNames[] = {
     "-alpha", "-topmost", "-zoomed", "-fullscreen",
-    NULL
+    "-type", NULL
 };
 
 /*
@@ -347,6 +347,8 @@ static void		UpdateTitle(TkWindow *winPtr);
 static void		UpdatePhotoIcon(TkWindow *winPtr);
 static void		UpdateVRootGeometry(WmInfo *wmPtr);
 static void		UpdateWmProtocols(WmInfo *wmPtr);
+static int		SetNetWmType(TkWindow *winPtr, Tcl_Obj *typePtr);
+static Tcl_Obj *	GetNetWmType(TkWindow *winPtr);
 static void 		SetNetWmState(TkWindow*, const char *atomName, int on);
 static void 		CheckNetWmState(WmInfo *, Atom *atoms, int numAtoms);
 static void 		UpdateNetWmState(WmInfo *);
@@ -1277,6 +1279,10 @@ WmSetAttribute(
 	SetNetWmState(winPtr, "_NET_WM_STATE_ABOVE",
 		wmPtr->reqState.topmost);
 	break;
+    case WMATT_TYPE:
+	if (TCL_OK != SetNetWmType(winPtr, value))
+	    return TCL_ERROR;
+	break;
     case WMATT_ZOOMED:
 	if (TCL_OK != Tcl_GetBooleanFromObj(interp, value,
 		&wmPtr->reqState.zoomed)) {
@@ -1330,6 +1336,8 @@ WmGetAttribute(
 	return Tcl_NewBooleanObj(wmPtr->attributes.zoomed);
     case WMATT_FULLSCREEN:
 	return Tcl_NewBooleanObj(wmPtr->attributes.fullscreen);
+    case WMATT_TYPE:
+	return GetNetWmType(winPtr);
     case _WMATT_LAST_ATTRIBUTE:	/*NOTREACHED*/
 	break;
     }
@@ -5325,6 +5333,108 @@ UpdateHints(
     XSetWMHints(winPtr->display, wmPtr->wrapperPtr->window, &wmPtr->hints);
 }
  
+struct ExwmhLookup {
+    const char *name;
+    const char *hint;
+};
+struct ExwmhLookup exwmh_map[] = {
+    { "desktop", "_NET_WM_WINDOW_TYPE_DESKTOP" },
+    { "dock", "_NET_WM_WINDOW_TYPE_DOCK" },
+    { "toolbar", "_NET_WM_WINDOW_TYPE_TOOLBAR" },
+    { "menu", "_NET_WM_WINDOW_TYPE_MENU" },
+    { "utility", "_NET_WM_WINDOW_TYPE_UTILITY" },
+    { "splash", "_NET_WM_WINDOW_TYPE_SPLASH" },
+    { "dialog", "_NET_WM_WINDOW_TYPE_DIALOG" },
+    { "dropdown", "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU" },
+    { "popup", "_NET_WM_WINDOW_TYPE_POPUP_MENU" },
+    { "tooltip", "_NET_WM_WINDOW_TYPE_TOOLTIP" },
+    { "notification", "_NET_WM_WINDOW_TYPE_NOTIFICATION" },
+    { "combo", "_NET_WM_WINDOW_TYPE_COMBO" },
+    { "dnd", "_NET_WM_WINDOW_TYPE_DND" },
+    { "normal", "_NET_WM_WINDOW_TYPE_NORMAL" },
+    { NULL, NULL }
+};
+
+int
+SetNetWmType(TkWindow *winPtr, Tcl_Obj *typePtr)
+{
+    Atom typeAtom, *atoms;
+    WmInfo *wmPtr;
+    TkWindow *wrapperPtr;
+    Tcl_Obj **objv;
+    int objc, n, index;
+    Tk_Window tkwin = (Tk_Window)winPtr;
+    Tcl_Interp *interp = Tk_Interp(tkwin);
+
+    if (TCL_OK != Tcl_ListObjGetElements(interp, typePtr, &objc, &objv)) {
+	return TCL_ERROR;
+    }
+
+    if (objc == 0) {
+	return TCL_OK;
+    }
+
+    if (!Tk_HasWrapper(tkwin)) {
+	return TCL_OK; /* error?? */
+    }
+
+    atoms = (Atom *)ckalloc(sizeof(Atom) * objc);
+    
+    for (n = 0; n < objc; ++n) {
+	if (TCL_OK != Tcl_GetIndexFromObjStruct(interp, objv[n], exwmh_map,
+		sizeof(exwmh_map[0]), "type", 0, &index)) {
+	    ckfree((char *)atoms);
+	    return TCL_ERROR;
+	}
+	atoms[n] = Tk_InternAtom(tkwin, exwmh_map[index].hint);
+    }
+
+    wmPtr = winPtr->wmInfoPtr;
+    if (wmPtr->wrapperPtr == NULL) {
+	CreateWrapper(wmPtr);
+    }
+    wrapperPtr = wmPtr->wrapperPtr;
+
+    typeAtom = Tk_InternAtom(tkwin, "_NET_WM_WINDOW_TYPE");
+    XChangeProperty(Tk_Display(tkwin), wrapperPtr->window, typeAtom,
+	XA_ATOM, 32, PropModeReplace, (unsigned char *) atoms, objc);
+
+    ckfree((char *)atoms);
+    return TCL_OK;
+}
+ 
+Tcl_Obj *
+GetNetWmType(TkWindow *winPtr)
+{
+    Atom typeAtom, actualType, *atoms;
+    int actualFormat;
+    unsigned long n, count, bytesAfter;
+    unsigned char *propertyValue = NULL;
+    long maxLength = 1024;
+    Tk_Window tkwin = (Tk_Window)winPtr;
+    TkWindow *wrapperPtr = winPtr->wmInfoPtr->wrapperPtr;
+    Tcl_Obj *typePtr;
+    Tcl_Interp *interp;
+
+    interp = Tk_Interp(tkwin);
+    typePtr = Tcl_NewListObj(0, NULL);
+
+    typeAtom = Tk_InternAtom(tkwin, "_NET_WM_WINDOW_TYPE");
+    if (Success == XGetWindowProperty(wrapperPtr->display,
+	    wrapperPtr->window, typeAtom, 0L, maxLength, False,
+	    XA_ATOM, &actualType, &actualFormat, &count,
+	    &bytesAfter, &propertyValue)) {
+	atoms = (Atom *)propertyValue;
+	for (n = 0; n < count; ++n) {
+	    Tcl_ListObjAppendElement(interp, typePtr,
+		Tcl_NewStringObj(Tk_GetAtomName(tkwin, atoms[n]), -1));
+	}
+	XFree(propertyValue);
+    }
+
+    return typePtr;
+}
+ 
 /*
  *--------------------------------------------------------------
  *
@@ -6599,6 +6709,19 @@ GetMaxSize(
     }
 }
  
+void
+TkSetTransientFor(Tk_Window tkwin, Tk_Window parent)
+{
+    if (parent == NULL) {
+	parent = Tk_Parent(tkwin);
+	while (!Tk_IsTopLevel(parent))
+	    parent = Tk_Parent(tkwin);
+    }
+    XSetTransientForHint(Tk_Display(tkwin),
+                         ((TkWindow *)tkwin)->wmInfoPtr->wrapperPtr->window,
+                         ((TkWindow *)parent)->wmInfoPtr->wrapperPtr->window);
+}
+ 
 /*
  *----------------------------------------------------------------------
  *
@@ -6628,6 +6751,7 @@ TkpMakeMenuWindow(
     WmInfo *wmPtr;
     XSetWindowAttributes atts;
     TkWindow *wrapperPtr;
+    Atom atom;
 
     if (!Tk_HasWrapper(tkwin)) {
 	return;
@@ -6640,10 +6764,17 @@ TkpMakeMenuWindow(
     if (transient) {
 	atts.override_redirect = True;
 	atts.save_under = True;
+	atom = Tk_InternAtom((Tk_Window) tkwin, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU");
     } else {
 	atts.override_redirect = False;
 	atts.save_under = False;
+	atom = Tk_InternAtom((Tk_Window) tkwin, "_NET_WM_WINDOW_TYPE_MENU");
+	TkSetTransientFor(tkwin, NULL);
     }
+    XChangeProperty(Tk_Display(tkwin), wrapperPtr->window,
+    Tk_InternAtom((Tk_Window) tkwin, "_NET_WM_WINDOW_TYPE"),
+	XA_ATOM, 32, PropModeReplace,
+	(unsigned char *) &atom, 1);
 
     /*
      * The override-redirect and save-under bits must be set on the wrapper