123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- MODULE WMMessages; (** AUTHOR "TF"; PURPOSE "Support for messages and events"; *)
- IMPORT
- Strings, KernelLog, Objects, Kernel, Locks, Modules, Reflection, SYSTEM, D:= Debugging;
- CONST
- InitialMsgQSize = 64;
- MaxMsgQSize = 32*1024; (* this is too huge anyway *)
- (** Predefined Messages *)
- MsgPointer* = 0; MsgKey* = 2; MsgClose* = 3; MsgStyleChanged* = 4;
- MsgFocus* = 5; MsgExt* = 6; MsgDrag* = 7;
- MsgInvokeEvent* = 8;
- MsgResized* = 9;
- MsgSetLanguage* = 10;
- MsgInvalidate*= 11;
- MsgSerialize*=12;
- MsgMerged*=15;
-
- MsgSubPointerMove* = 0; MsgSubPointerDown* = 1; MsgSubPointerUp* = 2; MsgSubPointerLeave* = 3;
- MsgSubFocusGot* = 0; MsgSubFocusLost* = 1; MsgSubMasterFocusGot* = 2; MsgSubMasterFocusLost* = 3;
- MsgSubAll*=0; MsgSubRectangle*=1; MsgSubNothing*=2; (* regions: all or rectangle as defined by x, y, dx, dy *)
- MsgDragOver* = 0; MsgDragDropped* = 1;
-
- MsgSubSerializeView*=0; MsgSubSerializeComponent*=1; MsgSubSerializeData*=2;
- (** Gather statistics about added/discarded messages? *)
- Statistics* = FALSE;
- TraceQueue = FALSE;
- MsgTypeMax* = 13;
- TYPE
- (** Generic Component Command *)
- CompCommand* = PROCEDURE { DELEGATE } (sender, par : ANY);
- String* = Strings.String;
- (** Generic message structure *)
- Message* = RECORD
- originator*, (** the originator if # NIL passes information about the view that directly or indirectely lead to the msg *)
- sender* : ANY; (** is the sender component. If the message is originated form a component *)
- token* : AsyncToken;
- event* : CompCommand;
- msgType*, msgSubType* : LONGINT; (** generic message type *)
- x*, y*, z*, dx*, dy*, dz* : LONGINT; (** in keyboard messages : ucs value in x, keysym in y *)
- flags* : SET; (** in pointer messages : keys in flags *)
- ext* : ANY; (** extended message *)
- END;
- MessageExtension* = POINTER TO RECORD END;
- (** AsyncToken can be used to synchronize asynchronous method invocation *)
- AsyncToken* = OBJECT
- VAR
- ready : BOOLEAN;
- result* : ANY;
- (** Reset is called in case the token was recycled *)
- PROCEDURE Reset*;
- BEGIN {EXCLUSIVE}
- ready := FALSE;
- END Reset;
- (** wait until the result is completed *)
- PROCEDURE AwaitCompletion*;
- BEGIN {EXCLUSIVE}
- AWAIT(ready)
- END AwaitCompletion;
- (** Return if the result is completed *)
- PROCEDURE IsCompleted*():BOOLEAN;
- BEGIN {EXCLUSIVE}
- RETURN ready
- END IsCompleted;
- (** Called by the asynchronous process to indicate the result is available *)
- PROCEDURE Completed*;
- BEGIN {EXCLUSIVE}
- ready := TRUE
- END Completed;
- END AsyncToken;
- (** Message handler that can be called from the sequencer *)
- MessageHandler* = PROCEDURE {DELEGATE} (VAR msg : Message);
- (** The TrapHandler must return TRUE if the process should restart. Otherwise the process is stopped *)
- TrapHandler* = PROCEDURE {DELEGATE} () : BOOLEAN;
- MsgQ = OBJECT
- VAR
- head, num: LONGINT;
- msgQ: POINTER TO ARRAY OF Message;
- owner: MsgSequencer;
- PROCEDURE &InitQ(o: MsgSequencer; size: SIZE);
- BEGIN
- head := 0; num := 0;
- NEW(msgQ, size);
- SELF.owner := o;
- END InitQ;
-
- PROCEDURE Grow(trace: BOOLEAN);
- VAR new: POINTER TO ARRAY (* MsgQSize*) OF Message; i: LONGINT; name: ARRAY 128 OF CHAR; VAR pc: ADDRESS;
- type: Modules.TypeDesc; msg: Message;
- BEGIN
- NEW(new, LEN(msgQ) * 3 DIV 2);
- FOR i := 0 TO LEN(msgQ)-1 DO
- new[i] := msgQ[(head+i) MOD LEN(msgQ)];
- IF trace THEN
- msg := new[i];
- IF msg.msgType < LEN(MsgName) THEN COPY(MsgName[msg.msgType], name) ELSE name := "" END;
- TRACE(i,"***************", name);
- TRACE(i, msg.msgType, msg.msgSubType);
- TRACE(msg.x, msg.y, msg.dx, msg.dy, msg.flags);
- IF msg.sender # NIL THEN
- type := Modules.TypeOf(msg.sender);
- IF (type # NIL) THEN
- COPY(type.mod.name, name); Strings.Append(name, "."); Strings.Append(name, type.name);
- TRACE(msg.sender, name);
- ELSE
- TRACE(msg.sender);
- END;
- END;
- IF msg.msgType = MsgInvokeEvent THEN
- Reflection.GetProcedureName(SYSTEM.VAL(ADDRESS, msg.event), name, pc );
- TRACE("Event procedure ", name);
- END;
- IF msg.ext # NIL THEN
- type := Modules.TypeOf(msg.ext);
- IF (type # NIL) THEN
- COPY(type.mod.name, name); Strings.Append(name, "."); Strings.Append(name, type.name);
- TRACE(msg.ext, name);
- ELSE
- TRACE(msg.ext);
- END;
- END;
- END;
- END;
- msgQ := new; head := 0;
- KernelLog.String("MessageQ increased: "); KernelLog.Int(LEN(msgQ),1); KernelLog.Ln;
- END Grow;
- (** Add a message to a queue. Discards the message if the queue is full *)
- PROCEDURE Add*(VAR msg : Message; debug:BOOLEAN): BOOLEAN;
- VAR i, pos: LONGINT; name: ARRAY 256 OF CHAR; pc: ADDRESS;
- type: Modules.TypeDesc;
- CONST
- MergePointers = TRUE;
- MergeInvalidates = TRUE;
- MergeInvokeEvents = FALSE;
- PROCEDURE Merge(VAR x,y,dx,dy: LONGINT; X,Y,dX,dY: LONGINT);
- VAR nx, ny, ndx, ndy: LONGINT;
- BEGIN
- nx := MIN(x,X);
- ny := MIN(y,Y);
- ndx := MAX(x+dx, X+dX) - nx;
- ndy := MAX(y+dy, Y+dY) - ny;
- x := nx;
- y := ny;
- dx := ndx;
- dy := ndy;
- END Merge;
- BEGIN
- IF debug THEN
- KernelLog.String("<----");
- IF msg.msgType < LEN(MsgName) THEN COPY(MsgName[msg.msgType], name) ELSE name := "" END;
- TRACE("WMMessages.MsgSequencer.Add", name);
- TRACE(num, msg.msgType, msg.msgSubType);
- TRACE(msg.x, msg.y, msg.dx, msg.dy);
- IF msg.sender # NIL THEN
- type := Modules.TypeOf(msg.sender);
- IF (type # NIL) THEN
- COPY(type.mod.name, name); Strings.Append(name, "."); Strings.Append(name, type.name);
- TRACE(msg.sender, name);
- ELSE
- TRACE(msg.sender);
- END;
- END;
- IF msg.msgType = MsgInvokeEvent THEN
- Reflection.GetProcedureName(SYSTEM.VAL(ADDRESS, msg.event), name, pc );
- TRACE("Event procedure ", name);
- END;
- IF msg.ext # NIL THEN
- type := Modules.TypeOf(msg.ext);
- IF (type # NIL) THEN
- COPY(type.mod.name, name); Strings.Append(name, "."); Strings.Append(name, type.name);
- TRACE(msg.ext, name);
- ELSE
- TRACE(msg.ext);
- END;
- END;
- (*D.TraceBack;*)
- END;
- IF MergePointers & (msg.msgType = MsgPointer) & (msg.msgSubType = MsgSubPointerMove) & (num > 0) THEN (* reduce pointer moves in buffer *)
- i := num - 1;
- WHILE i >= 0 DO
- pos := (head + i) MOD LEN(msgQ);
- IF (msgQ[pos].msgType = MsgPointer) & (msgQ[pos].msgSubType = MsgSubPointerMove) & (msgQ[pos].flags = msg.flags) THEN
- msgQ[pos].x := msg.x;
- msgQ[pos].y := msg.y;
- msgQ[pos].z := msg.z;
- RETURN TRUE
- END;
- DEC(i)
- END
- END;
-
-
- (* filter out duplicate MsgInvokeEvents - was a hack (but effecting in avoiding MessageQ congestion)*)
- IF MergeInvokeEvents & (msg.msgType = MsgInvokeEvent) & (msg.msgSubType = 0) & (num > 0) THEN
- i := num - 1;
- WHILE i >= 0 DO
- pos := (head + i) MOD LEN(msgQ);
- IF (msgQ[pos].msgType = MsgInvokeEvent) & (msgQ[pos].msgSubType = 0) & (msgQ[pos].event = msg.event)& (msgQ[pos].sender = msg.sender) & (msgQ[pos].ext = msg.ext) THEN
- msgQ[pos].msgType := MsgMerged;
- END;
- DEC(i)
- END
- END;
- IF MergeInvalidates & (msg.msgType = MsgInvalidate) & (num > 0) THEN
- i := num-1;
- pos := (head + i) MOD LEN(msgQ);
- IF (msgQ[pos].sender = msg.sender) & (msgQ[pos].msgType = MsgInvalidate) & (msgQ[pos].msgSubType = msg.msgSubType) THEN
- IF msg.msgSubType= MsgSubRectangle THEN
- IF Contained(msgQ[pos], msg) THEN
- IF TraceQueue OR debug THEN
- TRACE("container first ", msg.x, msg.dx, msg.y, msg.dy);
- TRACE(msgQ[pos].x, msgQ[pos].dx, msgQ[pos].y, msgQ[pos].dy);
- KernelLog.Ln;
- END;
- (* replace *)
- msgQ[pos].x := msg.x; msgQ[pos].y := msg.y; msgQ[pos].dx := msg.dx; msgQ[pos].dy := msg.dy;
- RETURN TRUE;
- ELSIF Contained(msg, msgQ[pos]) THEN
- IF TraceQueue OR debug THEN
- TRACE("contained first ", msg.x, msg.dx, msg.y, msg.dy);
- TRACE(msgQ[pos].x, msgQ[pos].dx, msgQ[pos].y, msgQ[pos].dy);
- KernelLog.Ln;
- END;
- (* keep *)
- RETURN TRUE;
- ELSE (* we assume that invaidates on the same component
- that immediately follow each other are very close to each other
- If this turns out to be untrue, we could add a heuristics here *)
- Merge(msgQ[pos].x, msgQ[pos].y, msgQ[pos].dx, msgQ[pos].dy, msg.x, msg.y, msg.dx, msg.dy);
- (* keep *)
- RETURN TRUE;
- END;
- ELSIF msg.msgSubType = MsgSubAll THEN
- (* keep *)
- IF TraceQueue OR debug THEN
- TRACE("keep first");
- KernelLog.Ln;
- END;
- RETURN TRUE;
- END;
- END;
- DEC(i);
- WHILE i >= 0 DO
- pos := (head + i) MOD LEN(msgQ);
- IF (msgQ[pos].sender = msg.sender) & (msgQ[pos].msgType = MsgInvalidate) & (msgQ[pos].msgSubType = msg.msgSubType) THEN
- IF msg.msgSubType= MsgSubRectangle THEN
- IF Contained(msgQ[pos], msg) THEN
- IF TraceQueue OR debug THEN
- TRACE("container ", pos);
- TRACE( msg.x, msg.dx, msg.y, msg.dy);
- TRACE(msgQ[pos].x, msgQ[pos].dx, msgQ[pos].y, msgQ[pos].dy);
- END;
- msgQ[pos].msgType := MsgMerged;
- i := 0;
- ELSIF Contained(msg, msgQ[pos]) THEN
- IF TraceQueue OR debug THEN
- TRACE("contained ", pos);
- TRACE(msg.x, msg.dx, msg.y, msg.dy);
- TRACE(msgQ[pos].x, msgQ[pos].dx, msgQ[pos].y, msgQ[pos].dy);
- END;
- msg.x := msgQ[pos].x; msg.y := msgQ[pos].y; msg.dx := msgQ[pos].dx; msg.dy := msgQ[pos].dy;
- msgQ[pos].msgType := MsgMerged;
- i := 0;
- (*ELSE
- Merge(msg.x, msg.y, msg.dx, msg.dy, msgQ[pos].x, msgQ[pos].y, msgQ[pos].dx, msgQ[pos].dy);
- msgQ[pos].msgSubType := MsgSubNothing;
- *)
- END;
- ELSIF msgQ[pos].msgSubType = MsgSubAll THEN
- IF TraceQueue OR debug THEN
- TRACE("replace ", pos);
- END;
- msgQ[pos].msgType := MsgMerged;
- i := 0;
- END;
- END;
- DEC(i);
- END;
- END;
- IF num >= MaxMsgQSize THEN RETURN FALSE END;
- IF num >= LEN(msgQ) THEN
- Grow(debug)
- END;
- IF Statistics THEN
- INC(messagesAdded);
- IF (msg.msgType >= 0) & (msg.msgType < MsgTypeMax) THEN
- INC(messagesAddedByType[msg.msgType]);
- END;
- END;
- msgQ[(head + num) MOD LEN(msgQ)] := msg; INC(num);
- IF debug THEN
- KernelLog.Ln;
- END;
- RETURN TRUE;
- END Add;
- (* Remove a message from the queue. Block if no message is available but awake if queue is terminated by call to Stop *)
- (* return if alive *)
- PROCEDURE Get(VAR msg : Message; debug: BOOLEAN) : BOOLEAN;
- VAR i: LONGINT; name: ARRAY 256 OF CHAR; pc: ADDRESS;
- type: Modules.TypeDesc;
- BEGIN
- msg := msgQ[head];
- (* clear references from the queue *)
- msgQ[head].originator := NIL;
- msgQ[head].sender := NIL;
- msgQ[head].ext := NIL;
- head := (head + 1) MOD LEN(msgQ);
- DEC(num);
- owner.originator := msg.originator;
- IF debug THEN
- KernelLog.String("---->");
- IF msg.msgType < LEN(MsgName) THEN COPY(MsgName[msg.msgType], name) ELSE name := "" END;
- TRACE("WMMessages.MsgSequencer.Get", name);
- TRACE(i, msg.msgType, msg.msgSubType);
- TRACE(msg.x, msg.y, msg.dx, msg.dy);
- IF msg.sender # NIL THEN
- type := Modules.TypeOf(msg.sender);
- IF (type # NIL) THEN
- COPY(type.mod.name, name); Strings.Append(name, "."); Strings.Append(name, type.name);
- TRACE(msg.sender, name);
- ELSE
- TRACE(msg.sender);
- END;
- END;
- IF msg.msgType = MsgInvokeEvent THEN
- Reflection.GetProcedureName(SYSTEM.VAL(ADDRESS, msg.event), name, pc );
- TRACE("Event procedure ", name);
- END;
- IF msg.ext # NIL THEN
- type := Modules.TypeOf(msg.ext);
- IF (type # NIL) THEN
- COPY(type.mod.name, name); Strings.Append(name, "."); Strings.Append(name, type.name);
- TRACE(msg.ext, name);
- ELSE
- TRACE(msg.ext);
- END;
- END;
- KernelLog.Ln;
- END;
- RETURN TRUE
- END Get;
- END MsgQ;
- (** Message sequencer *)
- MsgSequencer* = OBJECT
- VAR
- msgQ: MsgQ;
- invalidateQ: MsgQ;
- alive, continue, waiting, stopped: BOOLEAN;
- msg : Message;
- handler : MessageHandler;
- originator : ANY;
- me : ANY; (* Thread for caller identification *)
- lock- : Locks.RWLock;
- th, traphandler : TrapHandler;
- name* : String;
- PROCEDURE &New*(handler : MessageHandler);
- BEGIN
- SELF.handler := handler;
- NEW(lock);
- originator := NIL; me := NIL; th := NIL; traphandler := NIL;
- name := NIL;
- alive := FALSE; continue := TRUE;
- waiting := FALSE; stopped := FALSE;
- NEW(invalidateQ, SELF, InitialMsgQSize);
- NEW(msgQ, SELF, InitialMsgQSize);
- END New;
- (** Add a trap handler for this process. This handler only decides whether to continue or to abort the process.
- If continued, the lock will be reset *)
- PROCEDURE SetTrapHandler*(th : TrapHandler);
- BEGIN {EXCLUSIVE}
- traphandler := th
- END SetTrapHandler;
- (** Return true if called from (this) sequencer *)
- PROCEDURE IsCallFromSequencer*() : BOOLEAN;
- BEGIN
- RETURN Objects.ActiveObject() = me
- END IsCallFromSequencer;
- (** RETURN the originator (view) of the message that lead directly or indirectly to this request.
- Returns NIL if the call is not from the sequencer *)
- PROCEDURE GetOriginator*() : ANY;
- BEGIN
- IF Objects.ActiveObject() = me THEN RETURN originator
- ELSE RETURN NIL
- END
- END GetOriginator;
- (** Add a message to a queue. Discards the message if the queue is full *)
- PROCEDURE Add*(VAR msg : Message): BOOLEAN;
- BEGIN {EXCLUSIVE}
- IF msg.msgType = MsgInvalidate THEN
- RETURN invalidateQ.Add(msg, TraceQueue OR (debug = SELF));
- ELSE
- RETURN msgQ.Add(msg, TraceQueue OR (debug = SELF));
- END;
- END Add;
- PROCEDURE Handle(VAR msg : Message) : BOOLEAN;
- BEGIN
- (* if asynchronous call --> synchronize *)
- IF ~IsCallFromSequencer() THEN
- IF Add(msg) THEN RETURN TRUE END;
- ELSE
- (*
- IF debug = SELF THEN
- D.Enter;
- D.Ln;
- D.String("-- WMMessages.MsgSequencer.Handle --"); D.Ln;
- D.String("msg type "); D.Int(msg.msgType,1); D.Ln;
- D.String("time "); D.Int(Kernel.GetTicks(),1);D.Ln;
- D.Exit;
- END;
- *)
- IF msg.msgType = MsgInvokeEvent THEN (* MsgInvokeEvent *)
- IF msg.event # NIL THEN
- msg.event(msg.sender, msg.ext);
- IF msg.token # NIL THEN msg.token.Completed END
- END
- ELSE handler(msg) (* Generic message *)
- END;
- (* clear references *)
- msg.originator := NIL;
- msg.sender := NIL;
- msg.ext := NIL;
- originator := NIL;
- RETURN TRUE
- END;
- RETURN FALSE
- END Handle;
- (* put event into message queue *)
- PROCEDURE ScheduleEvent*(event : CompCommand; sender, par : ANY);
- VAR invokeMsg : Message;
- BEGIN
- invokeMsg.msgType := MsgInvokeEvent;
- invokeMsg.sender := sender; invokeMsg.ext := par;
- invokeMsg.event := event;
- IF ~Handle(invokeMsg) THEN END
- END ScheduleEvent;
- (** Stop the message sequencer. Must be called if the queue is no longer needed *)
- PROCEDURE Stop*;
- BEGIN {EXCLUSIVE}
- alive := FALSE; stopped := TRUE;
- END Stop;
- PROCEDURE WaitFree*;
- BEGIN {EXCLUSIVE}
- AWAIT (waiting & (msgQ.num = 0) & (invalidateQ.num = 0) OR ~alive)
- END WaitFree;
- (* Remove a message from the queue. Block if no message is available but awake if queue is terminated by call to Stop *)
- (* return if alive *)
- PROCEDURE Get(VAR msg : Message) : BOOLEAN;
- VAR b: BOOLEAN;
- BEGIN {EXCLUSIVE}
- waiting := TRUE;
- REPEAT
- AWAIT((msgQ.num # 0) OR (invalidateQ.num # 0) OR ~alive);
- waiting := FALSE;
- IF ~alive THEN RETURN FALSE END;
- IF (msgQ.num # 0) THEN
- b := msgQ.Get(msg, TraceQueue OR (debug = SELF));
- ELSE
- b := invalidateQ.Get(msg, TraceQueue OR (debug = SELF));
- END;
- UNTIL msg.msgType # MsgMerged;
- RETURN b;
- END Get;
- BEGIN {ACTIVE, SAFE}
- (* trap occured *)
- IF alive THEN
- th := traphandler; KernelLog.String("WMMessages: [TRAP]"); KernelLog.Ln;
- IF th # NIL THEN continue := th() ELSE continue := FALSE END;
- IF continue THEN lock.Reset ELSE RETURN END;
- END;
- alive := TRUE; me := Objects.ActiveObject();
- (* Message processing loop *)
- WHILE Get(msg) DO
- lock.AcquireWrite;
- (* Check alive again for the case that the sequencer has been stopped just after Get(msg) returned
- but before the lock could be acquired (WMComponents.FormWindow holds that lock when calling Sequencer.Stop) *)
- IF alive THEN
- IF ~Handle(msg) THEN KernelLog.String("WMMessages: A msg was not handled... "); KernelLog.Ln; END;
- END;
- lock.ReleaseWrite
- END
- END MsgSequencer;
- VAR
- tokenCache : Kernel.FinalizedCollection;
- ttoken : AsyncToken;
- (* Statistics *)
- messagesAddedByType- : ARRAY MsgTypeMax OF LONGINT;
- messagesAdded- : LONGINT;
- messagesDiscarded- : LONGINT;
- debug*: ANY;
- MsgName: ARRAY 32 OF ARRAY 32 OF CHAR;
- PROCEDURE TokenEnumerator(obj: ANY; VAR cont: BOOLEAN);
- BEGIN
- cont := FALSE; ttoken := obj(AsyncToken)
- END TokenEnumerator;
- (** Get an AsyncToken from the pool. Create a new one if the pool is empty *)
- PROCEDURE GetAsyncToken*() : AsyncToken;
- BEGIN {EXCLUSIVE}
- ttoken := NIL;
- tokenCache.Enumerate(TokenEnumerator);
- IF ttoken = NIL THEN NEW(ttoken)
- ELSE tokenCache.Remove(ttoken)
- END;
- ttoken.Reset;
- RETURN ttoken
- END GetAsyncToken;
- (** Recycle an AsyncToken. Must be unused. (is only used to disburden the garbage collector) *)
- PROCEDURE RecycleAsyncToken*(t : AsyncToken);
- BEGIN
- (* only recycle the token if the result is complete *)
- IF t.IsCompleted() THEN tokenCache.Add(t, NIL) END;
- END RecycleAsyncToken;
- PROCEDURE Contained(CONST this, container: Message): BOOLEAN;
- BEGIN
- RETURN (container.x <= this.x) & (container.dx >= this.dx) & (container.y <= this.y) & (container.dy >= this.dy)
- END Contained;
- BEGIN
- NEW(tokenCache);
- MsgName[MsgPointer] := "MsgPointer";
- MsgName[MsgKey] := "MsgKey";
- MsgName[MsgClose] := "MsgClose";
- MsgName[MsgStyleChanged] := "MsgStyleChanged";
- MsgName[MsgFocus] := "MsgFocus";
- MsgName[MsgExt] := "MsgExt";
- MsgName[MsgDrag] := "MsgDrag";
- MsgName[MsgInvokeEvent] := "MsgInvokeEvent";
- MsgName[MsgResized] := "MsgResized" ;
- MsgName[MsgSetLanguage] := "MsgSetLanguage";
- MsgName[MsgInvalidate] := "MsgInvalidate";
- MsgName[MsgSerialize] := "MsgSerialize";
- END WMMessages.
- Release.Rebuild --path="" Win32G WMMessages.Mod ~
|