I have the following code and want draw the same hole in two Form's on remote screen (client side), based on area drawn on server side.
I have the same Form (Form3) in both sides (server and client) that is a "mirror" where i'm drawing a area that must stays inside this same form on client side.
The Form3 in server side have 50% of max
AlphaBlend
value, this is necessary to see remote screen behind Form3.
Before all, i want say that i'm receiving the remote screen on server side and and mouse click positions works like expected.
Then this is my trouble:
The following code produce the result showed on image above. I think that this code is right but is missing align this hole with the Form3.
Someone could help with this? sorry if this is a bad question, but this is all my actual trouble and i tried express all in this question of better way that i'm able.
This is all relevant code:
Server side:
Form2 (where i see the remote screen):
unit Unit2;
interface
uses
Unit1;
type
TForm2 = class(TForm)
Panel1: TPanel;
CheckBox1: TCheckBox;
ScrollBox1: TScrollBox;
Image1: TImage;
PaintBox1: TPaintBox;
procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure PaintBox1MouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure PaintBox1Paint(Sender: TObject);
private
{ Private declarations }
FSelecting: Boolean;
FSelection: TRect;
pos1, pos2, pos3, pos4: Integer;
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if CheckBox1.Checked then
begin
FSelection.Left := X;
FSelection.Top := Y;
FSelecting := true;
end;
end;
procedure TForm2.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
begin
if FSelecting then
begin
FSelection.Right := X;
FSelection.Bottom := Y;
pbRec.Invalidate;
end;
end;
procedure TForm2.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if CheckBox1.Checked then
begin
FSelecting := false;
FSelection.Right := X;
FSelection.Bottom := Y;
PaintBox1.Invalidate;
FSelection.NormalizeRect;
if FSelection.IsEmpty then
begin
// None selection was made on PaintBox
end
else
begin
pos1 := FSelection.Left;
pos2 := FSelection.Top;
pos3 := X;
pos4 := Y;
end;
end;
end;
procedure TForm2.PaintBox1Paint(Sender: TObject);
begin
if CheckBox1.Checked then
begin
PaintBox1.Canvas.Brush.Style := bsClear;
PaintBox1.Canvas.Pen.Style := psSolid;
PaintBox1.Canvas.Pen.Color := clRed;
PaintBox1.Canvas.Rectangle(FSelection);
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
Socket: TCustomWinSocket;
begin
Socket := TCustomWinSocket(Form1.LV1.Selected.SubItems.Objects[0]);
if CheckBox1.Checked then
begin
Socket.SendText(intToStr(pos1) + ';' + intToStr(pos2) + ';' +
intToStr(pos3) + ';' + intToStr(pos4));
end;
end;
procedure TForm2.Button2Click(Sender: TObject);
begin
Form3 := TForm3.Create(Self);
Form3.Show;
end;
Form2 .DFM:
object Panel1: TPanel
Left = -1
Top = 0
Width = 773
Height = 89
Anchors = [akTop]
BevelEdges = [beLeft, beRight]
ParentDoubleBuffered = False
TabOrder = 0
end
object ScrollBox1: TScrollBox
Left = 0
Top = 0
Width = 765
Height = 472
HorzScrollBar.Smooth = True
HorzScrollBar.Tracking = True
VertScrollBar.Smooth = True
VertScrollBar.Tracking = True
Align = alClient
TabOrder = 1
object Image1: TImage
Left = 0
Top = 0
Width = 1362
Height = 621
AutoSize = True
end
object PaintBox1: TPaintBox
Left = 0
Top = 0
Width = 1362
Height = 621
Align = alClient
OnMouseDown = PaintBox1MouseDown
OnMouseMove = PaintBox1MouseMove
OnMouseUp = PaintBox1MouseUp
OnPaint = PaintBox1Paint
ExplicitWidth = 1364
ExplicitHeight = 622
end
Form3 (the "mirror" Form that also is the same on client side), this Form is centralized according of remote screen resolution:
unit Unit3;
interface
uses
...
type
TForm3 = class(TForm)
Panel1: TPanel;
Image1: TImage;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure CreateParams(var pr: TCreateParams); override;
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
uses
Unit1;
{$R *.dfm}
procedure TForm3.FormCreate(Sender: TObject);
var
MyString: String;
Splitted: TArray<String>;
begin
MyString := Form1.LV1.Selected.SubItems[6]; // Resolution of remote screen
Splitted := MyString.Split(['x']);
Self.Left := (Integer(Splitted[0]) - Self.Width) div 2;
Self.Top := (Integer(Splitted[1]) - Self.Height) div 2;
end;
procedure TForm3.CreateParams(var pr: TCreateParams);
begin
inherited;
pr.WndParent := Form2.Handle;
pr.ExStyle := pr.ExStyle or WS_EX_TOPMOST or WS_EX_TRANSPARENT;
pr.ExStyle := WS_EX_TRANSPARENT or WS_EX_TOPMOST;
end;
Form3 .DFM:
object Form3: TForm3
Left = 328
Top = 143
BorderStyle = bsNone
ClientHeight = 567
ClientWidth = 526
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 0
Top = 0
Width = 801
Height = 569
TabOrder = 0
object Image1: TImage
Left = 1
Top = 1
Width = 799
Height = 567
Align = alClient
ExplicitLeft = 2
ExplicitTop = 0
ExplicitHeight = 447
end
object Label1: TLabel
Left = 92
Top = 69
Width = 28
Height = 13
Caption = 'Nome'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clBlack
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentColor = False
ParentFont = False
end
Client side:
Form2 ("locker" Form):
unit Unit2;
private
{ Private declarations }
procedure CreateParams(var Params: TCreateParams); override;
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.WndParent := Application.Handle;
Params.ExStyle := Params.ExStyle or WS_EX_TOPMOST or WS_EX_TRANSPARENT;
Params.ExStyle := WS_EX_TRANSPARENT or WS_EX_TOPMOST;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
windowstate := wsmaximized;
Top := 0;
Left := 0;
Height := Screen.Height;
Width := Screen.Width;
end;
{
Properties of Form2:
Align => alNone
AlphaBlend => True
BorderStyle => BsNone
}
end.
Form3 (the same of server side):
unit Unit3;
interface
uses
...
type
TForm3 = class(TForm)
Panel1: TPanel;
Label1: TLabel;
procedure FormShow(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure CreateParams(var pr: TCreateParams); override;
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
uses
Unit2;
{$R *.dfm}
procedure TForm3.FormCreate(Sender: TObject);
begin
Self.Left := (GetSystemMetrics(SM_CXSCREEN) - Self.Width) div 2;
Self.Top := (GetSystemMetrics(SM_CYSCREEN) - Self.Height) div 2;
end;
procedure TForm3.FormShow(Sender: TObject);
begin
ShowWindow(Application.Handle, SW_HIDE);
end;
procedure TForm3.CreateParams(var pr: TCreateParams);
begin
inherited;
pr.WndParent := Form2.Handle;
end;
{
Properties of Form3:
Align => alNone
BorderStyle => BsNone
}
end.
Receiving the area on client side:
procedure CS1_Read(Self: Pointer; Sender: TObject; Socket: TCustomWinSocket);
var
X1, X2, Y1, Y2: Integer;
List: TStrings;
FormRegion, HoleRegion: HRGN;
StrCommand: string;
begin
if Pos(';', StrCommand) > 0 then
begin
List := TStringList.Create;
try
ExtractStrings([';'], [], PChar(StrCommand), List);
Form3 := TForm3.Create(Form2); // The Form2 already was created and is showing
X1 := Round(StrToIntDef(List[0], 0) - Form2.Left);
Y1 := Round(StrToIntDef(List[1], 0) - Form2.Top);
X2 := Round(StrToIntDef(List[2], 0) - Form2.Left);
Y2 := Round(StrToIntDef(List[3], 0) - Form2.Top);
FormRegion := CreateRectRgn(0, 0, Form3.Width, Form3.Height);
HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
SetWindowRgn(Form3.handle, FormRegion, true);
FormRegion := CreateRectRgn(0, 0, Form2.Width, Form2.Height);
HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
SetWindowRgn(Form2.handle, FormRegion, true);
Form3.ShowModal;
Form3.Release;
finally
List.Free;
end;
end;
end;
On the client side you have a semitransparent gray form (Form2) that has the size of the screen. On top of that form you have a opaque white form (Form3) centered on the screen. In Form3
you have a rectangular hole at Top = Y
and Left = X
in coordinates of Form3
.
I understand that your problem is that you want to draw a hole in Form2 that is aligned with the hole in Form3.
You need to convert the coordinate system of Form3
to that of Form2
with a simple addition:
Form2.Hole.Left := Form3.Left + Form3.Hole.Left;
Form2.Hole.Top := Form3.Top + Form3.Hole.Top;
That would align the holes. You seem to attempt something like that in your calculation, but you refer to Form2.Left
and Form2.Top
which is useless as they are both 0.
If I misunderstood your question and you actually would like Form3
hole to be aligned with the Form2
hole, then you would need to move Form3
to top - left of the screen and not center it ...
... Or, considering your comment: if i'm drawing on server side in a area far of Form3
(client) for example more to left side of screen, draw only the hole of Form2
and if i draw more to middle of screen, draw the both hole aligned would be accomplshed by simply swapping the terms:
Form3.Hole.Left := Form2.Hole.Left - Form3.Left
Form3.Hole.Top := Form2.Hole.Top - Form3.Top
This converts the Form2
coordinates to Form3
coordintaes, which may become negative values (iow outside of the form) in situations like your example.
Adapting the above to your code, you need to first deal with Form2 region with the received Form2.Hole
coordinates, then subtract Form3
coordinates (Left
and Top
) from X1..Y2 and then deal with Form3 region*.
X1 := Round(StrToIntDef(List[0], 0) - Form2.Left); // Form2 props can be removed as hardcoded to 0
Y1 := Round(StrToIntDef(List[1], 0) - Form2.Top); // -"-
X2 := Round(StrToIntDef(List[2], 0) - Form2.Left); // -"-
Y2 := Round(StrToIntDef(List[3], 0) - Form2.Top); // -"-
FormRegion := CreateRectRgn(0, 0, Form2.Width, Form2.Height);
HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
SetWindowRgn(Form2.handle, FormRegion, true);
X1 := X1 - Form3.Left;
Y1 := Y1 - Form3.Top;
X2 := X2 - Form3.Left;
Y2 := Y2 - Form3.Top;
FormRegion := CreateRectRgn(0, 0, Form3.Width, Form3.Height);
HoleRegion := CreateRectRgn(X1, Y1, X2, Y2);
CombineRgn(FormRegion, FormRegion, HoleRegion, RGN_DIFF);
SetWindowRgn(Form3.handle, FormRegion, true);
Edit
It appears counterintuitive that your Server.Form2
isn't the same size as the screen (and therefore Client.Form2
). But maybe I never really understood for what actual purpose the setup is used.
Anyway, with equally sized, centered Form3
's, but with different screen sizes at Server and at Client, you need to adjust the Form3.Hole
coordinates at the client with half the difference of Server and Client screen extents, or, since the Form3
forms are centered, you can calculate the horizontal and vertical correction like
ResolutionCorrectionX := Server.Form3.Left - Client.Form3.Left;
ResolutionCorrectionY := Server.Form3.Top - Client.Form3.Top;
that you then add to the X- and Y-coordinates for HoleRegion
of Form3.
X1 := X1 - Form3.Left + ResolutionCorrectionX; // and similar for X2, Y1 and Y2
Btw, just of curiosity, why are you using Round()
for calculations based on integers?