unit Form_RemoteScreen;
{$MODE Delphi}
interface
uses
Windows,
Messages,
SysUtils,
Classes,
Sockets, synautil, blcksock,
JwaTlHelp32,
GL,
//LResources,
Graphics,
Controls,
Forms,
Dialogs,
ExtCtrls,
StdCtrls, Buttons, ComCtrls,
ZLib,
BGRABitmap, BGRABitmapTypes, // ya lo tienes en Form_Main, pero agrégalo aquí también por seguridad
BGLVirtualScreen,
BGRAOpenGL;
//IdGlobal;
type
PKBDLLHOOKSTRUCT = ^TKBDLLHOOKSTRUCT;
TKBDLLHOOKSTRUCT = record
vkCode: DWORD;
scanCode: DWORD;
flags: DWORD;
time: DWORD;
dwExtraInfo: ULONG_PTR;
end;
type
{ Tfrm_RemoteScreen }
Tfrm_RemoteScreen = class(TForm)
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
Chat_Image: TImage;
FileShared_Image: TImage;
KeyboardRemote_CheckBox: TCheckBox;
Quality_Label: TLabel;
MouseRemote_CheckBox: TCheckBox;
Panel_controles: TPanel;
Resize_CheckBox: TCheckBox;
ScreenStart_Image: TImage;
CaptureKeys_Timer: TTimer;
Quality_TrackBar: TTrackBar;
Screen_Image: TBGLVirtualScreen;
ScrollBox1: TScrollBox;
procedure BitBtn1Click(Sender: TObject);
procedure BitBtn2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
procedure KeyboardRemote_CheckBoxChange(Sender: TObject);
procedure MouseRemote_CheckBoxChange(Sender: TObject);
procedure MouseRemote_CheckBoxClick(Sender: TObject);
procedure KeyboardRemote_CheckBoxClick(Sender: TObject);
procedure Resize_CheckBoxChange(Sender: TObject);
procedure Resize_CheckBoxClick(Sender: TObject);
procedure MouseRemote_CheckBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure KeyboardRemote_CheckBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure Resize_CheckBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure Screen_ImageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure Screen_ImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure Screen_ImageMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure Chat_ImageClick(Sender: TObject);
procedure FileShared_ImageClick(Sender: TObject);
procedure CaptureKeys_TimerTimer(Sender: TObject);
procedure Screen_ImagePaint(Sender: TObject);
procedure Quality_TrackBarChange(Sender: TObject);
procedure Screen_ImageRedraw(Sender: TObject; BGLContext: TBGLContext);
private
//FLastBitmap: TBitmap;
FLastBitmap: TBitmap; // puedes mantenerlo si quieres, pero ya no es necesario para dibujar
RemoteTexture: IBGLTexture; // ← NUEVA: textura acelerada por GPU
FKeyState: array[0..255] of Boolean;
FKeyboardHook: HHOOK;
procedure WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo); message WM_GETMINMAXINFO;
public
CtrlPressed, ShiftPressed, AltPressed: Boolean;
procedure UpdateScreenImage(Bitmap: TBitmap); // para sincronizar desde thread
end;
var
frm_RemoteScreen: Tfrm_RemoteScreen;
GlobalKeyboardSock: TSocket = INVALID_SOCKET;
const
WH_KEYBOARD_LL = 13;
implementation
{$R *.lfm}
uses
Form_Main,
Form_Chat,
Form_ShareFiles,
SynSock,
WinSock;
function LowLevelKeyboardProc(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
pKHS: PKBDLLHOOKSTRUCT;
vkCode: DWORD;
S: AnsiString;
begin
Result := 0;
if nCode < 0 then
begin
Result := CallNextHookEx(0, nCode, wParam, lParam);
Exit;
end;
pKHS := PKBDLLHOOKSTRUCT(lParam);
vkCode := pKHS^.vkCode;
if vkCode in [VK_LWIN, VK_RWIN] then
begin
if GlobalKeyboardSock <> INVALID_SOCKET then
begin
if (wParam = WM_KEYDOWN) or (wParam = WM_SYSKEYDOWN) then
S := AnsiString('<|REDIRECT|><|KEYDOWN|>' + IntToStr(vkCode) + '<|END|>')
else
S := AnsiString('<|REDIRECT|><|KEYUP|>' + IntToStr(vkCode) + '<|END|>');
Send(GlobalKeyboardSock, @S[1], Length(S), 0);
end;
Result := 1;
Exit;
end;
Result := CallNextHookEx(0, nCode, wParam, lParam);
end;
procedure SendStrRaw(ASock: TSocket; const Cmd: string);
var
S: AnsiString;
begin
if ASock = INVALID_SOCKET then Exit;
S := AnsiString(Cmd);
Send(ASock, @S[1], Length(S), 0);
end;
procedure Tfrm_RemoteScreen.WMGetMinMaxInfo(var Message: TWMGetMinMaxInfo);
var
MinMaxInfo: PMinMaxInfo;
begin
inherited;
MinMaxInfo := Message.MinMaxInfo;
MinMaxInfo^.ptMinTrackSize.X := 800;
MinMaxInfo^.ptMinTrackSize.Y := 500;
if Resize_CheckBox.Checked then
begin
MinMaxInfo^.ptMaxTrackSize.X := frm_Main.ResolutionTargetWidth;
MinMaxInfo^.ptMaxTrackSize.Y := frm_Main.ResolutionTargetHeight;
end
else
begin
MinMaxInfo^.ptMaxTrackSize.X := frm_Main.ResolutionTargetWidth + 25;
MinMaxInfo^.ptMaxTrackSize.Y := frm_Main.ResolutionTargetHeight + 130;
end;
end;
procedure Tfrm_RemoteScreen.FormCreate(Sender: TObject);
var
Cur: TCursorImage;
begin
SetWindowLong(Handle, GWL_EXSTYLE, WS_EX_APPWINDOW);
CtrlPressed := False;
ShiftPressed := False;
AltPressed := False;
DoubleBuffered := True;
// ← AGREGAR: permitir que Screen_Image pase eventos al form
Screen_Image.OnMouseDown := Screen_ImageMouseDown;
Screen_Image.OnMouseMove := Screen_ImageMouseMove;
Screen_Image.OnMouseUp := Screen_ImageMouseUp;
// ← AGREGAR: el ScrollBox también debe pasar el wheel
ScrollBox1.OnMouseWheel := FormMouseWheel;
end;
procedure Tfrm_RemoteScreen.BitBtn1Click(Sender: TObject);
begin
frm_ShareFiles.Show;
end;
procedure Tfrm_RemoteScreen.BitBtn2Click(Sender: TObject);
var
Renderer, Vendor, Version: string;
begin
// ... tu código existente ...
// Verificar OpenGL
Renderer := string(glGetString(GL_RENDERER));
Vendor := string(glGetString(GL_VENDOR));
Version := string(glGetString(GL_VERSION));
frm_Main.Log('GPU Renderer : ' + Renderer);
frm_Main.Log('GPU Vendor : ' + Vendor);
frm_Main.Log('OpenGL Ver : ' + Version);
// Si Renderer contiene "GDI" o "Software" = NO hay aceleración
if (Pos('GDI', UpperCase(Renderer)) > 0) or
(Pos('SOFTWARE', UpperCase(Renderer)) > 0) or
(Pos('LLVMPIPE', UpperCase(Renderer)) > 0) then
ShowMessage('⚠ SIN aceleración de hardware (software renderer)')
//frm_Main.Log('⚠ SIN aceleración de hardware (software renderer)')
else
ShowMessage('✓ Aceleración de hardware activa: ' + Renderer);
// frm_Main.Log('✓ Aceleración de hardware activa: ' + Renderer);
end;
procedure Tfrm_RemoteScreen.FormShow(Sender: TObject);
begin
FillChar(FKeyState, SizeOf(FKeyState), 0);
CtrlPressed := False;
ShiftPressed := False;
AltPressed := False;
Resize_CheckBox.Checked := False;
Resize_CheckBoxClick(nil);
// ← AGREGAR: forzar foco al form para que GetAsyncKeyState funcione
Application.ProcessMessages;
SetFocus;
end;
procedure Tfrm_RemoteScreen.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if Assigned(RemoteTexture) then FreeAndNil(RemoteTexture);
CaptureKeys_Timer.Enabled := False;
if FKeyboardHook <> 0 then
begin
UnhookWindowsHookEx(FKeyboardHook);
FKeyboardHook := 0;
end;
GlobalKeyboardSock := INVALID_SOCKET;
if frm_Main.MainSock <> INVALID_SOCKET then
frm_Main.SendMain('<|STOPACCESS|><|END|>');
frm_Main.SetOnline;
frm_Main.Show;
end;
procedure Tfrm_RemoteScreen.UpdateScreenImage(Bitmap: TBitmap);
var
TempBGRA: TBGRABitmap;
begin
if not Assigned(Bitmap) or (Bitmap.Width = 0) or (Bitmap.Height = 0) then Exit;
// Actualizar resolución target (mantienes tu lógica)
frm_Main.ResolutionTargetWidth := Bitmap.Width;
frm_Main.ResolutionTargetHeight := Bitmap.Height;
// Crear/copiar a TBGRABitmap y subir a GPU como textura
TempBGRA := TBGRABitmap.Create(Bitmap);
try
if Assigned(RemoteTexture) then
FreeAndNil(RemoteTexture); // libera la textura anterior
RemoteTexture := BGLTexture(TempBGRA); // ← Sube la imagen a la GPU (aceleración por hardware)
Screen_Image.Invalidate; // fuerza redraw con OpenGL
finally
TempBGRA.Free;
end;
end;
// ==================== MOUSE ====================
procedure Tfrm_RemoteScreen.Screen_ImageMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
var
RealX, RealY: Integer;
ExtraFlag: DWORD;
ImgW, ImgH: Integer;
begin
if not MouseRemote_CheckBox.Checked or
(frm_Main.KeyboardSock = INVALID_SOCKET) then Exit;
// Usar dimensiones reales del Image para el cálculo
ImgW := Screen_Image.Width;
ImgH := Screen_Image.Height;
if (ImgW = 0) or (ImgH = 0) then Exit;
RealX := MulDiv(X, frm_Main.ResolutionTargetWidth, ImgW);
RealY := MulDiv(Y, frm_Main.ResolutionTargetHeight, ImgH);
// Clampear para no salir del rango
if RealX < 0 then RealX := 0;
if RealY < 0 then RealY := 0;
if RealX >= frm_Main.ResolutionTargetWidth then RealX := frm_Main.ResolutionTargetWidth - 1;
if RealY >= frm_Main.ResolutionTargetHeight then RealY := frm_Main.ResolutionTargetHeight - 1;
ExtraFlag := 0;
if ssLeft in Shift then ExtraFlag := MOUSEEVENTF_LEFTDOWN;
if ssRight in Shift then ExtraFlag := MOUSEEVENTF_RIGHTDOWN;
if ssMiddle in Shift then ExtraFlag := MOUSEEVENTF_MIDDLEDOWN;
SendStrRaw(frm_Main.KeyboardSock,
'<|REDIRECT|><|SETMOUSEPOS|>' + IntToStr(RealX) + '<|>' +
IntToStr(RealY) + '<|>' + IntToStr(ExtraFlag) + '<|END|>');
end;
procedure Tfrm_RemoteScreen.Screen_ImageMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
RealX, RealY: Integer;
ImgW, ImgH: Integer;
begin
//SetFocus;
if not MouseRemote_CheckBox.Checked or
(frm_Main.KeyboardSock = INVALID_SOCKET) then Exit;
ImgW := Screen_Image.Width;
ImgH := Screen_Image.Height;
if (ImgW = 0) or (ImgH = 0) then Exit;
RealX := MulDiv(X, frm_Main.ResolutionTargetWidth, ImgW);
RealY := MulDiv(Y, frm_Main.ResolutionTargetHeight, ImgH);
if RealX < 0 then RealX := 0;
if RealY < 0 then RealY := 0;
if RealX >= frm_Main.ResolutionTargetWidth then RealX := frm_Main.ResolutionTargetWidth - 1;
if RealY >= frm_Main.ResolutionTargetHeight then RealY := frm_Main.ResolutionTargetHeight - 1;
case Button of
mbLeft: SendStrRaw(frm_Main.KeyboardSock,
'<|REDIRECT|><|SETMOUSELEFTCLICKDOWN|>' +
IntToStr(RealX) + '<|>' + IntToStr(RealY) + '<|END|>');
mbRight: SendStrRaw(frm_Main.KeyboardSock,
'<|REDIRECT|><|SETMOUSERIGHTCLICKDOWN|>' +
IntToStr(RealX) + '<|>' + IntToStr(RealY) + '<|END|>');
mbMiddle: SendStrRaw(frm_Main.KeyboardSock,
'<|REDIRECT|><|SETMOUSEMIDDLEDOWN|>' +
IntToStr(RealX) + '<|>' + IntToStr(RealY) + '<|END|>');
end;
end;
procedure Tfrm_RemoteScreen.Screen_ImageMouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
RealX, RealY: Integer;
begin
if not MouseRemote_CheckBox.Checked or
(frm_Main.KeyboardSock = INVALID_SOCKET) then Exit;
RealX := MulDiv(X, frm_Main.ResolutionTargetWidth, Screen_Image.Width);
RealY := MulDiv(Y, frm_Main.ResolutionTargetHeight, Screen_Image.Height);
case Button of
mbLeft: SendStrRaw(frm_Main.KeyboardSock,
'<|REDIRECT|><|SETMOUSELEFTCLICKUP|>' +
IntToStr(RealX) + '<|>' + IntToStr(RealY) + '<|END|>');
mbRight: SendStrRaw(frm_Main.KeyboardSock,
'<|REDIRECT|><|SETMOUSERIGHTCLICKUP|>' +
IntToStr(RealX) + '<|>' + IntToStr(RealY) + '<|END|>');
mbMiddle: SendStrRaw(frm_Main.KeyboardSock,
'<|REDIRECT|><|SETMOUSEMIDDLEUP|>' +
IntToStr(RealX) + '<|>' + IntToStr(RealY) + '<|END|>');
end;
end;
procedure Tfrm_RemoteScreen.FormMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
begin
if MouseRemote_CheckBox.Checked and (frm_Main.KeyboardSock <> INVALID_SOCKET) then
SendStrRaw(frm_Main.KeyboardSock, '<|REDIRECT|><|WHEELMOUSE|>' + IntToStr(WheelDelta) + '<|END|>');
Handled := True;
end;
procedure Tfrm_RemoteScreen.KeyboardRemote_CheckBoxChange(Sender: TObject);
begin
end;
procedure Tfrm_RemoteScreen.MouseRemote_CheckBoxChange(Sender: TObject);
begin
end;
// ==================== KEYBOARD ====================
procedure Tfrm_RemoteScreen.CaptureKeys_TimerTimer(Sender: TObject);
var
i: Byte;
IsDown: Boolean;
begin
if not Active or (frm_Main.KeyboardSock = INVALID_SOCKET) then Exit;
for i := 8 to 254 do
begin
IsDown := GetAsyncKeyState(i) and $8000 <> 0;
if IsDown and not FKeyState[i] then
begin
FKeyState[i] := True;
SendStrRaw(frm_Main.KeyboardSock,
'<|REDIRECT|><|KEYDOWN|>' + IntToStr(i) + '<|END|>');
end
else if not IsDown and FKeyState[i] then
begin
FKeyState[i] := False;
SendStrRaw(frm_Main.KeyboardSock,
'<|REDIRECT|><|KEYUP|>' + IntToStr(i) + '<|END|>');
end;
end;
// Actualizar estados de modificadores para compatibilidad
CtrlPressed := FKeyState[VK_CONTROL];
ShiftPressed := FKeyState[VK_SHIFT];
AltPressed := FKeyState[VK_MENU];
end;
procedure Tfrm_RemoteScreen.Screen_ImagePaint(Sender: TObject);
begin
{if not Assigned(FLastBitmap) then
begin
frm_Main.Log('PAINT FLastBitmap nil');
Exit;
end;
frm_Main.Log('PAINT ' + IntToStr(FLastBitmap.Width) + 'x' +
IntToStr(FLastBitmap.Height));
SetStretchBltMode(Screen_Image.Canvas.Handle, HALFTONE);
SetBrushOrgEx(Screen_Image.Canvas.Handle, 0, 0, nil);
StretchBlt(
Screen_Image.Canvas.Handle,
0, 0, Screen_Image.Width, Screen_Image.Height,
FLastBitmap.Canvas.Handle,
0, 0, FLastBitmap.Width, FLastBitmap.Height,
SRCCOPY
); }
end;
procedure Tfrm_RemoteScreen.Quality_TrackBarChange(Sender: TObject);
begin
GJpegQuality := Quality_TrackBar.Position;
// opcional: mostrar el valor actual
Quality_Label.Caption := 'Calidad: ' + IntToStr(Quality_TrackBar.Position);
end;
// =============================================
// REDRAW CON ACELERACIÓN POR HARDWARE (GPU)
// =============================================
procedure Tfrm_RemoteScreen.Screen_ImageRedraw(Sender: TObject; BGLContext: TBGLContext);
begin
BGLContext.Canvas.FillRect(0, 0, BGLContext.Width, BGLContext.Height, BGRA(0, 0, 0, 255));
if not Assigned(RemoteTexture) then
Exit;
// Estirar la textura al tamaño actual del control (modo stretched o 1:1)
RemoteTexture.StretchDraw(0, 0, BGLContext.Width, BGLContext.Height);
end;
procedure Tfrm_RemoteScreen.KeyboardRemote_CheckBoxClick(Sender: TObject);
begin
CaptureKeys_Timer.Enabled := KeyboardRemote_CheckBox.Checked;
if KeyboardRemote_CheckBox.Checked then
begin
GlobalKeyboardSock := frm_Main.KeyboardSock;
FKeyboardHook := SetWindowsHookEx(WH_KEYBOARD_LL,
@LowLevelKeyboardProc, HInstance, 0);
frm_Main.Log('Control remoto de teclado ACTIVADO');
end
else
begin
if FKeyboardHook <> 0 then
begin
UnhookWindowsHookEx(FKeyboardHook);
FKeyboardHook := 0;
end;
GlobalKeyboardSock := INVALID_SOCKET;
frm_Main.Log('Control remoto de teclado DESACTIVADO');
end;
end;
procedure Tfrm_RemoteScreen.Resize_CheckBoxChange(Sender: TObject);
begin
end;
// ==================== RESIZE / STRETCH ====================
procedure Tfrm_RemoteScreen.Resize_CheckBoxClick(Sender: TObject);
begin
if Resize_CheckBox.Checked then
begin
// Modo Estirado (Full Window)
Screen_Image.Align := alClient;
ScrollBox1.VertScrollBar.Visible := False;
ScrollBox1.HorzScrollBar.Visible := False;
end
else
begin
// Modo Tamaño Real (1:1)
Screen_Image.Align := alNone;
Screen_Image.Width := frm_Main.ResolutionTargetWidth;
Screen_Image.Height := frm_Main.ResolutionTargetHeight;
ScrollBox1.VertScrollBar.Visible := True;
ScrollBox1.HorzScrollBar.Visible := True;
end;
// Forzar redibujado inmediato
Screen_Image.Invalidate;
end;
procedure Tfrm_RemoteScreen.MouseRemote_CheckBoxClick(Sender: TObject);
begin
// Puedes cambiar cursor o icono si quieres
if MouseRemote_CheckBox.Checked then
begin
Screen_Image.Cursor := crCross; // cursor de mira (opcional)
frm_Main.Log('Control remoto de mouse ACTIVADO');
end
else
begin
Screen_Image.Cursor := crDefault;
frm_Main.Log('Control remoto de mouse DESACTIVADO');
end;
end;
procedure Tfrm_RemoteScreen.Chat_ImageClick(Sender: TObject);
begin
frm_Chat.Show;
end;
procedure Tfrm_RemoteScreen.FileShared_ImageClick(Sender: TObject);
begin
frm_ShareFiles.Show;
end;
procedure Tfrm_RemoteScreen.MouseRemote_CheckBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = VK_SPACE then Key := 0;
end;
procedure Tfrm_RemoteScreen.KeyboardRemote_CheckBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = VK_SPACE then Key := 0;
end;
procedure Tfrm_RemoteScreen.Resize_CheckBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = VK_SPACE then Key := 0;
end;
end.