Source file: /~heha/hsn/t_und_m.zip/SRC/TYPHOON.PAS

{$A+,B-,D+,F-,G+,I-,L+,N-,P+,Q-,R-,S-,T+,V+,X+,Y+}
{$M 8192,0}	{kein Heap}
{$DEFINE IE_HANDLER}	{allerdings ist's nicht für RTL-Darstellung!}
{Wird eigentlich nur unter Windows95, nicht 98, gebraucht.}
library typhoon;
{Im Gegensatz zu dem, was der Name impliziert, läuft dieses nützliche
 Programm anscheinend mit JEDER Rollmaus - und verhilft jedem noch
 so alten Windows-Programm zur gewünschen Funktion.
 Version 1.1 (06/02): "Schlauere", jedoch noch nicht perfekte Erkennung
  rollfähiger Fenster, verhindert Disaster mit Win95-Desktop
 Version 1.2 (08/02): Sonderfall: IE_Handler, getestet mit MSIE 5.0}

{$W-,K+}
{$C MOVEABLE PRELOAD PERMANENT}	{gleiche Attribute wie Unit SYSTEM}
{$D 5-Tasten-Rollrad-Maus-Treiber}
{$R Typhoon}	{Versionsinformation}

{Eine (normale) Drei-Tasten-Maus liefert folgende Codes an Mouse_Event:
 Aktion			AX	BX	CX	(alles hex.)
 Bewegung		0001	DeltaX	DeltaY	*
 Linke Taste drücken	0002	0000	0000	*
 Linke Taste loslassen	0004	0000	0000	*
 Rechte Taste drücken	0008	0000	0000	*
 Rechte Taste loslassen	0010	0000	0000	*
 Mittlere Taste drücken	0020	0000	0000
 Mittlere Taste losl.	0040	0000	0000

* auf MSDN-CD so dokumentiert
Diese Maus liefert unter Windows95A folgende Extra-Codes an Mouse_Event:

 Aktion			AX	SI	DI	(alles hex.)
 Rollrad weg (hoch)	0040	0078	4001	0078h=+120
 Rollrad hin (runter)	0040	FF88	4001	FF88h=-120
 Linke X-Taste drücken	0040	0100	4002
 Linke X-Taste losl.	0040	0200	4002
 Rechte X-Taste drücken	0040	0400	4002
 Rechte X-Taste losl.	0040	0800	4002

Als DX wird dennoch stets 0003 (=3 Tasten) übergeben.

Aufgrund der speziellen Prozedur-Anzapfung ist der Treiber-Einsprung
nicht vernünftig zu debuggen, selbst SoftICE macht Probleme.

Womit belegt man eigentlich die X-Buttons?
Da sie bei dieser Maus relativ leicht ungewollt betätigt werden können,
liegt die Belegung mit SHIFT und CONTROL nahe.
Ansonsten ist der rechte X-Button auch ein Kandidat für SHIFT+RButton
für den Aufruf von Babylon.
Purer Luxus wäre die Zuordnung von WM_Command-IDs je nach Programm,
z.B. mit Anlernen durch langes Festhalten auf einem Menüpunkt oder
Toolbar-Knopf.
Ab Win98 werden auch WM_XButtonDown usw. unterstützt, aber von welchem
Programm? Explorer?
}

uses WinProcs,WinTypes,Win31;

var
 ScrollWidth: Integer;
 Hook: HHook;

type
 PtrRec=record
  Ofs,Sel:Word;
 end;
 LongRec=record
  lo,hi: Word;
 end;
 WordRec=record
  lo,hi: Byte;
 end;

var
 lasthitcode:Word;	{Merker, weil Win98-Maustreiber zu doof}
 lastwnd: HWnd;		{bei HTNowhere ist das Fenster =GetActiveWindow}

function CheckScrollBar(Wnd:HWnd):Byte;
 var
  classname: array[0..11] of Char;
 begin
  CheckScrollBar:=0;
  GetClassName(Wnd,classname,sizeof(classname));
  if lstrcmpi('ScrollBar',classname)=0 then begin
   if GetWindowLong(Wnd,GWL_Style) and SBS_Vert <>0
   then CheckScrollBar:=2
   else CheckScrollBar:=1;
  end;
 end;

function HasSiblingScrollBar(Wnd:HWnd; var WScroll:HWnd):Byte;
{Stellt fest, ob das Fenster <Wnd> Rollbalken als Nachbarn hat,
 welche an das übergeordnete Fenster WM_HScroll oder WM_VScroll
 senden könnten. Wenn ja, wird das Handle in <WScroll> geschrieben.
 Findet auch die links befindlichen Rollbalken in RTL-Fenstern.}
 var
  hsw,vsw:HWnd;
 begin
  HasSiblingScrollBar:=0;
  Wnd:=GetWindow(Wnd,GW_HWndFirst);
  while Wnd<>0 do begin
   asm	push	[Wnd]
	call	CheckScrollBar
	mov	dx,[Wnd]
	test	al,1
	jz	@@n1
	mov	[hsw],dx
@@n1:	test	al,2
	jz	@@n2
	mov	[vsw],dx
@@n2:	or	[@Result],al
   end;
   Wnd:=GetWindow(Wnd,GW_HWndNext);
  end;
  asm	test	[@Result],2	{Ein vertikaler?}
	mov	ax,[vsw]
	jnz	@@1		{ja, hat Vorzug}
	mov	ax,[hsw]
@@1:	les	di,[WScroll]
	stosw			{zurückgeben!}
  end;
 end;

function idiv2(x: Integer):Integer;
{weil Turbo's shr nicht arithmetisch schiebt!}
 inline($58/$D1/$F8);           {pop ax; sar ax,1}

function Point_Finder(Wnd:HWnd; var WScroll:HWnd):Byte;
{Ermittelt das Fenster, das sich RECHTS oder UNTEN befindet}
 var
  tmp: HWnd;
  P: TPoint;
  R: TRect;
 begin
  Point_Finder:=0;
{  Top:=Wnd;
  repeat
   tmp:=GetParent(Top);
   if (tmp<>0) and (tmp<>GetDesktopWindow)
   then Top:=tmp else break;
  until false;
  if Top=Wnd then exit;		{kein Elternfenster}
  tmp:=GetParent(Wnd);
  if tmp=0 then exit;
  if tmp=GetDesktopWindow then exit;
  GetWindowRect(Wnd,R);
  {nach rechts gucken}
  P.x:=R.right+idiv2(GetSystemMetrics(SM_cxVScroll));
  P.y:=idiv2(R.top+R.bottom);
  tmp:=WindowFromPoint(P);
  if (tmp<>0) and (CheckScrollBar(tmp)=2) then begin
   Point_Finder:=2;
   WScroll:=tmp;
   exit;
  end;
  {nach unten gucken}
  P.x:=idiv2(R.left+R.right);
  P.y:=R.bottom+idiv2(GetSystemMetrics(SM_cyHScroll));
  tmp:=WindowFromPoint(P);
  if (tmp<>0) and (CheckScrollBar(tmp)=1) then begin
   Point_Finder:=1;
   WScroll:=tmp;
  end;
 end;

procedure HandleKeyState(var b:Char; press:Boolean); assembler;
 asm	les	di,[b]
	cmp	[press],0
	jz	@@release
	or	es:byte ptr[di],0FEh	{Gedrückt-Bits}
	xor	es:byte ptr[di],1	{Toggle-Bit}
	jmp	@@e
@@release:
	and	es:byte ptr[di],1	{Nur Toggle-Bit stehenlassen}
@@e:
 end;

procedure HandleExtraButtons(b:Byte);
 var
  KS: TKeyboardState;
 begin
  GetKeyboardState(KS);
  if b and $01 <>0 then HandleKeyState(KS[VK_Shift],true);
  if b and $02 <>0 then HandleKeyState(KS[VK_Shift],false);
  if b and $04 <>0 then HandleKeyState(KS[VK_Control],true);
  if b and $08 <>0 then HandleKeyState(KS[VK_Control],false);
  SetKeyboardState(KS);
 end;

function MouseProc(code: Integer; wParam:Word; lParam:LongInt):
  LongInt; export;
 var
  mhs: PMouseHookStruct absolute lParam;
  wnd: HWnd;
  hitcode: Word;
  msg,wpar: Word;
  lPar: LongInt;
  rol: Integer;
  style: LongInt;
  pa: Boolean;		{Schalter Zeilen(=false), Seiten(=true)}
{$IFDEF IE_HANDLER}
  R: TRect;
  s: array[0..25] of Char;
{$ENDIF}
 begin
  MouseProc:=CallNextHookEx(Hook,code,wParam,lParam);
  wnd:=mhs^.hwnd;
  hitcode:=mhs^.wHitTestCode;
  if hitcode<>HTNowhere
  then begin
   lasthitcode:=hitcode;
   lastwnd:=wnd;
  end else begin
   hitcode:=lasthitcode;
   wnd:=lastwnd;
  end;
  case LongRec(mhs^.dwExtraInfo).hi of
{das war das Register DI bei Mouse_Event}
   $4001: begin		{Was das Rollrad betrifft...}
    if wnd=0 then exit;
    style:=GetWindowLong(wnd,GWL_Style);
{$IFDEF IE_HANDLER}
    GetClassName(wnd,s,sizeof(s));
    if lstrcmp(s,'Internet Explorer_Server')=0 then begin
     style:=WS_VScroll;
     if hitcode=HTClient then begin
      GetClientRect(wnd,R);
      ClientToScreen(wnd,PPoint(@R.right)^);
      if mhs^.pt.x>R.right-GetSystemMetrics(SM_cxVScroll)
      then hitcode:=HTVScroll;
     end;
    end;
{$ENDIF}
    msg:=WM_VScroll;
    pa:=false;
    case hitcode of
     HTClient: begin
      if style and (WS_VScroll or WS_HScroll)=WS_HScroll
      then msg:=WM_HScroll;
     end;
     HTHScroll: begin
      msg:=WM_HScroll;
{     pa:=true;}	{seitenweises Horizontal-Rollen ist oft zuviel}
     end;
     HTVScroll: begin
      pa:=true;
     end;
     else exit;
    end;
    rol:=Integer(LongRec(mhs^.dwExtraInfo).lo);
    lPar:=0;		{bei Fenster mit Scroll-Stil Null lassen}
    if style and (WS_VScroll or WS_HScroll) =0 then begin
{Viele Editoren, insbesondere, wenn sie auf RichEdit basieren,
 verwenden eigene Rollbalken; diese werden hier als "Nachbarn"
 (Siblings=Geschwister) erwartet und gesucht.
 Ansonsten ist die VSCROLL-Nachricht für rollbalkenlose Fenster
 problematisch bis verheerend, z.B. verschieben sich Desktop-Symbole,
 oder MDI-Fenster verschwinden aus ihrem Rahmen.}
     LongRec(lPar).hi:=Wnd;
     case CheckScrollBar(Wnd) of
      1: msg:=WM_HScroll;	{HScroll: kleine Schritte}
      2: pa:=true;		{VScroll: seitenweise}
      else case Point_Finder(Wnd,LongRec(lPar).hi) of
       1: msg:=WM_HScroll;	{nur horizontaler Rollbalken gefunden}
       2: ;
       else case HasSiblingScrollBar(Wnd,LongRec(lPar).hi) of
	0: exit;		{keine: nichts tun! Raus hier!}
	1: msg:=WM_HScroll;	{nur HScroll: horizontal rollen}
       end;			{VScroll oder beide: vertikal rollen}
      end;
     end;
     Wnd:=GetParent(LongRec(lPar).hi);	{Immer ans Elternfenster}
     if Wnd=0 then exit;
    end;

    if pa then begin
     if ScrollWidth<0 then rol:=-rol;		{nur Vorzeichen}
    end else rol:=rol*ScrollWidth;		{Wert mit Vorzeichen}

    wpar:=SB_LineUp; if pa then wpar:=SB_PageUp;
    if rol<0 then begin
     wpar:=SB_LineDown; if pa then wpar:=SB_PageDown;
     rol:=-rol;
    end;
    while rol>0 do begin
     PostMessage(wnd,msg,wpar,lPar);
     Dec(rol,120);
    end;
    MouseProc:=1;	{keine Maus-Nachricht ans Programm liefern!}
   end;

   $4002: begin		{Was die Extra-Tasten betrifft...}
    HandleExtraButtons(WordRec(LongRec(mhs^.dwExtraInfo).lo).hi);
		{obere Bits - das war das Register SI bei Mouse_Event}
		{Stack-Speicher für TKeyboardState sparen}
    MouseProc:=1;	{keine Maus-Nachricht ans Programm liefern!}
   end;
  end;
 end;

procedure WEP;
 begin
  UnhookWindowsHookEx(Hook);
 end;

var
 ec: Integer;
begin
{RollMsg:=RegisterWindowMessage('MSWHEEL_ROLLMSG');}
{Leider wird diese Art Message nur stümperhaft unterstützt, daher
 erfolgt ein Verzicht darauf; es wird nur noch gerollt.
 Die WM_MOUSEWHEEL-Nachricht kann von 16bit aus nicht abgesetzt werden;
 damit will wohl M$ diese geschützte Art ausrotten, aber nicht mit mir!}
 ScrollWidth:=Integer(GetProfileInt('Windows','MouseWheelScroll',1));
 if ScrollWidth=0 then ScrollWidth:=1;	{sonst klemmt das Programm!}
 Hook:=SetWindowsHookEx(WH_Mouse,MouseProc,HInstance,0);
end.
Detected encoding: OEM (CP437)1
Wrong umlauts? - Assume file is ANSI (CP1252) encoded