e-mail    Debatní kniha    Mapa stránek    Hlavní  
 perličky 
 

Grafická myš v textovém režimu

Michal Rössler

Jestliže jste někdy viděli Norton Utilities (5.0 a vyšší), PC Tools (7.0 a vyšší) nebo novější verzi {TříPsů} na EGA/VGA, možná vás v nich zaujal kurzor myši. Má tvar šipky a pohybuje se plynule po jednom bodu (v textovém režimu!). Po dlouhodobém, úporném ladění se mi nakonec podařilo dosáhnout stejného výsledku.

Základem je procedura DrawMouse, která na obrazovku vykresluje šipku myši. Jako parametr má pozici kurzoru v bodech. Z této pozice musíme vypočítat souřadnice (max.) čtyř znaků, na kterých se kurzor zobrazí. Potom si tyto znaky přečteme z videopaměti; předtím samozřejmě musíme ještě obnovit původní místo, kde byl kurzor naposledy zobrazen. Souřadnice tohoto místa se uchovávají v proměnné LastPos. Když máme znaky z videopaměti přečtené, můžeme zjistit jejich definice (tj. definice jednotlivých znaku ve fontu, jak je používa znakový generátor pro zobrazování), které přepočítáme s definicí kurzoru myši. Vysledné definice bychom však měli přiřadit jiným, pro to rezervovaným znakům, aby se části kurzoru nakonec neobjevily na více místech. Když máme přepočítané definice, zapíšeme do videopaměti tyto čtyři rezervované znaky.

Program je připraven pro knihovnu Turbo Vision, kde ovladač myši vrací pozici ve znacích. My však potřebujeme pozici v bodech, proto program mění rozsah myši procedurou RangeMouse. Dále potřebujeme získat původní souřadnice, proto jsou upraveny procedury z modulu Drivers, aby vyhovovaly novým podmínkám.

Největším problémem je nyní ukrývání a opětovné zobrazování kurzoru myši. Mé řešení není přiliš ideální -- pouze nastavuji příznak, zda kurzor má být zobrazován, proto se mohou někdy objevit problémy s obnovováním původního místa kurzoru.

Nakonec je nutno na začátku programu předefinovat softwarový kurzor tak, aby se nezobrazoval, a zvýšit citlivost myši, aby se pohybovala po obrazovce rychleji.

Program byl odladěn v Turbo Pascalu 6.0 na monitoru EGA. Pro VGA je nutno poněkud upravit definice kurzoru.

{*****************************************************}
{                                                     }
{       Turbo Pascal Version 6.0                      }
{       Turbo Vision Unit                             }
{                                                     }
{       Copyright (c) 1993 MultiQuality software      }
{       Writen by Michal Rössler                      }
{                                                     }
{*****************************************************}

unit Mouse;

{$F+,O+,S-,X+,D+,L+}

interface

uses
  Objects, Drivers;
type
  { Ukazatel na definici znaku }
  PCharDef = ^TCharDef;
  { Typ definice znaku }
  TCharDef = array[1..32] of Byte;
  { Typ definice kurzoru myši }
  TCursDef = array[-15..32] of Byte;
  { Typ dvouznakové části řádku ve videopaměti }
  TDrawLine = array[1..2] of Word;
const
  { Definice šipky kurzoru pro EGA }
      CurArrow: TCursDef = (
    000,000,000,000,000,000,000,000,
    000,000,000,000,000,000,000,000,
    000,000,064,096,112,120,124,126,
    120,076,012,006,006,000,000,000,
    000,000,000,000,000,000,000,000,
    000,000,000,000,000,000,000,000
                        );
  { Definice obrysu kurzoru pro EGA }
  CurFrame: TCursDef = (
    000,000,000,000,000,000,000,000,
    000,000,000,000,000,000,000,000,
    128,192,160,144,136,132,130,129,
    134,178,082,009,009,007,000,000,
    000,000,000,000,000,000,000,000,
    000,000,000,000,000,000,000,000
                        );
  { Šířka znaku v bodech }
  CharWidth : Word = 8;
var
  { Aktuální režim obrazovky }
  ActMode   : Byte absolute $0000:$0449;
  { Počet sloupců na obrazovce }
  NumCols   : Word absolute $0000:$044A;
  { Velikost videostránky v bajtech }
  PageSize  : Word absolute $0000:$044C;
  { Začátek videostránky }
  VidStart  : Word absolute $0000:$044E;
  { Aktuální videostránka }
  ActPage   : Byte absolute $0000:$0462;
  { Počet řádků na obrazovce }
  NumRows   : Byte absolute $0000:$0484;
  { Výška jednoho znaku }
  CharHeight: Word absolute $0000:$0485;
  { Zobrazované znaky }
  Cr1, Cr2,
  { Čtené a obnovované znaky }
  Ch1, Ch2: TDrawLine;
  { Poslední pozice kurzoru myši }
  LastPos: TRect;

{ Vykreslení kurzoru myši }
procedure DrawMouse(R: TRect);
{ Nastavení rozsahu myši }
procedure RangeMouse(R: TRect);
{ Definice textového kurzoru }
procedure CursorMouse(Opt, Bgr, Cur: Word);
{ Nastavení citlivosti myši }
procedure SpeedMouse(X, Y: Word);
{ Nastavení pozice myši }
procedure SetMousePos(Col, Row: Word);


implementation

procedure SpeedMouse(X, Y: Word);
begin
   asm
     MOV AX, 15;
     MOV CX, X;
     MOV DX, Y;
     INT $33;
   end;
end;

procedure RangeMouse(R: TRect);
begin
  asm
    MOV AX, 7;
    MOV CX, R.A.X;
    MOV DX, R.B.X;
    INT $33;
    MOV AX, 8;
    MOV CX, R.A.Y;
    MOV DX, R.B.Y;
    INT $33;
  end;
end;

procedure SetMousePos(Col, Row: Word); assembler;
asm
  MOV AX, $04;
  MOV CX, Col;
  MOV DX, Row;
  INT $33;
end;

procedure CursorMouse(Opt, Bgr, Cur: Word); assembler;
asm
  MOV AX, $0A;
  MOV BX, Opt;
  MOV CX, Bgr;
  MOV DX, Cur;
  INT 33h;
end;

procedure SwapMouse(R: TRect);
var
  { Adresa videopaměti }
  VidOfs, VidSeg: Word;
  { Definice čtyř zapisovaných znaků }
  DefPtr1, DefPtr2, DefPtr3, DefPtr4: PCharDef;
  { Definice čtyř čtených znaků }
  ChrPtr1, ChrPtr2, ChrPtr3, ChrPtr4: PCharDef;
  { Proměnné pro přepočet definic znaku }
  W, X, Y, Z, Index: Integer;
begin
{ * * * * * * * * * Výpočet souřadnic * * * * * * * * }
  X := R.A.X;
  Y := R.A.Y;
  R.A.X := X div CharWidth;
  R.B.X := R.A.X + 1;
  R.A.Y := Y div CharHeight;
  R.B.Y := R.A.Y + 1;
  X := X mod CharWidth;
  Y := Y mod CharHeight;
{ * * * * * *  Obnovení poslední pozice * * * * * * * }
  { Jestliže nebyla myš ukryta }
  if not MouseHidden
  then begin
    { Vypočítej Offset videopaměti }
    VidOfs := (PageSize * ActPage
            + ((NumCols * LastPos.A.Y)
            + LastPos.A.X) * 2);
    { Zjisti Segment videopaměti }
    if ActMode = 7
    then
      { Monochromatický režim }
      VidSeg := $B000
    else
      { Ostatní režimy }
      VidSeg := $B800;
    { Přečti z videopaměti horní dva znaky s atributem }
    Move(Ch1, Ptr(VidSeg, VidOfs)^, 4);
    { Vypočítej nový Offset videopaměti }
    VidOfs := (PageSize * ActPage
               + ((NumCols * LastPos.B.Y)
               + LastPos.A.X) * 2);
    { Přečti z videopaměti dolní dva znaky s atributem }
    Move(Ch2, Ptr(VidSeg, VidOfs)^, 4);
  end else
    { Jinak povol obnovování staré pozice }
    MouseHidden := False;
{ * * * * * * * * Přečtení videopaměti * * * * * * * * }
  { Jestliže není myš viditelná, tak odejdi }
  if not MouseVisible then Exit;
  { Vypočítej Offset videopaměti }
  VidOfs := (PageSize * ActPage + ((NumCols * R.A.Y)
             + R.A.X) * 2);
  if ActMode = 7
  then
    { Monochromatický režim }
    VidSeg := $B000
  else
    { Ostatní režimy }
    VidSeg := $B800;
 { Přečti z videopaměti dolní dva znaky s atributem }
  Move(Ptr(VidSeg, VidOfs)^, Ch1, 4);
  { Vypočítej nový Offset videopaměti }
  VidOfs := (PageSize * ActPage
          + ((NumCols * R.B.Y) + R.A.X) * 2);
 { Přečti z videopaměti dolní dva znaky s atributem }
 Move(Ptr(VidSeg, VidOfs)^, Ch2, 4);
{ * * * * * * * * Inicializace fontu  * * * * * * * }
  inline($FA);
  PortW[$3C4] := $0402;
  PortW[$3C4] := $0704;
  PortW[$3CE] := $0204;
  PortW[$3CE] := $0005;
  PortW[$3CE] := $0406;
{ * * * * * * * * Přepočet definic znaků * * * * * }
  { Zjisti ukazatel na definici znaku #241 }
  DefPtr1 := Ptr($A000, 241*32);
  { Zjisti ukazatel na definici znaku #242 }
  DefPtr2 := Ptr($A000, 242*32);
  { Zjisti ukazatel na definici znaku #243 }
  DefPtr3 := Ptr($A000, 243*32);
  { Zjisti ukazatek na definici znaku #244 }
  DefPtr4 := Ptr($A000, 244*32);
  { Zjisti ukazatel na definici levého horního znaku }
  ChrPtr1 := Ptr($A000, Lo(Ch1[1])*32);
  { Zjisti ukazatel na definici pravého horního znaku }
  ChrPtr2 := Ptr($A000, Lo(Ch1[2])*32);
  { Zjisti ukazatel na definici levého dolního znaku }
  ChrPtr3 := Ptr($A000, Lo(Ch2[1])*32);
  { Zjisti ukazatel na definici pravého dolního znaku }
  ChrPtr4 := Ptr($A000, Lo(Ch2[2])*32);
  { Velikost posunu definice kurzoru směrem dolů }
  Z := CharHeight - Y;
  { smerem doleva }
  W := CharWidth - X;
  { Od definice 1. řádku }
  { až po definici posledního prováděj: }
  for Index := 1 to CharHeight do
  begin
    { Přepočítej levý horní znak }
    DefPtr1^[Index] := (ChrPtr1^[Index]
        or (CurArrow[Index - Y] shr X)) and
        not (CurFrame[Index - Y] shr X);
    { Přepočítej pravý horní znak }
    DefPtr2^[Index] := (ChrPtr2^[Index]
        or (CurArrow[Index - Y] shl W)) and
        not (CurFrame[Index - Y] shl W);
    { Přepočítej levý dolní znak }
    DefPtr3^[Index] := (ChrPtr3^[Index]
        or (CurArrow[Index + Z] shr X)) and
        not (CurFrame[Index + Z] shr X);
    { Přepočítej pravý dolní znak }
    DefPtr4^[Index] := (ChrPtr4^[Index]
        or (CurArrow[Index + Z] shl W)) and
        not (CurFrame[Index + Z] shl W);
  end;
{ * * * * * * * Deinicializace fontu  * * * * * * }
  PortW[$3C4] := $0302;
  PortW[$3C4] := $0304;
  PortW[$3CE] := $0004;
  PortW[$3CE] := $1005;
  if ActMode = 7
  then
    PortW[$3CE] := $0A06
  else
    PortW[$3CE] := $0E06;
  inline($FB);
{ * * * * * * * Zápis do videopaměti  * * * * * * }
  { Vypočítej Offset videopaměti }
  VidOfs := (PageSize * ActPage
          + ((NumCols * R.A.Y) + R.A.X) * 2);
  if ActMode = 7
  then
    { Monochromatický režim }
    VidSeg := $B000
  else
    { Ostatni rezimy }
    VidSeg := $B800;
  { Dosazení atributu kurzoru stejného, }
  { jako mají přečtené znaky }
  Cr1[1] := Ch1[1];
  Cr1[2] := Ch1[2];
  Cr2[1] := Ch2[1];
  Cr2[2] := Ch2[2];
  { Jako kurzor budou zobrazeny znaky #241..#244 }
  WordRec(Cr1[1]).Lo := 241;
  WordRec(Cr1[2]).Lo := 242;
  WordRec(Cr2[1]).Lo := 243;
  WordRec(Cr2[2]).Lo := 244;
  { Zápis horních znaků }
  Move(Cr1, Ptr(VidSeg, VidOfs)^, 4);
  { Výpočet nového Offsetu videopaměti }
  VidOfs := (PageSize * ActPage
          + ((NumCols * R.B.Y) + R.A.X) * 2);
  { Zápis dolních znaků }
  Move(Cr2, Ptr(VidSeg, VidOfs)^, 4);
  { Překopírování současné pozice do proměnné LastPos }
  LastPos.Copy(R)
end;
end.




unit Drivers;

interface

const
  { byla myš dosud skryta }
  MouseHidden: Boolean = True;
  { je nastavena grafická myš }
  MouseGraphic: Boolean = False;
var
  { pozice kurzoru v bodech }
  MousePos: TPoint;
  { má být kurzor zobrazován }
  MouseVisible: Boolean;
  { současná pozice kurzoru ve znacích }
  CurrentPos: TRect;

implementation

{ V těchto procedurách MouseWhere nahrazeno MousePos }
procedure GetMouseState; near; assembler;
procedure MouseInt; far; assembler;
procedure InitEvents; assembler;
procedure ShowMouse;
begin
  { Příznak "myš je viditelná" - má se zobrazovat }
  MouseVisible := True;
  { Příznak "myš byla až dosud schována }
  { - neobnovovat původní pozice }
  MouseHidden := True;
  asm
    CMP      ButtonCount,0
    .
    .     { Následuje původni text }
    .
  end;
end;

procedure HideMouse;
begin
  { Příznak "myš se nemá zobrazovat }
  MouseVisible := False;
  asm
    CMP      ButtonCount,0
    .
    .     { Následuje původní text }
    .
  end;
end;

procedure GetMouseEvent(var Event: TEvent);{assembler;}
begin
  asm
    CMP      MouseEvents,0
    .
    .     { Následuje původní text }
    .
    CALL      StoreEvent
  end;
  { Jestliže je grafická myš }
  if MouseGraphic
  then begin
     { Prepocitej MouseWhere opet na znaky }
     MouseWhere.X := MousePos.X div CharWidth;
     MouseWhere.Y := MousePos.Y div CharHeight;
     { Prepocitej Event.Where opet na znaky }
     Event.Where.X := Event.Where.X div CharWidth;
     Event.Where.Y := Event.Where.Y div CahrHeight;
   end
   else begin
     MouseWhere.X := MousePos.X;
     MouseWhere.Y := MousePos.Y;
   end
end;
end.



Pascal - hlavní
Překladače
Vlastní články
Převzaté články
Věci na stáhnutí
Odkazy k tématu
BP7 buglist
Chyba Run-time 200

BASIC