{%MainUnit win32int.pp}

{
 *****************************************************************************
  This file is part of the Lazarus Component Library (LCL)

  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************
}

{$IFOPT C-}
// Uncomment for local trace
//  {$C+}
//  {$DEFINE ASSERT_IS_ON}
{$ENDIF}

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.Create
  Params:  None
  Returns: Nothing

  Constructor for the class.
 ------------------------------------------------------------------------------}
constructor TWin32WidgetSet.Create;
begin
  FPendingWaitHandlerIndex := -1;
  inherited Create;
  FTimerData  := TFpList.Create;
  FMetrics.cbSize := SizeOf(FMetrics);
  FMetricsFailed := not Windows.SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
    SizeOf(FMetrics), @FMetrics, 0);
  if FMetricsFailed then
  begin
    FMetrics.iMenuHeight := GetSystemMetrics(SM_CYMENU);
    FDefaultFont := GetStockObject(DEFAULT_GUI_FONT);
  end
  else
  begin
    FDefaultFont := Windows.CreateFontIndirect(FMetrics.lfMessageFont);
  end;
  OnClipBoardRequest := nil;
  Pointer(InitCommonControlsEx) := GetProcAddress(GetModuleHandle(comctl32), 'InitCommonControlsEx');
  FCommonControlsVersion := GetFileVersion(comctl32);
  FDotsPatternBitmap := 0;
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.Destroy
  Params:  None
  Returns: Nothing

  Destructor for the class.
 ------------------------------------------------------------------------------}
destructor TWin32WidgetSet.Destroy;
var
  n: integer;
  TimerInfo : PWin32TimerInfo;
begin
  n := FTimerData.Count;
  if (n > 0) then
  begin
    DebugLn(Format('[TWin32WidgetSet.Destroy] WARNING: There are %d TimerInfo structures left, I''ll free them' ,[n]));
    while (n > 0) do
    begin
      dec(n);
      TimerInfo := PWin32Timerinfo(FTimerData[n]);
      Dispose(TimerInfo);
      FTimerData.Delete(n);
    end;
  end;

  FTimerData.Free;
  
  if FDotsPatternBitmap <> 0 then
    DeleteObject(FDotsPatternBitmap);

  if FAppHandle <> 0 then
  begin
    {$ifdef RedirectDestroyMessages}
    SetWindowLongPtrW(FAppHandle, GWL_WNDPROC, PtrInt(@DestroyWindowProc));
    {$endif}
    DestroyWindow(FAppHandle);
  end;
  Windows.UnregisterClassW(@ClsNameW, System.HInstance);
  Windows.UnregisterClassW(@ClsHintNameW, System.HInstance);
  if FDefaultFont <> 0 then
    Windows.DeleteObject(FDefaultFont);
  inherited Destroy;
end;

procedure TWin32WidgetSet.DCSetAntialiasing(CanvasHandle: HDC; AEnabled: Boolean);
begin
  if AEnabled then SetStretchBltMode(CanvasHandle, HALFTONE)
    else SetStretchBltMode(CanvasHandle, COLORONCOLOR);
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.AppInit
  Params:  None
  Returns: Nothing

  Initialize Windows
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.AppInit(var ScreenInfo: TScreenInfo);
const
  Win32ClassesToInit: array[0..5] of DWord = (ICC_DATE_CLASSES,
    ICC_UPDOWN_CLASS, ICC_TAB_CLASSES, ICC_PROGRESS_CLASS, ICC_BAR_CLASSES,
    ICC_PAGESCROLLER_CLASS);
  Done: boolean = False;
var
  ICC: TINITCOMMONCONTROLSEX;
  Handle: HWND;
  DC: HDC;
  AIcon: HICON;
  i: integer;
begin
  if Done then
    exit;
  Done := True;
  if not WinRegister then
  begin
    DebugLn('Trace:Win32Object.Init - Register Failed');
    Exit;
  end;

  OleInitialize(nil);
  //TODO: Remove when the WS interface is implemented
  //      Common controls only need to be initialized when used
  //      So they are initialized in the CreateHandle for common controls
  InitCommonControls;
  if InitCommonControlsEx <> nil then
  begin
    ICC.dwSize := SizeOf(TINITCOMMONCONTROLSEX);
    for i := Low(Win32ClassesToInit) to High(Win32ClassesToInit) do
    begin
      ICC.dwICC := Win32ClassesToInit[i];
      InitCommonControlsEx(@ICC);
    end;
  end;

  // Create parent of all windows, 'button on taskbar'
  if not IsLibrary then
  begin
    CreateAppHandle;
    // set nice main icon
    AIcon := Windows.LoadIcon(MainInstance, 'MAINICON');
    AppSetIcon(AIcon, AIcon);
  end;

  // initialize ScreenInfo
  Handle := GetDesktopWindow;
  DC := Windows.GetDC(Handle);
  ScreenInfo.PixelsPerInchX := GetDeviceCaps(DC, LOGPIXELSX);
  ScreenInfo.PixelsPerInchY := GetDeviceCaps(DC, LOGPIXELSY);
  ScreenInfo.ColorDepth := GetDeviceCaps(DC, BITSPIXEL);
  Windows.ReleaseDC(Handle, DC);

  // Thread.Synchronize support
  WakeMainThread := @HandleWakeMainThread;
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.AppMinimize
  Params:  None
  Returns: Nothing

  Minimizes the whole application to the taskbar
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.AppMinimize;
var
  AForm: TCustomForm;
  i: Integer;
begin
  FAppMinimizing := True;
  // issue #26463
  if Assigned(Application) and Application.MainFormOnTaskBar
  and Assigned(Application.MainForm) and Application.MainForm.HandleAllocated then
  begin
    HidePopups(Application.MainFormHandle);
    Application.MainForm.WindowState := wsMinimized;
  end
  else
  if Assigned(Application) and (Application.ModalLevel > 0) then
  begin
    for i := Screen.CustomFormZOrderCount - 1 downto 0 do
    begin
      AForm := Screen.CustomFormsZOrdered[i];
      if AForm.HandleAllocated and AForm.Visible and (fsModal in AForm.FormState) then
        ShowWindow(AForm.Handle, SW_SHOWMINIMIZED);
    end;
    ShowWindow(Win32WidgetSet.AppHandle, SW_SHOWMINNOACTIVE);
  end
  else
    Windows.SendMessage(FAppHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);

  FAppMinimizing := False;
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.AppRestore
  Params:  None
  Returns: Nothing

  Restore minimized whole application from taskbar
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.AppRestore;
var
  AForm: TCustomForm;
  i: Integer;
begin
  // issue #26463
  if Assigned(Application) and Application.MainFormOnTaskBar
  and Assigned(Application.MainForm) and Application.MainForm.HandleAllocated then
  begin
    ShowWindow(Application.MainForm.Handle, SW_RESTORE);
    RestorePopups;
    if (Screen.ActiveControl<>nil) and Screen.ActiveControl.HandleAllocated then
      SetFocus(Screen.ActiveControl.Handle);
  end
  else
  if Assigned(Application) and (Application.ModalLevel > 0) then
  begin
    ShowWindow(FAppHandle, SW_RESTORE);
    for i := Screen.CustomFormZOrderCount - 1 downto 0 do
    begin
      AForm := Screen.CustomFormsZOrdered[i];
      if AForm.HandleAllocated and AForm.Visible and (fsModal in AForm.FormState) then
      begin
        ShowWindow(AForm.Handle, SW_RESTORE);
      end;
    end;
  end
  else
    Windows.SendMessage(FAppHandle, WM_SYSCOMMAND, SC_RESTORE, 0);
end;


{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.AppBringToFront
  Params:  None
  Returns: Nothing

  Brings the entire application on top of all other non-topmost programs
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.AppBringToFront;
var
  Window: HWND;
begin
  if Application.MainFormOnTaskBar and (Application.MainFormHandle<>0) then
    Window := GetLastActivePopup(Application.MainFormHandle)
  else
    Window := GetLastActivePopup(AppHandle);
  if not((Window <> 0) and IsWindowVisible(Window) and IsWindowEnabled(Window)) then
  begin
    if Assigned(Screen) and Assigned(Screen.ActiveCustomForm) and Screen.ActiveCustomForm.HandleAllocated then
      Window := Screen.ActiveCustomForm.Handle
    else
    if Assigned(Application) and Application.MainFormOnTaskBar then
      Window := Application.MainFormHandle
    else
      Window := FAppHandle;
  end;
  if (Window <> 0) and IsWindowVisible(Window) and IsWindowEnabled(Window) then
    Windows.SetForegroundWindow(Window);
end;

procedure TWin32WidgetSet.SetDesigning(AComponent: TComponent);
begin
  //if Data<>nil then EnableWindow((AComponent As TWinControl).Handle, boolean(Data^));
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.SetCallback
  Params: Msg    - message for which to set a callback
          Sender - object to which callback will be sent
  Returns:  nothing

  Applies a Message to the sender
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.SetCallback(Msg: LongInt; Sender: TObject);
var
  Window: HWnd;
begin
  if Sender is TControlCanvas then
    Window := TControlCanvas(Sender).Handle
  else 
    Window := TWinControl(Sender).Handle;

  if Window = 0 then Exit;
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.RemoveCallbacks
  Params:   Sender - object from which to remove callbacks
  Returns:  nothing

  Removes Call Back Signals from the sender
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.RemoveCallbacks(Sender: TObject);
var
  Window: HWnd;
begin
  if Sender is TControlCanvas then
    Window := TControlCanvas(Sender).Handle
  else 
  if Sender is TCustomForm then
    Window := TCustomForm(Sender).Handle
  else
    Window := (Sender as TWinControl).Handle;

  if Window = 0 then Exit;
end;

function TWin32WidgetSet.InitStockFont(AFont: TObject; AStockFont: TStockFont): Boolean;
var
  Font: TFont absolute AFont;
  LogFont: TLogFont;

  procedure AssignDefault;
  var
    LogFont: TLogFont;
  begin
    GetObject(DefaultFont, SizeOf(LogFont), @LogFont);
    Font.Assign(LogFont);
  end;

begin
  case AStockFont of
    sfSystem:
      AssignDefault;
    sfHint:
      begin
        if FMetricsFailed then
          AssignDefault
        else
          Font.Assign(FMetrics.lfStatusFont);
        Font.Color := clInfoText;
      end;
    sfIcon:
      begin
        if SystemParametersInfo(SPI_GETICONTITLELOGFONT, SizeOf(LogFont), @LogFont, 0) then
          Font.Assign(LogFont)
        else
          AssignDefault
      end;
    sfMenu:
      begin
        if FMetricsFailed then
          AssignDefault
        else
          Font.Assign(FMetrics.lfMenuFont);
        Font.Color := clMenuText;
      end;
  end;
  Result := True;
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.AppProcessMessages
  Params:  None
  Returns: Nothing

  Handle all pending messages
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.AppProcessMessages;
var
  AMessage: TMsg;
  retVal, index: dword;
  pHandles: Windows.LPHANDLE;

    procedure CallWaitHandler;
    begin
      FWaitHandlers[index].OnEvent(FWaitHandlers[index].UserData, 0);
    end;

begin
  repeat
    if FPendingWaitHandlerIndex >= 0 then
    begin
      index := FPendingWaitHandlerIndex;
      FPendingWaitHandlerIndex := -1;
      CallWaitHandler;
    end;
{$ifdef DEBUG_ASYNCEVENTS}
    if Length(FWaitHandles) > 0 then
      DebugLn('[ProcessMessages] WaitHandleCount=', IntToStr(FWaitHandleCount),
        ', WaitHandle[0]=', IntToHex(FWaitHandles[0], 8));
{$endif}
    if FWaitHandleCount > 0 then
      pHandles := @FWaitHandles[0]
    else
      pHandles := nil;
    retVal := Windows.MsgWaitForMultipleObjects(FWaitHandleCount,
      pHandles, False, 0, QS_ALLINPUT);
    if (retVal < WAIT_OBJECT_0 + FWaitHandleCount) then
    begin
      index := retVal-WAIT_OBJECT_0;
      CallWaitHandler;
    end else
    if retVal = WAIT_OBJECT_0 + FWaitHandleCount then
    begin
      AMessage := Default(TMsg);
      while PeekMessage(AMessage, HWnd(nil), 0, 0, PM_REMOVE) do
      begin
        if AMessage.message = WM_QUIT then
        begin
          PostQuitMessage(AMessage.wParam);
          break;
        end;
        // Handle MDI form accelerators
        if Assigned(Application) and
           Assigned(Application.MainForm) and
           (Application.MainForm.FormStyle=fsMDIForm) and
           TranslateMDISysAccel(Win32WidgetSet.MDIClientHandle, @AMessage)
        then begin
          // handled by TranslateMDISysAccel
        end else begin
          TranslateMessage(@AMessage);
          DispatchMessageW(@AMessage);
        end;
      end;
    end else
    if retVal = WAIT_TIMEOUT then
    begin
      // check for pending to-be synchronized methods
      CheckSynchronize;
      CheckPipeEvents;
      break;
    end else
    if retVal = $FFFFFFFF then
    begin
      DebugLn('[TWin32WidgetSet.AppProcessMessages] MsgWaitForMultipleObjects returned: ', IntToStr(GetLastError));
      break;
    end;
  until false;
end;

procedure TWin32WidgetSet.CheckPipeEvents;
var
  lHandler: PPipeEventInfo;
  lBytesAvail: dword;
  SomethingChanged: Boolean;
  ChangedCount:integer;
begin
  lHandler := FWaitPipeHandlers;
  ChangedCount:=0;
  while (lHandler <> nil) and (ChangedCount<10) do
  begin
    SomethingChanged:=true;
    if Windows.PeekNamedPipe(lHandler^.Handle, nil, 0, nil, @lBytesAvail, nil) then
    begin
      if lBytesAvail <> 0 then
        lHandler^.OnEvent(lHandler^.UserData, [prDataAvailable])
      else
        SomethingChanged := false;
    end else
      lHandler^.OnEvent(lHandler^.UserData, [prBroken]);
    if SomethingChanged then
      lHandler := FWaitPipeHandlers
    else begin
      lHandler := lHandler^.Next;
      ChangedCount := 0;
    end;
    inc(ChangedCount);
  end;
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.AppWaitMessage
  Params:  None
  Returns: Nothing

  Passes execution control to Windows
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.AppWaitMessage;
var
  retVal, timeout: DWord;
  pHandles: Windows.LPHANDLE;
begin
  RedrawMenus;
  if FWaitPipeHandlers <> nil then
    timeout := 100
  else
    timeout := INFINITE;
  if FWaitHandleCount > 0 then
    pHandles := @FWaitHandles[0]
  else
    pHandles := nil;
  retVal := Windows.MsgWaitForMultipleObjects(FWaitHandleCount, pHandles,
    false, timeout, QS_ALLINPUT);
  if (retVal < WAIT_OBJECT_0 + FWaitHandleCount) then
    FPendingWaitHandlerIndex := retVal - WAIT_OBJECT_0;
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.AppTerminate
  Params:  None
  Returns: Nothing

  Tells Windows to halt and destroy
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.AppTerminate;
begin
  OleUninitialize;
end;

procedure TWin32WidgetSet.AppSetIcon(const Small, Big: HICON);
begin
  if FAppHandle <> 0 then
  begin
    Windows.SendMessage(FAppHandle, WM_SETICON, ICON_SMALL, LPARAM(Small));
    SetClassLongPtr(FAppHandle, GCL_HICONSM, LONG_PTR(Small));

    Windows.SendMessage(FAppHandle, WM_SETICON, ICON_BIG, LPARAM(Big));
    SetClassLongPtr(FAppHandle, GCL_HICON, LONG_PTR(Big));
  end;
end;

procedure TWin32WidgetSet.AppSetTitle(const ATitle: string);
begin
  if FAppHandle <> 0 then
  begin
    Windows.SetWindowTextW(FAppHandle, PWideChar(UTF8ToUTF16(ATitle)));
  end;
end;

procedure TWin32WidgetSet.AppSetVisible(const AVisible: Boolean);
begin
  if (FAppHandle <> 0) and not (Assigned(Application) and Application.MainFormOnTaskBar) then
  begin
    if AVisible then
      Windows.ShowWindow(FAppHandle, SW_SHOW)
    else
      Windows.ShowWindow(FAppHandle, SW_HIDE);
  end;
end;

function TWin32WidgetSet.AppRemoveStayOnTopFlags(const ASystemTopAlso: Boolean = False): Boolean;
begin
  if not IsLibrary then
    RemoveStayOnTopFlags(FAppHandle, ASystemTopAlso);
  Result := True;
end;

function TWin32WidgetSet.AppRestoreStayOnTopFlags(const ASystemTopAlso: Boolean = False): Boolean;
begin
  if not IsLibrary then
    RestoreStayOnTopFlags(FAppHandle);
  Result := True;
end;

procedure TWin32WidgetSet.AppSetMainFormOnTaskBar(const DoSet: Boolean);
begin
  // 1. Update the visibility of the TaskBar window
  if DoSet then
    ShowWindow(AppHandle, SW_HIDE)
  else
    ShowWindow(AppHandle, SW_SHOW);
  // 2. Recreate the main form - so it will (not) have an own taskbar item and WndParent = 0 (AppHandle)
  if Assigned(Application.MainForm) and Application.MainForm.HandleAllocated then
    RecreateWnd(Application.MainForm);
end;

procedure TWin32WidgetSet.AppSetupMainForm(AMainForm: TObject);
var
  // AMainForm is guaranteed to be a TCustomForm
  MainForm: TCustomForm absolute AMainForm;
begin
  //Handle CmdShow parameter when invoked from a shortcut.
  case System.CmdShow of
    SW_SHOWNORMAL: ; //don't alter WindowState
    SW_SHOWMINNOACTIVE,
    SW_SHOWMINIMIZED,
    SW_MINIMIZE: MainForm.WindowState := wsMinimized;
    SW_SHOWMAXIMIZED: MainForm.WindowState := wsMaximized;
    SW_SHOWFULLSCREEN: MainForm.WindowState := wsFullScreen;
  end;
end;

function TWin32WidgetSet.LCLPlatform: TLCLPlatform;
begin
  Result:= lpWin32;
end;

function TWin32WidgetSet.GetLCLCapability(ACapability: TLCLCapability): PtrUInt;
begin
  case ACapability of
    lcAsyncProcess: Result := LCL_CAPABILITY_YES;
    lcModalWindow: Result := LCL_CAPABILITY_NO;
    lcDragDockStartOnTitleClick: Result := LCL_CAPABILITY_YES;
    lcApplicationWindow: Result := LCL_CAPABILITY_YES;
    lcLMHelpSupport: Result := LCL_CAPABILITY_YES;
    lcNeedMininimizeAppWithMainForm: Result := LCL_CAPABILITY_NO;
    lcSendsUTF8KeyPress: Result := LCL_CAPABILITY_YES;
    lcTransparentWindow: Result := LCL_CAPABILITY_YES;
    lcAllowChildControlsInNativeControls: Result := LCL_CAPABILITY_YES;
    lcTextHint:
    begin
      if (ComCtlVersion >= ComCtlVersionIE6) then
        Result := LCL_CAPABILITY_YES
      else
        Result := LCL_CAPABILITY_NO;
    end;
  else
    Result := inherited;
  end;
end;

{------------------------------------------------------------------------------
  function: CreateTimer
  Params: Interval:
          TimerFunc: Callback
  Returns: a Timer id (use this ID to destroy timer)

  Design: A timer which calls TimerCallBackProc, is created.
    The TimerCallBackProc calls the TimerFunc.
 ------------------------------------------------------------------------------}
function TWin32WidgetSet.CreateTimer(Interval: integer; TimerFunc: TWSTimerProc) : THandle;
var
  TimerInfo: PWin32TimerInfo;
begin
  Result := 0;
  if (Interval > 0) and (TimerFunc <> nil) then begin
    New(TimerInfo);
    TimerInfo^.TimerFunc := TimerFunc;
    TimerInfo^.TimerID := Win32Extra.SetTimer(0, 0, Interval, @TimerCallBackProc);
    if TimerInfo^.TimerID=0 then
      dispose(TimerInfo)
    else begin
      FTimerData.Add(TimerInfo);
      Result := TimerInfo^.TimerID;
    end;
  end;
end;

{------------------------------------------------------------------------------
  function: DestroyTimer
  Params: TimerHandle
  Returns:
 ------------------------------------------------------------------------------}
function TWin32WidgetSet.DestroyTimer(TimerHandle: THandle) : boolean;
var
  n : integer;
  TimerInfo : PWin32Timerinfo;
begin
  Result:= false;
  n := FTimerData.Count;
  while (n>0) do begin
    dec(n);
    TimerInfo := FTimerData[n];
    if (TimerInfo^.TimerID=UINT_PTR(TimerHandle)) then
    begin
      Result := Boolean(Win32Extra.KillTimer(0, UINT_PTR(TimerHandle)));
      FTimerData.Delete(n);
      Dispose(TimerInfo);
    end;
  end;
end;

procedure TWin32WidgetSet.HandleWakeMainThread(Sender: TObject);
begin
  // wake up GUI thread by sending a message to it
  if FAppHandle <> 0 then
    Windows.PostMessage(FAppHandle, WM_NULL, 0, 0);
end;

procedure TWin32WidgetSet.UpdateMDIClientBounds;

  function CalculateClientArea: TRect;
  var
    I: Integer;
  begin
    Result := Rect(0, 0, 0, 0);
    Windows.GetClientRect(Application.MainFormHandle, Result);
    for I := 0 to Application.MainForm.ControlCount - 1 do
      if Application.MainForm.Controls[I].Visible then
        case Application.MainForm.Controls[I].Align of
          alLeft: Inc(Result.Left, Application.MainForm.Controls[I].Width);
          alTop: Inc(Result.Top, Application.MainForm.Controls[I].Height);
          alRight: Dec(Result.Right, Application.MainForm.Controls[I].Width);
          alBottom: Dec(Result.Bottom, Application.MainForm.Controls[I].Height);
        end;
  end;

var
  R: TRect;
begin
  if not (Assigned(Application.MainForm) and (Application.MainForm.FormStyle=fsMDIForm)) then Exit;
  R := CalculateClientArea;
  MoveWindow(Win32WidgetSet.MDIClientHandle, R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top, True);
end;

{ Private methods (in no significant order) }

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.WinRegister
  Params: None
  Returns: If the window was successfully registered

  Registers the main window class
 ------------------------------------------------------------------------------}
function TWin32WidgetSet.WinRegister: Boolean;
var
  WindowClassW: WndClassW;
begin
  with WindowClassW do
  begin
    Style := CS_DBLCLKS;
    LPFnWndProc := @WindowProc;
    CbClsExtra := 0;
    CbWndExtra := 0;
    hInstance := System.HInstance;
    hIcon := Windows.LoadIcon(MainInstance, 'MAINICON');
    if hIcon = 0 then
     hIcon := Windows.LoadIcon(0, IDI_APPLICATION);
    hCursor := Windows.LoadCursor(0, IDC_ARROW);
    hbrBackground := 0;
    LPSzMenuName := nil;
    LPSzClassName := @ClsNameW;
  end;
  Result := Windows.RegisterClassW(@WindowClassW) <> 0;
  if Result then
  begin
    with WindowClassW do
    begin
      style := style or CS_SAVEBITS;
      if WindowsVersion >= wvXP then
        style := style or CS_DROPSHADOW;
      hIcon := 0;
      hbrBackground := 0;
      LPSzClassName := @ClsHintNameW;
    end;
    Result := Windows.RegisterClassW(@WindowClassW) <> 0;
  end;
end;

procedure TWin32WidgetSet.CreateAppHandle;
var
  SysMenu: HMENU;
begin
  FAppHandle := CreateWindowW(@ClsNameW,
    PWideChar(UTF8ToUTF16(Application.Title)),
    WS_POPUP or WS_CLIPSIBLINGS or WS_SYSMENU or WS_MINIMIZEBOX,
    0, {Windows.GetSystemMetrics(SM_CXSCREEN) div 2,}
    0, {Windows.GetSystemMetrics(SM_CYSCREEN) div 2,}
    0, 0, HWND(nil), HMENU(nil), HInstance, nil);
  AllocWindowInfo(FAppHandle);
  // remove useless menuitems from sysmenu
  SysMenu := Windows.GetSystemMenu(FAppHandle, False);
  Windows.DeleteMenu(SysMenu, SC_MAXIMIZE, MF_BYCOMMAND);
  Windows.DeleteMenu(SysMenu, SC_SIZE, MF_BYCOMMAND);
  Windows.DeleteMenu(SysMenu, SC_MOVE, MF_BYCOMMAND);
end;

function TWin32WidgetSet.GetMDIClientHandle: HWND;
const
  MDIClientW: array[0..9] of WideChar = ('M', 'D', 'I', 'C', 'L', 'I', 'E', 'N', 'T', #0);
var
  CCS: TCLIENTCREATESTRUCT;
begin
  if (FMDIClientHandle=0) and
     Assigned(Application) and
     Assigned(Application.MainForm) and
     (Application.MainForm.FormStyle=fsMDIForm) then
  begin
    CCS.hWindowMenu := 0;
    CCS.idFirstChild := 0;
    FMDIClientHandle := CreateWindowW(@MDIClientW, nil,
      WS_CHILD or WS_CLIPCHILDREN or WS_CLIPSIBLINGS or WS_VSCROLL or WS_HSCROLL,
      0, 0, 0, 0, Application.MainForm.Handle, 0, HInstance, @CCS);
    ShowWindow(FMDIClientHandle, SW_SHOW);
  end;
  Result := FMDIClientHandle;
end;

function TWin32WidgetSet.CreateThemeServices: TThemeServices;
begin
  Result := TWin32ThemeServices.Create;
end;

function TWin32WidgetSet.GetAppHandle: THandle;
begin
  Result:= FAppHandle;
end;

procedure TWin32WidgetSet.SetAppHandle(const AValue: THandle);
begin
  // Do it only if handle is not yet created (for example for DLL initialization)
  FAppHandle := AValue;
end;

function TWin32WidgetSet.GetDotsPatternBitmap: HBitmap;
const
  Dots: array[0..3] of Word = ($55, $AA, $55, $AA);
begin
  if FDotsPatternBitmap = 0 then
    FDotsPatternBitmap := CreateBitmap(4, 4, 1, 1, @Dots);
  Result := FDotsPatternBitmap;
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.DCReDraw
  Params: CanvasHandle - HDC to redraw
  Returns: Nothing

  Redraws (the window of) a canvas
 ------------------------------------------------------------------------------}
procedure TWin32WidgetSet.DCRedraw(CanvasHandle: HDC);
begin
  // TODO: implement me!
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.SetPixel
  Params:  Canvas - canvas to set color on
           X, Y   - position
           AColor - new color for specified position
  Returns: nothing

  Set the color of the specified pixel on the canvas
 ------------------------------------------------------------------------------}
var
  IntSetPixel : function (DC:HDC; X, Y:longint; cl:Windows.COLORREF):Windows.COLORREF; stdcall = nil;
  IntSendMessage : function(HandleWnd: HWND; Msg: Cardinal; WParam: WParam; LParam: LParam): LResult; stdcall = nil;

function VistaSetPixel(DC:HDC; X,Y:longint; cl:Windows.COLORREF):Windows.COLORREF; stdcall;
var
  pen, oldpen: HPEN;
  p : Windows.TPOINT;
begin
  if x and $100=0 then
    Result:=Windows.SetPixel(DC,X,Y,cl)
  else
  begin
    pen := Windows.CreatePen(PS_SOLID, 1, ColorToRGB(cl));
    oldpen := Windows.SelectObject(DC, pen);
    Windows.MoveToEx(DC, X, Y, @p);
    Windows.LineTo(DC, X, Y + 1);
    Windows.SelectObject(DC, oldpen);
    Windows.DeleteObject(pen);
    Windows.MoveToEx(DC, P.X, P.Y, nil);
    Result:=cl;
  end;
end;

procedure TWin32WidgetSet.DCSetPixel(CanvasHandle: HDC; X, Y: integer; AColor: TGraphicsColor);
begin
  IntSetPixel(CanvasHandle, X, Y, ColorToRGB(AColor));
end;

{------------------------------------------------------------------------------
  Method: TWin32WidgetSet.GetPixel
  Params:  Canvas - canvas to get color from
           X, Y   - position
  Returns: Color at specified point

  Get the color of the specified pixel on the canvas
 -----------------------------------------------------------------------------}
function  TWin32WidgetSet.DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor;
begin
  Result := Windows.GetPixel(CanvasHandle, X, Y);
end;

{$IFDEF ASSERT_IS_ON}
  {$UNDEF ASSERT_IS_ON}
  {$C-}
{$ENDIF}
