{ $Id$ Event handling for the Win32 version of the FPC API Copyright (c) 1999 by Florian Klaempfl This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. } unit Event; { We need this unit to implement keyboard and mouse, because win32 uses only one message queue for mouse and key events } interface type TEventProcedure = Procedure; { these procedures must be used to set the event handlers } { these doesn't do something, they signal only the } { the upper layer that an event occured, this event } { must be handled with Win32-API function by the upper } { layer } Procedure SetMouseEventHandler(p : TEventProcedure); Procedure SetKeyboardEventHandler(p : TEventProcedure); Procedure SetFocusEventHandler(p : TEventProcedure); Procedure SetMenuEventHandler(p : TEventProcedure); Procedure SetResizeEventHandler(p : TEventProcedure); Procedure SetUnknownEventHandler(p : TEventProcedure); { these procedures must be used to get the event handlers } Function GetMouseEventHandler : TEventProcedure; Function GetKeyboardEventHandler : TEventProcedure; Function GetFocusEventHandler : TEventProcedure; Function GetMenuEventHandler : TEventProcedure; Function GetResizeEventHandler : TEventProcedure; Function GetUnknownEventHandler : TEventProcedure; implementation uses windows, dos; const { these procedures are called if an event occurs } MouseEventHandler : procedure = nil; KeyboardEventHandler : procedure = nil; FocusEventHandler : procedure = nil; MenuEventHandler : procedure = nil; ResizeEventHandler : procedure = nil; UnknownEventHandler : procedure = nil; { if this counter is zero, the event handler thread is killed } InstalledHandlers : Byte = 0; var HandlerChanging : TCriticalSection; OldExitProc : Pointer; EventThreadHandle : Handle; EventThreadID : DWord; { true, if the event handler should be stoped } ExitEventHandleThread : boolean; Function GetMouseEventHandler : TEventProcedure; begin GetMouseEventHandler:=MouseEventHandler; end; Function GetKeyboardEventHandler : TEventProcedure; begin GetKeyboardEventHandler:=KeyboardEventHandler; end; Function GetFocusEventHandler : TEventProcedure; begin GetFocusEventHandler:=FocusEventHandler; end; Function GetMenuEventHandler : TEventProcedure; begin GetMenuEventHandler:=MenuEventHandler; end; Function GetResizeEventHandler : TEventProcedure; begin GetResizeEventHandler:=ResizeEventHandler; end; Function GetUnknownEventHandler : TEventProcedure; begin GetUnknownEventHandler:=UnknownEventHandler; end; { removes an event from the event queue } { necessary, if no handler is installed } Procedure DestroyOneEvent; var ir : TInputRecord; dwRead : DWord; begin ReadConsoleInput(TextRec(Input).Handle,ir,1,dwRead); end; Function EventHandleThread(p : pointer) : DWord;StdCall; var ir : TInputRecord; dwRead : DWord; begin while not(ExitEventHandleThread) do begin { wait for an event } WaitForSingleObject(TextRec(Input).Handle,INFINITE); { guard this code, else it is doomed to crash, if the thread is switched between the assigned test and the call and the handler is removed } if not(ExitEventHandleThread) then begin EnterCriticalSection(HandlerChanging); { read, but don't remove the event } if (PeekConsoleInput(TextRec(Input).Handle,ir,1,dwRead)) and (dwRead>0) then { call the handler } case ir.EventType of KEY_EVENT: begin if assigned(KeyboardEventHandler) then KeyboardEventHandler else DestroyOneEvent; end; _MOUSE_EVENT: begin if assigned(MouseEventHandler) then MouseEventHandler else DestroyOneEvent; end; WINDOW_BUFFER_SIZE_EVENT: begin if assigned(ResizeEventHandler) then ResizeEventHandler else DestroyOneEvent; end; MENU_EVENT: begin if assigned(MenuEventHandler) then MenuEventHandler else DestroyOneEvent; end; FOCUS_EVENT: begin if assigned(FocusEventHandler) then FocusEventHandler else DestroyOneEvent; end; else begin if assigned(UnknownEventHandler) then UnknownEventHandler else DestroyOneEvent; end; end; LeaveCriticalSection(HandlerChanging); end; end; end; Procedure NewEventHandlerInstalled(p,oldp : TEventProcedure); var oldcount : Byte; ir : TInputRecord; written : DWord; begin oldcount:=InstalledHandlers; if Pointer(oldp)<>nil then dec(InstalledHandlers); if Pointer(p)<>nil then inc(InstalledHandlers); { start event handler thread } if (oldcount=0) and (InstalledHandlers=1) then begin ExitEventHandleThread:=false; EventThreadHandle:=CreateThread(nil,0,@EventHandleThread, nil,0,EventThreadID); end { stop and destroy event handler thread } else if (oldcount=1) and (InstalledHandlers=0) then begin ExitEventHandleThread:=true; { create a dummy event and sent it to the thread, so we can leave WatiForSingleObject } ir.EventType:=KEY_EVENT; { mouse event can be disabled by mouse.inc code in DoneMouse so use a key event instead PM } WriteConsoleInput(TextRec(Input).Handle,ir,1,written); { wait, til the thread is ready } WaitForSingleObject(EventThreadHandle,INFINITE); CloseHandle(EventThreadHandle); end; end; Procedure SetMouseEventHandler(p : TEventProcedure); var oldp : TEventProcedure; begin EnterCriticalSection(HandlerChanging); oldp:=MouseEventHandler; MouseEventHandler:=p; NewEventHandlerInstalled(MouseEventHandler,oldp); LeaveCriticalSection(HandlerChanging); end; Procedure SetKeyboardEventHandler(p : TEventProcedure); var oldp : TEventProcedure; begin EnterCriticalSection(HandlerChanging); oldp:=KeyboardEventHandler; KeyboardEventHandler:=p; NewEventHandlerInstalled(KeyboardEventHandler,oldp); LeaveCriticalSection(HandlerChanging); end; Procedure SetFocusEventHandler(p : TEventProcedure); var oldp : TEventProcedure; begin EnterCriticalSection(HandlerChanging); oldp:=FocusEventHandler; FocusEventHandler:=p; NewEventHandlerInstalled(FocusEventHandler,oldp); LeaveCriticalSection(HandlerChanging); end; Procedure SetMenuEventHandler(p : TEventProcedure); var oldp : TEventProcedure; begin EnterCriticalSection(HandlerChanging); oldp:=MenuEventHandler; MenuEventHandler:=p; NewEventHandlerInstalled(MenuEventHandler,oldp); LeaveCriticalSection(HandlerChanging); end; Procedure SetResizeEventHandler(p : TEventProcedure); var oldp : TEventProcedure; begin EnterCriticalSection(HandlerChanging); oldp:=ResizeEventHandler; ResizeEventHandler:=p; NewEventHandlerInstalled(ResizeEventHandler,oldp); LeaveCriticalSection(HandlerChanging); end; Procedure SetUnknownEventHandler(p : TEventProcedure); var oldp : TEventProcedure; begin EnterCriticalSection(HandlerChanging); oldp:=UnknownEventHandler; UnknownEventHandler:=p; NewEventHandlerInstalled(UnknownEventHandler,oldp); LeaveCriticalSection(HandlerChanging); end; Procedure DoExit; begin { Uninstall all handlers } { this stops also the event handler thread } SetMouseEventHandler(nil); SetKeyboardEventHandler(nil); SetFocusEventHandler(nil); SetMenuEventHandler(nil); SetResizeEventHandler(nil); SetUnknownEventHandler(nil); { delete the critical section object } DeleteCriticalSection(HandlerChanging); ExitProc:=OldExitProc; end; begin InitializeCriticalSection(HandlerChanging); OldExitProc:=ExitProc; ExitProc:=@DoExit; end. { $Log$ Revision 1.1 2000-07-13 06:29:41 michael + Initial import Revision 1.1 2000/01/06 01:20:31 peter * moved out of packages/ back to topdir Revision 1.1 1999/11/24 23:36:38 peter * moved to packages dir Revision 1.5 1999/09/22 12:55:18 pierre * use KEY_EVENT for Closing Handle Thread Revision 1.4 1999/07/17 17:21:35 florian * fixed the win32 keyboard event handling Revision 1.3 1999/07/14 08:45:15 florian * commited a new keyboard by Armin Diehl * fixed event handling, mainly the prototype of eventhandlethread was wrong Revision 1.2 1999/06/21 16:43:51 peter * win32 updates from Maarten Bekkers Revision 1.1 1999/01/08 14:37:03 florian + initial version, not working yet }