// ***************************************************************
//  madVRTestPattern.pas      version: 1.0.0  ·  date: 2013-06-15
//  -------------------------------------------------------------
//  madVR test pattern / calibration remote controlling
//  -------------------------------------------------------------
//  Copyright (C) 2013 - 2013 www.madshi.net, All Rights Reserved
// ***************************************************************

// 2013-06-15 1.0.0 initial version

unit madVRTestPattern;

interface

uses Windows, madTypes;

// ============================================================================
// I. THE EASY WAY
// ============================================================================

// ----------------------------------------------------------------------------
// connecting to madVR - three options

// (1) "madVR_ConnectDialog" searches for madVR instances on the local PC and
//     optionally also the LAN, then shows all found instances in a dialog and
//     lets the user choose which one to connect to.
//     Searching the LAN will only work if the user has enabled network access
//     in the madVR tray icon configuration. Also, searching the LAN might
//     popup a Firewall confirmation window.
// "result=true"  means: Connection to the selected madVR instance was opened.
// "result=false" means: The user aborted the connection process.
function madVR_ConnectDialog (searchLan: boolean = true; parentWindow: HWND = 0) : boolean;

// (2) "madVR_BlindConnect" searches for madVR instances on the local PC and
//     optionally also the LAN, then simply connects to the first found
//     instance. Local PC instances are preferred over LAN instances.
//     Searching the LAN will only work if the user has enabled network access
//     in the madVR tray icon configuration. Also, searching the LAN might
//     popup a Firewall confirmation window.
// "result=true"  means: A madVR instance was found and a connection opened.
// "result=false" means: No madVR instance found, or connection failed.
function madVR_BlindConnect (searchLan: boolean = true; timeOut: dword = 3000) : boolean;

// (3) If you want to connect to a madVR instance running on a LAN PC with a
//     known IP address, "madVR_ConnectToIp" lets you do just that.
//     Connecting to a madVR LAN instance will only work if the user has
//     enabled network access in the madVR tray icon configuration. Also,
//     using "madVR_ConnectToIp" might popup a Firewall confirmation window.
// "result=true"  means: A madVR instance was found and a connection opened.
// "result=false" means: No madVR instance found, or connection failed.
function madVR_ConnectToIp (const ipAddress: AnsiString; timeOut: dword = 1000) : boolean;

// ----------------------------------------------------------------------------
// remote controlling the connected madVR instance

// "madVR_Disable3dlut" disables 3dlut processing.
// The 3dlut stays disabled until the connection is closed.
// Disable the 3dlut if you want to calibrate/profile the display, or if you
// want to measure the display behaviour prior to calibration.
// Don't disable the 3dlut if you want to measure the final display after full
// calibration.
function madVR_Disable3dlut () : boolean;

// "madVR_SetDeviceGammaRamp" sets the madVR internal VideoLUTs.
// Parameter is identical to the win32 API "SetDeviceGammaRamp".
function madVR_SetDeviceGammaRamp (ramp: pointer) : boolean;

// "madVR_SetOsdText" shows a "text" on the top left of the video image.
function madVR_SetOsdText (const text: UnicodeString) : boolean;

// "madVR_SetBackground" lets you define how much percect of the madVR
// rendering window is painted in the test pattern color, and how much is
// painted with a specific background color.
// Using a background color can make sense for plasma measurements.
function madVR_SetBackground (patternAreaInPercent: integer; backgroundColor: COLORREF) : boolean;

// "madVR_ShowProgressBar" initializes the madVR progress bar.
// It will progress one step with every "madVR_ShowRGB" call (see below).
function madVR_ShowProgressBar (numberOfRgbMeasurements: integer) : boolean;

// "madVR_ShowRGB" shows a specific RGB color test pattern.
// Values are gamma corrected with "black = 0.0" and "white = 1.0".
// You can go below 0.0 or above 1.0 for BTB/WTW, if you want. Of course a test
// pattern with BTB/WTW will only work if the connected madVR instance is
// configured to TV level output.
// "madVR_ShowRGB" blocks until the GPU has actually output the test pattern to
// the display. How fast the display will actually show the test pattern will
// depend on the display's input latency, which is outside of madVR's control.
function madVR_ShowRGB (const r, g, b: double) : boolean;

// ----------------------------------------------------------------------------
// disconnecting from madVR

// "madVR_Disconnect" closes the current connection to madVR.
function madVR_Disconnect : boolean;

// ----------------------------------------------------------------------------
// checking madVR availability

// "madVR_IsAvailable" checks whether the madHcNet32.dll can be found.
// It must either be in the current directory, or in the search path.
// Or alternatively it will also work if madVR is installed on the current PC.
function madVR_IsAvailable : boolean;

// ----------------------------------------------------------------------------


// ============================================================================
// II. THE HARD WAY
// ============================================================================

// ----------------------------------------------------------------------------
// finding / enumerating madVR instances on the LAN

// The following API lets you automatically locate madVR instances running
// anywhere on either the local PC or remote PCs connected via LAN.
// For every found madVR instance this full information record is returned:

type
  TMadVRInstance = packed record   // Example 1:       // Example 2:
    handle       : THandle;        // 1                // 2
    instance     : int64;          // 0x40001000       // 0x40001000
    ipAddress    : PAnsiChar;      // "192.168.1.1"    // "192.168.1.1"
    computerName : PWideChar;      // "HTPC"           // "HTPC"
    userName     : PWideChar;      // "Walter"         // "Walter"
    os           : PWideChar;      // "Windows XP"     // "Windows 8"
    processId    : NativeUInt;     // 220              // 248
    processName  : PWideChar;      // "MPC-HC"         // "madVR test pattern generator"
    exeFile      : PWideChar;      // "mplayerc.exe"   // "madTPG.exe"
    exeVersion   : PWideChar;      // 1.3.2066.0       // 1.0.0.0
    madVRVersion : PWideChar;      // 0.86.4.0         // 0.86.4.0
    gpuName      : PWideChar;      // "Radeon 3850"    // "GeForce 650"
    monitorName  : PWideChar;      // "JVC HD-350"     // "JVC HD-350"
  end;
  PMadVRInstance = ^TMadVRInstance;

// "madVR_Find_Async" also starts a search for madVR instances, but instead of
// returning information directly, it will send a message to the specified
// "window" for every found madVR instance.
// After the search is complete, "madVR_Find_Async" will keep an eye open for
// newly started and closed down madVR instances and automatically report them
// to your "window", as well.
// In order to stop notification, call "madVR_Find_Async" with NULL parameters.
// wParam: 0 = a new madVR instance was detected
//         1 = a known madVR instance closed down
// lParam: "PMadVRInstance" of the new/closed madVR instance
//         do not free the PMadVRInstance, the memory is managed by madVR
function madVR_Find_Async (window: HWND; msg: dword) : boolean;

// ----------------------------------------------------------------------------
// connection to a specific madVR instance

// "madVR_Connect" connects you to the specified madVR instance.
// If a previous connection exists, it will be closed automatically.
// The "handle" and "instance" originate from a "madVR_Find_Async" search.
function madVR_Connect (handle: THandle; instance: int64) : boolean;

// ----------------------------------------------------------------------------


// ============================================================================
// III. GUI LOCALIZATION
// ============================================================================

// Localize/customize all texts used by madVR_ConnectDialog.
procedure Localize_ConnectDialog (const title     : UnicodeString;   // madVR selection...
                                  const text      : UnicodeString;   // Please make sure that madVR is running on the target computer and then select it here:
                                  const columns   : UnicodeString;   // ip address|computer|pid|process|gpu|monitor|os
                                  const notListed : UnicodeString;   // The madVR instance I'm looking for is not listed
                                  const select    : UnicodeString;   // Select
                                  const cancel    : UnicodeString    // Cancel
                                 );

// Localize/customize all texts used by madVR_IpAddressDialog.
// This is a dialog used internally by madVR_ConnectDialog.
procedure Localize_IpAddressDialog (const title        : UnicodeString;   // find mad* instance...
                                    const text         : UnicodeString;   // Please enter the IP address of the computer on which mad* is running:
                                    const connect      : UnicodeString;   // Connect
                                    const cancel       : UnicodeString;   // Cancel
                                    const warningTitle : UnicodeString;   // Warning...
                                    const warningText1 : UnicodeString;   // There doesn't seem to be any madVR instance running on that computer.
                                    const warningText2 : UnicodeString    // The target computer does not react.\n\n
                                   );                                     // Please check if it's turned on and connected to the LAN.\n
                                                                          // You may also want to double check your firewall settings.

// ----------------------------------------------------------------------------

implementation

// ----------------------------------------------------------------------------

var
  HcNetDll : HMODULE = 0;
  InitDone : boolean = false;
  InitSuccess : boolean = false;

var
  ConnectDialog      : function (searchLan: bool; parentWindow: HWND) : bool; stdcall = nil;
  BlindConnect       : function (searchLan: bool; timeOut: dword) : bool; stdcall = nil;
  ConnectToIp        : function (ipAddress: PAnsiChar; timeOut: dword) : bool; stdcall = nil;
  Disable3dlut       : function () : bool; stdcall = nil;
  SetDeviceGammaRamp : function (ramp: pointer) : bool; stdcall = nil;
  SetOsdText         : function (text: PWideChar) : bool; stdcall = nil;
  SetBackground      : function (patternAreaInPercent: integer; backgroundColor: COLORREF) : bool; stdcall = nil;
  ShowProgressBar    : function (numberOfRgbMeasurements: integer) : bool; stdcall = nil;
  ShowRGB            : function (r, g, b: double) : bool; stdcall = nil;
  Disconnect         : function : bool; stdcall = nil;
  Find_Async         : function (window: HWND; msg: dword) : bool; stdcall = nil;
  Connect            : function (handle: THandle; instance: int64) : bool; stdcall = nil;
  LocConnectDialog   : procedure (title, text, columns, notListed, select, cancel: PWideChar); stdcall = nil;
  LocIpAddressDialog : procedure (title, text, connect, cancel, warningTitle, warningText1, warningText2: PWideChar); stdcall = nil;

function Init : boolean;
var hk1  : HKEY;
    size : dword;
    us1  : UnicodeString;
    i1   : integer;
begin
  if not InitDone then begin
    HcNetDll := LoadLibraryW('madHcNet32.dll');
    if (HcNetDll = 0) and (RegOpenKeyExW(HKEY_CLASSES_ROOT, 'CLSID\{E1A8B82A-32CE-4B0D-BE0D-AA68C772E423}\InprocServer32', 0, KEY_QUERY_VALUE or $200 (*KEY_WOW64_32KEY*), hk1) = ERROR_SUCCESS) then begin
      size := MAX_PATH * 2 + 2;
      SetLength(us1, MAX_PATH);
      i1 := RegQueryValueExW(hk1, nil, nil, nil, pointer(us1), @size);
      if i1 = ERROR_MORE_DATA then begin
        SetLength(us1, size div 2 + 1);
        i1 := RegQueryValueExW(hk1, nil, nil, nil, pointer(us1), @size);
      end;
      if i1 = ERROR_SUCCESS then begin
        us1 := PWideChar(us1);
        for i1 := Length(us1) downto 1 do
          if us1[i1] = '\' then begin
            Delete(us1, i1 + 1, maxInt);
            break;
          end;
        HcNetDll := LoadLibraryW(PWideChar(us1 + 'madHcNet32.dll'));
      end;
      RegCloseKey(hk1);
    end;
    ConnectDialog      := GetProcAddress(HcNetDll, 'madVR_ConnectDialog'     );
    BlindConnect       := GetProcAddress(HcNetDll, 'madVR_BlindConnect'      );
    ConnectToIp        := GetProcAddress(HcNetDll, 'madVR_ConnectToIp'       );
    Disable3dlut       := GetProcAddress(HcNetDll, 'madVR_Disable3dlut'      );
    SetDeviceGammaRamp := GetProcAddress(HcNetDll, 'madVR_SetDeviceGammaRamp');
    SetOsdText         := GetProcAddress(HcNetDll, 'madVR_SetOsdText'        );
    SetBackground      := GetProcAddress(HcNetDll, 'madVR_SetBackground'     );
    ShowProgressBar    := GetProcAddress(HcNetDll, 'madVR_ShowProgressBar'   );
    ShowRGB            := GetProcAddress(HcNetDll, 'madVR_ShowRGB'           );
    Disconnect         := GetProcAddress(HcNetDll, 'madVR_Disconnect'        );
    Find_Async         := GetProcAddress(HcNetDll, 'madVR_Find_Async'        );
    Connect            := GetProcAddress(HcNetDll, 'madVR_Connect'           );
    LocConnectDialog   := GetProcAddress(HcNetDll, 'Localize_ConnectDialog'  );
    LocIpAddressDialog := GetProcAddress(HcNetDll, 'Localize_IpAddressDialog');
    InitSuccess := (@ConnectDialog      <> nil) and
                   (@BlindConnect       <> nil) and
                   (@ConnectToIp        <> nil) and
                   (@Disable3dlut       <> nil) and
                   (@SetDeviceGammaRamp <> nil) and
                   (@SetOsdText         <> nil) and
                   (@SetBackground      <> nil) and
                   (@ShowProgressBar    <> nil) and
                   (@ShowRGB            <> nil) and
                   (@Disconnect         <> nil) and
                   (@Find_Async         <> nil) and
                   (@Connect            <> nil) and
                   (@LocConnectDialog   <> nil) and
                   (@LocIpAddressDialog <> nil);
    InitDone := true;
  end;
  result := InitSuccess;
end;

// ----------------------------------------------------------------------------

function madVR_ConnectDialog(searchLan: boolean = true; parentWindow: HWND = 0) : boolean;
begin
  result := Init and ConnectDialog(searchLan, parentWindow);
end;

function madVR_BlindConnect(searchLan: boolean = true; timeOut: dword = 3000) : boolean;
begin
  result := Init and BlindConnect(searchLan, timeOut);
end;

function madVR_ConnectToIp(const ipAddress: AnsiString; timeOut: dword = 1000) : boolean;
begin
  result := Init and ConnectToIp(PAnsiChar(ipAddress), timeOut);
end;

function madVR_Disable3dlut() : boolean;
begin
  result := Init and Disable3dlut;
end;

function madVR_SetDeviceGammaRamp(ramp: pointer) : boolean;
begin
  result := Init and SetDeviceGammaRamp(ramp);
end;

function madVR_SetOsdText(const text: UnicodeString) : boolean;
begin
  result := Init and SetOsdText(PWideChar(text));
end;

function madVR_SetBackground(patternAreaInPercent: integer; backgroundColor: COLORREF) : boolean;
begin
  result := Init and SetBackground(patternAreaInPercent, backgroundColor);
end;

function madVR_ShowProgressBar(numberOfRgbMeasurements: integer) : boolean;
begin
  result := Init and ShowProgressBar(numberOfRgbMeasurements);
end;

function madVR_ShowRGB(const r, g, b: double) : boolean;
begin
  result := Init and ShowRGB(r, g, b);
end;

function madVR_Disconnect : boolean;
begin
  result := Init and Disconnect;
end;

function madVR_Find_Async(window: HWND; msg: dword) : boolean;
begin
  result := Init and Find_Async(window, msg);
end;

function madVR_Connect(handle: THandle; instance: int64) : boolean;
begin
  result := Init and Connect(handle, instance);
end;

function madVR_IsAvailable : boolean;
begin
  result := Init;
end;

procedure Localize_ConnectDialog(const title, text, columns, notListed, select, cancel: UnicodeString);
begin
  if Init then
    LocConnectDialog(PWideChar(title), PWideChar(text), PWideChar(columns), PWideChar(notListed), PWideChar(select), PWideChar(cancel));
end;

procedure Localize_IpAddressDialog(const title, text, connect, cancel, warningTitle, warningText1, warningText2: UnicodeString);
begin
  if Init then
    LocIpAddressDialog(PWideChar(title), PWideChar(text), PWideChar(connect), PWideChar(cancel), PWideChar(warningTitle), PWideChar(warningText1), PWideChar(warningText2));
end;

// ----------------------------------------------------------------------------

end.
