SRL Archive - OSR Help

how to draw on something ELSE than smart?


eska

2012-07-07 11:51:00

Hello again!

I'm currently making a diablo 3 script using simba and so far everything is working very well.

Yesterday I wanted to add a bit of "eye candy" to my script: I wanted to add some box around my TPA, pretty much exacly what DebugATPABounds() does, but not on a debug window, I want it on diablo 3 itself. That way I don't get a second window and it makes debuging even easier.

Now, Diablo 3 is obviously not using SMART, so I can't use SMAT_DrawBox.

I've read tutorial about canvas and forms but most of what I was looking for was outdated (scar).

I've seen people say it can be done making a form (transparent or invisible?), placing it right over the client, and drawing on the form, but I haven't seen any code that suggest this could actualy be done.

Is it possible? If so, how so? Thanks for your time.

Brandon

2012-07-07 12:09:18

It isn't naturally possible without a native programming language that utilizes WinAPI if you're on windows.

By that I mean that a Canvas in Pascal is almost equivalent to an HDC in windows which is defined as a handle/pointer to a Device Context. A DC is something required to draw on the overlay of windows but when that window minimizes, it will redraw itself and your drawing will disappear unless you hook it. TCanvas is wrapped so that you can't get the DC yourself in Simba since it's pascal script.

You can draw on a form yes but your form will have to be non modal. The thing that confuses me is that the person says completely transparent. If it's transparent, how is the colours you draw going to show? I will test this since I play around with forms alot but in theory that shouldn't work and it should be impossible without external libraries/extensions.

eska

2012-07-07 12:15:16

Yes drawing on a form isn't hard, but making it.. transparent and only having the drawing visible? I have no clue how to do that.

I'll keep looking, but like I said it's only eye candy, I can work without it no problem.

But damn it would look cool.

Brandon

2012-07-07 12:45:02

From: eska
Yes drawing on a form isn't hard, but making it.. transparent and only having the drawing visible? I have no clue how to do that.

I'll keep looking, but like I said it's only eye candy, I can work without it no problem.

But damn it would look cool.


Basically you make a borderless form. You draw to it via DrawBitmap and Form.Canvas. You make it transparent using my extension. I've extended it to allow transparency. It can also be used to set the form to a specific position in a specific window. Explore it. For more on the FormGUI project see: http://villavu.com/forum/showthread.php?p=1059430

I got a bit lazy and didn't fully modify it or remove the multithreaded Timer but I trust you can do that. Just make it so that on menu click it will set your form transparent and set to a window.

Script:

{$I SRL/SRL.Simba}

type
Form = record
Drawing: TForm;
end;

var
InterfaceForm: Form;

procedure Form_Init;
begin
with InterfaceForm do
begin
Drawing := CreateForm;
with Drawing do
begin
Left := 376;
Top := 175;
BorderStyle := bsNone;
Caption := 'Drawing';
ClientHeight := 201;
ClientWidth := 304;
Color := 0;
Font.Color := clWindowText;
Font.Height := - 11;
Font.Name := 'Tahoma';
Font.Style := [];
PixelsPerInch := 96;
end;
end;
end;

procedure Form_SafeInit;
var
v: TVariantArray;
begin
SetLength(v, 0);
ThreadSafeCall('Form_Init', v);
end;

function Form_ShowModal: Boolean;
begin
InterfaceForm.Drawing.SHOW;
Result := InterfaceForm.Drawing.Showing;
end;

function Form_SafeShowModal: Boolean;
var
v: TVariantArray;
begin
SetLength(v, 0);
Result := ThreadSafeCall('Form_ShowModal', v);
end;

begin
Form_SafeInit;
if Form_SafeShowModal then
WriteLn('Form returned modalresult ok');
Wait(1200);
FreeForm(InterfaceForm.Drawing);
end.


Extension:
[PHP]
Program WindowHandles;

const
LWA_COLORKEY = 1;
LWA_ALPHA = 2;
LWA_BOTH = 3;
WS_EX_LAYERED = $80000;
GWL_EXSTYLE = - 20;

type
TCharArray = array of Char;

var
SimbaMenu, FormGUI: TMenuItem;
Extensions: string;
FormTitleBarName: PChar;

Function FindWindow(ClassName, WindowName: PChar): HWND; external 'FindWindowA@User32.dll stdcall';

Function SetParent(Child, Parent: HWND): HWND; external 'SetParent@User32.dll stdcall';

Function SetWindowPos(Handle, HandleAfter: HWND; X, Y, CX, CY: Integer; Flags: Word): Boolean; external 'SetWindowPos@User32.dll stdcall';

Function GetWindowLong(Handle: HWND; nIndex: LongInt): LongInt; external 'GetWindowLongA@User32.dll stdcall';

Function SetWindowLong(Handle: HWND; nIndex: LongInt; dwNewLong: Longint): LongInt; external 'SetWindowLongA@User32.dll stdcall';

Function SetLayeredWindowAttributes(Handle: HWND; Color, Alpha: Longint; dwFlags: Byte): Boolean; external 'SetLayeredWindowAttributes@User32.dll stdcall';

Procedure SetWindowTransparent(Handle: HWND; Color: LongInt; nTrans: Integer);
var
Attribute: LongInt;
begin
Attribute := GetWindowLong(Handle, GWL_EXSTYLE);
SetWindowLong(Handle, GWL_EXSTYLE, Attribute or WS_EX_LAYERED);
SetLayeredWindowAttributes(Handle, Color, nTrans, 1);
end;

Procedure SetFormTransparent(ClassName, WindowName: PChar);
begin
SetWindowTransparent(FindWindow(ClassName, WindowName), 0, LWA_COLORKEY);
end;

Function FindSmart: HWND;
var
Smart: HWND;
begin
Smart := FindWindow('SunAwtFrame', '');
Result := Smart;
end;

Procedure SetParentSmart(Sender: TObject);
var
FormHandle, SmartHandle: HWND;
begin
FormTitleBarName := 'FormGUI';
FormHandle := FindWindow('', FormTitleBarName);
SmartHandle := FindSmart;
if ((SmartHandle <> 0) and (FormHandle <> 0)) then
begin
SetParent(FormHandle, SmartHandle);
SetWindowPos(FormHandle, 0, 6, 345, 505, 128, 1);
end;
FormTitleBarName := 'FormBTN';
FormHandle := FindWindow('', FormTitleBarName);
if ((SmartHandle <> 0) and (FormHandle <> 0)) then
begin
SetParent(FormHandle, SmartHandle);
SetWindowPos(FormHandle, 0, 476, 324, 505, 128, 1);
end;
end;

var
Thread: TTimer;

Procedure Thread_Init;
begin
Thread := TTimer.Create(nil);
Thread.ONTIMER := @SetParentSmart;
Thread.INTERVAL := 10000;
Thread.ENABLED := True;
end;

Procedure Init;
begin
SimbaMenu := TMenuItem.Create(Simba_MainMenu);
SimbaMenu.Caption := 'FormGUI';
Simba_MainMenu.Items.Add(SimbaMenu);
FormGUI := TMenuItem.Create(SimbaMenu);
FormGUI.Caption := 'FormGUI';
FormGUI.ShortCut := 123;
FormGUI.OnClick := @ SetParentSmart;
SimbaMenu.Add(FormGUI);
Thread_Init;
end;

Function GetName: string;
begin
result := 'FormGUI';
end;

Function GetVersion: string;
begin
result := '1.0';
end;

Procedure Free;
begin
end;

Procedure Attach;
begin
writeln(' FormGUI By Ggzz ');
Writeln('# FormGUI Simba Extension #');
end;

Procedure Detach;
begin
writeln('FormGUI: GoodBye!');
end;

begin
end.
[/PHP]

eska

2012-07-07 13:37:45

I don't quite understand how to use your extension.

I saved it and activated it but I don't understand how I should call the functions in your extension from my script.

First time messing with extension like this :s Thanks for your help by the way. This really seems like it could work.

Brandon

2012-07-07 13:47:02

K Given a specific Window Name you can grab the handle to the window you wish to draw on..

EDIT: Here you go.. This should definitely do it:

Script To Test it:

{$I SRL/SRL.Simba}

type
Form = record
Drawing: TForm;
end;

var
InterfaceForm: Form;

procedure Form_Init;
begin
with InterfaceForm do
begin
Drawing := CreateForm;
with Drawing do
begin
Left := 376;
Top := 175;
BorderStyle := bsNone;
Caption := 'Drawing';
ClientHeight := 201;
ClientWidth := 304;
Color := 0;
Font.Color := clWindowText;
Font.Height := - 11;
Font.Name := 'Tahoma';
Font.Style := [];
PixelsPerInch := 96;
end;
end;
end;

procedure Form_SafeInit;
var
v: TVariantArray;
begin
SetLength(v, 0);
ThreadSafeCall('Form_Init', v);
end;

function Form_ShowModal: Boolean;
begin
InterfaceForm.Drawing.SHOW;
Result := InterfaceForm.Drawing.Showing;
end;

function Form_SafeShowModal: Boolean;
var
v: TVariantArray;
begin
SetLength(v, 0);
Result := ThreadSafeCall('Form_ShowModal', v);
end;

procedure FreeDrawingForm;
begin
FreeForm(InterfaceForm.Drawing);
end;

Procedure SetFormGUI(FormHandle, Width, Height: Integer; ParentTitle, ParentClass: String; FormPosition: TPoint);
var
Path: String;
begin
Path := IncludePath + 'FormGUI.INI';
WriteINI('FormInfo', 'Changed', 'Y', Path);
WriteINI('FormInfo', 'FormHandle', ToStr(FormHandle), Path);
WriteINI('FormInfo', 'ParentTitle', ParentTitle, Path);
WriteINI('FormInfo', 'ParentClass', ParentClass, Path);
WriteINI('FormInfo', 'ChildPosition', '[' + ToStr(FormPosition) + ']', Path);
WriteINI('FormInfo', 'ChildWidth', ToStr(Width), Path);
WriteINI('FormInfo', 'ChildHeight', Height, Path);
end;

var
TPA: TPointArray;
TPAArea: TBox;

begin
Form_SafeInit;
Form_SafeShowModal;
AddOnTerminate('FreeDrawingForm');

TPA := TPAFromBox(IntToBox(0, 0, 20, 20));
TPAArea := GetTPABounds(TPA);
SetFormGUI(InterfaceForm.Drawing.Handle, 1 + (TPAArea.X2 - TPAArea.X1), 1 + (TPAArea.Y2 - TPAArea.Y1), 'Diablo', 'Class', Point(0, 0));
//Every time you pass different parameters to SetFormGUI, you should destroy the form first and remake it using SafeINIT and safe show modal.

InterfaceForm.Drawing.CANVAS.Pen.COLOR := CLRed;
InterfaceForm.Drawing.CANVAS.Rectangle(0, 0, 20, 20); //Make sure ur drawing doesn't go out of the bounds of your form!
end.

New Extension:
[PHP]
Program WindowHandles;

const
LWA_COLORKEY = 1;
LWA_ALPHA = 2;
LWA_BOTH = 3;
WS_EX_LAYERED = $80000;
GWL_EXSTYLE = - 20;

type
TCharArray = array of Char;
GUIInput = record
FormHandle: Integer;
ParentTitle: String;
ParentClass: String;
ChildPosition: TPoint;
ChildWidth: Integer;
ChildHeight: Integer;
//TPAToDebug: TPointArray;
end;

var
SimbaMenu, FormGUI: TMenuItem;
Extensions: string;
FormTitleBarName: PChar;
UserGUIInfo: GUIInput;
Thread: TTimer;

Function FindWindow(ClassName, WindowName: PChar): HWND; external 'FindWindowA@User32.dll stdcall';

Function SetParent(Child, Parent: HWND): HWND; external 'SetParent@User32.dll stdcall';

Function SetWindowPos(Handle, HandleAfter: HWND; X, Y, CX, CY: Integer; Flags: Word): Boolean; external 'SetWindowPos@User32.dll stdcall';

Function GetWindowLong(Handle: HWND; nIndex: LongInt): LongInt; external 'GetWindowLongA@User32.dll stdcall';

Function SetWindowLong(Handle: HWND; nIndex: LongInt; dwNewLong: Longint): LongInt; external 'SetWindowLongA@User32.dll stdcall';

Function SetLayeredWindowAttributes(Handle: HWND; Color, Alpha: Longint; dwFlags: Byte): Boolean; external 'SetLayeredWindowAttributes@User32.dll stdcall';

Procedure SetWindowTransparent(Handle: HWND; Color: LongInt; nTrans: Integer);
var
Attribute: LongInt;
begin
Attribute := GetWindowLong(Handle, GWL_EXSTYLE);
SetWindowLong(Handle, GWL_EXSTYLE, Attribute or WS_EX_LAYERED);
SetLayeredWindowAttributes(Handle, Color, nTrans, 1);
end;

Procedure SetFormTransparent(ClassName, WindowName: PChar);
begin
SetWindowTransparent(FindWindow(ClassName, WindowName), 0, LWA_COLORKEY);
end;

Function FindSmart: HWND;
var
Smart: HWND;
begin
Smart := FindWindow('SunAwtFrame', '');
Result := Smart;
end;

Procedure SetParentSmart(Sender: TObject);
var
FormHandle, SmartHandle: HWND;
begin
FormTitleBarName := 'FormGUI';
FormHandle := FindWindow('', FormTitleBarName);
SmartHandle := FindSmart;
if ((SmartHandle <> 0) and (FormHandle <> 0)) then
begin
SetParent(FormHandle, SmartHandle);
SetWindowPos(FormHandle, 0, 6, 345, 505, 128, 1);
end;
FormTitleBarName := 'FormBTN';
FormHandle := FindWindow('', FormTitleBarName);
if ((SmartHandle <> 0) and (FormHandle <> 0)) then
begin
SetParent(FormHandle, SmartHandle);
SetWindowPos(FormHandle, 0, 476, 324, 505, 128, 1);
end;
end;

Function StringToTPA(TPA: String): TPointArray;
var
I, J, X, Y: Integer;
Str: String;
TempTPA: TPointArray;
begin
if (ExecRegExpr('^\[\([0-9]*,(\s*|^\s)[0-9]*\)*', TPA) and ExecRegExpr('\)\]$', TPA)) then
begin
if (Not ExecRegExpr('~|!|@|#|\$|%|\^|&|\*|_|\+|=|{|}|"|''|:|;|\.|<|>|\?|/|[a-z]|[A-Z]|(\[-|-\(|\)-|\),-|-\])|\\|\.|(\((\s*|^\s)\()|(,(\s*|^\s),)|(\)(\s*|^\s)\))', TPA)) then
begin
writeln('TPA String is a Valid TPointArray Match.');

For I:= 1 To High(TPA) do
begin
if (TPA = '(') then
begin
Inc(I);
while (ExecRegExpr('[0-9]', TPA)) do
begin
str:= str + TPA;
Inc(I);
if (TPA = ',') then
begin
X:= StrToInt(str);
str:= '';
break;
end;
end;
end;

if (TPA = ',') then
begin
I:= I + 2;
while (ExecRegExpr('[0-9]', TPA)) do
begin
str:= str + TPA;
Inc(I);
if (TPA = ')') then
begin
Y:= StrToInt(str);
str:= '';
break;
end;
end;
I:= I - 1;
end;

if (TPA = ')') then
begin
SetLength(TempTPA, Length(TempTPA) + 1);
TempTPA[J]:= Point(X, Y);
Inc(J);
SetLength(Result, Length(TempTPA));
Result:= TempTPA;
end;
end;
end else
begin
writeln('Invalid String To TPointArray Match!');
SetLength(Result, 1);
Result[0]:= Point(-99999, -99999);
end;
end else
begin
writeln('Invalid String To TPointArray Match!');
SetLength(Result, 1);
Result[0]:= Point(-99999, -99999);
end;
end;

Function GetUserInput: Boolean;
var
Path: String;
TempTPA: TPointArray;
begin
Path := IncludePath + 'FormGUI.INI';
Result := False;
If (UpperCase(ReadINI('FormInfo', 'Changed', Path)) = 'Y') then
begin
UserGUIInfo.FormHandle := StrToIntDef(ReadINI('FormInfo', 'FormHandle', Path), -1);
UserGUIInfo.ParentTitle := ReadINI('FormInfo', 'ParentTitle', Path);
UserGUIInfo.ParentClass := ReadINI('FormInfo', 'ParentClass', Path);

TempTPA := StringToTPA(ReadINI('FormInfo', 'ChildPosition', Path));
UserGUIInfo.ChildPosition := TempTPA[0];

UserGUIInfo.ChildWidth := StrToIntDef(ReadINI('FormInfo', 'ChildWidth', Path), 0);
UserGUIInfo.ChildHeight := StrToIntDef(ReadINI('FormInfo', 'ChildHeight', Path), 0);
//UserGUIInfo.TPAToDebug := StringToTPA(ReadINI('FormInfo', 'TPAToDebug', Path));

Result := True;
end;
end;

Function ValidWindowInfo(Info: GUIInput): Boolean;
begin
Result := (Length(Info.ParentTitle) > 0) or (Length(Info.ParentClass) > 0);
end;

Procedure ValidateUserInput(Sender: TObject);
var
Path: String;
begin
Path := IncludePath + 'FormGUI.INI';

If (FileExists(Path)) then
If GetUserInput then
begin
WriteINI('FormInfo', 'Changed', 'N', Path);
If ValidWindowInfo(UserGUIInfo) then
If (UserGUIInfo.FormHandle > 0) then
begin
If ((UserGUIInfo.ChildPosition.X >= 0) AND (UserGUIInfo.ChildPosition.Y >= 0)) then
SetWindowPos(UserGUIInfo.FormHandle, 0, UserGUIInfo.ChildPosition.X, UserGUIInfo.ChildPosition.Y, UserGUIInfo.ChildWidth, UserGUIInfo.ChildHeight, 4);

SetParent(UserGUIInfo.FormHandle, FindWindow(UserGUIInfo.ParentClass, UserGUIInfo.ParentTitle));
end;
end;
end;


Procedure Thread_Init;
begin
Thread := TTimer.Create(nil);
Thread.ONTIMER := @ValidateUserInput;
Thread.INTERVAL := 5000;
Thread.ENABLED := True;
end;

Procedure Init;
begin
SimbaMenu := TMenuItem.Create(Simba_MainMenu);
SimbaMenu.Caption := 'FormGUI';
Simba_MainMenu.Items.Add(SimbaMenu);
FormGUI := TMenuItem.Create(SimbaMenu);
FormGUI.Caption := 'FormGUI';
FormGUI.ShortCut := 123;
FormGUI.OnClick := @ValidateUserInput;
SimbaMenu.Add(FormGUI);
Thread_Init;
end;

Function GetName: string;
begin
result := 'FormGUI';
end;

Function GetVersion: string;
begin
result := '1.0';
end;

Procedure Free;
begin
end;

Procedure Attach;
begin
writeln(' FormGUI By Ggzz ');
Writeln('# FormGUI Simba Extension #');
end;

Procedure Detach;
begin
writeln('FormGUI: GoodBye!');
end;

begin
end.
[/PHP]

Brandon

2012-07-07 15:10:54

Bump.. Just edited it. Excuse the double post. If anyone has any idea on how to turn a String to a TPoint rather than my long StringToTPA Function, then kindly edit that part for him plox. I don't wanna write more Regexes -_-

Oh and if you have a better way that reading from the ini with a thread timer then feel free to help out there as well.

Usage: Drawing On Other External Windows via Transparent Forms. Example would be drawing a TPA on Firefox ;).

eska

2012-07-07 15:33:11

Thanks a bunch. I'll try to make it work.

Right now, it draw the big black rectagle and inside it there is the small red rectangle.

When I press F12, it crops everything execpt what was inside the red square (basicaly a black square) and moves it to the top left of the computer screen.

I'm guessing it can't find where diablo is so it return 0 or something.

Brandon

2012-07-07 15:38:10

From: eska
Thanks a bunch. I'll try to make it work.

Right now, it draw the big black rectagle and inside it there is the small red rectangle.

When I press F12, it crops everything execpt what was inside the red square (basicaly a black square) and moves it to the top left of the computer screen.

I'm guessing it can't find where diablo is so it return 0 or something.


Hmm sounds like amazing progress but I think you passed the wrong window title to it.

Do you happen to have Spy++?

Perhaps if you can, modify it to dynamically grab the Diablo handle for you..
Something like:


Function FindDiabloWindow(TitlePrefix: String): HWND;
var
T: TSysProcArr;
I: Integer;
begin
T:= GetProcesses();
for I := 0 to high(T) do
if ExecRegExpr(TitlePrefix, T[I].Title) then //If the Window Title Contains...
begin
Result := T[I].Handle;
break;
end;
end;

begin
FindDiabloWindow('Diablo');
end.

And just give the FormGUI the handle to it. I'd still recommend spy++. Why? Because you won't have it is very helpful for finding windows. Spy++ can give you the window class whereas Simba cannot. Now that could be a huge advantage incase of a two windows that have the same title.

Know what I mean? Whenever the function cannot find a window, usually you need to pass it a class if there is one. If there is none then that means ur title is just wrong. If you don't know the class, it's usually suitable to set it null.

But afaik you seem to be doing it right except that your diablo window handle is wrong.

Gu3st...

2012-07-07 15:40:38

Painting directly on D3 using canvases is possible, but because of the constant 60 fps, you'd have to write over D3 at 60+ fps as well, meaning there wont be much room/resources for anything else than painting :p

eska

2012-07-07 15:43:28

From: Zyt3x
Painting directly on D3 using canvases is possible, but because of the constant 60 fps, you'd have to write over D3 at 60+ fps as well, meaning there wont be much room/resources for anything else than painting :p


Yeah that's why we are trying to draw on something in front of D3 (AKA a form).

Racing against d3 refresh rate isn't a very interesting option :s

EDIT:
Got it to place the square right at the top left corner of D3!
Now I need to draw what I want, where I want (not just at the point 0, 0) and how I want (not just the inside of the red square).