XTkShellWidgetsImpl.mesa
Copyright Ó 1988, 1989, 1990, 1991, 1992 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, October 18, 1988 11:22:01 am PDT
Christian Jacobi, August 13, 1993 10:30 am PDT
DIRECTORY
Ascii,
Atom,
Random,
Rope,
RopeList,
Xl,
XlICCCMTypes,
XlConventions,
XlDispatch,
XlPredefinedAtoms,
XTk,
XTkDB,
XTkIcon,
XTkFriends,
XTkMigration,
XTkNotification,
XTkPrivate,
XTkShellWidgets;
XTkShellWidgetsImpl:
CEDAR
MONITOR
IMPORTS Atom, Random, Rope, RopeList, XlDispatch, Xl, XlConventions, XTk, XTkDB, XTkFriends, XTkIcon, XTkMigration, XTkNotification, XTkPrivate
EXPORTS XTkShellWidgets
SHARES Xl, XTk =
BEGIN OPEN Xl, XTk, XTkShellWidgets;
shellClass: ImplementorClass ¬ XTkFriends.CreateClass[[key: $top, wDataNum: 1, preferredSizeLR: ShellPreferredSizeLR, preStopFastAccess: ShellPreStopFastAccess, configureLR: ShellConfigureLR, initInstPart: ShellInitInstPart, className: ShellClassName, eventMask: [structureNotify: TRUE], backgroundKey: $white, removeChildLR: ShellRemoveChildLR, internalEnumerateChildren: ShellInternalEnumerateChildren, forgetScreenLR: ShellForgetScreenLR, bindScreenLX: ShellBindScreenLX]];
ToWindow:
PROC [x:
REF
READONLY
ANY]
RETURNS [w: Xl.Window ¬ Xl.nullWindow] = {
WITH x
SELECT
FROM
widget: REF READONLY WidgetRep => w ¬ widget.window;
window: REF READONLY Window => w ¬ window;
ENDCASE => {};
};
IsShell:
PUBLIC
PROC [w: XTk.Widget]
RETURNS [
BOOL] = {
RETURN [XTk.HasClass[w, shellClass]];
};
connectionDeadSet: EventFilter ~ Xl.CreateEventFilter[finalEvent];
ConnectionDiedHandler: EventProcType = {
--on rootTQ
SELECT event.type
FROM
finalEvent => {
ev: Xl.FinalEvent ~ NARROW[event];
shell: Widget ~ NARROW[clientData];
IF ~ev.refCountTransition
AND shell.connection=ev.connection
AND shell.state<=screened
THEN {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
closure: REF Closure ¬ shellIP.connectionDiedClosure;
XTkFriends.PreStopFastAccess[shell, errorConnection];
shellIP.connection ¬ NIL;
IF closure=
NIL
THEN DestroyShell[shell]
ELSE closure.proc[shell, closure.registerData, $connectionDied, ev];
};
};
ENDCASE => {};
};
shellRootEvents: Xl.EventFilter ~ Xl.CreateEventFilter[destroyNotify, configureNotify];
shellOtherEvents: Xl.EventFilter ~ Xl.CreateEventFilter[clientMessage];
OtherShellEvents: <<NOT rootTQ>> Xl.EventProcType = {
shell: Widget ~ NARROW[clientData];
shellIP: ShellInstPart ~ GetShellInstPart[shell];
IF shell.state>realized OR shell.fastAccessAllowed#ok THEN RETURN;
SELECT event.type
FROM
clientMessage => {
client: Xl.ClientMessageEvent ~ NARROW[event];
IF client.window#shell.window OR client.format#32 THEN RETURN;
IF client.typeAtom#shellIP.wmProtocolsAtom THEN RETURN;
SELECT client.w[0]
FROM
shellIP.cachedWmAtom => {
SetFocus[shell, [client.w[1]], NIL];
};
Xl.MakeAtom[shell.connection, "WMLETE←WINDOW"] => {
IF shellIP.deletionProtocol
THEN {
closure: REF Closure ¬ shellIP.wmDeletionClosure;
IF closure=
NIL
THEN DestroyByWindowManager[shell, NIL, $wmDeleteWindow, client]
ELSE closure.proc[shell, closure.registerData, $wmDeleteWindow, client];
};
};
ENDCASE => RETURN;
};
ENDCASE => {};
};
ShellEventLR: <<on rootTQ>> Xl.EventProcType = {
ENABLE ABORTED => GOTO oops;
shell: Widget ~ NARROW[clientData];
shellIP: ShellInstPart ~ GetShellInstPart[shell];
IF shell.state>realized OR shell.fastAccessAllowed#ok THEN RETURN;
SELECT event.type
FROM
configureNotify => {
g: Geometry;
cn: ConfigureNotifyEvent ~ NARROW[event];
IF shellIP.crazyShell
THEN {
IF cn.window#shellIP.parentWindow THEN RETURN;
}
ELSE {
IF cn.window#shell.window THEN RETURN;
shell.actual.pos ¬ cn.geometry.pos;
shell.actual.borderWidth ¬ cn.geometry.borderWidth;
};
IF shell.actual.size#cn.geometry.size
THEN {
g ¬ [size: cn.geometry.size, pos: [dontUse, dontUse], borderWidth: dontUse];
--Note: The sizing has not yet happened! .
ShellCheatConfigureLR[shell, g];
};
};
destroyNotify => {
d: Xl.DestroyNotifyEvent ~ NARROW[event];
SELECT d.window
FROM
shellIP.parentWindow, shell.window => {
closure: REF Closure ¬ shellIP.windowDiedClosure;
IF shell.connection#d.connection OR shell.state>screened THEN RETURN;
XTkFriends.PreStopFastAccess[shell, errorWindow];
IF closure=
NIL
THEN DestroyShell[shell]
ELSE closure.proc[shell, closure.registerData, $destroyNotify, d];
};
ENDCASE => {}
};
ENDCASE => {};
EXITS oops => NULL;
};
ShellRemoveChildLR: XTk.RemoveChildProc = {
shellIP: ShellInstPart ~ GetShellInstPart[widget];
oldChild: XTk.Widget ¬ shellIP.child;
IF oldChild#
NIL
THEN {
shellIP.child ¬ NIL;
done ¬ TRUE;
XTkFriends.ForgetScreenLR[oldChild];
};
};
AddChildLR:
PROC [shell: XTk.Widget, newChild: Widget] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
oldChild: Widget ¬ shellIP.child;
IF oldChild=newChild THEN RETURN;
SELECT newChild.parent
FROM
NIL, shell => XTkFriends.AssignParentAndCheckScreenLR[newChild, shell];
ENDCASE => ERROR;
IF oldChild#
NIL
THEN {
shellIP.child ¬ NIL;
XTkFriends.OrphanizeLR[oldChild, normal];
};
IF newChild.s.mapping=dontUse THEN newChild.s.mapping ¬ mapped;
shellIP.child ¬ newChild;
NoteChildChange[shell];
XTkFriends.ReconfigureChildrenLR[shell];
};
ShellInternalEnumerateChildren: XTk.InternalEnumerateChildrenProc = {
child: XTk.Widget ¬ ShellChild[self];
IF child#
NIL
AND child.state<dead
THEN {
stop ¬ proc[self, child, data].stop
};
};
ShellCheatConfigureLR:
PROC [shell: XTk.Widget, geometry: Geometry] = {
CheatConfigureLR: ConfigureProc = {
IF geometry.pos.x # dontUse THEN widget.actual.pos.x ¬ geometry.pos.x;
IF geometry.pos.y # dontUse THEN widget.actual.pos.y ¬ geometry.pos.y;
IF geometry.size.width # dontUse THEN widget.actual.size.width ¬ geometry.size.width;
IF geometry.size.height # dontUse THEN widget.actual.size.height ¬ geometry.size.height;
IF geometry.borderWidth # dontUse THEN widget.actual.borderWidth ¬ geometry.borderWidth;
IF mapping # dontUse THEN widget.actualMapping ¬ mapping;
BEGIN
c: Xl.Connection ~ widget.connection;
win: Xl.Window ~ widget.window;
Xl.ConfigureWindow[c: c, window: win, geometry: geometry];
END;
ShellPropagateConfigureLR[widget, FALSE];
XTkFriends.CallNotifiers[widget, $ShellPostReconfigure];
IF widget.fastAccessAllowed=ok THEN Xl.Flush[widget.connection];
};
IF geometry.pos.x = shell.actual.pos.x THEN geometry.pos.x ¬ dontUse;
IF geometry.pos.y = shell.actual.pos.y THEN geometry.pos.y ¬ dontUse;
IF geometry.size.width = shell.actual.size.width THEN geometry.size.width ¬ dontUse;
IF geometry.size.height = shell.actual.size.height THEN geometry.size.height ¬ dontUse;
IF geometry.borderWidth = shell.actual.borderWidth THEN geometry.borderWidth ¬ dontUse;
IF geometry#[[dontUse, dontUse], [dontUse, dontUse], dontUse]
THEN {
CheatConfigureLR[shell, geometry, dontUse, FALSE];
};
};
DestroyByWindowManager: <<any thread>> XTk.WidgetNotifyProc = {
IF widget.fastAccessAllowed=ok
THEN {
XTkFriends.PreStopFastAccess[widget, normal];
IF widget.actualMapping=mapped
THEN {
Xl.UnmapWindow[widget.connection, widget.window, XTkPrivate.detailsForNoErrors];
};
};
DestroyShell[widget];
};
ShellChild:
PROC [shell: ShellWidget]
RETURNS [Widget¬
NIL] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
RETURN [shellIP.child];
};
ShellPreferredSizeLR: PreferredSizeProc = {
child: Widget = ShellChild[widget];
maySkip[x] ¬ TRUE; maySkip[y] ¬ TRUE;
IF widget.s.geometry.size.width > 0
THEN {
maySkip[w] ¬ TRUE;
proposed.size.width ¬ widget.s.geometry.size.width;
};
IF widget.s.geometry.size.height > 0
THEN {
maySkip[h] ¬ TRUE;
proposed.size.height ¬ widget.s.geometry.size.height;
};
IF widget.s.geometry.pos.x # dontUse
THEN {
already maySkip[x] ← TRUE;
proposed.pos.x ¬ widget.s.geometry.pos.x;
};
IF widget.s.geometry.pos.y # dontUse
THEN {
already maySkip[y] ← TRUE;
proposed.pos.y ¬ widget.s.geometry.pos.y;
};
IF widget.s.geometry.borderWidth # dontUse
THEN {
maySkip[b] ¬ TRUE;
proposed.borderWidth ¬ widget.s.geometry.borderWidth;
};
IF child =
NIL
OR (maySkip[w]
AND maySkip[h]
AND maySkip[b])
THEN RETURN [proposed]
ELSE {
g: Geometry ¬ XTkFriends.PreferredSizeLR[child, NIL, proposed, maySkip];
IF g.borderWidth<0 THEN g.borderWidth ¬ 0;
preferred.size.width ¬
SELECT
TRUE
FROM
widget.s.geometry.size.width>0 => widget.s.geometry.size.width,
g.size.width>0 => g.size.width + g.borderWidth*2,
ENDCASE => dontUse;
preferred.size.height ¬
SELECT
TRUE
FROM
widget.s.geometry.size.height>0 => widget.s.geometry.size.height,
g.size.height>0 => g.size.height + g.borderWidth*2,
ENDCASE => dontUse;
};
preferred.pos ¬ widget.s.geometry.pos;
preferred.borderWidth ¬ widget.s.geometry.borderWidth;
};
EnsureRootThread:
PROC [inq: Xl.
TQ]
RETURNS [tq: Xl.
TQ] = {
IF inq=
NIL
THEN {
tq ¬ Xl.CreateTQ[$root, XTk.rootLockingOrder, FALSE]
}
ELSE {
tq ¬ inq;
IF Xl.GetLockOrderNum[tq]<XTk.rootLockingOrder THEN ERROR;
};
};
InterOpShellCreateWindowLR:
PROC [widget: Widget, shellIP: ShellInstPart] = {
connection: Xl.Connection ~ widget.connection;
gr: GeometryRec ¬ GetGeometry[connection, shellIP.parentWindow]; --crash if not a window...
IF widget.actual.pos.x<0 THEN widget.actual.pos.x ¬ 0;
IF widget.actual.pos.y<0 THEN widget.actual.pos.y ¬ 0;
IF widget.actual.borderWidth<0 THEN widget.actual.borderWidth ¬ 0;
IF widget.actual.size.width<=0
THEN
widget.actual.size.width ¬ MAX[gr.geometry.size.width - 2*widget.actual.borderWidth, 1];
IF widget.actual.size.height<=0
THEN
widget.actual.size.height ¬ MAX[gr.geometry.size.height - 2*widget.actual.borderWidth, 1];
--my own direct events
XTk.AddTemporaryMatch[widget, [proc: ShellEventLR, handles: shellRootEvents, tq: widget.rootTQ, data: widget], [structureNotify: TRUE]];
--my parents events: we need to know about resizing
AddDispatch[connection, shellIP.parentWindow, NEW[MatchRep ¬ [proc: ShellEventLR, handles: shellRootEvents, tq: widget.rootTQ, data: widget]], [structureNotify: TRUE]];
widget.window ¬ Xl.CreateWindow[c: connection, matchList: XTkFriends.CollectMatchesLR[widget], parent: shellIP.parentWindow, geometry: widget.actual, depth: widget.depth, attributes: widget.attributes];
ShellPropagateConfigureLR[widget, TRUE];
IF widget.actualMapping=mapped
THEN {
Xl.MapWindow[connection, widget.window, XTkPrivate.detailsForFlushNoErrors];
};
};
skipPos: GeometryRequest ~ [TRUE, TRUE, FALSE, FALSE, FALSE];
NormalShellCreateWindowLR:
PROC [widget: Widget, shellIP: ShellInstPart] = {
g: Geometry; cheatWM: BOOL; goodSize, goodPos: BOOL ¬ TRUE;
connection: Xl.Connection ~ widget.connection;
hints: REF XTkShellWidgets.ICCCMHints ~ GetHints[widget];
IF shellIP.parentWindow=nullWindow
THEN {
shellIP.parentWindow ¬ Xl.DefaultScreen[connection].root;
};
shellIP.wmProtocolsAtom ¬ Xl.MakeAtom[connection, "WM←PROTOCOLS"];
IF widget.s.geometry.borderWidth<0 THEN widget.s.geometry.borderWidth ¬ 0;
g ¬ XTkFriends.PreferredSizeLR[widget: widget, proposed: widget.s.geometry, maySkip: skipPos];--exceptional to query its own preference
IF g.size.width <= 0 THEN {g.size.width ¬ 40; goodSize ¬ FALSE};
IF g.size.height <= 0 THEN {g.size.width ¬ 20; goodSize ¬ FALSE};
IF g.borderWidth < 0 THEN {g.borderWidth ¬ 0};
widget.s.geometry.size ¬ g.size;
widget.s.geometry.borderWidth ¬ g.borderWidth;
IF goodSize
THEN {
hints.wmNormalHints.obsoleteSz ¬ widget.s.geometry.size;
hints.wmNormalHints.clientSize ¬ TRUE;
hints.wmNormalHintsChanged ¬ TRUE;
};
cheatWM ¬ widget.attributes.overrideRedirect=illegal AND XlConventions.WMQueryPosition[connection];
IF cheatWM THEN widget.attributes.overrideRedirect ¬ true;
IF widget.s.geometry.pos.x = dontUse
THEN {
widget.s.geometry.pos.x ¬ Random.ChooseInt[max: 500]; goodPos ¬ FALSE
};
IF widget.s.geometry.pos.y = dontUse
THEN {
widget.s.geometry.pos.y ¬ Random.ChooseInt[max: 400]; goodPos ¬ FALSE
};
IF goodPos
THEN {
hints.wmNormalHints.obsoletePos ¬ widget.s.geometry.pos;
hints.wmNormalHintsChanged ¬ TRUE;
};
XTk.AddTemporaryMatch[widget, [proc: ShellEventLR, handles: shellRootEvents, tq: widget.rootTQ, data: widget]];
XTk.AddTemporaryMatch[widget, [proc: OtherShellEvents, handles: shellOtherEvents, tq: Xl.CreateTQ[], data: widget]];
widget.actual ¬ widget.s.geometry;
widget.window ¬ Xl.CreateWindow[c: connection, matchList: XTkFriends.CollectMatchesLR[widget], parent: shellIP.parentWindow, geometry: widget.actual, depth: widget.depth, attributes: widget.attributes];
ProtectedUpdateHints[widget];
ShellPropagateConfigureLR[widget, TRUE]; --size will probably change later if window manager orders different size...
IF widget.actualMapping=mapped
THEN {
Xl.MapWindow[connection, widget.window];
};
IF cheatWM
THEN {
widget.attributes.overrideRedirect ¬ illegal;
Xl.ChangeWindowAttributes[connection, widget.window, [overrideRedirect: false]];
};
IF shellIP.childrenWithColorMap#NIL THEN TrackColorMap[widget, NIL];
};
ShellConfigureLR: ConfigureProc = {
--Note (except for creation):
-- Ordering new size with Configure is a rare event
-- Normally sizing has already happened by window manager!
existW: BOOL ¬ widget.actualMapping<unconfigured;
createW: BOOL ¬ mapping<unconfigured AND ~existW;
IF geometry.pos.x # dontUse THEN widget.actual.pos.x ¬ geometry.pos.x;
IF geometry.pos.y # dontUse THEN widget.actual.pos.y ¬ geometry.pos.y;
IF geometry.size.width # dontUse
THEN
widget.actual.size.width ¬ geometry.size.width;
IF geometry.size.height # dontUse
THEN
widget.actual.size.height ¬ geometry.size.height;
IF geometry.borderWidth # dontUse
THEN
widget.actual.borderWidth ¬ geometry.borderWidth;
IF mapping # dontUse THEN widget.actualMapping ¬ mapping;
IF createW
THEN {
shellIP: ShellInstPart ~ GetShellInstPart[widget];
shellIP.cachedWmAtom ¬ Xl.MakeAtom[widget.connection, "WM←TAKE𡤏OCUS"];
widget.actualMapping ¬ mapping;
IF shellIP.crazyShell
THEN InterOpShellCreateWindowLR[widget, shellIP]
ELSE NormalShellCreateWindowLR[widget, shellIP];
}
ELSE {
--order new size anyway
XTkFriends.SimpleConfigureOneLevelLR[widget: widget, geometry: geometry, mapping: mapping, reConsiderChildren: reConsiderChildren];
ShellPropagateConfigureLR[widget, reConsiderChildren];
XTkFriends.CallNotifiers[widget, $ShellPostReconfigure];
};
IF widget.fastAccessAllowed=ok THEN Xl.Flush[widget.connection];
};
ShellPropagateConfigureLR:
PROC [widget: Widget, reConsiderChildren:
BOOL] = {
child: Widget = ShellChild[widget];
IF child#
NIL
THEN {
g: Geometry ¬ [size: widget.actual.size, pos: [0, 0], borderWidth: BorderWidth[child]];
childMap: Mapping ¬ IF reConsiderChildren THEN child.s.mapping ELSE dontUse;
IF g.size.width<=0 THEN g.size.width ¬ 1;
IF g.size.height<=0 THEN g.size.height ¬ 1;
IF reConsiderChildren THEN XTk.SetWidgetFlag[widget, XTk.mustReConsiderChildren, FALSE];
XTkFriends.ConfigureLR[child, g, childMap, reConsiderChildren];
};
};
ShellInstPart: TYPE = REF ShellInstPartRec;
ShellInstPartRec:
TYPE =
RECORD [
child: XTk.Widget ¬ NIL,
originalRootTQ: Xl.TQ ¬ NIL,
connection: REF ¬ NIL,
parentWindow: Window ¬ Xl.nullWindow,
className: ATOM ¬ NIL,
iconName: ROPE ¬ NIL,
packageName: ROPE ¬ NIL,
shortName: ROPE ¬ NIL,
finallyUnmapped: BOOL ¬ FALSE,
crazyShell: BOOL ¬ FALSE, --interoperability thing: parentWindow # root
hints: REF XTkShellWidgets.ICCCMHints ¬ NIL,
--normal shells only
deletionProtocol: BOOL ¬ FALSE,
wmDeletionClosure: REF Closure ¬ NIL,
windowDiedClosure: REF Closure ¬ NIL,
connectionDiedClosure: REF Closure ¬ NIL,
focusGoal: REF ¬ NIL,
focusTime: TimeStamp ¬ [0],
dontQueryGeometry: BOOL ¬ FALSE,
childrenWithColorMap: LIST OF Widget ¬ NIL,
wmProtocolsAtom: XAtom ¬ [0], --speed up !
connectionWatcherMatch: Xl.Match ¬ NIL,
cachedWmAtom: XAtom ¬ [0]
];
IsARoot:
PROC [connection: Connection, window: Window]
RETURNS [
BOOL¬
FALSE] = {
FOR i:
INT
IN [0..ScreenCount[connection])
DO
IF NthScreen[connection, i].root=window THEN RETURN [TRUE];
ENDLOOP
};
ShellInitInstPart: InitInstancePartProc = {
shellIP: ShellInstPart ~ NEW[ShellInstPartRec];
XTkFriends.AssignInstPart[widget, shellClass, shellIP];
IF widget.parent#NIL THEN ERROR;
};
Closure: TYPE = RECORD [proc: XTk.WidgetNotifyProc, registerData: REF ¬ NIL]; --use a ref to gain atomicity
RegisterCallConnectionDied:
PUBLIC
PROC [shell: ShellWidget, proc: WidgetNotifyProc, registerData:
REF ¬
NIL] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
closure: REF Closure ~ IF proc#NIL THEN NEW[Closure ¬ [proc, registerData]] ELSE NIL;
shellIP.connectionDiedClosure ¬ closure;
};
RegisterCallWindowDied:
PUBLIC PROC [shell: ShellWidget, proc: WidgetNotifyProc, registerData:
REF ¬
NIL] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
closure: REF Closure ~ IF proc#NIL THEN NEW[Closure ¬ [proc, registerData]] ELSE NIL;
shellIP.windowDiedClosure ¬ closure;
};
RegisterCallWMDeleteWindow:
PUBLIC
PROC [shell: ShellWidget, proc: XTk.WidgetNotifyProc, registerData:
REF ¬
NIL] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
closure: REF Closure ~ IF proc#NIL THEN NEW[Closure ¬ [proc, registerData]] ELSE NIL;
shellIP.wmDeletionClosure ¬ closure;
};
GetHints:
PUBLIC
PROC [shell: ShellWidget]
RETURNS [
REF XTkShellWidgets.ICCCMHints] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
IF shellIP.hints=
NIL
THEN {
shellIP.hints ¬ NEW[XTkShellWidgets.ICCCMHints];
shellIP.hints.wmHints.initialState ¬ 1;
};
RETURN [shellIP.hints];
};
UpdateHints:
PUBLIC
PROC [shell: ShellWidget] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
action: PROC = {ProtectedUpdateHints[shell]};
IF shell.fastAccessAllowed#ok THEN RETURN;
Xl.CallWithLock[shellIP.originalRootTQ, action];
};
ProtectedUpdateHints:
PROC [shell: ShellWidget] = {
c: Xl.Connection ¬ shell.connection;
shellIP: ShellInstPart ~ GetShellInstPart[shell];
h: REF XTkShellWidgets.ICCCMHints ~ GetHints[shell];
IF c=NIL OR h=NIL OR shell.fastAccessAllowed#ok THEN RETURN;
IF h.wmHintsChanged
THEN {
h.wmHintsChanged ¬ FALSE;
h.wmHints.windowGroup ¬ ToWindow[h.windowGroup];
XlConventions.SetWMHints[c, shell.window, h.wmHints];
};
IF h.wmNormalHintsChanged
THEN {
h.wmNormalHintsChanged ¬ FALSE;
XlConventions.SetWMNormalHints[c, shell.window, h.wmNormalHints];
};
IF h.iconNameChanged
THEN {
h.iconNameChanged ¬ FALSE;
XlConventions.SetIconName[c, shell.window, h.iconName];
};
IF h.windowHeaderChanged
THEN {
h.windowHeaderChanged ¬ FALSE;
XlConventions.SetWindowName[c, shell.window, h.windowHeader];
};
IF h.wmClassChanged
THEN {
h.wmClassChanged ¬ FALSE;
XlConventions.SetWMClass[c, shell.window, h.wmClassClass, h.wmClassInstance];
};
IF h.transientForChanged
THEN {
h.transientForChanged ¬ FALSE;
XlConventions.SetWMTransient[c, shell.window, ToWindow[h.transientFor]];
};
IF h.protocolsChanged
THEN {
h.protocolsChanged ¬ FALSE;
XlConventions.SetWMProtocols[c, shell.window, h.protocols];
};
Xl.Flush[c, TRUE];
};
ForgetHints:
PROC [h:
REF XTkShellWidgets.ICCCMHints] = {
IF h#
NIL
THEN {
h.wmHints.windowGroup ¬ Xl.nullWindow;
h.wmHints.iconMask ¬ Xl.nullPixmap;
h.wmHints.iconPixmap ¬ Xl.nullPixmap;
h.wmHints.iconWindow ¬ Xl.nullWindow;
h.wmHintsChanged ¬ h.wmHints#[];
h.wmNormalHintsChanged ¬ h.wmNormalHints#[];
h.windowHeaderChanged ¬ h.windowHeader#NIL;
h.iconNameChanged ¬ h.iconName#NIL;
h.transientForChanged ¬ h.transientFor#NIL;
h.wmClassChanged ¬ h.wmClassInstance#NIL OR h.wmClassClass#NIL;
h.protocolsChanged ¬ h.protocols#NIL;
};
};
CreateShell:
PUBLIC
PROC [widgetSpec: WidgetSpec, child: Widget ¬
NIL, windowHeader:
ROPE ¬
NIL, iconName:
ROPE ¬
NIL, className:
ATOM ¬
NIL, packageName:
ROPE ¬
NIL, shortName:
ROPE ¬
NIL, rootTQ:
TQ ¬
NIL, dontQueryGeometry:
BOOL ¬
FALSE, deletionProtocol:
BOOL ¬
TRUE, focusProtocol:
BOOL ¬
TRUE, standardMigration:
BOOL ¬
TRUE]
RETURNS [widget: Widget] = {
widget ¬ XTk.CreateWidget[widgetSpec, shellClass];
rootTQ ¬ EnsureRootThread[rootTQ];
BEGIN
shellIP: ShellInstPart ~ GetShellInstPart[widget];
shellIP.originalRootTQ ¬ rootTQ;
shellIP.crazyShell ¬ FALSE;
shellIP.dontQueryGeometry ¬ dontQueryGeometry;
shellIP.className ¬ (IF className#NIL THEN className ELSE $Shell);
shellIP.deletionProtocol ¬ deletionProtocol;
shellIP.packageName ¬ packageName;
IF widgetSpec.instName=
NIL
THEN {
IF ~Rope.IsEmpty[shortName] THEN widgetSpec.instName ← Atom.MakeAtom[shortName];
}
ELSE {
IF Rope.IsEmpty[shortName] THEN shortName ← Atom.GetPName[widgetSpec.instName];
};
shellIP.shortName ¬ shortName;
shellIP.iconName ¬ iconName;
END;
BEGIN
hints: REF XTkShellWidgets.ICCCMHints ¬ GetHints[widget];
IF windowHeader#
NIL
THEN {
hints.windowHeader ¬ windowHeader;
hints.windowHeaderChanged ¬ TRUE;
};
IF iconName#
NIL
THEN {
hints.iconName ¬ iconName;
hints.iconNameChanged ¬ TRUE;
};
IF className#
NIL
AND hints.wmClassClass=
NIL
THEN {
hints.wmClassClass ¬ Atom.GetPName[className];
hints.wmClassChanged ¬ TRUE;
};
IF focusProtocol THEN SetFocusMethod[shell: widget, focusProtocol: true];
IF deletionProtocol
THEN {
hints.protocols ¬ AddRope["WMLETE←WINDOW", hints.protocols];
hints.protocolsChanged ¬ TRUE;
};
END;
IF child#
NIL
THEN
AddChildLR[shell: widget, newChild: child];
IF standardMigration
THEN
XTkMigration.RegisterMigrator[widget, XTkMigration.StandardMigrator];
};
ApplicationClassName:
PUBLIC
PROC [shell: ShellWidget]
RETURNS [className
: ATOM] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
RETURN [shellIP.className]
};
BindScreenShell:
PUBLIC
PROC [shell: ShellWidget, connection:
REF ¬
NIL, parentWindow: Xl.Window ¬ Xl.nullWindow] = {
MyCreateConnection:
PROC [r: Rope.
ROPE ¬
NIL, atom:
ATOM ¬
NIL] = {
c ¬ Xl.CreateConnection[server: r, applicationKey: atom];
refCountObject ¬ NIL;
mustDecrementRefCount ¬ TRUE;
};
refCountObject: REF ¬ NIL;
mustDecrementRefCount: BOOL ¬ FALSE;
c: Xl.Connection;
screenDepth: Xl.ScreenDepth;
shellIP: ShellInstPart ~ GetShellInstPart[shell];
IF shellIP.connection#NIL THEN ERROR; --already bound
shell.connection ¬ NIL;
IF connection=
NIL
THEN {
--This is a convention by which the environment gets a chance to fill in the connection, or, make a readable error message.
XTkNotification.CallAll[$GetShellConnection, shell, shellIP.className];
c ¬ shellIP.connection ¬ shell.connection;
--The created connection has a ref count increased which needs to be undone
IF Xl.Alive[c]
THEN {
refCountObject ¬ shell;
mustDecrementRefCount ¬ TRUE;
};
};
IF ~Xl.Alive[c]
THEN {
IF connection=
NIL
THEN MyCreateConnection[]
ELSE
WITH connection
SELECT
FROM
a: ATOM => MyCreateConnection[NIL, a];
r: Rope.ROPE => MyCreateConnection[r];
rt: REF READONLY TEXT => MyCreateConnection[Rope.FromRefText[rt]];
xc: Xl.Connection => c ¬ xc;
ENDCASE => ERROR;
IF ~Xl.Alive[c]
THEN {
err: REF Xl.EventRep.errorNotify ~ NEW[Xl.EventRep.errorNotify];
err.connection ¬ c;
err.errorKind ¬ requestFromDeadConnection;
err.explanation ¬ "XTk.BindScreenShell using dead connection";
ERROR Xl.XError[err];
};
shell.connection ¬ shellIP.connection ¬ c;
};
shellIP.parentWindow ¬ parentWindow;
screenDepth ¬ Xl.QueryScreenDepth[c, shellIP.parentWindow];
IF parentWindow=Xl.nullWindow
THEN {
Try to select a more favorable screenDepth
screen: Xl.Screen ¬ screenDepth.screen;
IF screenDepth.depth#1
AND screenDepth.depth#8
THEN {
FOR sdl: Xl.ScreenDepthL ¬ screen.screenDepthL, sdl.rest
WHILE sdl#
NIL
DO
IF sdl.first.screen=screen
AND sdl.first.nVisualTypes>0
THEN {
IF sdl.first.depth=1
OR sdl.first.depth=8
THEN {
screenDepth ¬ sdl.first
};
};
ENDLOOP;
};
};
XTkFriends.BindScreenLR[widget: shell, rootTQ: shellIP.originalRootTQ, screen: screenDepth.screen, screenDepth: screenDepth];
IF mustDecrementRefCount THEN Xl.DecRefCount[c, refCountObject];
};
ShellBindScreenLX: XTk.BindScreenProc = {
shellIP: ShellInstPart ~ GetShellInstPart[widget];
shellIP.connectionWatcherMatch ¬ NEW[Xl.MatchRep ¬ [proc: ConnectionDiedHandler, handles: connectionDeadSet, tq: rootTQ, data: widget]];
XlDispatch.AddMatch[screen.connection, Xl.nullWindow, shellIP.connectionWatcherMatch, Xl.unspecifiedEvents, XTkPrivate.detailsForNoErrors];
Xl.IncRefCount[screen.connection, widget];
IF ~shellIP.dontQueryGeometry
THEN {
g: Xl.Geometry ¬ XTkDB.GetGeometryFromDB[widget];
IF g.size.width>0 THEN widget.s.geometry.size.width ¬ g.size.width;
IF g.size.height>0 THEN widget.s.geometry.size.height ¬ g.size.height;
IF g.pos.x>=0 THEN widget.s.geometry.pos.x ¬ g.pos.x;
IF g.pos.y>=0 THEN widget.s.geometry.pos.y ¬ g.pos.y;
IF g.borderWidth>=0 THEN widget.s.geometry.borderWidth ¬ g.borderWidth;
IF g.size.width>0
AND g.size.height>0
THEN {
h: REF XTkShellWidgets.ICCCMHints ~ GetHints[widget];
h.wmNormalHints.userSize ¬ h.wmNormalHints.clientSize ¬ TRUE;
h.wmNormalHintsChanged ¬ TRUE;
};
IF g.pos.y>=0
AND g.pos.x>=0
THEN {
h: REF XTkShellWidgets.ICCCMHints ~ GetHints[widget];
h.wmNormalHints.userPos ¬ h.wmNormalHints.clientPos ¬ TRUE;
h.wmNormalHintsChanged ¬ TRUE;
};
};
};
ForgetScreenShell:
PUBLIC
PROC [shell: ShellWidget] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
action:
PROC = {
shellIP.connection ¬ NIL;
XTkFriends.ForgetScreenLR[shell]
};
Xl.CallWithLock[shellIP.originalRootTQ, action];
};
RefCountData: TYPE = RECORD [c: Xl.Connection, object: REF];
ShellForgetScreenLR: XTk.TerminateProc = {
c: Xl.Connection ¬ widget.connection;
shellIP: ShellInstPart ~ GetShellInstPart[widget];
ForgetHints[shellIP.hints];
IF Xl.Alive[c]
THEN {
rcd: REF RefCountData ~ NEW[RefCountData ¬ [c, widget]]; --Save the connection-object pair as the original widget.connection might change before QueuedDecrementRefCount is done.
wm: Xl.Match ¬ shellIP.connectionWatcherMatch;
IF wm#
NIL
THEN {
XlDispatch.RemoveMatch[c, Xl.nullWindow, wm, XTkPrivate.detailsForNoErrors];
shellIP.connectionWatcherMatch ¬ NIL;
};
--enqueued to be last thing on rootTQ, and, connection stays alive for our continuation (e.g. super classes TerminateProc).
Xl.Enqueue[tq: widget.rootTQ, proc: QueuedDecrementRefCount, data: rcd];
};
};
QueuedDecrementRefCount<<old rootTQ>>: Xl.EventProcType = {
rcd: REF RefCountData ¬ NARROW[clientData];
Xl.DecRefCount[rcd.c, rcd.object];
};
CreateInteroperabilityShell:
PUBLIC
PROC [widgetSpec: WidgetSpec ¬ [], child: Widget ¬
NIL, className:
ATOM ¬
NIL, rootTQ:
TQ ¬
NIL]
RETURNS [widget: Widget] = {
widget ¬ XTk.CreateWidget[widgetSpec, shellClass];
rootTQ ¬ EnsureRootThread[rootTQ];
BEGIN
shellIP: ShellInstPart ~ GetShellInstPart[widget];
shellIP.crazyShell ¬ TRUE;
shellIP.originalRootTQ ¬ rootTQ;
shellIP.dontQueryGeometry ¬ TRUE;
shellIP.className ¬ (IF className#NIL THEN className ELSE $InterOpShell);
END;
IF child#
NIL
THEN
AddChildLR[shell: widget, newChild: child];
};
BindInteroperabilityShell:
PUBLIC
PROC [shell: ShellWidget, connection: Xl.Connection, foreignParent: Xl.Window] = {
BindScreenShell[shell, connection, foreignParent];
};
SetShellChild:
PUBLIC
PROC [shell: ShellWidget, child: Widget] = {
action:
PROC = {
IF child.s.mapping=dontUse THEN child.s.mapping ¬ mapped;
AddChildLR[shell: shell, newChild: child]
};
shellIP: ShellInstPart ~ GetShellInstPart[shell];
Xl.CallWithLock[shellIP.originalRootTQ, action];
};
ConfigureRec: TYPE = RECORD [widget: Widget, geometry: Xl.Geometry, mapping: Mapping, reConsiderChildren: BOOL];
ForkRealizeShell:
PUBLIC
PROC [shell: ShellWidget, geometry: Xl.Geometry, mapping: Mapping, reConsiderChildren:
BOOL] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
cr: REF ConfigureRec ¬ NEW[ConfigureRec ¬ [shell, geometry, mapping, reConsiderChildren]];
Xl.Enqueue[shellIP.originalRootTQ, ForkedRealizeShell, cr];
};
ForkedRealizeShell<<rootTQ>>: Xl.EventProcType = {
cr: REF ConfigureRec ¬ NARROW[clientData];
ReallyRealizeShellLR[cr.widget, cr.geometry, cr.mapping, cr.reConsiderChildren];
};
RealizeShell:
PUBLIC
PROC [shell: ShellWidget, geometry: Xl.Geometry, mapping: Mapping, reConsiderChildren:
BOOL] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
action: PROC = {ReallyRealizeShellLR[shell, geometry, mapping, reConsiderChildren]};
Xl.CallWithLock[shellIP.originalRootTQ, action];
};
ReallyRealizeShellLR:
PROC [shell: ShellWidget, geometry: Xl.Geometry, mapping: Mapping, reConsiderChildren:
BOOL] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
IF mapping=dontUse
THEN {
IF shell.actualMapping>=dontUse THEN mapping ¬ mapped
};
IF shell.state>screened THEN BindScreenShell[shell];
XTkIcon.SetIconName[shell, TRUE, shellIP.iconName];
XTkIcon.SetIconMask[shell, TRUE, shellIP.packageName, shellIP.shortName];
XTkFriends.ConfigureLR[shell, geometry, mapping, reConsiderChildren];
BEGIN
c: Xl.Connection ¬ shell.connection;
IF Xl.Alive[c] THEN Xl.Flush[c, TRUE];
END;
};
DestroyShell:
PUBLIC
PROC [shell: ShellWidget] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
action: PROC = {XTkFriends.DestroyWidgetLR[shell]};
Xl.CallWithLock[shellIP.originalRootTQ, action];
};
ShellPreStopFastAccess: TerminateProc = {
shellIP: ShellInstPart ~ GetShellInstPart[widget];
shellIP.focusTime ¬ [0];
IF ~shellIP.finallyUnmapped
AND widget.window#nullWindow
THEN {
shellIP.finallyUnmapped ¬ TRUE;
IF reason=normal AND Xl.Alive[widget.connection] THEN Xl.UnmapWindow[widget.connection, widget.window, XTkPrivate.detailsForFlushSoonNoErrors];
};
WITH shellIP.focusGoal
SELECT
FROM
w: XTk.Widget => {};
ENDCASE => shellIP.focusGoal ¬ NIL;
};
GetShellInstPart:
PROC [w: Widget]
RETURNS [ShellInstPart] =
INLINE {
RETURN [ NARROW[XTkFriends.InstPart[w, shellClass]] ];
};
EntryTrackCM:
ENTRY
PROC [shell: ShellWidget, shellIP: ShellInstPart, w: Widget] = {
ENABLE UNWIND => NULL;
IF shellIP#
NIL
THEN {
cnt: INT ¬ 1;
lag: LIST OF Widget ¬ shellIP.childrenWithColorMap;
IF w#
NIL
THEN {
lag ¬ CONS[w, lag];
--prevent duplications
FOR l:
LIST
OF Widget ¬ lag, lag.rest
WHILE l#
NIL
DO
IF l.first=w THEN RETURN;
ENDLOOP
};
WHILE lag#
NIL
AND lag.first.state>=dead
DO
lag ¬ lag.rest;
ENDLOOP;
shellIP.childrenWithColorMap ¬ lag;
IF lag=NIL THEN RETURN;
DO
IF lag.rest=NIL THEN EXIT;
IF lag.rest.first.state>=dead
THEN lag.rest ¬ lag.rest.rest
ELSE {lag ¬ lag.rest; cnt ¬ cnt+1}
ENDLOOP;
IF shell.fastAccessAllowed=ok
THEN {
d: REF Xl.Card32Sequence ¬ MakeColorMapData[shellIP, cnt];
PutTheColorMapProp[shell, d ! Xl.XError => CONTINUE];
buffer ¬ d;
}
};
};
buffer: REF Xl.Card32Sequence ¬ NIL;
MakeColorMapData:
INTERNAL
PROC [shellIP: ShellInstPart, cnt:
INT]
RETURNS [d:
REF Xl.Card32Sequence ¬
NIL] = {
IF cnt>0
THEN {
lw: LIST OF Widget ¬ shellIP.childrenWithColorMap;
IF buffer#
NIL
AND buffer.leng>=cnt
THEN {d ¬ buffer; buffer ¬ NIL}
ELSE d ¬ NEW[Card32Sequence[cnt]];
FOR i:
INT
IN [0..cnt)
DO
IF lw#
NIL
THEN {d[i] ¬ lw.first.window; lw ¬ lw.rest}
ELSE d[i] ¬ Xl.nullWindow;
ENDLOOP
};
};
PutTheColorMapProp:
PROC [shell: ShellWidget, value:
REF Xl.Card32Sequence] = {
IF value#
NIL
THEN {
propertyKey: XAtom ¬ Xl.MakeAtom[shell.connection, "WM𡤌OLORMAP←WINDOWS"];
Xl.ChangeProperty[shell.connection, shell.window, propertyKey, XlPredefinedAtoms.window, replace, value];
};
};
TrackColorMap:
PUBLIC
PROC [shell: ShellWidget, w: Widget] = {
IF shell=NIL THEN shell ¬ XTk.RootWidget[w];
IF shell#
NIL
AND shell.fastAccessAllowed=ok
THEN {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
IF shellIP.crazyShell THEN RETURN; --don't know how...
EntryTrackCM[shell, shellIP, w];
};
ShellClassName: ClassNameProc = {
shellIP: ShellInstPart ~ GetShellInstPart[widget];
RETURN [shellIP.className];
};
AddRope:
PROC [r: Rope.
ROPE, list:
LIST
OF Rope.
ROPE]
RETURNS [
LIST
OF Rope.
ROPE] = {
IF ~RopeList.Memb[list, r] THEN list ¬ CONS[r, list];
RETURN [list]
};
SetFocusMethod:
PUBLIC
PROC [shell: ShellWidget, focusProtocol, inputHint: Xl.
BOOL3 ¬ illegal] = {
h: REF XTkShellWidgets.ICCCMHints ¬ GetHints[shell];
IF focusProtocol#illegal
THEN {
wmTakeFocus: Rope.ROPE ¬ "WM←TAKE𡤏OCUS";
SELECT focusProtocol
FROM
true => {
h.protocols ¬ AddRope[wmTakeFocus, h.protocols];
h.protocolsChanged ¬ TRUE
};
false => {
h.protocols ¬ RopeList.DRemove[h.protocols, wmTakeFocus];
h.protocolsChanged ¬ TRUE
};
ENDCASE => {};
};
IF inputHint#illegal
THEN {
SELECT inputHint
FROM
true => {h.wmHints.input ¬ 1; h.wmHintsChanged ¬ TRUE};
false => {h.wmHints.input ¬ 0; h.wmHintsChanged ¬ TRUE};
ENDCASE => {};
};
UpdateHints[shell];
};
SetFocusTarget:
PUBLIC
PROC [shell: ShellWidget, child:
REF, time: Xl.TimeStamp] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
IF ValidTime[shellIP.focusTime, time] THEN shellIP.focusGoal ¬ child
};
ValidTime:
PROC [lastTime, eventTime: Xl.TimeStamp]
RETURNS [
BOOL] = {
RETURN [
Xl.Period[from: lastTime, to: eventTime]>=0
OR eventTime=Xl.currentTime OR lastTime=Xl.currentTime
OR
--this is it! We want to prevent invalid times. But we want to survive if a crazy server did not get an event for LAST[TimeStamp]/2. So we assume a 2 minutes is the maximum delay which invalid times could be caused due to unsynchronized nonsense
Xl.Period[from: eventTime, to: lastTime]>120000 ]
};
SetFocus:
PUBLIC
PROC [shell: ShellWidget, time: Xl.TimeStamp, child:
REF] = {
w: Window;
shellIP: ShellInstPart ~ GetShellInstPart[shell];
IF ValidTime[shellIP.focusTime, time]
THEN {
IF child=NIL THEN child ¬ shellIP.focusGoal ELSE shellIP.focusGoal ¬ child;
w ¬ ToWindow[child];
IF w#Xl.nullWindow
THEN {
shellIP.focusTime ¬ time;
IF shell.fastAccessAllowed=ok
THEN
Xl.SetInputFocus[shell.connection, w, parent, time, XTkPrivate.detailsForFlushNoErrors];
};
};
};
FocusTime:
PUBLIC
PROC [shell: ShellWidget]
RETURNS [Xl.TimeStamp] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
RETURN [shellIP.focusTime];
};
Might consider caching iconic/open state to suppress unnecessary operation. However, then we would need to keep atomically track of state. I'm not against it, but I'm not yet sure whether it is worth the effort.
Iconify:
PUBLIC
PROC [shell: ShellWidget] = {
--Forget about such things as using rootTQ. We never know iconic state atomically anyway since interactive actions of user through window manager are not synchronized.
IF shell.state>=dead THEN RETURN;
shell.s.mapping ¬ mapped;
SetIconicHint[shell, TRUE]; --irrelevant if open or iconic, useful if was withdrawn...
IF shell.state=realized
AND shell
.fastAccessAllowed=ok
THEN {
root: Xl.Window ¬ shell.screenDepth.screen.root;
atom: Xl.XAtom ¬ Xl.MakeAtom[shell.connection, "WM𡤌HANGE←STATE"];
Xl.SendClientMessage32[c: shell.connection, destination: root, propagate:
FALSE,
eventMask: [substructureRedirect: TRUE, substructureNotify: TRUE],
window: shell.window,
type: atom,
data: [3, 0, 0, 0, 0],
details: XTkPrivate.detailsForFlushSoonNoErrors
];
};
--ELSE wont open and iconify shell; just make sure shell will be iconic on realization
};
OpenIcon:
PUBLIC
PROC [shell: ShellWidget] = {
--Forget about such things as using rootTQ. We never know iconic state atomically anyway since interactive actions of user through window manager are not synchronized.
IF shell.state>=dead THEN RETURN;
SetIconicHint[shell, FALSE]; --irrelevant if iconic or opened; useful if withdrawn...
shell.s.mapping ¬ mapped;
IF shell.state=realized
AND shell
.fastAccessAllowed=ok
THEN {
Xl.MapWindow[shell.connection, shell.window, XTkPrivate.detailsForFlushNoErrors];
--ELSE wont realize shell; just make sure shell will be opened on realization
}
};
WithDraw:
PUBLIC
PROC [shell: ShellWidget] = {
--Forget about such things as using rootTQ. We never know iconic state atomically anyway since interactive actions of user through window manager are not synchronized.
IF shell.state>=dead THEN RETURN;
shell.s.mapping ¬ unmapped;
IF shell.state=realized
AND shell
.fastAccessAllowed=ok
THEN {
root: Xl.Window ¬ shell.screenDepth.screen.root;
eb: Xl.EventRep.unmapNotify;
eb.eventWindow ¬ root;
eb.window ¬ shell.window;
eb.fromConfigure ¬ FALSE;
Xl.UnmapWindow[shell.connection, shell.window];
Xl.SendEvent[c: shell.connection, destination: root, propagate: FALSE, eventMask: [substructureRedirect: TRUE, substructureNotify: TRUE], eventBody: eb, details: XTkPrivate.detailsForFlushSoonNoErrors];
};
--ELSE shell stays withdrawn; however OpenIcon and Iconify will be delayed until realization.
};
SetIconicHint:
PROC [shell: ShellWidget, iconic:
BOOL] = {
shellIP: ShellInstPart ~ GetShellInstPart[shell];
h: REF XTkShellWidgets.ICCCMHints ¬ GetHints[shell];
h.wmHints.initialState ¬ IF iconic THEN 3 ELSE 1;
h.wmHintsChanged ¬ TRUE;
UpdateHints[shell];
};
SetHeader:
PUBLIC PROC [shell: ShellWidget, header:
ROPE]
= {
h: REF XTkShellWidgets.ICCCMHints ¬ GetHints[shell];
h.windowHeader ¬ header;
h.windowHeaderChanged ¬ TRUE;
UpdateHints[shell];
};
SetIconName:
PUBLIC PROC [shell: ShellWidget, header:
ROPE] = {
h: REF XTkShellWidgets.ICCCMHints ¬ GetHints[shell];
h.iconNameChanged ¬ TRUE;
UpdateHints[shell];
};
END.