* InputLock V1.0 * By Preben Nielsen * * This program is intended as a help to Amiga-users who has cats * or other pets (or children) that messes with the Amiga as soon * as you leave it for a second. * * It installes an input-handler which lets you lock the keyboard * and mouse by pressing a few buttons (currently LALT-CTRL-'l'). * * Once installed, the program only uses 190 bytes of memory. To * remove the handler simply run the program again. * * NOTE: There's no need to 'RUN' or 'RUNBACK' this program from the * CLI. It terminates immediately. * *HISTORY * Made with Hisoft V2.12 * * V1.0 11-Apr-91: First attempt. Works of course OPT O+ OPT O1+ ; Tells when a branch could be optimised to short OPT i+ ; Tells when '#' is probably missing incdir "AsmInc:" include "exec/exec_lib.i" include "exec/io.i" include "exec/memory.i" include "exec/interrupts.i" include "devices/input.i" include "devices/inputevent.i" include "libraries/dosextens.i" include "libraries/dos_lib.i" Prepare MACRO IFC '\1','Exec_Call' movea.l 4.W,A6 ENDC IFC '\1','Intuition_Call' movea.l IntBase(DB),A6 ENDC IFC '\1','Gfx_Call' movea.l GfxBase(DB),A6 ENDC IFC '\1','Dos_Call' movea.l DosBase(DB),A6 ENDC ENDM CallLib MACRO jsr _LVO\1(A6) ENDM Call MACRO bsr \1 ENDM CallS MACRO bsr.S \1 ENDM Push MACRO movem.l \1,-(SP) ENDM Pop MACRO movem.l (SP)+,\1 ENDM rAPtr MACRO name DefSiz set DefSiz+4 DefPtr set DefPtr-4 \1 = DefPtr ENDM rLong MACRO name DefSiz set DefSiz+4 DefPtr set DefPtr-4 \1 = DefPtr ENDM rWord MACRO name DefSiz set DefSiz+2 DefPtr set DefPtr-2 \1 = DefPtr ENDM rByte MACRO name DefSiz set DefSiz+1 DefPtr set DefPtr-1 \1 = DefPtr ENDM rStorage MACRO name,size ; Define storage DefSiz set DefSiz+\2 DefPtr set DefPtr-\2 \1 = DefPtr ENDM rEVEN MACRO ; Word boundary IFNE DefPtr&1 DefPtr set DefPtr-1 DefSiz set DefSiz+1 ENDC ENDM rStart MACRO ; Define var section DefPtr set 0 DefSiz set 0 ENDM rEnd MACRO ; End var section RelSize = DefSiz ENDM rAlloc MACRO ; Allocate vars link DB,#-RelSize ENDM rFree MACRO ; Deallocate vars unlk DB ENDM rClear MACRO ; Reset all vars movem.l D0/DB,-(SP) move.w #RelSize-1,D0 rClr.\@ clr.b -(DB) dbf D0,rClr.\@ movem.l (SP)+,D0/DB ENDM DB EQUR A4 SECTION InputLock,CODE InitProcess rAlloc ; Allocate memory for variables rClear ; Clear the memory Prepare Exec_Call suba.l A1,A1 CallLib FindTask ; Find us move.l D0,PProcess(DB) movea.l D0,A2 tst.l pr_CLI(A2) bne.S GetLibs WBStart lea pr_MsgPort(A2),A0 CallLib WaitPort ; wait for a message lea pr_MsgPort(A2),A0 CallLib GetMsg ; then get it move.l D0,WBMsg(DB) ; save it for later reply GetLibs lea DosName(PC),A1 CallLib OldOpenLibrary move.l D0,DosBase(DB) beq.S Error CallLib Forbid lea IHS(PC),A1 lea ihs_PortName(A1),A1 CallLib FindPort move.l D0,D2 CallLib Permit move.l D2,D0 ; Does Forbid/Permit destroy scratch-registers ? beq.S DoInstall DoRemove moveq #REMOVED,D7 lea IHS(PC),A0 lea PSEndIHS1(PC),A1 lea PSEndIHS2(PC),A2 Call RemoveHandler beq.S ShowMsg moveq #CANTREMOVE,D7 bra.S ShowMsg DoInstall moveq #INSTALLED,D7 lea IHS(PC),A0 lea PSPrepIHS1(PC),A1 lea PSPrepIHS2(PC),A2 Call InstallHandler beq.S ShowMsg moveq #CANTINSTALL,D7 ShowMsg move.l D7,D0 Call CONMsg Error Exit Prepare Exec_Call FreeDos move.l DosBase(DB),D0 beq.S ReplyWB move.l D0,A1 CallLib CloseLibrary ReplyWB move.l WBMsg(DB),D2 beq.S AllDone CallLib Forbid ; We were started from WB movea.l D2,A1 CallLib ReplyMsg ; Reply WBMessage AllDone rFree moveq #0,D0 rts FHandle EQUR D5 * Call: D0 = Msg-number CONMsg Push D0-D7/A0-A6 Prepare Dos_Call move.l D0,D4 moveq #0,D6 CallLib Output move.l D0,FHandle bne.S 1$ moveq #1,D6 lea CONName(PC),A0 move.l A0,D1 move.l #MODE_OLDFILE,D2 CallLib Open move.l D0,FHandle beq.S 2$ 1$ moveq #INFOMSG,D0 Call SendMsg move.l D4,D0 Call SendMsg tst.l D6 beq.S 2$ moveq #127,D1 CallLib Delay move.l FHandle,D1 CallLib Close 2$ Pop D0-D7/A0-A6 rts * Call: D0 = Msg-number SendMsg neg.l D0 lsl.l #1,D0 lea MsgTable(PC),A0 add.w 0(A0,D0),A0 move.l A0,D2 moveq #-1,D3 1$ addq.l #1,D3 tst.b (A0)+ bne.S 1$ move.l FHandle,D1 Prepare Dos_Call CallLib Write rts INFOMSG =0 INSTALLED =-1 REMOVED =-2 CANTINSTALL =-3 CANTREMOVE =-4 MsgText MACRO dc.w \1-MsgTable ENDM MsgTable MsgText Msg MsgText Msg1 MsgText Msg2 MsgText Msg3 MsgText Msg4 CONName dc.b 'CON:100/60/330/63/InputLock',0 Msg dc.b 10,$9B,'0;33m InputLock V1.0',10 dc.b $9B,'0;31m 1991 by ',$9B,'0;33mPreben Nielsen',$9B,'0;31m',10,' ',0 Msg1 dc.b 'has just been installed...',10,0 Msg2 dc.b 'has just been removed...',10,0 Msg3 dc.b 'Error: Cannot install handler',10,0 Msg4 dc.b 'Error: Cannot remove handler',10,0 EVEN rtsValue EQUR D7 * This is general-purpose inputhandler removal-routine * It only needs an ihs with a port-name to remove the handler * Call: A0 = ihs * A1 = first ihs-installation-routine or NULL * A2 = second ihs-installation-routine or NULL * Return: D0 = 0 means succes RemoveHandler Push D1/rtsValue/A0-A3/A6 moveq #-1,rtsValue move.l A2,A3 move.l A0,A2 move.l A1,D1 beq.S 1$ jsr (A1) ; A0 = ihs beq.S 2$ move.l D0,A2 1$ move.l A2,A0 Prepare Exec_Call moveq #IND_REMHANDLER,D0 Call TellInputDevice move.l D0,rtsValue bne.S 2$ lea ihs_Port(A2),A1 CallLib RemPort moveq #0,D0 bra.S 3$ 2$ moveq #-1,D0 3$ move.l A3,D1 beq.S 4$ move.l A2,A0 jsr (A3) ; A0 = ihs, D0 = 0 means succes 4$ move.l rtsValue,D0 Pop D1/rtsValue/A0-A3/A6 rts * This is general-purpose inputhandler installation-routine * It only needs an ihs with a port-name to install the handler * Call: A0 = ihs * A1 = first ihs-installation-routine or NULL * A2 = second ihs-installation-routine or NULL * Return: D0 = 0 means succes InstallHandler Push D1/rtsValue/A0-A3/A6 moveq #-1,rtsValue move.l A2,A3 move.l A0,A2 move.l A1,D1 beq.S 1$ jsr (A1) ; A0 = ihs beq.S 2$ move.l D0,A2 1$ move.l A2,A0 moveq #IND_ADDHANDLER,D0 Call TellInputDevice move.l D0,rtsValue bne.S 2$ lea ihs_Port(A2),A1 lea ihs_PortName(A2),A0 move.l A0,MP+LN_NAME(A1) ;MsgPort->mp_Node.ln_Name=Name; clr.b MP+LN_PRI(A1) ;MsgPort->mp_Node.ln_Pri =0; move.b #NT_MSGPORT,MP+LN_TYPE(A1) ;MsgPort->mp_Node.ln_Type=NT_MSGPORT; move.b #PA_IGNORE,MP_FLAGS(A1) ;MsgPort->mp_Flags =PA_IGNORE; Prepare Exec_Call CallLib AddPort moveq #0,D0 bra.S 3$ 2$ moveq #-1,D0 3$ move.l A3,D1 beq.S 4$ move.l A2,A0 jsr (A3) ; A0 = ihs, D0 = 0 means succes 4$ move.l rtsValue,D0 Pop D1/rtsValue/A0-A3/A6 rts * Open the input device. Set up the I/O block to add or remove the * input handler, and send the request to the input device. Finally, * close the device * Call: A0 = ihs * D0 = Function to perform (IND_ADDHANDLER/IND_REMHANDLER) * Return: D0 = 0 means succes TellInputDevice Push D1-D2/rtsValue/A0-A3/A6 Prepare Exec_Call moveq #-1,rtsValue move.l D0,D2 move.l A0,A2 lea IReq(DB),A0 moveq #IOSTD_SIZE,D0 Call MemClear lea IPort(DB),A0 moveq #MP_SIZE,D0 Call MemClear move.l A0,A3 move.b #NT_MSGPORT,MP+LN_TYPE(A3) ; mp_Node.ln_Type=NT_MSGPORT; move.b #PA_SIGNAL,MP_FLAGS(A3) ; mp_Flags =PA_SIGNAL; moveq #-1,D0 CallLib AllocSignal move.b D0,MP_SIGBIT(A3) ; mp_SigBit =MPSigBit; bmi.S 2$ suba.l A1,A1 CallLib FindTask move.l D0,MP_SIGTASK(A3) ; mp_SigTask =FindTask(0); lea IReq(DB),A1 move.l A3,IO+MN_REPLYPORT(A1) ; ExtReq->io_Message.mn_ReplyPort =taskReplyPort; move.b #NT_MESSAGE,IO+MN+LN_TYPE(A1) ; ExtReq->io_Message.mn_Node.ln_Type=NT_MESSAGE; lea InputName(PC),A0 ; input.device moveq #0,D0 ; unit# moveq #0,D1 ; flags CallLib OpenDevice tst.w D0 ; flag: error if > 0 bne.S 1$ lea IReq(DB),A1 move.w D2,IO_COMMAND(A1) lea ihs_Interrupt(A2),A0 move.l A0,IO_DATA(A1) CallLib DoIO move.l D0,rtsValue lea IReq(DB),A1 CallLib CloseDevice 1$ move.b MP_SIGBIT(A3),D0 CallLib FreeSignal 2$ move.l rtsValue,D0 Pop D1-D2/rtsValue/A0-A3/A6 rts * Call: A0 = Memory area * D0:16 = Count MemClear Push D0-D1/A0 moveq #0,D1 bra.S 2$ 1$ move.b D1,(A0)+ 2$ dbf D0,1$ Pop D0-D1/A0 rts * Call: A0 = Source * A1 = Destination * D0:16= Count MemCopy Push D0/A0-A1 bra.S 2$ 1$ move.b (A0)+,(A1)+ 2$ dbf D0,1$ Pop D0/A0-A1 rts * Each handler should have such a pair of installation-routine * The first one is passed to InstallHandler in A1 and it * is called immediately when entering InstallHandler * The second one is passed to InstallHandler in A2 and it * is called after attempt to install handler and message-port * ----------------------------------------------------------------- * Call: A0 = ihs * Return: D0 has to point to ihs to be used when installation proceeds * If D0 = 0 then installation is aborted PSPrepIHS1 Push D1/A0-A1/A6 Prepare Exec_Call move.l #HandlerSize,D0 move.l #MEMF_PUBLIC|MEMF_CLEAR,D1 CallLib AllocMem move.l D0,HMem(DB) beq.S 2$ move.l D0,A1 lea IHS(PC),A0 move.l #HandlerSize,D0 Call MemCopy move.l D0,ihs_Length(A1) ; This will enable removal by other programs lea HandlerCode-IHS(A1),A0 move.l A0,ihs_Interrupt+IS_CODE(A1) ; HandlerBlock.HInterrupt.is_Code = Handler clr.l ihs_Interrupt+IS_DATA(A1) ; HandlerBlock.HInterrupt.is_Data = 0 move.b #HPRI,ihs_Interrupt+LN_PRI(A1) ; HandlerBlock.HInterrupt.is_Node.ln_Pri = PRI move.l A1,D0 2$ Pop D1/A0-A1/A6 rts * Call: A0 = ihs * D0 = 0 means everything went perfect * -1 means something went wrong during installation PSPrepIHS2 Push D0-D1/A0-A1/A6 tst.l D0 beq.S 1$ move.l HMem(DB),D0 beq.S 1$ move.l D0,A1 move.l #HandlerSize,D0 Prepare Exec_Call CallLib FreeMem 1$ Pop D0-D1/A0-A1/A6 rts * Each handler should have such a pair of ending-routine * The first one is passed to RemoveHandler in A1 and it * is called immediately when entering RemoveHandler * The second one is passed to RemoveHandler in A2 and it * is called after attempt to remove handler and message-port * ----------------------------------------------------------------- * Call: A0 = ihs * Return: D0 has to point to ihs to be used when removal proceeds * If D0 = 0 then removal is aborted PSEndIHS1 Push D1-D2/A0-A1/A6 Prepare Exec_Call CallLib Forbid lea ihs_PortName(A0),A1 CallLib FindPort move.l D0,D2 CallLib Permit move.l D2,D0 ; Does Forbid/Permit destroy scratch-registers ? Pop D1-D2/A0-A1/A6 rts * Call: A0 = ihs * D0 = 0 means everything went perfect * -1 means something went wrong during removal PSEndIHS2 Push D0-D1/A0-A1/A6 tst.l D0 bmi.S 1$ Prepare Exec_Call move.l ihs_Length(A0),D0 move.l A0,A1 CallLib FreeMem 1$ Pop D0-D1/A0-A1/A6 rts *====================== Input-handler start ========================= ihs_Port =0 ihs_Interrupt =MP_SIZE ihs_ID =MP_SIZE+IS_SIZE ihs_Length =MP_SIZE+IS_SIZE+4 ihs_Flags =MP_SIZE+IS_SIZE+8 ihs_PortName =MP_SIZE+IS_SIZE+10 ihs_Start MACRO dcb.b MP_SIZE ; Message-Port structure dcb.b IS_SIZE ; Interrupt structure dc.l 'P_IH' ; ID (Handler made by me) dc.l 0 ; Length of handler dc.w 0 ; Flags dc.b \1,0 EVEN ENDM HPRI =127 ; Handler-priority HDisabled =0 HNoExtRemoval =1 ; Future * This is the handler-block IHS ihs_Start <'InputLock V1.0 Port'> * Local variables Chain dc.l 0 Status dc.w 0 INPUTDISABLED =0 * For each event in the event list: * If we were waiting for this event then process it. * When all the events have been checked, return the event list so that * others can do their things. PEvent EQUR A2 ; Previous Event Event EQUR A1 ; This Event Bits EQUR A0 Next =ie_NextEvent Class =ie_Class Code =ie_Code Qual =ie_Qualifier * These are the qualifier-keys the input-handler waits for QUALIFIERS =IEQUALIFIER_LALT|IEQUALIFIER_CONTROL * These are the keys the input-handler acts on Toggle_Key =$28 ; 'l' * Call: A0 = List of InputEvents, A1 = HandlerData HandlerCode Push Bits/Event/PEvent move.w IHS+ihs_Flags(PC),D0 btst #HDisabled,D0 ; Future feature bne.S NoMoreEvents lea Chain(PC),PEvent move.l A0,Next(PEvent) lea Status(PC),Bits ieLoop move.l Next(PEvent),Event move.l Event,D0 beq.S NoMoreEvents cmpi.b #IECLASS_RAWMOUSE,Class(Event) beq.S CheckLock cmpi.b #IECLASS_RAWKEY,Class(Event) bne.S DontRemove move.w Qual(Event),D0 andi.w #QUALIFIERS|IEQUALIFIER_REPEAT,D0 cmpi.w #QUALIFIERS,D0 bne.S CheckLock cmp.w #Toggle_Key,Code(Event) bne.S CheckLock bchg #INPUTDISABLED,(Bits) bra.S Remove CheckLock btst #INPUTDISABLED,(Bits) bne.S Remove * Just move on to next Event DontRemove move.l Event,PEvent bra.S ieLoop * Remove event from chain and move on to next Event Remove move.l Next(Event),Next(PEvent) bra.S ieLoop * Lets return NoMoreEvents Pop Bits/Event/PEvent move.l Chain(PC),D0 ; Return (shortened ?) chain rts HandlerSize = *-IHS *====================== Input-handler end =========================== *====================== Data-definition start ======================= rStart rAPtr PProcess rAPtr WBMsg rAPtr DosBase rStorage IReq,IOSTD_SIZE rStorage IPort,MP_SIZE rAPtr HMem rEnd DosName dc.b 'dos.library',0 InputName dc.b 'input.device',0 END * These defines are here for quick reference only --- InputEvent.ie_Class --- IECLASS_NULL 0x00 A NOP input event IECLASS_RAWKEY 0x01 A raw keycode from the keyboard device IECLASS_RAWMOUSE 0x02 The raw mouse report from the game port device IECLASS_EVENT 0x03 A private console event IECLASS_POINTERPOS 0x04 A Pointer Position report IECLASS_TIMER 0x06 A timer event IECLASS_GADGETDOWN 0x07 select button pressed down over a Gadget (address in ie_EventAddress) IECLASS_GADGETUP 0x08 select button released over the same Gadget (address in ie_EventAddress) IECLASS_REQUESTER 0x09 some Requester activity has taken place. See Codes REQCLEAR and REQSET IECLASS_MENULIST 0x0A this is a Menu Number transmission (Menu number is in ie_Code) IECLASS_CLOSEWINDOW 0x0B User has selected the active Window's Close Gadget IECLASS_SIZEWINDOW 0x0C this Window has a new size IECLASS_REFRESHWINDOW 0x0D the Window pointed to by ie_EventAddress needs to be refreshed IECLASS_NEWPREFS 0x0E new preferences are available IECLASS_DISKREMOVED 0x0F the disk has been removed IECLASS_DISKINSERTED 0x10 the disk has been inserted IECLASS_ACTIVEWINDOW 0x11 the window is about to be made active IECLASS_INACTIVEWINDOW 0x12 the window is about to be made inactive IECLASS_MAX 0x12 the last class --- InputEvent.ie_Code --- IECLASS_RAWKEY IECODE_UP_PREFIX 0x80 IECODE_KEY_CODE_FIRST 0x00 IECODE_KEY_CODE_LAST 0x77 IECODE_COMM_CODE_FIRST 0x78 IECODE_COMM_CODE_LAST 0x7F IECLASS_ANSI IECODE_C0_FIRST 0x00 IECODE_C0_LAST 0x1F IECODE_ASCII_FIRST 0x20 IECODE_ASCII_LAST 0x7E IECODE_ASCII_DEL 0x7F IECODE_C1_FIRST 0x80 IECODE_C1_LAST 0x9F IECODE_LATIN1_FIRST 0xA0 IECODE_LATIN1_LAST 0xFF IECLASS_RAWMOUSE IECODE_LBUTTON 0x68 also uses IECODE_UP_PREFIX IECODE_RBUTTON 0x69 IECODE_MBUTTON 0x6A IECODE_NOBUTTON 0xFF IECLASS_EVENT IECODE_NEWACTIVE 0x01 active input window changed IECLASS_REQUESTER Codes REQSET is broadcast when the first Requester (not subsequent ones) opens in the Window IECODE_REQSET 0x01 REQCLEAR is broadcast when the last Requester clears out of the Window IECODE_REQCLEAR 0x00 --- InputEvent.ie_Qualifier --- IEQUALIFIER_LSHIFT 0x0001 IEQUALIFIER_RSHIFT 0x0002 IEQUALIFIER_CAPSLOCK 0x0004 IEQUALIFIER_CONTROL 0x0008 IEQUALIFIER_LALT 0x0010 IEQUALIFIER_RALT 0x0020 IEQUALIFIER_LCOMMAND 0x0040 IEQUALIFIER_RCOMMAND 0x0080 IEQUALIFIER_NUMERICPAD 0x0100 IEQUALIFIER_REPEAT 0x0200 IEQUALIFIER_INTERRUPT 0x0400 IEQUALIFIER_MULTIBROADCAST 0x0800 IEQUALIFIER_MIDBUTTON 0x1000 IEQUALIFIER_RBUTTON 0x2000 IEQUALIFIER_LEFTBUTTON 0x4000 IEQUALIFIER_RELATIVEMOUSE 0x8000 IEQUALIFIERB_LSHIFT 0 IEQUALIFIERB_RSHIFT 1 IEQUALIFIERB_CAPSLOCK 2 IEQUALIFIERB_CONTROL 3 IEQUALIFIERB_LALT 4 IEQUALIFIERB_RALT 5 IEQUALIFIERB_LCOMMAND 6 IEQUALIFIERB_RCOMMAND 7 IEQUALIFIERB_NUMERICPAD 8 IEQUALIFIERB_REPEAT 9 IEQUALIFIERB_INTERRUPT 10 IEQUALIFIERB_MULTIBROADCAST 11 IEQUALIFIERB_MIDBUTTON 12 IEQUALIFIERB_RBUTTON 13 IEQUALIFIERB_LEFTBUTTON 14 IEQUALIFIERB_RELATIVEMOUSE 15 struct InputEvent { struct InputEvent *ie_NextEvent ;the chronologically next event UBYTE ie_Class ;the input event class UBYTE ie_SubClass ;optional subclass of the class UWORD ie_Code ;the input event code UWORD ie_Qualifier ;qualifiers in effect for the event union { struct { WORD ie_x ;the pointer position for the event WORD ie_y } ie_xy APTR ie_addr } ie_position struct timeval ie_TimeStamp ;the system tick at the event }; ie_X =ie_position.ie_xy.ie_x ie_Y =ie_position.ie_xy.ie_y ie_EventAddress =ie_position.ie_addr