XlInputExtensionImpl.mesa
Copyright Ó 1991, 1992, 1993 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, December 30, 1991 1:59 pm PST
Christian Jacobi, February 2, 1993 4:07 pm PST
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;
Data structure is not monitored and should be queried by a single process
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];
};
Events
ExtensionStateRec: TYPE = RECORD [
Accumulated state while accepting multi packet events
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
This extension supports chunks of multiple events following immediately...
We wait until all connected events are collected and the report the event to our client; if the events do not appear in the right order we will not crash (it could be client generated), but we won't report exactly what has been seen either.
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];
Don't test sequence numbers for the benefit of client generated event sequences.
IF seq#state.lastSequenceNo THEN <<OOPS>> 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<cnt+first THEN RETURN <<OOPS>>;
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 <<OOPS>>;
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 <<OOPS>>;
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.