<<>> <> <> <> <> <<>> DIRECTORY Rope, Xl, XlDispatch, XlEndianPrivate, XlExtensions, XlInputExtension, XlInputExtensionPrivate, XlKeyButPrivate, XlPrivate; XlInputExtensionImpl: CEDAR MONITOR LOCKS c USING c: Xl.Connection IMPORTS Rope, Xl, XlDispatch, XlEndianPrivate, XlExtensions, XlKeyButPrivate, XlPrivate EXPORTS XlInputExtension, XlInputExtensionPrivate SHARES Xl ~ BEGIN OPEN XlInputExtension, XlInputExtensionPrivate; extensionKey: ATOM ~ $XInputExtension; deviceKeyPressKey: PUBLIC REF ¬ $deviceKeyPress; deviceKeyReleaseKey: PUBLIC REF ¬ $deviceKeyRelease; deviceButtonPressKey: PUBLIC REF ¬ $deviceButtonPress; deviceButtonReleaseKey: PUBLIC REF ¬ $deviceButtonRelease; deviceMotionNotifyKey: PUBLIC REF ¬ $deviceMotionNotify; deviceFocusInKey: PUBLIC REF ¬ $deviceFocusIn; deviceFocusOutKey: PUBLIC REF ¬ $deviceFocusOut; proximityInKey: PUBLIC REF ¬ $proximityIn; proximityOutKey: PUBLIC REF ¬ $proximityOut; deviceStateNotifyKey: PUBLIC REF ¬ $deviceStateNotify; deviceMappingNotifyKey: PUBLIC REF ¬ $deviceMapping; changeDeviceNotifyKey: PUBLIC REF ¬ $changeDeviceNotify; extPropKey: REF ATOM ~ NEW[ATOM ¬ extensionKey]; lastConnectionData: REF ConnectionData ¬ NEW[ConnectionData]; GetConnectionData: PUBLIC PROC [c: Xl.Connection] RETURNS [cd: REF ConnectionData] = { cd ¬ lastConnectionData; IF cd.c=c THEN RETURN[cd]; cd ¬ lastConnectionData ¬ NARROW[Xl.GetConnectionPropAndInit[c, extPropKey, InitExtension]]; }; InitExtension: Xl.InitializeProcType = {--init availability of extension for this connection cd: REF ConnectionData ~ NEW[ConnectionData]; cd.ex ¬ XlExtensions.StartExtension[c, extensionKey]; IF cd.ex#NIL THEN cd.ex.state ¬ NEW[ExtensionStateRec]; RETURN [cd] }; GetExtensionVersion: PUBLIC PROC [c: Xl.Connection, name: Rope.ROPE] RETURNS [present: BOOL ¬ FALSE, majorVersion: INT ¬ 0, minorVersion: INT ¬ 0] = { reply: XlPrivate.Reply; n: INT ~ Rope.Length[name]; cd: REF ConnectionData ~ GetConnectionData[c]; IF n>c.info.maxRequestLengthBytes THEN ERROR; IF cd.ex#NIL THEN { DoIt: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opGetExtensionVersion, 2+(n+3)/4]; XlPrivate.BPut16[c, n]; -- length of name XlPrivate.BSkip[c, 2]; XlPrivate.BPutPaddedRope[c, name]; reply ¬ XlPrivate.FinishWithReply[c]; }; XlPrivate.DoWithLocks[c, DoIt, NIL]; XlPrivate.CheckReply[reply]; XlPrivate.Skip[reply, 7]; majorVersion ¬ XlPrivate.Read16[reply]; minorVersion ¬ XlPrivate.Read16[reply]; present ¬ XlPrivate.Read8[reply]#0; XlPrivate.DisposeReply[c, reply]; }; }; <<>> ListInputDevices: PUBLIC PROC [c: Xl.Connection] RETURNS [diList: LIST OF DeviceInfo ¬ NIL] = { diRest: LIST OF DeviceInfo ¬ NIL; infoRest: LIST OF REF InputInfo ¬ NIL; reply: XlPrivate.Reply; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex#NIL THEN { DoIt: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opListInputDevices, 1]; reply ¬ XlPrivate.FinishWithReply[c]; }; replyLength: CARD32; numberOfDevices: BYTE; XlPrivate.DoWithLocks[c, DoIt, NIL]; XlPrivate.CheckReply[reply]; XlPrivate.Skip[reply, 3]; replyLength ¬ XlPrivate.Read32[reply]; numberOfDevices ¬ XlPrivate.Read8[reply]; XlPrivate.Skip[reply, 23]; infoRest ¬ NIL; FOR nd: INT IN [0..numberOfDevices) DO di: DeviceInfo; di.type ¬ [XlPrivate.ERead32[reply]]; di.id ¬ XlPrivate.ERead8[reply] MOD 128; di.numClasses ¬ XlPrivate.ERead8[reply]; SELECT XlPrivate.ERead8[reply] FROM 0 => di.use ¬ isXPointer; 1 => di.use ¬ isXKeyboard; ENDCASE => di.use ¬ isExtensionDevice; XlPrivate.Skip[reply, 1]; FOR nc: INT IN [0..di.numClasses) DO inf: REF InputInfo; infoRest: LIST OF REF InputInfo; classId: BYTE ¬ XlPrivate.ERead8[reply]; length: BYTE ¬ XlPrivate.ERead8[reply]; SELECT classId FROM 0 => { i: REF keyInfo InputInfo ¬ NEW[keyInfo InputInfo]; i.min ¬ XlPrivate.ERead8[reply]; i.max ¬ XlPrivate.ERead8[reply]; i.number ¬ XlPrivate.ERead16[reply]; XlPrivate.Skip[reply, 2]; inf ¬ i; }; 1 => { i: REF buttonInfo InputInfo ¬ NEW[buttonInfo InputInfo]; i.number ¬ XlPrivate.ERead16[reply]; inf ¬ i; }; 2 => { numAxes: BYTE ¬ XlPrivate.ERead8[reply]; mode: BYTE ¬ XlPrivate.ERead8[reply]; i: REF valuatorInfo InputInfo ¬ NEW[valuatorInfo InputInfo[numAxes]]; i.supportsRelative ¬ ((mode) MOD 2) = 1; i.supportsAbsolute ¬ ((mode / 2) MOD 2) = 1; i.motionBufferSize ¬ XlPrivate.ERead32[reply]; FOR n: INT IN [0..numAxes) DO i.axisInfo[n].resolution ¬ XlPrivate.ERead32[reply]; i.axisInfo[n].min ¬ XlPrivate.ERead32[reply]; i.axisInfo[n].max ¬ XlPrivate.ERead32[reply]; ENDLOOP; inf ¬ i; }; ENDCASE => {}; inf.class ¬ VAL[classId]; <<--append this class (info)>> IF di.info=NIL THEN di.info ¬ infoRest ¬ LIST[inf] ELSE {infoRest.rest ¬ LIST[inf]; infoRest ¬ infoRest.rest} ENDLOOP; di.name ¬ XlPrivate.EReadRope[reply]; <<--append this device>> IF diRest=NIL THEN diList ¬ diRest ¬ LIST[di] ELSE {diRest.rest ¬ LIST[di]; diRest ¬ diRest.rest} ENDLOOP; XlPrivate.DisposeReply[c, reply]; }; }; OpenDevice: PUBLIC PROC [c: Xl.Connection, id: DeviceId] RETURNS [od: REF OpenDeviceRec] = { reply: XlPrivate.Reply; cd: REF ConnectionData ~ GetConnectionData[c]; replyLength: CARD32; numberOfClasses: BYTE; DoIt: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opOpenDevice, 2]; XlPrivate.BPut8[c, id]; XlPrivate.BSkip[c, 3]; reply ¬ XlPrivate.FinishWithReply[c]; }; IF cd.ex=NIL THEN ERROR; XlPrivate.DoWithLocks[c, DoIt, NIL]; XlPrivate.CheckReply[reply]; XlPrivate.Skip[reply, 3]; replyLength ¬ XlPrivate.Read32[reply]; numberOfClasses ¬ XlPrivate.Read8[reply]; XlPrivate.Skip[reply, 23]; od ¬ NEW[OpenDeviceRec[numberOfClasses]]; od.id ¬ id; FOR i: INT IN [0..numberOfClasses) DO od[i].inputClass ¬ VAL[XlPrivate.ERead8[reply]]; od[i].eventTypeBase ¬ XlPrivate.ERead8[reply]; ENDLOOP; XlPrivate.DisposeReply[c, reply]; }; <<>> CloseDevice: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, details: Xl.Details ¬ NIL] = { action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opCloseDevice, 2]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 3]; XlPrivate.FinishWithDetails[c, details]; }; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex#NIL THEN XlPrivate.DoWithLocks[c, action, details]; }; <<>> SetDeviceMode: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, relativeMode: BOOL] RETURNS [status: DeviceStatus] = { reply: XlPrivate.Reply; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opSetDeviceMode, 2]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BPut8[c, ORD[relativeMode]]; XlPrivate.BSkip[c, 2]; reply ¬ XlPrivate.FinishWithReply[c]; }; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex=NIL THEN ERROR; XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.Skip[reply, 7]; SELECT XlPrivate.Read8[reply] FROM 0 => status ¬ success; 1 => status ¬ alreadyGrabbed; ENDCASE => status ¬ deviceBusy; XlPrivate.DisposeReply[c, reply]; }; <<>> EventClassesLength: PUBLIC PROC [interest: LIST OF EventClass] RETURNS [count: INT ¬ 0] = { limit: INT = 200; FOR l: LIST OF EventClass ¬ interest, l.rest WHILE l#NIL DO IF count>=limit THEN ERROR; count ¬ count+1; ENDLOOP; }; PutEventClasses: PUBLIC PROC [c: Xl.Connection, interest: LIST OF EventClass, count: INT] = { FOR i: INT IN [0..count) DO XlPrivate.BPut32[c, LOOPHOLE[interest.first, CARD32]]; IF interest.rest#NIL THEN interest ¬ interest.rest ENDLOOP; }; SelectExtensionEvents: PUBLIC PROC [c: Xl.Connection, window: Xl.Window, interest: LIST OF EventClass, details: Xl.Details ¬ NIL] = { count: INT ¬ EventClassesLength[interest]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opSelectExtensionEvent, 3+count]; XlPrivate.BPutDrawable[c, window]; XlPrivate.BPut16[c, count]; XlPrivate.BSkip[c, 2]; PutEventClasses[c, interest, count]; XlPrivate.FinishWithDetails[c, details]; }; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex#NIL THEN XlPrivate.DoWithLocks[c, action, details]; }; MakeEventClass: PUBLIC PROC [od: REF READONLY OpenDeviceRec, request: RequestableEvent] RETURNS [EventClass ¬ 0] = { id: CARD32 ¬ od.id; x: CARD32 ¬ (id MOD 128) * 256; IF request IN [devicePointerMotionHint..noExtensionEvent] THEN {x ¬ x + ORD[request]; RETURN [LOOPHOLE[x]]} ELSE { class: InputClass; offset: CARD32 ¬ 0; SELECT request FROM deviceKeyPress => {class ¬ keyClass; offset ¬ 0}; deviceKeyRelease => {class ¬ keyClass; offset ¬ 1}; deviceButtonPress => {class ¬ buttonClass; offset ¬ 0}; deviceButtonRelease => {class ¬ buttonClass; offset ¬ 1}; deviceMotionNotify => {class ¬ valuatorClass; offset ¬ 0}; deviceFocusIn => {class ¬ focusClass; offset ¬ 0}; deviceFocusOut => {class ¬ focusClass; offset ¬ 1}; proximityIn => {class ¬ proximityClass; offset ¬ 0}; proximityOut => {class ¬ proximityClass; offset ¬ 1}; deviceStateNotify => {class ¬ otherClass; offset ¬ 0}; deviceMappingNotify => {class ¬ otherClass; offset ¬ 1}; changeDeviceNotify => {class ¬ otherClass; offset ¬ 2}; ENDCASE => ERROR; --class is not known (by this version of the extension) FOR i: INT IN [0..od.numClasses) DO IF od.classes[i].inputClass=class THEN { x ¬ x + od.classes[i].eventTypeBase + offset; RETURN [LOOPHOLE[x]] }; ENDLOOP; }; ERROR; --class is not implemented (by this server instance) }; GetSelectedExtensionEvents: PUBLIC PROC [c: Xl.Connection, window: Xl.Window] RETURNS [thisClient: EventClasses ¬ NIL, allClients: EventClasses ¬ NIL] = { reply: XlPrivate.Reply; thisCnt, allCnt: NAT; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opGetSelectedExtensionEvents, 2]; XlPrivate.BPutDrawable[c, window]; reply ¬ XlPrivate.FinishWithReply[c]; }; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex#NIL THEN { XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.CheckReply[reply]; XlPrivate.Skip[reply, 7]; thisCnt ¬ XlPrivate.Read16[reply]; allCnt ¬ XlPrivate.Read16[reply]; thisClient ¬ NEW[Xl.Card32Sequence[thisCnt]]; allClients ¬ NEW[Xl.Card32Sequence[allCnt]]; XlPrivate.Skip[reply, 20]; FOR i: NAT IN [0..thisCnt) DO thisClient[i] ¬ XlPrivate.ERead32[reply]; ENDLOOP; FOR i: NAT IN [0..allCnt) DO allClients[i] ¬ XlPrivate.ERead32[reply]; ENDLOOP; XlPrivate.DisposeReply[c, reply]; }; }; ChangeDeviceDontPropagateList: PUBLIC PROC [c: Xl.Connection, window: Xl.Window, eventClass: LIST OF EventClass, mode: ListModifierMode, details: Xl.Details ¬ NIL] = { count: INT ¬ EventClassesLength[eventClass]; cd: REF ConnectionData ~ GetConnectionData[c]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opChangeDeviceDontPropagateList, 3+count]; XlPrivate.BPutDrawable[c, window]; XlPrivate.BPut16[c, count]; XlPrivate.BPut8[c, ORD[mode]]; XlPrivate.BPut8[c, 0]; PutEventClasses[c, eventClass, count]; XlPrivate.FinishWithDetails[c, details]; }; IF cd.ex#NIL THEN XlPrivate.DoWithLocks[c, action, details]; }; GetDeviceDontPropagateList: PUBLIC PROC [c: Xl.Connection, window: Xl.Window] RETURNS [dontPropagateList: EventClasses ¬ NIL] = { reply: XlPrivate.Reply; cnt: NAT; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opGetDeviceDontPropagateList, 2]; XlPrivate.BPutDrawable[c, window]; reply ¬ XlPrivate.FinishWithReply[c]; }; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex#NIL THEN { XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.CheckReply[reply]; XlPrivate.Skip[reply, 7]; cnt ¬ XlPrivate.Read16[reply]; dontPropagateList ¬ NEW[Xl.Card32Sequence[cnt]]; XlPrivate.Skip[reply, 22]; FOR i: NAT IN [0..cnt) DO dontPropagateList[i] ¬ XlPrivate.ERead32[reply]; ENDLOOP; XlPrivate.DisposeReply[c, reply]; }; }; DeviceTimeCoords: PUBLIC TYPE = XlPrivate.ReplyRec; <> GetDeviceMotionEvents: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, start, stop: Xl.TimeStamp] RETURNS [REF DeviceTimeCoords ¬ NIL] = { reply: XlPrivate.Reply ¬ NIL; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opGetDeviceDontPropagateList, 2]; XlPrivate.BPut32[c, start]; XlPrivate.BPut32[c, stop]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 3]; reply ¬ XlPrivate.FinishWithReply[c]; }; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex#NIL THEN { XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.CheckReply[reply]; RETURN [reply] }; }; IsAbsolute: PUBLIC PROC [dtc: REF DeviceTimeCoords] RETURNS [BOOL] = { RETURN [XlPrivate.Get8[dtc, 13]=0]; }; AxisCount: PUBLIC PROC [dtc: REF DeviceTimeCoords] RETURNS [NAT] = { IF dtc=NIL THEN RETURN [0]; RETURN [XlPrivate.Get8[dtc, 12]]; }; NumberOfEvents: PUBLIC PROC [dtc: REF DeviceTimeCoords] RETURNS [NAT] = { IF dtc=NIL THEN RETURN [0]; RETURN [XlPrivate.Get32[dtc, 8]]; }; TimeOfEvent: PUBLIC PROC [dtc: REF DeviceTimeCoords, eventNum: NAT] RETURNS [time: Xl.TimeStamp] = { vpe: BYTE ~ XlPrivate.Get8[dtc, 12]; XlPrivate.SetPos[dtc, 32+eventNum*(4*vpe+4)]; time ¬ [XlPrivate.ERead32[dtc]] }; ValuatorOfEvent: PUBLIC PROC [dtc: REF DeviceTimeCoords, eventNum: NAT, valuatorNum: NAT] RETURNS [CARD32] = { vpe: BYTE ~ XlPrivate.Get8[dtc, 12]; XlPrivate.SetPos[dtc, 36+eventNum*(4*vpe+4)+valuatorNum*4]; RETURN [ XlPrivate.ERead32[dtc] ] }; ChangePointerDevice: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, xaxis, yaxis: BYTE] RETURNS [status: ChangeReply] = { reply: XlPrivate.Reply ¬ NIL; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opChangePointerDevice, 2]; XlPrivate.BPut8[c, xaxis]; XlPrivate.BPut8[c, yaxis]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 1]; reply ¬ XlPrivate.FinishWithReply[c]; }; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex=NIL THEN ERROR; XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.Skip[reply, 7]; SELECT XlPrivate.Read8[reply] FROM 0 => status ¬ success; 1 => status ¬ alreadyGrabbed; ENDCASE => status ¬ deviceFrozen; XlPrivate.DisposeReply[c, reply]; }; GrabDevice: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, grabWindow: Xl.Window, ownerEvents: BOOL, eventList: LIST OF EventClass, thisDeviceMode: Xl.Synchronicity, otherDeviceMode: Xl.Synchronicity, time: Xl.TimeStamp] RETURNS [Xl.GrabStatus] = { reply: XlPrivate.Reply ¬ NIL; got: BYTE; count: INT ¬ EventClassesLength[eventList]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opGrabDevice, 5+count]; XlPrivate.BPutDrawable[c, grabWindow]; XlPrivate.BPutTime[c, time]; XlPrivate.BPut16[c, count]; XlPrivate.BPut8[c, ORD[thisDeviceMode]]; XlPrivate.BPut8[c, ORD[otherDeviceMode]]; XlPrivate.BPut8[c, ORD[ownerEvents]]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 2]; PutEventClasses[c, eventList, count]; reply ¬ XlPrivate.FinishWithReply[c]; }; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex=NIL THEN ERROR; XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.CheckReply[reply]; got ¬ XlPrivate.Read8[reply]; IF got>ORD[Xl.GrabStatus.LAST] THEN ERROR; XlPrivate.DisposeReply[c, reply]; RETURN [VAL[got]] }; UngrabDevice: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, time: Xl.TimeStamp ¬ Xl.currentTime, details: Xl.Details ¬ NIL] = { cd: REF ConnectionData ~ GetConnectionData[c]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opUngrabDevice, 3]; XlPrivate.BPutTime[c, time]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 3]; XlPrivate.FinishWithDetails[c, details]; }; IF cd.ex#NIL THEN XlPrivate.DoWithLocks[c, action, details]; }; GrabDeviceButton: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, grabWindow: Xl.Window, modifiers: Xl.SetOfKeyButMask, modifierDevice: DeviceIdBase, button: BYTE ¬ 0, ownerEvents: BOOL, eventList: LIST OF EventClass, thisDeviceMode: Xl.Synchronicity, otherDeviceMode: Xl.Synchronicity, details: Xl.Details] = { count: INT ¬ EventClassesLength[eventList]; ownerEventsInternal: BYTE ¬ ORD[ownerEvents]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opGrabDeviceButton, 5+count]; XlPrivate.BPutDrawable[c, grabWindow]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BPut8[c, modifierDevice]; XlPrivate.BPut16[c, count]; XlPrivate.BPut16[c, XlKeyButPrivate.SetToWire[modifiers]]; XlPrivate.BPut8[c, ORD[thisDeviceMode]]; XlPrivate.BPut8[c, ORD[otherDeviceMode]]; XlPrivate.BPut8[c, button]; XlPrivate.BPut8[c, ownerEventsInternal]; --? XlPrivate.BSkip[c, 2]; PutEventClasses[c, eventList, count]; XlPrivate.FinishWithDetails[c, details]; }; cd: REF ConnectionData ~ GetConnectionData[c]; IF cd.ex=NIL THEN ERROR; XlPrivate.DoWithLocks[c, action, details]; }; UngrabDeviceButton: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, grabWindow: Xl.Window, modifiers: Xl.SetOfKeyButMask, modifierDevice: DeviceIdBase, button: BYTE, details: Xl.Details] = { cd: REF ConnectionData ~ GetConnectionData[c]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opUngrabDeviceButton, 4]; XlPrivate.BPutDrawable[c, grabWindow]; XlPrivate.BPut16[c, XlKeyButPrivate.SetToWire[modifiers]]; XlPrivate.BPut8[c, modifierDevice]; XlPrivate.BPut8[c, button]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 3]; XlPrivate.FinishWithDetails[c, details]; }; IF cd.ex#NIL THEN XlPrivate.DoWithLocks[c, action, details]; }; AllowDeviceEvents: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, time: Xl.TimeStamp ¬ Xl.currentTime, mode: AllowDeviceEventsMode, details: Xl.Details ¬ NIL] = { cd: REF ConnectionData ~ GetConnectionData[c]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opAllowDeviceEvents, 2]; XlPrivate.BPutTime[c, time]; XlPrivate.BPut8[c, ORD[mode]]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 2]; XlPrivate.FinishWithDetails[c, details]; }; IF cd.ex#NIL THEN XlPrivate.DoWithLocks[c, action, details]; }; IntLength: PROC [list: LIST OF INT] RETURNS [cnt: BYTE ¬ 0] = { FOR l: LIST OF INT ¬ list, l.rest WHILE l#NIL DO IF cnt>=255 THEN ERROR; cnt ¬ cnt+1; ENDLOOP }; SetDeviceValuators: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, first: INT, values: LIST OF INT] RETURNS [status: ValuatorStatus] = { reply: XlPrivate.Reply; cnt: BYTE ¬ IntLength[values]; cd: REF ConnectionData ~ GetConnectionData[c]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opSetDeviceValuators, 2+cnt]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BPut8[c, first]; XlPrivate.BPut8[c, cnt]; XlPrivate.BSkip[c, 1]; FOR i: INT IN [0..cnt) DO XlPrivate.BPut32[c, LOOPHOLE[values.first, CARD32]]; IF values.rest#NIL THEN values ¬ values.rest; ENDLOOP; reply ¬ XlPrivate.FinishWithReply[c]; }; IF cd.ex=NIL THEN ERROR; IF first<0 OR first+cnt>255 THEN ERROR; XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.Skip[reply, 7]; SELECT XlPrivate.Read8[reply] FROM 0 => status ¬ success; 1 => status ¬ alreadyGrabbed; ENDCASE => ERROR; XlPrivate.DisposeReply[c, reply]; }; GetDeviceFocus: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId] RETURNS [dfr: DeviceFocusRec] = { reply: XlPrivate.Reply; x: BYTE; cd: REF ConnectionData ~ GetConnectionData[c]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opGetDeviceFocus, 2]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 3]; reply ¬ XlPrivate.FinishWithReply[c]; }; IF cd.ex=NIL THEN ERROR; XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.Skip[reply, 7]; dfr.window ¬ [[XlPrivate.ERead32[reply]]]; dfr.time ¬ [XlPrivate.ERead32[reply]]; x ¬ XlPrivate.Read8[reply]; IF x>ORD[DeviceFocusReversion.LAST] THEN ERROR; dfr.revertTo ¬ VAL[x]; XlPrivate.DisposeReply[c, reply]; }; SetDeviceFocus: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId, dfr: DeviceFocusRec ¬ [], details: Xl.Details] = { cd: REF ConnectionData ~ GetConnectionData[c]; action: PROC [c: Xl.Connection] = { XlPrivate.BInit[c, cd.ex.majorOpcode, opSetDeviceFocus, 4]; XlPrivate.BPutDrawable[c, dfr.window]; XlPrivate.BPutTime[c, dfr.time]; XlPrivate.BPut8[c, ORD[dfr.revertTo]]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 2]; XlPrivate.FinishWithDetails[c, details]; }; IF cd.ex=NIL THEN ERROR; XlPrivate.DoWithLocks[c, action, details]; }; QueryDeviceState: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId] RETURNS [s: InputStates] = { cd: REF ConnectionData ~ GetConnectionData[c]; reply: XlPrivate.Reply; action: PROC [c: Xl.Connection] ~ { XlPrivate.BInit[c, cd.ex.majorOpcode, opQueryDeviceState, 2]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 3]; reply ¬ XlPrivate.FinishWithReply[c]; }; lastClass: LIST OF REF InputState ¬ NIL; IF cd.ex=NIL THEN ERROR; XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.CheckReply[reply]; XlPrivate.Skip[reply, 7]; s.numberOfClasses ¬ XlPrivate.Read8[reply]; XlPrivate.Skip[reply, 23]; FOR i: BYTE IN [0..s.numberOfClasses) DO this: REF InputState ¬ NIL; x: BYTE ¬ XlPrivate.ERead8[reply]; length: BYTE ¬ XlPrivate.ERead8[reply]; number: BYTE ¬ XlPrivate.ERead8[reply]; mode: BYTE ¬ XlPrivate.ERead8[reply]; SELECT x FROM ORD[InputClass[keyClass]] => { keyThis: REF keyClass InputState ¬ NEW[keyClass InputState]; FOR i: INT IN [0..32) DO keyThis.keys[i] ¬ XlPrivate.ERead8[reply]; ENDLOOP; this ¬ keyThis; }; ORD[InputClass[buttonClass]] => { butThis: REF buttonClass InputState ¬ NEW[buttonClass InputState]; FOR i: INT IN [0..32) DO butThis.buttons[i] ¬ XlPrivate.ERead8[reply]; ENDLOOP; this ¬ butThis; }; ORD[InputClass[valuatorClass]] => { valThis: REF valuatorClass InputState ¬ NEW[valuatorClass InputState]; valThis.relativeMode ¬ ((mode) MOD 2)=0; valThis.inProximity ¬ ((mode / 2) MOD 2)=0; valThis.valuators ¬ NEW[Int32Sequence[number]]; FOR i: BYTE IN [0..number) DO valThis.valuators[i] ¬ XlPrivate.ERead32[reply]; ENDLOOP; this ¬ valThis; }; ENDCASE => ERROR; this.number ¬ number; IF lastClass=NIL THEN {lastClass ¬ s.states ¬ LIST[this]} ELSE {lastClass.rest ¬ LIST[this]; lastClass ¬ lastClass.rest} ENDLOOP; XlPrivate.DisposeReply[c, reply]; }; GetDeviceButtonMapping: PUBLIC PROC [c: Xl.Connection, deviceId: DeviceId] RETURNS [Xl.PointerMapping] = { <<--Should we do some caching ?>> reply: XlPrivate.Reply; action: PROC [c: Xl.Connection] ~ { XlPrivate.BInit[c, cd.ex.majorOpcode, opGetDeviceButtonMapping, 2]; XlPrivate.BPut8[c, deviceId]; XlPrivate.BSkip[c, 3]; reply ¬ XlPrivate.FinishWithReply[c]; }; n: BYTE; cd: REF ConnectionData ~ GetConnectionData[c]; pm: REF Xl.PointerMappingSequence; IF cd.ex=NIL THEN ERROR; XlPrivate.DoWithLocks[c, action, NIL]; XlPrivate.CheckReply[reply]; XlPrivate.Skip[reply, 7]; n ¬ XlPrivate.Read8[reply]; XlPrivate.Skip[reply, 23]; pm ¬ NEW[Xl.PointerMappingSequence[n+1]]; pm[0] ¬ 0; FOR i: BYTE IN [1..pm.leng) DO pm[i] ¬ XlPrivate.ERead8[reply]; ENDLOOP; XlPrivate.DisposeReply[c, reply]; RETURN [pm]; }; <> ExtensionStateRec: TYPE = RECORD [ <> lastMultiEvent: REF Xl.EventRep.extension ¬ NIL, --assigned if it could have a continuation lastMultiExt: REF ExtEventRep ¬ NIL, valuators: REF Int32Sequence ¬ NIL, keys: REF PACKED ARRAY [0..31] OF BYTE ¬ NIL, buttons: REF PACKED ARRAY [0..31] OF BYTE ¬ NIL, lastSequenceNo: Xl.SequenceNo ]; FinishMulti: PROC [state: REF ExtensionStateRec] = { XlDispatch.FindAndDispatch[state.lastMultiEvent]; state.lastMultiEvent ¬ NIL; state.lastMultiExt ¬ NIL; state.buttons ¬ NIL; state.keys ¬ NIL; state.valuators ¬ NIL; }; ProcessEvents: XlExtensions.ProcessExtensionEventProc = {--decodes arriving event <> <> eventOffset: EventOffset ~ VAL[xEvent.originalCodeByte MOD 128 - self.firstEvent]; state: REF ExtensionStateRec ~ NARROW[self.state]; DeviceBaseEvent: PROC [xEvent: REF Xl.EventRep.extension, state: REF ExtensionStateRec] RETURNS [e: REF deviceBase ExtEventRep ¬ NEW[deviceBase ExtEventRep]] = { state.lastMultiEvent ¬ xEvent; state.lastMultiExt ¬ e; e.seq ¬ state.lastSequenceNo ¬ XlEndianPrivate.InlineExtensionGet16[xEvent, 2]; e.time ¬ [XlEndianPrivate.InlineExtensionGet32[xEvent, 4]]; e.rootWindow ¬ DecodeWindow[xEvent, 8]; xEvent.dispatchDrawable ¬ e.eventWindow ¬ DecodeWindow[xEvent, 12]; e.childWindow ¬ DecodeWindow[xEvent, 16]; e.rootPos.x ¬ DecodeIntFrom16[xEvent, 20]; e.rootPos.y ¬ DecodeIntFrom16[xEvent, 22]; e.eventPos.x ¬ DecodeIntFrom16[xEvent, 24]; e.eventPos.y ¬ DecodeIntFrom16[xEvent, 26]; e.state ¬ XlKeyButPrivate.SetFromWire[XlEndianPrivate.InlineExtensionGet16[xEvent, 28]]; e.sameScreen ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 30]#0; e.deviceId ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 31] MOD 128; xEvent.decoded ¬ e; }; DeviceFocusEvent: PROC [xEvent: REF Xl.EventRep.extension, state: REF ExtensionStateRec, in: BOOL] = { e: REF deviceFocus ExtEventRep ~ NEW[deviceFocus ExtEventRep]; detail: BYTE ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 1]; mode: BYTE ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 12]; state.lastMultiEvent ¬ NIL; state.lastMultiExt ¬ NIL; IF detail>ORD[Xl.FocusDetail.LAST] OR mode>ORD[Xl.GrabMode.LAST] THEN RETURN; e.detail ¬ VAL[detail]; e.mode ¬ VAL[mode]; e.seq ¬ XlEndianPrivate.InlineExtensionGet16[xEvent, 2]; e.time ¬ [XlEndianPrivate.InlineExtensionGet32[xEvent, 4]]; e.eventWindow ¬ DecodeWindow[xEvent, 8]; e.deviceId ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 13] MOD 128; IF in THEN {e.eventOffset ¬ deviceFocusIn; xEvent.match ¬ deviceFocusInKey} ELSE {e.eventOffset ¬ deviceFocusOut; xEvent.match ¬ deviceFocusOutKey}; xEvent.decoded ¬ e; XlDispatch.FindAndDispatch[xEvent]; }; xEvent.extension ¬ extensionKey; SELECT eventOffset FROM deviceValuator => { DeviceValuator: PROC [xEvent: REF Xl.EventRep.extension, state: REF ExtensionStateRec] = { seq: Xl.SequenceNo ¬ XlEndianPrivate.InlineExtensionGet16[xEvent, 2]; <> <> RETURN; --wrong formed events could have been sent by an other client.>> valuators: REF Int32Sequence ¬ state.valuators; cnt: NAT ¬ MIN[XlEndianPrivate.InlineExtensionGet8[xEvent, 6], 6]; first: NAT ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 7]; Scan: PROC [] = { IF valuators.leng>; FOR i: NAT IN [0..cnt) DO valuators[first+i] ¬ LOOPHOLE[XlEndianPrivate.InlineExtensionGet32[xEvent, i*4 + 8]] ENDLOOP; IF first+6>=cnt THEN { <<--don't test keys and buttons; the valuators are reported last>> FinishMulti[state]; } }; WITH state.lastMultiExt SELECT FROM b: REF deviceBase ExtEventRep => { IF valuators=NIL THEN b.valuators ¬ state.valuators ¬ valuators ¬ NEW[Int32Sequence[cnt+first]]; b.deviceState ¬ XlKeyButPrivate.SetFromWire[XlEndianPrivate.InlineExtensionGet16[xEvent, 4]]; Scan[]; }; s: REF deviceState ExtEventRep => { IF valuators=NIL THEN RETURN; Scan[]; }; ENDCASE => {} }; DeviceValuator[xEvent, state]; }; deviceFocusIn => { DeviceFocusEvent[xEvent, state, TRUE]; }; deviceFocusOut => { DeviceFocusEvent[xEvent, state, FALSE]; }; deviceStateNotify => { DeviceStateNotify: PROC [xEvent: REF Xl.EventRep.extension, state: REF ExtensionStateRec] = { x: BYTE ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 11]; e: REF deviceState ExtEventRep ~ NEW[deviceState ExtEventRep]; state.lastMultiEvent ¬ xEvent; state.lastMultiExt ¬ e; state.valuators ¬ NIL; state.keys ¬ NIL; state.buttons ¬ NIL; e.deviceId ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 1] MOD 128; e.seq ¬ state.lastSequenceNo ¬ XlEndianPrivate.InlineExtensionGet16[xEvent, 2]; e.time ¬ [XlEndianPrivate.InlineExtensionGet32[xEvent, 4]]; e.numKeys ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 8]; e.numButtons ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 9]; e.numValuators ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 10]; IF (x) MOD 2 # 0 THEN { e.keys ¬ state.keys ¬ NEW[PACKED ARRAY [0..31] OF BYTE]; state.keys[0] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 12]; state.keys[1] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 13]; state.keys[2] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 14]; state.keys[3] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 15]; }; IF (x/2) MOD 2 # 0 THEN { e.buttons ¬ state.buttons ¬ NEW[PACKED ARRAY [0..31] OF BYTE]; state.buttons[0] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 16]; state.buttons[1] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 17]; state.buttons[2] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 18]; state.buttons[3] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 19]; }; IF (x/4) MOD 2 # 0 THEN { e.valuators ¬ state.valuators ¬ NEW[Int32Sequence[e.numValuators]]; state.valuators[0] ¬ DecodeInt32[xEvent, 20]; state.valuators[1] ¬ DecodeInt32[xEvent, 24]; state.valuators[2] ¬ DecodeInt32[xEvent, 28]; }; e.relativeMode ¬ (x MOD 040H) MOD 2 # 0; e.inProximity ¬ (x MOD 080H) MOD 2 # 0; e.eventOffset ¬ deviceStateNotify; xEvent.match ¬ deviceStateNotifyKey; xEvent.decoded ¬ e; IF (state.keys=NIL OR e.numKeys<32) AND (state.buttons=NIL OR e.numButtons<32) AND (state.valuators=NIL OR e.numValuators<3) THEN FinishMulti[state]; }; DeviceStateNotify[xEvent, state]; }; deviceKeyStateNotify => { <<--after deviceStateNotify>> DeviceKeyStateNotify: PROC [xEvent: REF Xl.EventRep.extension, state: REF ExtensionStateRec] = { WITH state.lastMultiExt SELECT FROM s: REF deviceState ExtEventRep => { IF state.keys=NIL THEN RETURN; FOR i: INT IN [4..32) DO state.keys[i] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, i]; ENDLOOP; IF (XlEndianPrivate.InlineExtensionGet8[xEvent, 1] / 080H) MOD 2 = 0 THEN FinishMulti[state]; }; ENDCASE => {} }; DeviceKeyStateNotify[xEvent, state]; }; deviceButtonStateNotify => { <<--immediately after deviceStateNotify>> DeviceButtonStateNotify: PROC [xEvent: REF Xl.EventRep.extension, state: REF ExtensionStateRec] = { WITH state.lastMultiExt SELECT FROM s: REF deviceState ExtEventRep => { IF state.buttons=NIL THEN RETURN; FOR i: INT IN [4..32) DO state.buttons[i] ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, i]; ENDLOOP; IF (XlEndianPrivate.InlineExtensionGet8[xEvent, 1] / 080H) MOD 2 = 0 THEN FinishMulti[state]; }; ENDCASE => {} }; DeviceButtonStateNotify[xEvent, state]; }; deviceMappingNotify => { DeviceMappingNotify: PROC [xEvent: REF Xl.EventRep.extension] = { e: REF deviceMapping ExtEventRep ~ NEW[deviceMapping ExtEventRep]; request: BYTE ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 4]; IF request>2 THEN RETURN <>; e.deviceId ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 1] MOD 128; e.seq ¬ XlEndianPrivate.InlineExtensionGet16[xEvent, 2]; e.time ¬ [XlEndianPrivate.InlineExtensionGet32[xEvent, 8]]; e.firstKeycode ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 5]; e.count ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 6]; e.request ¬ VAL[request]; e.eventOffset ¬ deviceMappingNotify; xEvent.match ¬ deviceMappingNotifyKey; xEvent.decoded ¬ e; XlDispatch.FindAndDispatch[xEvent]; }; DeviceMappingNotify[xEvent] }; changeDeviceNotify => { ChangeDeviceNotify: PROC [xEvent: REF Xl.EventRep.extension] = { e: REF deviceChange ExtEventRep ~ NEW[deviceChange ExtEventRep]; what: BYTE ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 8]; IF what>1 THEN RETURN <>; e.newWhat ¬ VAL[what]; e.deviceId ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 1] MOD 128; e.seq ¬ XlEndianPrivate.InlineExtensionGet16[xEvent, 2]; e.time ¬ [XlEndianPrivate.InlineExtensionGet32[xEvent, 4]]; e.eventOffset ¬ changeDeviceNotify; xEvent.match ¬ changeDeviceNotifyKey; xEvent.decoded ¬ e; XlDispatch.FindAndDispatch[xEvent]; }; ChangeDeviceNotify[xEvent] }; deviceKeyPress => { moreEvents: BOOL; e: REF deviceBase ExtEventRep ~ DeviceBaseEvent[xEvent, state]; e.keyCode ¬ VAL[XlEndianPrivate.InlineExtensionGet8[xEvent, 1]]; e.eventOffset ¬ deviceKeyPress; xEvent.match ¬ deviceKeyPressKey; moreEvents ¬ (XlEndianPrivate.InlineExtensionGet8[xEvent, 31] / 128) # 0; IF ~moreEvents THEN XlDispatch.FindAndDispatch[xEvent]; }; deviceKeyRelease => { moreEvents: BOOL; e: REF deviceBase ExtEventRep ~ DeviceBaseEvent[xEvent, state]; e.keyCode ¬ VAL[XlEndianPrivate.InlineExtensionGet8[xEvent, 1]]; e.eventOffset ¬ deviceKeyRelease; xEvent.match ¬ deviceKeyReleaseKey; moreEvents ¬ (XlEndianPrivate.InlineExtensionGet8[xEvent, 31] / 128) # 0; IF ~moreEvents THEN XlDispatch.FindAndDispatch[xEvent]; }; deviceButtonPress => { moreEvents: BOOL; e: REF deviceBase ExtEventRep ~ DeviceBaseEvent[xEvent, state]; e.button ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 1]; e.eventOffset ¬ deviceButtonPress; xEvent.match ¬ deviceButtonPressKey; moreEvents ¬ (XlEndianPrivate.InlineExtensionGet8[xEvent, 31] / 128) # 0; IF ~moreEvents THEN XlDispatch.FindAndDispatch[xEvent]; }; deviceButtonRelease => { moreEvents: BOOL; e: REF deviceBase ExtEventRep ~ DeviceBaseEvent[xEvent, state]; e.button ¬ XlEndianPrivate.InlineExtensionGet8[xEvent, 1]; e.eventOffset ¬ deviceButtonRelease; xEvent.match ¬ deviceButtonReleaseKey; moreEvents ¬ (XlEndianPrivate.InlineExtensionGet8[xEvent, 31] / 128) # 0; IF ~moreEvents THEN XlDispatch.FindAndDispatch[xEvent]; }; deviceMotionNotify => { moreEvents: BOOL; e: REF deviceBase ExtEventRep ~ DeviceBaseEvent[xEvent, state]; e.normalHint ¬ VAL[XlEndianPrivate.InlineExtensionGet8[xEvent, 1] MOD 2]; e.eventOffset ¬ deviceMotionNotify; xEvent.match ¬ deviceMotionNotifyKey; moreEvents ¬ (XlEndianPrivate.InlineExtensionGet8[xEvent, 31] / 128) # 0; IF ~moreEvents THEN XlDispatch.FindAndDispatch[xEvent]; }; proximityIn => { moreEvents: BOOL; e: REF deviceBase ExtEventRep ~ DeviceBaseEvent[xEvent, state]; e.eventOffset ¬ proximityIn; xEvent.match ¬ proximityInKey; moreEvents ¬ (XlEndianPrivate.InlineExtensionGet8[xEvent, 31] / 128) # 0; IF ~moreEvents THEN XlDispatch.FindAndDispatch[xEvent]; }; proximityOut => { moreEvents: BOOL; e: REF deviceBase ExtEventRep ~ DeviceBaseEvent[xEvent, state]; e.eventOffset ¬ proximityOut; xEvent.match ¬ proximityOutKey; moreEvents ¬ (XlEndianPrivate.InlineExtensionGet8[xEvent, 31] / 128) # 0; IF ~moreEvents THEN XlDispatch.FindAndDispatch[xEvent]; }; ENDCASE => ERROR; }; DecodeIntFrom16: PROC [xEvent: REF Xl.EventRep.extension, pos: INT] RETURNS [i: INT] = { i ¬ LOOPHOLE[XlEndianPrivate.InlineExtensionGet16[xEvent, pos], INT16]; }; DecodeInt32: PROC [xEvent: REF Xl.EventRep.extension, pos: INT] RETURNS [i: INT] = { i ¬ LOOPHOLE[XlEndianPrivate.InlineExtensionGet32[xEvent, pos], INT32]; }; DecodeWindow: PROC [xEvent: REF Xl.EventRep.extension, pos: INT] RETURNS [w: Xl.Window] = { w ¬ LOOPHOLE[XlEndianPrivate.InlineExtensionGet32[xEvent, pos], Xl.Window]; }; XlExtensions.DefineExtensionClass[extensionKey, ProcessEvents, ORD[EventOffset.LAST]+1]; END.