{%MainUnit gtk3int.pas}

function TGtk3WidgetSet.Arc(DC: HDC; Left, top, right, bottom, angle1,
  angle2: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.Arc not implemented ...');
  {$ENDIF}
  Result:=inherited Arc(DC, Left, top, right, bottom, angle1, angle2);
end;

function TGtk3WidgetSet.AngleChord(DC: HDC; x1, y1, x2, y2, angle1,
  angle2: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.AngleChord not implemented ...');
  {$ENDIF}
  Result:=inherited AngleChord(DC, x1, y1, x2, y2, angle1, angle2);
end;

function TGtk3WidgetSet.BeginPaint(Handle: hWnd; var PS: TPaintStruct): hdc;
var
  Widget: TGtk3Widget;
  GtkWidget: PGtkWidget;
  DC: TGtk3DeviceContext;
begin
  Widget := TGtk3Widget(Handle);
  if Widget <> nil then
  begin
    GtkWidget := Widget.GetContainerWidget;
    if Widget.CairoContext <> nil then
      DC := TGtk3DeviceContext.CreateFromCairo(GtkWidget, Widget.CairoContext)
    else
      DC := TGtk3DeviceContext.Create(GtkWidget, True);
  end
  else
    DC := TGtk3DeviceContext.Create(PGtkWidget(nil), True);

  PS.hdc := HDC(DC);

  if Handle<>0 then
  begin
    DC.vClipRect := Widget.PaintData.ClipRect^;
    (*
    // if current handle has paintdata information,
    // setup hdc with it
    //DC.DebugClipRect('BeginPaint: Before');
    if Widget.PaintData.ClipRegion <> nil then
    begin
      //Write('>>> Setting Paint ClipRegion: ');
      //DebugRegion('PaintData.ClipRegion: ', Widget.PaintData.ClipRegion);
      DC.setClipRegion(Widget.PaintData.ClipRegion);
      DC.setClipping(True);
    end;
    if Widget.PaintData.ClipRect <> nil then
    begin
      New(DC.vClipRect);
      DC.vClipRect^ := Widget.PaintData.ClipRect^;
    end;
    *)
  end;

  Result := PS.hdc;
end;

function TGtk3WidgetSet.BitBlt(DestDC: HDC; X, Y, Width, Height: Integer;
  SrcDC: HDC; XSrc, YSrc: Integer; Rop: DWORD): Boolean;
begin
  {$ifdef VerboseGtk3DeviceContext}
    DebugLn('Trace:> [TGtk3WidgetSet.BitBlt]');
  {$endif}
  Result := StretchBlt(DestDC, X, Y, Width, Height, SrcDC, XSrc, YSrc, Width,
                       Height, ROP);

  {$ifdef VerboseGtk3DeviceContext}
    DebugLn('Trace:< [TGtk3WidgetSet.BitBlt]');
  {$endif}
end;

function TGtk3WidgetSet.CallNextHookEx(hHk: HHOOK; ncode: Integer;
  wParam: WParam; lParam: LParam): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.CallNextHookEx not implemented ...');
  {$ENDIF}
  Result:=inherited CallNextHookEx(hHk, ncode, wParam, lParam);
end;

function TGtk3WidgetSet.CallWindowProc(lpPrevWndFunc: TFarProc; Handle: HWND;
  Msg: UINT; wParam: WParam; lParam: lParam): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.CallWindowProc not implemented ...');
  {$ENDIF}
  Result:=inherited CallWindowProc(lpPrevWndFunc, Handle, Msg, wParam, lParam);
end;

function TGtk3WidgetSet.ClientToScreen(Handle: HWND; var P: TPoint): Boolean;
begin
  {$ifdef VerboseGtk3WinApi}
    DebugLn('Trace:> [TGtk3WidgetSet.ClientToScreen] ',dbgs(P));
  {$endif}
  // Result:=inherited ClientToScreen(Handle, P);
  Result := False;
  P := Point(0, 0);
  if IsValidHandle(Handle) then
    Result := TGtk3Widget(Handle).ClientToScreen(P);
  {$ifdef VerboseGtk3WinApi}
    DebugLn('Trace:< [TGtk3WidgetSet.ClientToScreen] ',dbgs(P),' result=',dbgs(Result));
  {$endif}
end;

function TGtk3WidgetSet.ClipboardFormatToMimeType(FormatID: TClipboardFormat
  ): string;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.ClipboardFormatToMimeType not implemented ...');
  {$ENDIF}
  Result:=inherited ClipboardFormatToMimeType(FormatID);
end;

const
  sPrimary = 'PRIMARY';
  sClipboard = 'CLIPBOARD';

function TGtk3WidgetSet.ClipboardGetData(ClipboardType: TClipboardType;
  FormatID: TClipboardFormat; Stream: TStream): boolean;
var
  pclp:PGtkClipboard;
  pc:pgchar;
  pcname:PgdkAtom;
begin
  case ClipboardType of
  ctPrimarySelection: pcname:=TGdkAtom.intern(sPrimary,false);
  ctSecondarySelection: pcname:=nil;
  ctClipboard: pcname:=TGdkAtom.intern(sClipboard,false);
  end;
  if not Assigned(pcname) then exit;
  pclp:=TGtkClipboard.get(pcname);
  if not Assigned(pclp) then exit(false);
  // text handling
  //pclp^.request_text(@ClipboardTextReceivedFunc,Stream);
  pc:=pclp^.wait_for_text;
  Stream.Write(pc^,strlen(pc));
  Result:=true;
end;

function TGtk3WidgetSet.ClipboardGetFormats(ClipboardType: TClipboardType;
  var Count: integer; var List: PClipboardFormat): boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.ClipboardGetFormats not implemented ...');
  {$ENDIF}
  Result:=inherited ClipboardGetFormats(ClipboardType, Count, List);
end;

function TGtk3WidgetSet.ClipboardGetOwnerShip(ClipboardType: TClipboardType;
  OnRequestProc: TClipboardRequestEvent; FormatCount: integer;
  Formats: PClipboardFormat): boolean;
var
  pclp:PGtkClipboard;
  pcname:PgdkAtom;
begin
  case ClipboardType of
  ctPrimarySelection: pcname:=TGdkAtom.intern(sPrimary,false);
  ctSecondarySelection: pcname:=nil;
  ctClipboard: pcname:=TGdkAtom.intern(sClipboard,false);
  end;
  if not Assigned(pcname) then exit;
  pclp:=TGtkClipboard.get(pcname);
  if not Assigned(pclp) then exit(false);
{  // text handling
  //pclp^.request_text(@ClipboardTextReceivedFunc,Stream);
  pc:=pclp^.wait_for_text;
  Stream.Write(pc^,strlen(pc)); }
  Result:=true;
end;

function TGtk3WidgetSet.ClipboardRegisterFormat(const AMimeType: string): TClipboardFormat;
 var xGA:PGdkAtom;
begin
  if Assigned(Application) then
  begin
    xGA:=TGdkAtom.intern(PChar(AMimeType), False);
    Result := TClipboardFormat(xGA);
  end else
    RaiseGDBException(
      'ERROR: TGtk3WidgetSet.ClipboardRegisterFormat gdk not initialized');
end;

function TGtk3WidgetSet.CombineRgn(Dest, Src1, Src2: HRGN;
  fnCombineMode: Longint): Longint;
var
  RDest,RSrc1,RSrc2: Pcairo_region_t;
  ARegion: Pcairo_region_t;
  AStatus: Tcairo_status_t;
  ACairoRect: Tcairo_rectangle_int_t;
begin
  {$IFDEF VerboseGtk3DeviceContext}
  DebugLn('TGtk3WidgetSet.CombineRgn combineMode=',dbgs(fnCombineMode));
  {$ENDIF}
  Result := ERROR;
  if not IsValidGDIObject(Dest) or not IsValidGDIObject(Src1) then
    exit;
  RDest := TGtk3Region(Dest).Handle;
  RSrc1 := TGtk3Region(Src1).Handle;
  if (fnCombineMode<>RGN_COPY) and not IsValidGDIObject(Src2) then
    exit
  else
    RSrc2 := TGtk3Region(Src2).Handle;
  AStatus := CAIRO_STATUS_READ_ERROR;
  ARegion := nil;
  case fnCombineMode of
     RGN_AND:
     begin
       ARegion := cairo_region_copy(RSrc1);
       AStatus := cairo_region_intersect(ARegion, RSrc2);
     end;
     RGN_COPY:
     begin
       ARegion := cairo_region_copy(RSrc2);
       AStatus := CAIRO_STATUS_SUCCESS;
     end;
     RGN_DIFF:
     begin
      ARegion := cairo_region_copy(RSrc1);
      AStatus := cairo_region_subtract(ARegion, RSrc2);
     end;
     RGN_OR:
     begin
       ARegion := cairo_region_copy(RSrc1);
       AStatus := cairo_region_union(ARegion, RSrc2);
     end;
     RGN_XOR:
     begin
       ARegion := cairo_region_copy(RSrc1);
       AStatus := cairo_region_xor(ARegion, RSrc2);
     end;
  end;
  if (AStatus <> CAIRO_STATUS_SUCCESS) or (ARegion = nil) or cairo_region_is_empty(ARegion) then
    Result := NullRegion
  else
  begin

    cairo_region_destroy(RDest);
    RDest := cairo_region_copy(ARegion);
    TGtk3Region(Dest).Handle := RDest;
    if cairo_region_num_rectangles(ARegion) > 1 then
      Result := ComplexRegion
    else
      Result := SimpleRegion;
  end;
  {$IFDEF VerboseGtk3DeviceContext}
  if Result <> NullRegion then
  begin
    cairo_region_get_extents(ARegion, @ACairoRect);
    DebugLn('TGtk3WidgetSet.CombineRgn combineMode=',dbgs(fnCombineMode),' Result=',dbgs(Result),' X=',dbgs(ACairoRect.x),' Y=',dbgs(ACairoRect.y),' W=',dbgs(ACairoRect.width),' h=',dbgs(ACairoRect.height));
  end;
  {$ENDIF}
  cairo_region_destroy(ARegion);
end;

function TGtk3WidgetSet.CreateBitmap(Width, Height: Integer; Planes,
  BitCount: Longint; BitmapBits: Pointer): HBITMAP;
var
  Format: Tcairo_format_t;
  NewBits: PByte;
  NewBitsSize: PtrUInt;
  ARowStride, RSS: Integer;
begin
  {$IFDEF VerboseGtk3WinAPI}
    DebugLn('Trace:> [Gtk3WinAPI CreateBitmap]',
     ' Width:', dbgs(Width),
     ' Height:', dbgs(Height),
     ' Planes:', dbgs(Planes),
     ' BitCount:', dbgs(BitCount),
     ' BitmapBits: ', dbgs(BitmapBits));
  {$ENDIF}
  case BitCount of
    1: Format := CAIRO_FORMAT_A1;
    8: Format := CAIRO_FORMAT_A8;
    24: Format := CAIRO_FORMAT_RGB24;
    else
      Format := CAIRO_FORMAT_ARGB32;
  end;

  RSS := GetBytesPerLine(Width, BitCount, rileWordBoundary);
  if BitmapBits <> nil then
  begin
    ARowStride := GetBytesPerLine(Width, BitCount, rileDWordBoundary);
    if not CopyImageData(Width, Height, RSS, BitCount, BitmapBits, Rect(0, 0, Width, Height),
      riloBottomToTop, riloBottomToTop, rileDWordBoundary, NewBits, NewBitsSize) then
    begin
      // this was never tested
      ARowStride := RSS;
      NewBits := AllocMem(RSS * Height);
      Move(BitmapBits^, NewBits^, RSS * Height);
    end;
    Result := HBitmap(TGtk3Image.Create(NewBits, Width, Height, ARowStride, Format, True));
  end
  else
    Result := HBitmap(TGtk3Image.Create(nil, Width, Height, Format));

  {$IFDEF VerboseGtk3WinAPI}
  DebugLn('Trace:< [Gtk3WinAPI CreateBitmap] Bitmap:', dbghex(Result));
  {$ENDIF}
end;

function TGtk3WidgetSet.CreateBrushIndirect(const LogBrush: TLogBrush): HBRUSH;
var
  ABrush: TGtk3Brush;
begin
  Result := 0;
  // DebugLn('TGtk3WidgetSet.CreateBrushIndirect color=',dbgs(logBrush.lbColor),' style=',dbgs(logBrush.lbStyle));
  ABrush := TGtk3Brush.Create;
  try
    // todo: hatch
    ABrush.Style := LogBrush.lbStyle;
    ABrush.Color := ColorToRGB(TColor(logBrush.lbColor));
    ABrush.LogBrush := LogBrush;
    // ABrush.LogBrush.lbColor := ABrush.Color;
    Result := HBRUSH(ABrush);
  except
    Result := 0;
    DebugLn('TGtk3WidgetSet.CreateBrushIndirect: Failed');
  end;

  {$IFDEF VerboseGtk3DeviceContext}
  DebugLn('Trace:< [Gtk3WinAPI CreateBrushIndirect] Result: ', dbghex(Result));
  {$ENDIF}
end;

function TGtk3WidgetSet.CreateCaret(Handle: HWND; Bitmap: hBitmap; width,
  Height: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.CreateCaret not implemented ...');
  {$ENDIF}
  Result := inherited CreateCaret(Handle, Bitmap, width, Height);
end;

function TGtk3WidgetSet.CreateCompatibleBitmap(DC: HDC; Width, Height: Integer
  ): HBITMAP;
var
  Gtk3DC: TGtk3DeviceContext;
  Format: Tcairo_format_t = CAIRO_FORMAT_ARGB32;
  ADepth: Integer;
  AVisual: PGdkVisual;
  ABpp: gint;
  ARowStride: PtrUInt;
begin
  {$IFDEF VerboseGtk3WinAPI}
  DebugLn('Trace:> [WinAPI CreateCompatibleBitmap]',
     ' DC:', dbghex(DC),
     ' Width:', dbgs(Width),
     ' Height:', dbgs(Height));
  {$ENDIF}
  Result := 0;
  if IsValidDC(DC) then
  begin
    Gtk3DC := TGtk3DeviceContext(DC);
    ADepth := Gtk3DC.getDepth;
    ABpp := Gtk3DC.getBpp;
  end else
  begin
    AVisual := gdk_window_get_visual(gdk_get_default_root_window);
    ADepth := gdk_visual_get_depth(AVisual);
    ABpp := AVisual^.get_bits_per_rgb;
    g_object_unref(AVisual);
  end;
  case ADepth of
    1: Format := CAIRO_FORMAT_A1;
    2: Format := CAIRO_FORMAT_A8;
    24: Format := CAIRO_FORMAT_RGB24;
    else
      Format := CAIRO_FORMAT_ARGB32;
  end;
  ARowStride := GetBytesPerLine(Width, ABpp, rileDWordBoundary);
  Result := HBitmap(TGtk3Image.Create(nil, Width, Height, ARowStride, Format));
  {$IFDEF VerboseGtk3WinAPI}
  DebugLn('Trace:< [Gtk3WinAPI CreateCompatibleBitmap] Bitmap:', dbghex(Result));
  {$ENDIF}
end;

function TGtk3WidgetSet.CreateCompatibleDC(DC: HDC): HDC;
begin
  Result := HDC(TGtk3DeviceContext.Create(PGtkWidget(nil), False));
end;

function TGtk3WidgetSet.CreateEllipticRgn(X1, Y1, X2, Y2: Integer): HRGN;
begin
  Result:=HRGN(TGtk3Region.CreateEllipse(X1,Y1,X2,Y2));
end;

function TGtk3WidgetSet.CreateRoundRectRgn(X1, Y1, X2, Y2, nW, nH: Integer): HRGN;
begin
  Result:=HRGN(TGtk3Region.Create(X1,Y1,X2,Y2,nW,nH));
end;


function TGtk3WidgetSet.CreateFontIndirect(const LogFont: TLogFont): HFONT;
begin
  Result := CreateFontIndirectEx(LogFont, '');
end;

function TGtk3WidgetSet.CreateFontIndirectEx(const LogFont: TLogFont;
  const LongFontName: string): HFONT;
begin
  Result := HFONT(TGtk3Font.Create(LogFont, LongFontName));
end;

function TGtk3WidgetSet.CreateIconIndirect(IconInfo: PIconInfo): HICON;
begin
  Result := 0;
  if IsValidGDIObject(IconInfo^.hbmColor) then
  begin
    if IconInfo^.fIcon then
    begin
      Result := HICON(TGtk3Image.Create(TGtk3Image(IconInfo^.hbmColor).Handle));
    end else
    begin
      Result := HCURSOR({%H-}PtrUInt(gdk_cursor_new_from_pixbuf(gdk_display_get_default,
        TGtk3Image(IconInfo^.hbmColor).Handle, IconInfo^.xHotSpot, IconInfo^.yHotSpot)));
      // create cursor from pixbuf
     { W := gdk_pixbuf_get_width(TGtk3Image(IconInfo^.hbmColor).Handle);
      H := gdk_pixbuf_get_height(TGtk3Image(IconInfo^.hbmColor).Handle);
      DebugLn('TGtk3WidgetSet.CreateIconIndirect W=',dbgs(W),' H=',dbgs(H));
      PixBuf := gdk_pixbuf_new_subpixbuf(TGtk3Image(IconInfo^.hbmColor).Handle, 0, 0, W, H);
      Result := HCURSOR({%H-}PtrUInt(gdk_cursor_new_from_pixbuf(gdk_display_get_default,
        pixbuf, IconInfo^.xHotSpot, IconInfo^.yHotSpot)));
      if pixbuf <> nil then
        g_object_unref(PixBuf); }
    end;
  end;
end;

function TGtk3WidgetSet.CreatePalette(const LogPalette: TLogPalette): HPALETTE;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.CreatePalette not implemented ...');
  {$ENDIF}
  Result := 0;
end;

function TGtk3WidgetSet.CreatePenIndirect(const LogPen: TLogPen): HPEN;
var
  APen: TGtk3Pen;
begin
  Result := 0;
  APen := TGtk3Pen.Create;
  with LogPen do
  begin
    case lopnStyle and PS_STYLE_MASK of
      PS_SOLID: APen.Style := psSolid;
      PS_DASH: APen.Style := psDash;
      PS_DOT: APen.Style := psDot;
      PS_DASHDOT: APen.Style := psDashDot;
      PS_DASHDOTDOT: APen.Style := psDashDotDot;
      PS_NULL: APen.Style := psClear;
    else
      APen.Style := psSolid;
    end;
    APen.Color := ColorToRgb(TColor(lopnColor));
    APen.Cosmetic := lopnWidth.X <= 0 ;
    if not APen.Cosmetic then
      APen.Width := lopnWidth.X;
  end;
  APen.LogPen := LogPen;

  Result := HPEN(APen);
end;

function TGtk3WidgetSet.CreatePolygonRgn(Points: PPoint; NumPts: Integer;
  FillMode: integer): HRGN;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.CreatePolygonRgn not implemented ...');
  {$ENDIF}
  Result:=inherited CreatePolygonRgn(Points, NumPts, FillMode);
end;

function TGtk3WidgetSet.CreateRectRgn(X1, Y1, X2, Y2: Integer): HRGN;
begin
  Result := HRGN(TGtk3Region.Create(True, X1, Y1, X2, Y2));
end;

procedure TGtk3WidgetSet.DeleteCriticalSection(var CritSection: TCriticalSection
  );
var
  ACritSec: System.PRTLCriticalSection;
begin
  ACritSec:=System.PRTLCriticalSection(CritSection);
  System.DoneCriticalsection(ACritSec^);
  Dispose(ACritSec);
  CritSection:=0;
end;

function TGtk3WidgetSet.DeleteDC(hDC: HDC): Boolean;
begin
  {$ifdef VerboseGtk3DeviceContext}
  DebugLn('TGtk3WidgetSet.DeleteDC Handle: ', dbghex(hDC));
  {$endif}

  if not IsValidDC(hDC) then
    exit(False);

  TGtk3DeviceContext(hDC).Free;
  Result := True;
end;

function TGtk3WidgetSet.DeleteObject(GDIObject: HGDIOBJ): Boolean;
begin
  Result := False;

  if GDIObject = 0 then
    Exit(True);

  if not IsValidGDIObject(GDIObject) then
    Exit;
  {$ifdef VerboseGtk3DeviceContext}
  DebugLn('TGtk3WidgetSet.DeleteObject GDIObject: ', dbghex(GdiObject),' name ',dbgsName(TObject(GdiObject)));
  {$endif}
  if TObject(GDIObject) is TGtk3ContextObject then
  begin
    if TGtk3ContextObject(GDIOBJECT).Shared then
      // DebugLn('ERROR: TGtk3WidgetSet.DeleteObject trial to delete shared object ',dbgsName(TGtk3ContextObject(GdiObject)))
    else
      TGtk3ContextObject(GDIObject).Free;
  end else
    TObject(GDIObject).Free;
end;

function TGtk3WidgetSet.DestroyCaret(Handle: HWND): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.DestroyCaret not implemented ...');
  {$ENDIF}
  Result:=inherited DestroyCaret(Handle);
end;

function TGtk3WidgetSet.DestroyCursor(Handle: HCURSOR): Boolean;
begin
  Result := Handle <> 0;
  if Result then
    g_object_unref(PGdkCursor(Handle));
   // gdk_cursor_destroy({%H-}PGdkCursor(Handle));
end;

function TGtk3WidgetSet.DestroyIcon(Handle: HICON): Boolean;
begin
  Result := False;
  if IsValidGDIObject(Handle) then
  begin
    TGtk3Image(Handle).Free;
    Result := True;
  end;
end;

function TGtk3WidgetSet.DPtoLP(DC: HDC; var Points; Count: Integer): BOOL;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.DPToLP not implemented ...');
  {$ENDIF}
  Result:=inherited DPtoLP(DC, Points, Count);
end;

function TGtk3WidgetSet.DrawFrameControl(DC: HDC; const aRect: TRect; uType,
  uState: Cardinal): Boolean;
begin
  Result:=false;
  if IsValidDC(DC) then
    Result:=TGtk3DeviceContext(DC).drawFrameControl(aRect,uType,uState);
end;

function TGtk3WidgetSet.DrawFocusRect(DC: HDC; const aRect: TRect): boolean;
begin
  Result := False;
  if IsValidDC(DC) then
    Result:=TGtk3DeviceContext(DC).drawFocusRect(aRect);
end;

function TGtk3WidgetSet.DrawEdge(DC: HDC; var ARect: TRect; Edge: Cardinal;
  grfFlags: Cardinal): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.DrawEdge not implemented ...');
  {$ENDIF}
  Result := False; // inherited DrawEdge(DC, ARect, Edge, grfFlags);
end;

function TGtk3WidgetSet.DrawText(DC: HDC; Str: PChar; Count: Integer;
  var Rect: TRect; Flags: Cardinal): Integer;
const
  TabString = '        ';
var
  pIndex: Longint;
  AStr: String;

  TM: TTextmetric;
  theRect: TRect;
  Lines: PPChar;
  I, NumLines: Longint;
  TempDC: HDC;
  TempPen: HPEN;
  TempBrush: HBRUSH;
  l: LongInt;
  Pt: TPoint;
  SavedRect: TRect; // if font orientation <> 0
  LineHeight: Integer;
  Size: TSize;

  function LeftOffset: Longint;
  begin
    if (Flags and DT_RIGHT) = DT_RIGHT then
      Result := DT_RIGHT
    else
      if (Flags and DT_CENTER) = DT_CENTER then
        Result := DT_CENTER
    else
      Result := DT_LEFT;
  end;

  function TopOffset: Longint;
  begin
    if (Flags and DT_BOTTOM) = DT_BOTTOM then
      Result := DT_BOTTOM
    else
      if (Flags and DT_VCENTER) = DT_VCENTER then
        Result := DT_VCENTER
    else
      Result := DT_TOP;
  end;

  function CalcRect: Boolean;
  begin
    Result := (Flags and DT_CALCRECT) = DT_CALCRECT;
  end;

  function TextExtentPoint(Str: PChar; Count: Integer; var Sz: TSize): Boolean;
  var
    NewStr: String;
  begin
    if (Flags and DT_EXPANDTABS) <> 0 then
    begin
      NewStr := StringReplace(Str, #9, TabString, [rfReplaceAll]);
      Result := GetTextExtentPoint(DC, PChar(NewStr), Length(NewStr), Sz);
    end
    else
      Result := GetTextExtentPoint(Dc, Str, Count, Sz);
  end;

  procedure DoCalcRect;
  var
    AP: TSize;
    J, MaxWidth,
    LineWidth, LineHeight: Integer;
  begin
    theRect := Rect;

    MaxWidth := theRect.Right - theRect.Left;
    //DefHeight := theRect.Bottom - theRect.Top;

    (*
    if Flags and DT_CENTER  <> 0then
      Alignment := DT_CENTER
    else
    if Flags and DT_RIGHT <> 0 then
      Alignment := DT_RIGHT
    else
      Alignment := DT_LEFT;

    TGtk3DeviceContext(DC).CurrentFont.Layout^.set_alignment(Alignment);
    if Flags and DT_WORDBREAK <> 0  then
      TGtk3DeviceContext(DC).CurrentFont.Layout^.set_wrap(PANGO_WRAP_WORD);

    // ADevOffset := TGtk3DeviceContext(DC).Offset;
    // TGtk3DeviceContext(DC).CurrentFont.Layout^.set_width(Rect.Right - Rect.Left);
    // TGtk3DeviceContext(DC).CurrentFont.Layout^.set_height(Rect.Bottom - Rect.Top);

    TGtk3DeviceContext(DC).CurrentFont.Layout^.set_text(Str, Count);


    // TGtk3DeviceContext(DC).CurrentFont.Layout^.get_iter^.get_line_extents(@PR1, @PR2);
    // DebugLn('DoCalcRect LINE EXTENTS Rect=',dbgs(Rect),' PR1 ',dbgs(RectFromGdkRect(TGdkRectangle(PR1))),' PR2 ',dbgs(RectFromGdkRect(TGdkRectangle(PR2))));
    TGtk3DeviceContext(DC).CurrentFont.Layout^.get_extents(@PR1, @PR2);
    // get_extents(@PR1, @PR2);

    DebugLn('DoCalcRect Rect=',dbgs(Rect),' PR1 ',dbgs(RectFromPangoRect(PR1)),' PR2 ',dbgs(RectFromPangoRect(PR2)),' ALIGNMENT ',dbgs(Alignment));

    // DebugLn('DoCalcRect Rect=',dbgs(Rect),' PR1 ',Format('x %d y %d width %d height %d'
    *)

    if (Flags and DT_SINGLELINE) > 0 then
    begin
      // ignore word and line breaks
      TextExtentPoint(PChar(AStr), length(AStr), AP{%H-});
      theRect.Bottom := theRect.Top + TM.tmHeight;
      if (Flags and DT_CALCRECT)<>0 then
      begin
        theRect.Right := theRect.Left +  AP.cX;
        theRect.Bottom := theRect.Top + AP.cY;
      end
      else
      begin
        theRect.Right := theRect.Left + Min(MaxWidth, AP.cX);
        theRect.Bottom := theRect.Top + AP.cY;
        if (Flags and DT_VCENTER) > 0 then
        begin
          OffsetRect(theRect, 0, ((Rect.Bottom - Rect.Top) - (theRect.Bottom - theRect.Top)) div 2);
        end
        else
        if (Flags and DT_BOTTOM) > 0 then
        begin
          OffsetRect(theRect, 0, (Rect.Bottom - Rect.Top) - (theRect.Bottom - theRect.Top));
        end;
      end;
    end
    else
    begin
      // consider line breaks
      if (Flags and DT_WORDBREAK) = 0 then
      begin
        // do not break at word boundaries
        TextExtentPoint(PChar(AStr), length(AStr), AP);
        MaxWidth := AP.cX;
        //DefHeight := AP.cY;
      end;

      Gtk3WordWrap(DC, PChar(AStr), MaxWidth, Lines, NumLines);
      //DebugLn(['WORD WRAP RESULTED IN ',NumLines,' lines for ',AStr,' MAX=',MaxWidth]);
      if (Flags and DT_CALCRECT)<>0 then
      begin
        LineWidth := 0;
        LineHeight := 0;
        if (Lines <> nil) then
        begin
          for J := 0 to NumLines - 1 do
          begin
            TextExtentPoint(Lines[J], StrLen(Lines[J]), AP);
            LineWidth := Max(LineWidth, AP.cX);
            Inc(LineHeight, AP.cY);
          end;
        end;
        LineWidth := Min(MaxWidth, LineWidth);
      end else
      begin
        LineWidth := MaxWidth;
        LineHeight := NumLines*TM.tmHeight;
      end;

      theRect.Right := theRect.Left + LineWidth;
      theRect.Bottom := theRect.Top + LineHeight;
      if NumLines>1 then
        Inc(theRect.Bottom, (NumLines-1)*TM.tmExternalLeading);// space between lines

      //DebugLn('TGtk3WidgetSet.DrawText A ',dbgs(theRect),' TM.tmHeight=',dbgs(TM.tmHeight),
      //        ' LineWidth=',dbgs(LineWidth),' NumLines=',dbgs(NumLines));
    end;

    if not CalcRect then
      case LeftOffset of
        DT_CENTER:
          OffsetRect(theRect, (Rect.Right - theRect.Right) div 2, 0);
        DT_RIGHT:
          OffsetRect(theRect, Rect.Right - theRect.Right, 0);
      end;
  end;

  // if our Font.Orientation <> 0 we must recalculate X,Y offset
  // also it works only with DT_TOP DT_LEFT. Gtk2 can handle multiline
  // text in this case too.
  procedure CalculateOffsetWithAngle(const AFontAngle: Integer;
    var TextLeft,TextTop: Integer);
  var
    OffsX, OffsY: integer;
    Angle: Double;
    Size: TSize;
    R: TRect;
  begin
    R := SavedRect;
    OffsX := R.Right - R.Left;
    OffsY := R.Bottom - R.Top;
    Size.cx := OffsX;
    Size.cy := OffsY;
    Angle := AFontAngle / 10;
    if Angle < 0 then
      Angle := 360 + Angle;

    if Angle <= 90 then
    begin
      OffsX := 0;
      OffsY := Trunc(Size.cx * sin(Angle * Pi / 180));
    end else
    if Angle <= 180 then
    begin
      OffsX := Trunc(Size.cx * -cos(Angle * Pi / 180));
      OffsY := Trunc(Size.cx * sin(Angle * Pi / 180) +
         Size.cy * cos((180 - Angle) * Pi / 180));
    end else
    if Angle <= 270 then
    begin
      OffsX := Trunc(Size.cx * -cos(Angle * Pi / 180) +
        Size.cy * sin((Angle - 180) * Pi / 180));
      OffsY := Trunc(Size.cy * sin((270 - Angle) * Pi / 180));
    end else
    if Angle <= 360 then
    begin
      OffsX := Trunc(Size.cy * sin((360 - Angle) * Pi / 180));
      OffsY := 0;
    end;
    TextTop := OffsY;
    TextLeft := OffsX;
  end;

  function NeedOffsetCalc: Boolean;
  begin
    Result := (TGtk3DeviceContext(DC).CurrentFont.LogFont.lfOrientation <> 0) and
        (Flags and DT_SINGLELINE <> 0) and
        (Flags and DT_VCENTER = 0) and (Flags and DT_CENTER = 0) and
        (Flags and DT_RIGHT = 0) and (Flags and  DT_BOTTOM = 0) and
        (Flags and DT_CALCRECT = 0) and not IsRectEmpty(SavedRect);
  end;


  procedure DrawLineRaw(theLine: PChar; LineLength, TopPos: Longint);
  var
    Points: array[0..1] of TSize;
    LeftPos: Longint;
  begin
    if LeftOffset <> DT_LEFT then
      GetTextExtentPoint(DC, theLine, LineLength, {%H-}Points[0]);

    if TempBrush = HBRUSH(-1) then
      TempBrush := SelectObject(DC, GetStockObject(NULL_BRUSH));
    case LeftOffset of
      DT_LEFT:
        LeftPos := theRect.Left;
      DT_CENTER:
        LeftPos := theRect.Left + (theRect.Right - theRect.Left) div 2
                 - Points[0].cX div 2;
      DT_RIGHT:
        LeftPos := theRect.Right - Points[0].cX;
    end;

    Pt := Point(0, 0);
    // Draw line of Text
    if NeedOffsetCalc then
    begin
      Pt.X := SavedRect.Left;
      Pt.Y := SavedRect.Top;
      CalculateOffsetWithAngle(TGtk3DeviceContext(DC).CurrentFont.LogFont.lfOrientation, Pt.X, Pt.Y);
    end;
    TextUtf8Out(DC, LeftPos + Pt.X, TopPos + Pt.Y, theLine, lineLength);
  end;

  procedure DrawLine(theLine: PChar; LineLength, TopPos: Longint);
  var
    Points: array[0..1] of TSize;
    LogP: TLogPen;
    LeftPos: Longint;
  begin
    if TempBrush = HBRUSH(-1) then
      TempBrush := SelectObject(DC, GetStockObject(NULL_BRUSH));

    FillByte({%H-}Points[0],SizeOf(Points[0])*2,0);
    if LeftOffset <> DT_Left then
      GetTextExtentPoint(DC, theLine, LineLength, Points[0]);

    case LeftOffset of
      DT_LEFT:
        LeftPos := theRect.Left;
      DT_CENTER:
        LeftPos := theRect.Left + (theRect.Right - theRect.Left) div 2
                 - Points[0].cX div 2;
      DT_RIGHT:
        LeftPos := theRect.Right - Points[0].cX;
    end;

    Pt := Point(0, 0);
    if NeedOffsetCalc then
    begin
      Pt.X := SavedRect.Left;
      Pt.Y := SavedRect.Top;
      CalculateOffsetWithAngle(TGtk3DeviceContext(DC).CurrentFont.LogFont.lfOrientation, Pt.X, Pt.Y);
    end;
    // Draw line of Text
    TextUtf8Out(DC, LeftPos + Pt.X, TopPos + Pt.Y, theLine, LineLength);

    // Draw Prefix
    if (pIndex > 0) and (pIndex<=LineLength) then
    begin
      // Create & select pen of font color
      if TempPen = HPEN(-1) then
      begin
        LogP.lopnStyle := PS_SOLID;
        LogP.lopnWidth.X := 1;
        LogP.lopnColor := GetTextColor(DC);
        TempPen := SelectObject(DC, CreatePenIndirect(LogP));
      end;

      {Get prefix line position}
      GetTextExtentPoint(DC, theLine, pIndex - 1, Points[0]);
      Points[0].cX := LeftPos + Points[0].cX;
      Points[0].cY := TopPos + tm.tmHeight - TM.tmDescent + 1;

      GetTextExtentPoint(DC, @aStr[pIndex], UTF8CodepointSize(@aStr[pIndex]), Points[1]);
      Points[1].cX := Points[0].cX + Points[1].cX;
      Points[1].cY := Points[0].cY;

      {Draw prefix line}
      Polyline(DC, PPoint(@Points[0]), 2);
    end;
  end;
begin
  Result := 0;
  if (Str=nil) or (Str[0]=#0) or not IsValidDC(DC) then
  begin
    // DebugLn('TGtk3DeviceContext.DrawText params error Str Valid ? ',dbgs(Str<>nil),' DC Valid ? ',dbgs(IsValidDC(DC)),' Str#0 ',dbgs(Str[0] = #0));
    exit;
  end;

  if (Count < -1) or (IsRectEmpty(Rect) and
    ((Flags and DT_CALCRECT = 0) and (Flags and DT_NOCLIP = 0))) then Exit;

  // Don't try to use StrLen(Str) in cases count >= 0
  // In those cases str is NOT required to have a null terminator !
  if Count = -1 then
    Count := StrLen(Str);

  Lines := nil;
  NumLines := 0;
  TempDC := HDC(-1);
  TempPen := HPEN(-1);
  TempBrush := HBRUSH(-1);
  // DebugLn('TGtk3DeviceContext.DrawText ',Str,' count=',dbgs(Count),' DT_CALCRECT ',dbgs(Flags and DT_CALCRECT <> 0),' ARect=',dbgs(Rect));
  try
    if (Flags and (DT_SINGLELINE or DT_CALCRECT or DT_NOPREFIX or DT_NOCLIP or DT_EXPANDTABS)) =
       (DT_SINGLELINE or DT_NOPREFIX or DT_NOCLIP) then
    begin
      System.Move(Rect, TheRect, SizeOf(TRect));
      SavedRect := Rect;
      DrawLineRaw(Str, Count, Rect.Top);
      Result := Rect.Bottom - Rect.Top;
      Exit;
    end;

    SetLength(AStr{%H-},Count);
    if Count>0 then
      System.Move(Str^,AStr[1],Count);

    if (Flags and DT_EXPANDTABS) <> 0 then
      AStr := StringReplace(AStr, #9, TabString, [rfReplaceAll]);

    if (Flags and DT_NOPREFIX) <> DT_NOPREFIX then
      pIndex := DeleteAmpersands(AStr)
    else
      pIndex := -1;

    GetTextMetrics(DC, TM{%H-});
    DoCalcRect;
    Result := theRect.Bottom - theRect.Top;
    if (Flags and DT_CALCRECT) = DT_CALCRECT then
    begin
      // DebugLn('TGtk3WidgetSet.DrawText DT_CALCRECT Rect ',dbgs(Rect),' TheRect ',dbgs(theRect),' Result ',dbgs(Result));
      System.Move(TheRect, Rect, SizeOf(TRect));
      exit;
    end;

    TempDC := SaveDC(DC);

    if (Flags and DT_NOCLIP) <> DT_NOCLIP then
    begin
      if theRect.Right > Rect.Right then
        theRect.Right := Rect.Right;
      if theRect.Bottom > Rect.Bottom then
        theRect.Bottom := Rect.Bottom;
      // DebugLn('******* CALLING NOT IMPLEMENTED INTERSECTCLIP RECT ');
      IntersectClipRect(DC, theRect.Left, theRect.Top,
        theRect.Right, theRect.Bottom);
    end;

    if (Flags and DT_SINGLELINE) = DT_SINGLELINE
    then begin
      // DebugLn(['TGtk2WidgetSet.DrawText Draw single line']);
      SavedRect := TheRect;
      DrawLine(PChar(AStr), length(AStr), theRect.Top);
      Exit; //we're ready
    end;

    // multiple lines
    if Lines = nil then Exit;  // nothing to do
    if NumLines = 0 then Exit; //


    //DebugLn(['TGtk2WidgetSet.DrawText Draw multiline']);
    SavedRect := Classes.Rect(0, 0, 0, 0); // no font orientation change if multilined text
    for i := 0 to NumLines - 1 do
    begin
      if theRect.Top > theRect.Bottom then Break;

      if  ((Flags and DT_EDITCONTROL) = DT_EDITCONTROL)
      and (tm.tmHeight > (theRect.Bottom - theRect.Top+1))
      then Break;

      if Lines[i] <> nil then
      begin
        l:=StrLen(Lines[i]);
        DrawLine(Lines[i], l, theRect.Top);
        dec(pIndex,l+length(LineEnding));
        Size:=default(TSize);
        GetTextExtentPoint(DC, Lines[i], l, Size);
        LineHeight := Size.cY;
      end
      else
        LineHeight := TM.tmHeight;
      Inc(theRect.Top, LineHeight + TM.tmExternalLeading); // space between lines
    end;

  finally
    Reallocmem(Lines, 0);
    if TempBrush <> HBRUSH(-1) then
      SelectObject(DC, TempBrush);// DeleteObject not needed here, because it was a default Brush
    if TempPen <> HPEN(-1) then
      DeleteObject(SelectObject(DC, TempPen));
    if TempDC <> HDC(-1) then
      RestoreDC(DC, TempDC);
  end;
end;

function TGtk3WidgetSet.Ellipse(DC: HDC; x1, y1, x2, y2: Integer): Boolean;
var
  bFill, bBorder: Boolean;
begin
  bFill := TGtk3DeviceContext(DC).CurrentBrush.Style <> BS_NULL;
  bBorder := TGtk3DeviceContext(DC).CurrentPen.Style <> psClear;
  TGtk3DeviceContext(DC).drawEllipse(x1, y1, x2 - x1 - 1, y2 - y1 - 1, bFill, bBorder);
  Result := True;
end;

function TGtk3WidgetSet.EnableScrollBar(Wnd: HWND; wSBflags, wArrows: Cardinal
  ): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.EnableScrollBar not implemented ...');
  {$ENDIF}
  Result := inherited EnableScrollBar(Wnd, wSBflags, wArrows);
end;

function TGtk3WidgetSet.EnableWindow(hWnd: HWND; bEnable: Boolean): Boolean;
begin
  Result := False;
  if hWnd <> 0 then
  begin
    Result := TGtk3Widget(HWND).Enabled;
    TGtk3Widget(HWND).Enabled := bEnable;
  end;
end;

function TGtk3WidgetSet.EndPaint(Handle: hwnd; var PS: TPaintStruct): Integer;
begin
  Result := 0;
  if IsValidDC(PS.HDC) then
  begin
    TGtk3DeviceContext(PS.HDC).Free;
    PS.HDC := 0;
    Result := 1;
  end;
end;

procedure TGtk3WidgetSet.EnterCriticalSection(var CritSection: TCriticalSection
  );
var
  ACritSec: System.PRTLCriticalSection;
begin
  ACritSec:=System.PRTLCriticalSection(CritSection);
  System.EnterCriticalsection(ACritSec^);
end;

function TGtk3WidgetSet.EnumDisplayMonitors(hdc: HDC; lprcClip: PRect;
  lpfnEnum: MonitorEnumProc; dwData: LPARAM): LongBool;
var
  i: integer;
begin
  Result := True;
  for i := 0 to gdk_screen_get_n_monitors(gdk_screen_get_default) - 1 do
  begin
    Result := Result and lpfnEnum(i + 1, 0, nil, dwData);
    if not Result then break;
  end;
end;

function TGtk3WidgetSet.EnumFontFamiliesEx(DC: HDC; lpLogFont: PLogFont;
  Callback: FontEnumExProc; Lparam: LParam; Flags: dword): longint;
type
  TPangoFontFaces = packed record
    FamilyName: String;
    Faces: Array of String;
  end;
  PPangoFontFaces = Array of TPangoFontFaces;

var
  i: Integer;
  FontType: Integer;
  EnumLogFont: TEnumLogFontEx;
  Metric: TNewTextMetricEx;
  FontList: TStringList;
  Faces: PPangoFontFaces;

  AStyle: String;
  StylesCount: Integer;
  StylesList: TStringList;
  y: Integer;
  CharsetList: TByteList;
  CS: Byte;

  function Gtk3GetFontFamiliesDefault(var AList: TStringList): Integer;
  var
    i, j: Integer;
    AFamilies: PPPangoFontFamily;
    AFaces: PPPangoFontFace;
    ANumFaces: Integer;
  begin
    AList.Clear;
    SetLength(Faces, 0);
    Result := -1;
    AFamilies := nil;

    pango_context_list_families(gdk_pango_context_get, @AFamilies, @Result);
    SetLength(Faces, Result);
    for i := 0 to Result - 1 do
    begin
      j := AList.Add(StrPas(pango_font_family_get_name(AFamilies[i])));
      AList.Objects[j] := TObject(PtrUInt(pango_font_family_is_monospace(AFamilies[i])));
      Faces[i].FamilyName := AList[j];
      AFaces := nil;
      pango_font_family_list_faces(AFamilies[i], @AFaces, @ANumFaces);
      SetLength(Faces[i].Faces, ANumFaces);
      for j := 0 to ANumFaces - 1 do
        Faces[i].Faces[j] := StrPas(pango_font_face_get_face_name(AFaces[j]));
      g_free(AFaces);
    end;
    g_free(AFamilies);
  end;

  function Gtk3GetFontFamilies(var List: TStringList;
    const APitch: Byte;
    const AFamilyName: String;
    const {%H-}AWritingSystem: Byte): Integer;
  var
    StrLst: TStringList;
    NewList: TStringList;
    S: String;
    j: integer;
  begin
    Result := -1;
    StrLst := TStringList.Create;
    NewList := TStringList.Create;

    try
      Gtk3GetFontFamiliesDefault(StrLst);
      for j := 0 to StrLst.Count - 1 do
      begin
        S := StrLst[j];
        if APitch <> DEFAULT_PITCH then
        begin
          case APitch of
            FIXED_PITCH, MONO_FONT:
            begin
              if StrLst.Objects[j] <> nil then
                NewList.Add(S);
            end;
            VARIABLE_PITCH:
            begin
              if StrLst.Objects[j] = nil then
                NewList.Add(S);
            end;
          end;
        end else
          NewList.Add(S);
      end;

      if AFamilyName <> '' then
      begin
        for j := NewList.Count - 1 downto 0 do
        begin
          S := NewList[j];;
          if S <> AFamilyName then
            NewList.Delete(J);
        end;
      end;
      for j := 0 to NewList.Count - 1 do
      begin
        S := NewList[j];
        List.Add(S);
      end;
      Result := List.Count;
    finally
      StrLst.Free;
      NewList.Free;
    end;
  end;

  function GetStyleAt(AIndex: Integer): String;
  var
    S: String;
  begin
    Result := '';
    if (AIndex >= 0) and (AIndex < StylesList.Count) then
    begin
      S := StylesList[AIndex];
      Result := S;
    end;
  end;

  function FillLogFontA(const AIndex: Integer; var ALogFontA: TLogFontA;
    var {%H-}AMetric: TNewTextMetricEx; var {%H-}AFontType: Integer;
    out AStyle: String): Integer;
  var
    Font: PPangoFontDescription;
    FontStyle: TPangoStyle;
    FontWeight: TPangoWeight;
    S: String;
    i: Integer;
  begin
    S := FontList[AIndex];
    Font := pango_font_description_from_string(PChar(S));

    FontStyle := pango_font_description_get_style(Font);
    FontWeight := pango_font_description_get_weight(Font);

    ALogFontA.lfItalic := Byte(FontStyle = PANGO_STYLE_ITALIC);

    // keep newer pango compat to LCL
    if FontWeight = PANGO_WEIGHT_BOOK {380 as of pango 1.24} then
      FontWeight := PANGO_WEIGHT_NORMAL
    else
    if FontWeight = PANGO_WEIGHT_ULTRAHEAVY {1000 as of pango 1.24} then
      FontWeight := PANGO_WEIGHT_HEAVY;

    ALogFontA.lfWeight := Integer(FontWeight);

    ALogFontA.lfHeight := pango_font_description_get_size(Font);
    if not pango_font_description_get_size_is_absolute(Font) then
      ALogFontA.lfHeight := PANGO_PIXELS(ALogFontA.lfHeight);

    // pango does not have underline and strikeout params for font
    // ALogFontA.lfUnderline := ;
    // ALogFontA.lfStrikeOut := ;

    StylesList.Clear;
    for i := High(Faces[AIndex].Faces) downto 0 do
      StylesList.Add(Faces[AIndex].Faces[i]);

    AStyle := '';
    Result := StylesList.Count;

    if StylesList.Count > 0  then
      AStyle := GetStyleAt(0);

    // current pango support in fpc is really poor, we cannot
    // get PangoScript since it's in pango >= 1.4
    // FillCharsetListForFont()
  end;

begin
  Result := 0;
  {$ifdef VerboseEnumFonts}
  DebugLn(['[TGtk3WidgetSet.EnumFontFamiliesEx] Charset=',lpLogFont^.lfCharSet,
  ' face ',lpLogFont^.lfFaceName,' pitchAndFamily=',lpLogFont^.lfPitchAndFamily]);
  {$endif}
  Result := 0;
  Metric.ntmentm.ntmAvgWidth := 0; // just to shutup compiler
  if (lpLogFont^.lfCharSet = DEFAULT_CHARSET) and
     (lpLogFont^.lfFaceName= '') and
     (lpLogFont^.lfPitchAndFamily = 0) then
  begin
    FontType := 0;
    FontList := TStringList.create;
    try
      if Gtk3GetFontFamiliesDefault(FontList) > 0 then
      begin
        for i := 0 to FontList.Count - 1 do
        begin
          EnumLogFont.elfLogFont.lfFaceName := FontList[i];
          Result := Callback(EnumLogFont, Metric, FontType, LParam);
        end;
      end;
    finally
      FontList.free;
    end;
  end else
  begin
    Result := 0;
    FontType := TRUETYPE_FONTTYPE;
    FontList := TStringList.Create;
    StylesList := TStringList.Create;
    CharsetList := TByteList.Create;
    for i := 0 to CharsetEncodingList.Count - 1 do
    begin
      CS := TCharSetEncodingRec(CharsetEncodingList.Items[i]^).CharSet;
      if CharsetList.IndexOf(CS) = -1 then
        CharsetList.Add(CS);
    end;
    try
      if Gtk3GetFontFamilies(FontList, lpLogFont^.lfPitchAndFamily,
        lpLogFont^.lfFaceName, lpLogFont^.lfCharSet) > 0 then
      begin
        for i := 0 to FontList.Count - 1 do
        begin
          EnumLogFont.elfLogFont.lfFaceName := FontList[i];
          EnumLogFont.elfLogFont.lfPitchAndFamily := lpLogFont^.lfPitchAndFamily;
          EnumLogFont.elfFullName := FontList[i];

          StylesCount := FillLogFontA(i, EnumLogFont.elfLogFont, Metric, FontType, AStyle);
          EnumLogFont.elfStyle := AStyle;

          if CharSetList.Count > 0 then
            EnumLogFont.elfLogFont.lfCharSet := CharsetList.Items[0];

          Result := Callback(EnumLogFont, Metric, FontType, LParam);
          for y := 1 to StylesCount - 1 do
          begin
            AStyle := GetStyleAt(y);
            EnumLogFont.elfStyle := AStyle;
            Result := Callback(EnumLogFont, Metric, FontType, LParam);
          end;
          for y := 1 to CharSetList.Count - 1 do
          begin
            EnumLogFont.elfLogFont.lfCharSet := CharsetList.Items[y];
            Result := Callback(EnumLogFont, Metric, FontType, LParam);
          end;
        end;
      end;
    finally
      CharSetList.Free;
      StylesList.Free;
      FontList.Free;
    end;
  end;
end;

function TGtk3WidgetSet.EqualRgn(Rgn1: HRGN; Rgn2: HRGN): Boolean;
begin
  Result := Rgn1 = Rgn2;
  if Result then
    exit;
  if not IsValidGDIObject(Rgn1) or not IsValidGDIObject(Rgn2) then
    exit;
  Result := cairo_region_equal(TGtk3Region(Rgn1).Handle,TGtk3Region(Rgn2).Handle);
end;

function TGtk3WidgetSet.IntersectClipRect(dc: hdc; Left, Top, Right,Bottom: Integer): Integer;
var
  rgn,clip:HRGN;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.ExcludeClipRect not implemented ...');
  {$ENDIF}
  rgn:=Self.CreateRectRgn(Left,Top,Right,Bottom);
  //Self.SelectClipRGN(dc,rgn);
  clip:=Self.CreateEmptyRegion;
  Self.GetClipRGN(dc,clip);
  Self.CombineRgn(clip,rgn,clip,RGN_AND);
  Self.SelectClipRGN(dc,clip);
  DeleteObject(clip);
  DeleteObject(rgn);
  // fail Self.ExtSelectClipRGN(dc,rgn,RGN_AND);
 { ncorg:=Tgtk3DeviceContext(dc).fncOrigin;
  GetWindowOrgEx(DC, @DCOrigin);
  Result:=inherited IntersectClipRect(dc, Left, Top, Right, Bottom);}
  Result:=0;
end;


function TGtk3WidgetSet.ExcludeClipRect(dc: hdc; Left, Top, Right,
  Bottom: Integer): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.ExcludeClipRect not implemented ...');
  {$ENDIF}
  Result:=inherited ExcludeClipRect(dc, Left, Top, Right, Bottom);
end;

function TGtk3WidgetSet.ExtCreatePen(dwPenStyle, dwWidth: DWord;
  const lplb: TLogBrush; dwStyleCount: DWord; lpStyle: PDWord): HPEN;
var
  APen: TGtk3Pen;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.ExtCreatePen not implemented ...');
  {$ENDIF}
  APen := TGtk3Pen.Create;
  APen.IsExtPen := True;
  case dwPenStyle and PS_STYLE_MASK of
    PS_SOLID: APen.Style := psSolid;
    PS_DASH: APen.Style := psDash;
    PS_DOT: APen.Style := psDot;
    PS_DASHDOT: APen.Style := psDashDot;
    PS_DASHDOTDOT: APen.Style := psDashDotDot;
    PS_NULL: APen.Style := psClear;
  else
    APen.Style := psSolid;
  end;

  APen.Cosmetic := (dwPenStyle and PS_TYPE_MASK) = PS_COSMETIC;

  if (dwPenStyle and PS_TYPE_MASK) = PS_GEOMETRIC then
  begin
    APen.Width := dwWidth;
    case dwPenStyle and PS_JOIN_MASK of
      PS_JOIN_ROUND: APen.JoinStyle := pjsRound;
      PS_JOIN_BEVEL: APen.JoinStyle := pjsBevel;
      PS_JOIN_MITER: APen.JoinStyle := pjsMiter;
    end;
    case dwPenStyle and PS_ENDCAP_MASK of
      PS_ENDCAP_ROUND: APen.EndCap := pecRound;
      PS_ENDCAP_SQUARE: APen.EndCap := pecSquare;
      PS_ENDCAP_FLAT: APen.EndCap := pecFlat;
    end;
  end;

  if (dwPenStyle and PS_STYLE_MASK) = PS_USERSTYLE then
  begin
    //TODO: APen.setDashPattern
  end;

  APen.Color := TColor(lplb.lbColor);  // ColorToRGB() ?
  APen.LogPen.lopnColor := lplb.lbColor;
  APen.LogPen.lopnStyle := (dwPenStyle and PS_STYLE_MASK) or (dwPenStyle and PS_JOIN_MASK) or (dwPenStyle and PS_ENDCAP_MASK);
  APen.LogPen.lopnWidth.X := dwWidth;
  APen.LogPen.lopnWidth.Y := dwWidth;

  Result := HPen(APen);
end;

function TGtk3WidgetSet.ExtSelectClipRGN(dc: hdc; rgn: hrgn; Mode: Longint): Integer;
var
  GtkDC: TGtk3DeviceContext absolute DC;
  ARect: TGdkRectangle;
  DCOrigin: TPoint;
  R: Classes.TRect;
  Clip: HRGN;
  Tmp: HRGN;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.ExtSelectClipRGN not implemented ...');
  {$ENDIF}
  if not IsValidDC(DC) then
  begin
    Result := ERROR;
    exit;
  end else
    Result := SIMPLEREGION;
  // DebugLn('WARNING: TGtk3WidgetSet.ExtSelectClipRGN not implemented ...Mode=',dbgs(Mode));
  case Mode of
    RGN_COPY: Result := SelectClipRGN(DC, RGN);
      RGN_OR,
      RGN_XOR,
      RGN_AND:
        begin
          // as MSDN says only RGN_COPY allows NULL RGN param.
          if not IsValidGDIObject(RGN) then
          begin
            Result := ERROR;
            exit;
          end;
          // get existing clip
          gdk_cairo_get_clip_rectangle(GtkDC.pcr, @ARect);
          R := RectFromGdkRect(ARect);
          if IsRectEmpty(R) then
          begin
            // no clip, just select RGN
            Result := SelectClipRGN(DC, RGN);
            exit;
          end;

          // get transformation
          GetWindowOrgEx(DC, @DCOrigin);
          // DebugLn('ExtSelectClipRgn DCOrigin=',dbgs(DCOrigin),' R=',dbgs(R));
          // OffsetRect(R, -DCOrigin.X, -DCOrigin.Y);
          // DebugLn('ExtSelectClipRgn after DCOrigin=',dbgs(DCOrigin),' R=',dbgs(R));
          Clip := CreateRectRGN(0, 0, R.Right - R.Left, R.Bottom - R.Top);

          cairo_region_translate(TGtk3Region(Clip).Handle, -DCOrigin.X, -DCOrigin.Y);

          // create target clip
          Tmp := CreateEmptyRegion;
          // CreateEmptyRegion;
          // combine
          Result := CombineRGN(Tmp, Clip, RGN, Mode);
          // commit
          SelectClipRGN(DC, Tmp);
          // clean up
          DeleteObject(Clip);
          DeleteObject(Tmp);
        end;
      RGN_DIFF:
      begin
        //DebugLn('WARNING: TGtk3DeviceContext.ExtSelectClipRgn RGN_DIFF not implemented .');
        //exit;
        // when substracting we must have active clipregion
        // with all of its rects.
        gdk_cairo_get_clip_rectangle(GtkDC.pcr, @ARect);
        R := RectFromGdkRect(ARect);
        if IsRectEmpty(R) then
        begin
          // no clip, just select RGN
          Result := SelectClipRGN(DC, RGN);
          exit;
        end;

        Clip := CreateRectRGN(R.Left, R.Top, R.Right, R.Bottom);

        Tmp := CreateEmptyRegion;
        Result := CombineRGN(Tmp, HRGN(Clip), RGN, MODE);

        // X11 paintEngine comment only !
        // we'll NOT reset num of rects here (performance problem) like we do
        // in ExcludeClipRect, because this function must be correct,
        // if someone want accurate ExcludeClipRect with X11 then
        // use code from intfbasewinapi.inc TWidgetSet.ExcludeClipRect()
        // which calls this function and then combineRgn.
        SelectClipRGN(DC, Tmp);
        DeleteObject(Clip);
        DeleteObject(Tmp);
      end;
  end;
end;

function TGtk3WidgetSet.ExtTextOut(DC: HDC; X, Y: Integer; Options: Longint;
  Rect: PRect; Str: PChar; Count: Longint; Dx: PInteger): Boolean;
var
  GtkDC: TGtk3DeviceContext absolute DC;
  AHWND: HWND;
  AOffset: TPoint;
  AScrollinfo: TScrollInfo;
var
  DevCtx: TGtk3DeviceContext absolute DC;

  LineStart, LineEnd, StrEnd: PChar;
  Width, Height: Integer;
  TopY, LineLen, LineHeight, SavedDC: Integer;
  TxtPt: TPoint;
  DCOrigin: TPoint;
  Foreground, BackgroundColor: PGDKColor;
  CurDx: PInteger;
  CurStr: PChar;
  R: TRect;

  function FindLineLen(p: PChar; Max: integer): integer;
  begin
    Result:=0;
    while Result<Max do begin
      if p[Result] in [#10,#13] then exit;
      inc(Result);
    end;
    Result:=-1;
  end;

  procedure DoTextOut(X,Y : Integer; Str: Pchar; CurCount: Integer);
  var
    CurScreenX: LongInt;
    CharLen: LongInt;
  begin
    if (Dx <> nil) then
    begin
      CurScreenX := X;
      while CurCount > 0 do
      begin
        CharLen := UTF8CodepointSize(CurStr);
        DevCtx.drawText(CurScreenX, Y, CurStr, CharLen);
        inc(CurScreenX, CurDx^);
        inc(CurDx);
        inc(CurStr, CharLen);
        dec(CurCount, CharLen);
      end;
    end
    else begin
      DevCtx.drawText(X, Y, Str, Count);
    end;
  end;

begin
  Result := False;
  {$IFDEF VerboseGtk3DeviceContext}
  DebugLn(Format('TGtk3WidgetSet.ExtTextOut x=%d y=%d Text=%s count=%d ',[X, Y, String(Str), Count]));
  {$ENDIF}

  if IsValidDC(DC)then
  begin
    cairo_save(GtkDc.pcr);
    if Options and ETO_CLIPPED = ETO_CLIPPED then
    begin
      cairo_rectangle(GtkDc.pcr, Rect^.Left, Rect^.Top, Rect^.Right - Rect^.Left, Rect^.Bottom - Rect^.Top);
      cairo_clip(GtkDc.pcr);
    end;
    if Options and ETO_OPAQUE = ETO_OPAQUE then
      DevCtx.fillRect(Rect, HBRUSH(GtkDC.CurrentBrush));

    LineLen := FindLineLen(Str,Count);
    TopY := Y;
    TxtPt.X := X;;
    LineHeight := DevCtx.CurrentPen.Width; // Cairo assumes Heigh = Width
    TxtPt.Y := TopY;;

    CurDx:=Dx;
    CurStr:=Str;
    LineStart:=Str;
    if LineLen < 0 then
    begin
      LineLen:=Count;
      if Count> 0 then
        DoTextOut(TxtPt.X, TxtPt.Y, LineStart, LineLen);
    end else
    begin  //write multiple lines
      StrEnd := Str + Count;
      while LineStart < StrEnd do
      begin
        LineEnd := LineStart + LineLen;
        if LineLen>0 then
          DoTextOut(TxtPt.X, TxtPt.Y, LineStart, LineLen);
        inc(TxtPt.Y, LineHeight);
        LineStart := LineEnd + 1; // skip #13
        if (LineStart<StrEnd) and (LineStart^ in [#10,#13])
        and (LineStart^ <> LineEnd^) then
          inc(LineStart); // skip #10
        Count := StrEnd - LineStart;
        LineLen := FindLineLen(LineStart, Count);
        if LineLen < 0 then
          LineLen := Count;
      end;
    end;
    cairo_restore(GtkDc.pcr);
    Result := True;
  end;
end;

function TGtk3WidgetSet.FillRect(DC: HDC; const Rect: TRect; Brush: HBRUSH
  ): Boolean;
begin
  Result := False;
  if IsValidDC(DC) then
  begin
    with Rect do
      TGtk3DeviceContext(DC).fillRect(Left, Top, Right - Left, Bottom - Top, Brush);
    Result := True;
  end;
end;

function TGtk3WidgetSet.FillRgn(DC: HDC; RegionHnd: HRGN; hbr: HBRUSH): Bool;
var
  R: TRect;
begin
  Result := False;
  if IsValidDC(DC) and IsValidGDIObject(RegionHnd) then
  begin
    R := TGtk3Region(RegionHnd).GetExtents;
    TGtk3DeviceContext(DC).fillRect(R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top);
    Result := True;
  end;
end;

function TGtk3WidgetSet.Frame3d(DC: HDC; var ARect: TRect;
  const FrameWidth: integer; const Style: TBevelCut): Boolean;
const
  PixelOffset = 0.5;
  NColorLight = clWindow;
  NColorDark = clBtnShadow;
var
  Color1, Color2: TGdkRGBA;
  cr: Pcairo_t;
  i: Integer;
begin
  Result := False;
  if not IsValidDC(DC) then
    Exit;
  Result := True;
  if FrameWidth <= 0 then
    Exit;

  case Style of
    bvNone:
      Exit;
    bvSpace:
      begin
        InflateRect(ARect, -FrameWidth, -FrameWidth);
        Exit;
      end;
    bvRaised:
      begin
        Color1 := TColorToTGdkRGBA(ColorToRGB(NColorLight));
        Color2 := TColorToTGdkRGBA(ColorToRGB(NColorDark));
      end;
    bvLowered:
      begin
        Color1 := TColorToTGdkRGBA(ColorToRGB(NColorDark));
        Color2 := TColorToTGdkRGBA(ColorToRGB(NColorLight));
      end;
  end;

  cr := TGtk3DeviceContext(DC).pcr;
  cairo_save(cr);
  try
    cairo_set_line_width(cr, 1);
    cairo_set_line_cap(cr, Tcairo_line_cap_t.CAIRO_LINE_CAP_ROUND);
    cairo_set_line_join(cr, Tcairo_line_join_t.CAIRO_LINE_JOIN_ROUND);

    for i := 1 to FrameWidth do
    begin
      cairo_set_source_rgb(cr, Color1.red, Color1.green, Color1.blue);
      cairo_move_to(cr, ARect.Left+PixelOffset, ARect.Top+PixelOffset);
      cairo_line_to(cr, ARect.Right+PixelOffset-1, ARect.Top+PixelOffset);
      cairo_move_to(cr, ARect.Left+PixelOffset, ARect.Top+PixelOffset);
      cairo_line_to(cr, ARect.Left+PixelOffset, ARect.Bottom+PixelOffset-1);
      cairo_stroke(cr);

      cairo_set_source_rgb(cr, Color2.red, Color2.green, Color2.blue);
      cairo_move_to(cr, ARect.Left+PixelOffset, ARect.Bottom+PixelOffset-1);
      cairo_line_to(cr, ARect.Right+PixelOffset-1, ARect.Bottom+PixelOffset-1);
      cairo_move_to(cr, ARect.Right+PixelOffset-1, ARect.Top+PixelOffset);
      cairo_line_to(cr, ARect.Right+PixelOffset-1, ARect.Bottom+PixelOffset-1);
      cairo_stroke(cr);

      InflateRect(ARect, -1, -1);
    end;
  finally
    cairo_restore(cr);
  end;
end;

function TGtk3WidgetSet.FrameRect(DC: HDC; const ARect: TRect; hBr: HBRUSH): Integer;
const
  PixelOffset = 0.5;
var
  cr: Pcairo_t;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('TGtk3WidgetSet.FrameRect ARect=',dbgs(ARect));
  {$ENDIF}
  Result := 0;
  if not IsValidDC(DC) then
    exit;
  cr := TGtk3DeviceContext(DC).pcr;
  cairo_rectangle(cr, ARect.Left+PixelOffset, ARect.Top+PixelOffset, ARect.Right-ARect.Left-1, ARect.Bottom-ARect.Top-1);
  if IsValidGDIObject(hBr) then
    TGtk3DeviceContext(DC).SetSourceColor(TGtk3Brush(HBR).Color);
  cairo_set_line_width(cr, 1);
  cairo_stroke(cr); //Don't touch
end;

function TGtk3WidgetSet.HideCaret(hWnd: HWND): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.HideCaret not implemented ...');
  {$ENDIF}
  Result:=inherited HideCaret(hWnd);
end;

function TGtk3WidgetSet.GetActiveWindow: HWND;
var
  AWindow: PGdkWindow;
  AGtkWin: PGtkWindow;
  AData, AWidget: gpointer;
  Hndl: HWND;
  i: Integer;
begin
  Result := 0;
  AWindow := gdk_screen_get_active_window(gdk_screen_get_default);
  if AWindow <> nil then
  begin
    gdk_window_get_user_data(AWindow,@AWidget);
    if AWidget <> nil then
    begin
      AData := g_object_get_data(AWidget, 'lclwidget');
      // DebugLn('TGtk3WidgetSet.GetActiveWindow found window from data ...',dbgsName(TGtk3Widget(AData).LCLObject));
      exit({%H-}HWND(AData));
    end;
    for i := 0 to Screen.FormCount - 1 do
    begin
      if Screen.Forms[i].HandleAllocated then
      begin
        Hndl := Screen.Forms[i].Handle;
        AGtkWin := PGtkWindow(TGtk3Window(Hndl).Widget);
        if AGtkWin^.get_window = AWindow then
        begin
          AGtkWin^.get_focus;
          Result := Hndl;
        end;
      end;
    end;
  end;
end;

function TGtk3WidgetSet.GetBitmapBits(Bitmap: HBITMAP; Count: Longint;
  Bits: Pointer): Longint;
begin
  Result:=inherited GetBitmapBits(Bitmap, Count, Bits);
end;

function TGtk3WidgetSet.GetBkColor(DC: HDC): TColorRef;
begin
  Result := 0;
  if IsValidDC(DC) then
    Result := TGtk3DeviceContext(DC).CurrentBrush.Color;
end;

function TGtk3WidgetSet.GetCapture: HWND;
begin
  Result := HwndFromGtkWidget(gtk_grab_get_current);
  {$IFDEF VerboseGtk3WinApi}
  DebugLn('TGtk3WidgetSet.GetCapture ',dbgHex(Result));
  {$ENDIF}
end;

function TGtk3WidgetSet.GetCaretPos(var lpPoint: TPoint): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetCaretPos not implemented ...');
  {$ENDIF}
  Result:=inherited GetCaretPos(lpPoint);
end;

function TGtk3WidgetSet.GetCaretRespondToFocus(handle: HWND;
  var ShowHideOnFocus: boolean): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetCaretPosRespondToFocus not implemented ...');
  {$ENDIF}
  Result:=inherited GetCaretRespondToFocus(handle, ShowHideOnFocus);
end;

function TGtk3WidgetSet.GetCharABCWidths(DC: HDC; p2, p3: UINT; const ABCStructs
  ): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetCharABCWidths not implemented ...');
  {$ENDIF}
  Result:=inherited GetCharABCWidths(DC, p2, p3, ABCStructs);
end;

function TGtk3WidgetSet.GetClientBounds(handle: HWND; var ARect: TRect
  ): Boolean;
begin
  {$IF DEFINED(VerboseGtk3WinAPI) OR DEFINED(GTK3DEBUGSIZE)}
  DebugLn('[Gtk3WinAPI GetClientBounds]');
  {$ENDIF}
  if Handle = 0 then
    Exit(False);
  ARect := TGtk3Widget(handle).getClientBounds;
  Result := True;
end;

function TGtk3WidgetSet.GetClientRect(handle: HWND; var ARect: TRect): Boolean;
begin
  {$IF DEFINED(VerboseGtk3WinAPI) OR DEFINED(GTK3DEBUGSIZE)}
  DebugLn('[Gtk3WinAPI GetClientRect]');
  {$ENDIF}
  if Handle = 0 then
    Exit(False);
  ARect := TGtk3Widget(handle).getClientRect;
  Result := True;
end;

function TGtk3WidgetSet.GetClipBox(DC: hDC; lpRect: PRect): Longint;
var
  GtkDC: TGtk3DeviceContext absolute DC;
  cr: Pcairo_t;
  //Pt: TPoint;
  ARect: TGdkRectangle;
begin
  //{$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  //DebugLn('WARNING: TGtk3WidgetSet.GetClipBox not implemented ...');
  //{$ENDIF}
  Result := NULLREGION;
  if lpRect <> nil then
    lpRect^ := Rect(0,0,0,0);

  if not IsValidDC(DC) then
    Result := ERROR;

  if Result <> ERROR then
  begin
    cr := GtkDC.pcr;
    if gdk_cairo_get_clip_rectangle(cr, @ARect) then
    begin
      lpRect^ := RectFromGdkRect(ARect);
      Result := SimpleRegion;
    end;
  end;
end;

function TGtk3WidgetSet.GetClipRGN(DC: hDC; RGN: hRGN): Longint;
var
  ARect: TGdkRectangle;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.GetClipRgn not implemented ...');
  {$ENDIF}
  Result := -1;
  if not IsValidDC(DC) or (RGN = 0) then
    exit;
  gdk_cairo_get_clip_rectangle(TGtk3DeviceContext(DC).pcr, @ARect);
  // DebugLn('GetClipRgn ',dbgs(TGtk3Region(RGN).GetExtents),' clipRect ',dbgs(RectFromGdkRect(ARect)));
  if IsRectEmpty(RectFromGdkRect(ARect)) then
    exit(0)
  else
  begin
    cairo_region_destroy(TGtk3Region(RGN).Handle);
    TGtk3Region(RGN).Handle := cairo_region_create_rectangle(Pcairo_rectangle_int_t(@ARect));
    Result := 1;
  end;
end;

function TGtk3WidgetSet.GetCmdLineParamDescForInterface: TStringList;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetCmdLineParamDescForInterface not implemented ...');
  {$ENDIF}
  Result:=inherited GetCmdLineParamDescForInterface;
end;

function TGtk3WidgetSet.GetCurrentObject(DC: HDC; uObjectType: UINT): HGDIOBJ;
var
  GtkDC: TGtk3DeviceContext absolute DC;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.GetCurrentObject not implemented ...');
  {$ENDIF}
  // Result:=inherited GetCurrentObject(DC, uObjectType);
  Result := 0;
  if not IsValidDC(DC) then
    Exit;
  case uObjectType of
    OBJ_BITMAP: Result := HGDIOBJ(GtkDC.CurrentImage);
    OBJ_BRUSH: Result := HGDIOBJ(GtkDC.CurrentBrush);
    OBJ_FONT: Result := HGDIOBJ(GtkDC.CurrentFont);
    OBJ_PEN: Result := HGDIOBJ(GtkDC.CurrentPen);
    OBJ_REGION: Result := HGDIOBJ(GtkDC.CurrentRegion);
  end;
end;

function TGtk3WidgetSet.GetCursorPos(var lpPoint: TPoint): Boolean;
var
  ADeviceManager: PGdkDeviceManager;
  APointer: PGdkDevice;
  AScreen: PGdkScreen;
begin
  ADeviceManager := gdk_display_get_device_manager(gdk_display_get_default);
  APointer := gdk_device_manager_get_client_pointer(ADeviceManager);
  AScreen := gdk_screen_get_default;
  gdk_device_get_position(APointer, @AScreen, @lpPoint.X, @lpPoint.Y);
  Result := True;
end;

function TGtk3WidgetSet.GetDC(hWnd: HWND): HDC;
var
  Widget: TGtk3Widget;
begin
  if Gtk3WidgetSet.IsValidHandle(hWnd) then
  begin
    Widget := TGtk3Widget(hWnd);
    Result := Widget.Context;
    if Result = 0 then
      Result := HDC(Gtk3DefaultContext);
  end else
    Result := HDC(Gtk3ScreenContext);
end;

function TGtk3WidgetSet.GetDCOriginRelativeToWindow(PaintDC: HDC;
  WindowHandle: HWND; var OriginDiff: TPoint): boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetDCOriginRelativeToWindow not implemented ...');
  {$ENDIF}
  Result:=inherited GetDCOriginRelativeToWindow(PaintDC, WindowHandle,
    OriginDiff);
end;

function TGtk3WidgetSet.GetDesignerDC(WindowHandle: HWND): HDC;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetDesignerDC not implemented ...');
  {$ENDIF}
  Result:=inherited GetDesignerDC(WindowHandle);
end;

function TGtk3WidgetSet.GetDeviceCaps(DC: HDC; Index: Integer): Integer;
var
  pscr: PGdkScreen;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  if (Index <> BITSPIXEL) and (Index <> LOGPIXELSX) and (Index <> LOGPIXELSY) then
    DebugLn('WARNING: TGtk3WidgetSet.GetDeviceCaps not implemented ...Index=',dbgs(Index),' DC=',dbgs(DC));
  {$ENDIF}
  Result := 0; // inherited GetDeviceCaps(DC, Index);
  case Index of

    HORZRES : { Horizontal width in pixels }
    begin
      if IsValidDC(DC) then
      begin
        Result := TGtk3DeviceContext(DC).getDeviceSize.X;
      end else
        Result := GetSystemMetrics(SM_CXSCREEN);
    end;

    VERTRES : { Vertical height in pixels }
    begin
      if IsValidDC(DC) then
      begin
        Result := TGtk3DeviceContext(DC).getDeviceSize.Y;
      end else
        Result := GetSystemMetrics(SM_CYSCREEN);
    end;

    HORZSIZE : { Horizontal size in millimeters }
      Result := LazUtilities.RoundToInt((GetDeviceCaps(DC, HORZRES) * 100) /
                    (GetDeviceCaps(DC, LOGPIXELSX) * 25.4));

    VERTSIZE : { Vertical size in millimeters }
      Result := LazUtilities.RoundToInt((GetDeviceCaps(DC, VERTRES) * 100) /
                    (GetDeviceCaps(DC, LOGPIXELSY) * 25.4));

    BITSPIXEL:
    begin
      if IsValidDC(DC) then
        Result := TGtk3DeviceContext(DC).getDepth
      else
        Result := gdk_window_get_visual(gdk_get_default_root_window)^.get_depth;
    end;
    PLANES: Result := 1;
    SIZEPALETTE: Result := gdk_window_get_visual(gdk_get_default_root_window)^.get_colormap_size;

    LOGPIXELSX, LOGPIXELSY : { Logical pixels per inch in X and Y }
    begin
      pscr := TGdkScreen.get_default;
      Result := round(pscr^.get_resolution);
    end;
  end;

end;

function TGtk3WidgetSet.GetDeviceSize(DC: HDC; var p: TPoint): boolean;
var
  ARect: TGdkRectangle;
begin
  Result := False;
  if not IsValidDC(DC) then
    exit;
  if TGtk3DeviceContext(DC).Parent <> nil then
  begin
    if Gtk3IsGdkWindow(TGtk3DeviceContext(DC).Parent^.window) then
    begin
      p.X := gdk_window_get_width(TGtk3DeviceContext(DC).Parent^.window);
      p.Y := gdk_window_get_height(TGtk3DeviceContext(DC).Parent^.window);
      Result := True;
    end;
  end else
  if (TGtk3DeviceContext(DC).ParentPixmap <> nil) and
    Gtk3IsGdkPixbuf(TGtk3DeviceContext(DC).ParentPixmap) then
  begin
    p.X := TGtk3DeviceContext(DC).ParentPixmap^.get_width;
    p.Y := TGtk3DeviceContext(DC).ParentPixmap^.get_height;
    Result := True;
  end else
  if TGtk3DeviceContext(DC).pcr <> nil then
  begin
    gdk_cairo_get_clip_rectangle(TGtk3DeviceContext(DC).pcr, @ARect);
    p.X := ARect.Width;
    p.Y := ARect.Height;
    Result := True;
  end;
end;

function TGtk3WidgetSet.GetDIBits(DC: HDC; Bitmap: HBitmap; StartScan,
  NumScans: UINT; Bits: Pointer; var BitInfo: BitmapInfo; Usage: UINT): Integer;
begin
  Result:=inherited GetDIBits(DC, Bitmap, StartScan, NumScans, Bits, BitInfo,
    Usage);
end;

function TGtk3WidgetSet.GetDpiForMonitor(hmonitor: HMONITOR;
  dpiType: TMonitorDpiType; out dpiX: UINT; out dpiY: UINT): HRESULT;
var
  w,w_mm,h,h_mm:gint;
  rr:double;
  pscr:PGdkScreen;
begin
  (*  MONITOR_DPI_TYPE = (
    MDT_EFFECTIVE_DPI = 0,
    MDT_ANGULAR_DPI = 1,
    MDT_RAW_DPI = 2,
    MDT_DEFAULT = MDT_EFFECTIVE_DPI);
  TMonitorDpiType = MONITOR_DPI_TYPE;
  *)
  pscr:=TGdkScreen.get_default;
  w:=pscr^.get_width;
  w_mm:=pscr^.get_width_mm;
  h:=pscr^.get_height;
  h_mm:=pscr^.get_height_mm;

  dpiX:=round(25.4*w/w_mm);
  dpiY:=round(25.4*h/h_mm);

  rr:=TGdkScreen.get_default^.get_resolution();

  Result:=0;
  //Result:=inherited GetDpiForMonitor(hmonitor, dpiType, dpiX, dpiY);
end;

function TGtk3WidgetSet.GetFocus: HWND;
var
  i: Integer;
  AWidget: PGtkWidget;
  AList: PGList;
  AHandle: TGtk3Window;
  AWindow: PGtkWindow;
  AActiveWindow: HWND;
begin
  AWidget := nil;

  AActiveWindow := GetActiveWindow;
  if AActiveWindow <> 0 then
  begin
    AWidget := PGtkWindow(TGtk3Widget(AActiveWindow).Widget)^.get_focus;
  end else
  begin
    // worst case scenario is to search for widget or when application
    // isn't active anymore
    AList := gtk_window_list_toplevels;
    for i := 0 to g_list_length(AList) - 1 do
    begin
      if Gtk3IsGtkWindow(PGObject(g_list_nth(AList, i)^.data)) then
      begin
        // gtk3 this is really ugly, it returns .is_active for non active
        // windows, while docs says that is_active is window with kbd focus
        AWindow := PGtkWindow(g_list_nth(AList, i)^.data);
        AHandle := TGtk3Window(HwndFromGtkWidget(AWindow));
        if Assigned(AHandle) and (Screen.FocusedForm = AHandle.LCLObject) and
           (AWindow^.is_active) then
        begin
          AWidget := PGtkWindow(g_list_nth(AList, i)^.data)^.get_focus;
          if AWidget <> nil then
            break;
        end;
      end;
    end;
    g_list_free(AList);
  end;

  Result := HwndFromGtkWidget(AWidget);
  {$IFDEF GTK3DEBUGFOCUS}
  DebugLn('TGtk3WidgetSet.GetFocus current focus ',dbgHex(Result));
  if IsValidHandle(Result) then
    DebugLn('TGtk3WidgetSet.GetFocus current focus ',dbgsName(TGtk3Widget(Result).LCLObject));
  {$ENDIF}
end;

function TGtk3WidgetSet.GetFontLanguageInfo(DC: HDC): DWord;
begin
  Result:=inherited GetFontLanguageInfo(DC);
end;

function TGtk3WidgetSet.GetForegroundWindow: HWND;
var
  i: Integer;
  AWidget: PGtkWindow;
  AWindow: PGtkWindow;
  AList: PGList;
begin
  Result := 0;
  AWidget := nil;
  AWindow := nil;
  AList := gtk_window_list_toplevels;
  for i := 0 to g_list_length(AList) - 1 do
  begin
    if Gtk3IsGtkWindow(PGObject(g_list_nth(AList, i)^.data)) then
    begin
      AWidget := g_list_nth(AList, i)^.data;
      if AWidget^.get_visible and AWidget^.is_toplevel and AWidget^.is_active then
      begin
        AWindow := AWidget;
        break;
      end;
    end;
  end;
  g_list_free(AList);
  Result := HwndFromGtkWidget(AWindow);
end;

function TGtk3WidgetSet.GetKeyState(nVirtKey: Integer): Smallint;
const
  StateDown    = SmallInt($FF80);
var
  AKeyMap: PGdkKeymap;
  AModifiers: TGdkModifierType;
begin
  Result := 0;

  case nVirtKey of
    VK_LSHIFT:   nVirtKey := VK_SHIFT;
    VK_LCONTROL: nVirtKey := VK_CONTROL;
    VK_LMENU:    nVirtKey := VK_MENU;
  end;

  (*
  // GdkModifierType
  GDK_SHIFT_MASK: TGdkModifierType = 1;
  GDK_LOCK_MASK: TGdkModifierType = 2;
  GDK_CONTROL_MASK: TGdkModifierType = 4;
  GDK_MOD1_MASK: TGdkModifierType = 8;
  GDK_MOD2_MASK: TGdkModifierType = 16;
  GDK_MOD3_MASK: TGdkModifierType = 32;
  GDK_MOD4_MASK: TGdkModifierType = 64;
  GDK_MOD5_MASK: TGdkModifierType = 128;
  GDK_BUTTON1_MASK: TGdkModifierType = 256;
  GDK_BUTTON2_MASK: TGdkModifierType = 512;
  GDK_BUTTON3_MASK: TGdkModifierType = 1024;
  GDK_BUTTON4_MASK: TGdkModifierType = 2048;
  GDK_BUTTON5_MASK: TGdkModifierType = 4096;
  GDK_MODIFIER_RESERVED_13_MASK: TGdkModifierType = 8192;
  GDK_MODIFIER_RESERVED_14_MASK: TGdkModifierType = 16384;
  GDK_MODIFIER_RESERVED_15_MASK: TGdkModifierType = 32768;
  GDK_MODIFIER_RESERVED_16_MASK: TGdkModifierType = 65536;
  GDK_MODIFIER_RESERVED_17_MASK: TGdkModifierType = 131072;
  GDK_MODIFIER_RESERVED_18_MASK: TGdkModifierType = 262144;
  GDK_MODIFIER_RESERVED_19_MASK: TGdkModifierType = 524288;
  GDK_MODIFIER_RESERVED_20_MASK: TGdkModifierType = 1048576;
  GDK_MODIFIER_RESERVED_21_MASK: TGdkModifierType = 2097152;
  GDK_MODIFIER_RESERVED_22_MASK: TGdkModifierType = 4194304;
  GDK_MODIFIER_RESERVED_23_MASK: TGdkModifierType = 8388608;
  GDK_MODIFIER_RESERVED_24_MASK: TGdkModifierType = 16777216;
  GDK_MODIFIER_RESERVED_25_MASK: TGdkModifierType = 33554432;
  GDK_SUPER_MASK: TGdkModifierType = 67108864;
  GDK_HYPER_MASK: TGdkModifierType = 134217728;
  GDK_META_MASK: TGdkModifierType = 268435456;
  GDK_MODIFIER_RESERVED_29_MASK: TGdkModifierType = 536870912;
  GDK_RELEASE_MASK: TGdkModifierType = 1073741824;
  GDK_MODIFIER_MASK: TGdkModifierType = 1543512063;
  *)
  // AModifierMask := gdk_keymap_get_modifier_mask(AKeyMap, 0);

  AKeyMap := TGdkKeymap.get_for_display(gdk_display_get_default);
  AModifiers := TGdkModifierType(AKeyMap^.get_modifier_state);
  case nVirtKey of
    VK_LBUTTON:
      if GDK_BUTTON1_MASK in AModifiers  then
        Result := Result or StateDown;
    VK_RBUTTON:
      if GDK_BUTTON2_MASK in AModifiers  then
        Result := Result or StateDown;
    VK_MBUTTON:
      if GDK_BUTTON3_MASK in AModifiers  then
        Result := Result or StateDown;
    VK_XBUTTON1:
      if GDK_BUTTON4_MASK in AModifiers  then
        Result := Result or StateDown;
    VK_XBUTTON2:
      if GDK_BUTTON5_MASK in AModifiers  then
        Result := Result or StateDown;
    VK_MENU:
      if GDK_MOD1_MASK in AModifiers  then
        Result := Result or StateDown;
    VK_SHIFT:
      if GDK_SHIFT_MASK in AModifiers  then
        Result := Result or StateDown;
    VK_CONTROL:
      if GDK_CONTROL_MASK in AModifiers  then
        Result := Result or StateDown;
    VK_LWIN, VK_RWIN:
      if GDK_META_MASK in AModifiers  then
        Result := Result or StateDown;
   {$ifdef VerboseGtk3WinAPI}
    else
      DebugLn('TGtk3WidgetSet.GetKeyState TODO ', DbgSVKCode(Word(nVirtkey)));
   {$endif}
  end;
end;

function TGtk3WidgetSet.GetMapMode(DC: HDC): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetMapMode not implemented ...');
  {$ENDIF}
  Result:=inherited GetMapMode(DC);
end;

function TGtk3WidgetSet.GetMonitorInfo(Monitor: HMONITOR; lpmi: PMonitorInfo
  ): Boolean;
var
  MonitorRect, MonitorWorkArea: TGdkRectangle;
begin
  Result := (lpmi <> nil) and (lpmi^.cbSize >= SizeOf(TMonitorInfo)) or (Monitor = 0);
  if not Result then Exit;
  Dec(Monitor);
  gdk_screen_get_monitor_geometry(gdk_screen_get_default, Monitor, @MonitorRect);

  with MonitorRect do
    lpmi^.rcMonitor := Bounds(x, y, width, height);
  // there is no way to determine workarea in gtk
  gdk_screen_get_monitor_workarea(gdk_screen_get_default, Monitor, @MonitorWorkArea);
  with MonitorWorkArea do
    lpmi^.rcWork := Bounds(x, y, width, height);
  lpmi^.rcWork := lpmi^.rcMonitor;
  if Monitor = gdk_screen_get_primary_monitor(gdk_screen_get_default) then
    lpmi^.dwFlags := MONITORINFOF_PRIMARY
  else
    lpmi^.dwFlags := 0;
end;

function TGtk3WidgetSet.GetObject(GDIObj: HGDIOBJ; BufSize: Integer;
  Buf: Pointer): Integer;
begin
  Result := 0;
  if not IsValidGDIObject(GDIObj) then
  begin
    {$ifdef VerboseGtk3WinAPI}
    DebugLn('Trace:< TGtk3WidgetSet.GetObject Invalid GDI Object');
    {$endif}
    Exit;
  end;
  Result:=TGtk3ContextObject(GDIObj).Get(bufsize, buf);
end;

function TGtk3WidgetSet.GetParent(Handle: HWND): HWND;
begin
  if Handle <> 0 then
    Result := HWND(TGtk3Widget(Handle).getParent)
  else
    Result := 0;
end;

function TGtk3WidgetSet.GetProp(Handle: hwnd; Str: PChar): Pointer;
begin
  Result := nil;
  if not IsValidHandle(Handle) then
    exit;
  Result := g_object_get_data(TGtk3Widget(Handle).Widget, PgChar(Str));
end;

function TGtk3WidgetSet.GetRgnBox(RGN: HRGN; lpRect: PRect): Longint;
begin
  Result := SIMPLEREGION;
  if IsValidGDIObject(RGN) then
  begin
    lpRect^ := TGtk3Region(RGN).GetExtents;
  end;
end;

function TGtk3WidgetSet.GetROP2(DC: HDC): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetROP2 not implemented ...');
  {$ENDIF}
  Result := inherited GetROP2(DC);
end;

function TGtk3WidgetSet.GetScrollBarSize(Handle: HWND; BarKind: Integer
  ): integer;
var
  BarWidget: PGtkWidget;
  Scrolled: PGtkScrolledWindow;
begin
  Result := 0;
  if not IsValidHandle(Handle) then
    exit;
  BarWidget := nil;
  if wtScrollbar in TGtk3Widget(Handle).WidgetType then
    BarWidget := TGtk3Widget(Handle).Widget
  else
  if wtScrollingWin in TGtk3Widget(Handle).WidgetType then
  begin
    Scrolled := TGtk3ScrollableWin(Handle).GetScrolledWindow;
    if Scrolled <> nil then
    begin
      if BarKind = SM_CYVSCROLL then
        BarWidget := Scrolled^.get_vscrollbar
      else
        BarWidget := Scrolled^.get_hscrollbar;
    end;
  end;
  if BarWidget <> nil then
  begin
    if BarKind = SM_CYVSCROLL then
      Result := BarWidget^.get_allocated_width
    else
      Result := BarWidget^.get_allocated_height;
  end;
end;

function TGtk3WidgetSet.GetScrollbarVisible(Handle: HWND; SBStyle: Integer
  ): boolean;
var
  AWidget: TGtk3Widget;
begin
  Result := False;
  if not IsValidHandle(Handle) then
    exit;
  AWidget := TGtk3Widget(Handle);

  if wtScrollBar in AWidget.WidgetType then
    Result := AWidget.Visible
  else
  begin
    if wtScrollingWin in AWidget.WidgetType then
    begin
      if SBStyle = SB_Horz then
        Result := TGtk3ScrollableWin(Handle).getHorizontalScrollbar^.get_visible
      else
      if SBStyle = SB_Vert then
        Result := TGtk3ScrollableWin(Handle).getVerticalScrollbar^.get_visible
    end;
  end;
end;

function TGtk3WidgetSet.GetScrollInfo(Handle: HWND; SBStyle: Integer;
  var ScrollInfo: TScrollInfo): Boolean;
var
  Adjustment: PGtkAdjustment;
  AWidget: TGtk3Widget;
  AScrollWin: PGtkScrolledWindow;
begin
  Result := False;
  if not IsValidHandle(Handle) then
    exit;
  AWidget := TGtk3Widget(Handle);
  Adjustment := nil;
  AScrollWin := nil;
  if wtScrollBar in AWidget.WidgetType then
    Adjustment := PGtkScrollBar(AWidget.Widget)^.adjustment
  else
  if wtScrollingWin in AWidget.WidgetType then
    AScrollWin := TGtk3ScrollableWin(Handle).GetScrolledWindow;

  case SBStyle of
    SB_Horz:
    begin
      if not Assigned(Adjustment) and Assigned(AScrollWin) then
        Adjustment := AScrollWin^.get_hadjustment;
    end;
    SB_Vert:
    begin
      if not Assigned(Adjustment) and Assigned(AScrollWin) then
        Adjustment := AScrollWin^.get_vadjustment;
    end;
    SB_CTL:
    begin

    end;
    SB_BOTH:
    begin

    end;
  end;

  if Adjustment = nil then
  begin
    DebugLn('TGtk3WidgetSet.GetScrollInfo error: cannot get PGtkAdjustment from ',dbgsName(AWidget.LCLObject));
    exit;
  end;

  // POS
  if (ScrollInfo.fMask and SIF_POS) <> 0 then
    ScrollInfo.nPos := Round(Adjustment^.Value);

  // RANGE
  if (ScrollInfo.fMask and SIF_RANGE) <> 0 then
  begin
    ScrollInfo.nMin:= Round(Adjustment^.Lower);
    ScrollInfo.nMax:= Round(Adjustment^.Upper);
  end;
  // PAGE
  if (ScrollInfo.fMask and SIF_PAGE) <> 0 then
  begin
    ScrollInfo.nPage := Round(Adjustment^.Page_Size);
  end;
  // TRACKPOS
  if (ScrollInfo.fMask and SIF_TRACKPOS) <> 0 then
  begin
    ScrollInfo.nTrackPos := Round(Adjustment^.Value);
  end;

  Result := True;
end;

function TGtk3WidgetSet.GetStockObject(Value: Integer): TLCLHandle;
begin
  Result := 0;

  case Value of
    BLACK_BRUSH:         // Black brush.
      Result := FStockBlackBrush;
    DKGRAY_BRUSH:        // Dark gray brush.
      Result := FStockDKGrayBrush;
    GRAY_BRUSH:          // Gray brush.
      Result := FStockGrayBrush;
    LTGRAY_BRUSH:        // Light gray brush.
      Result := FStockLtGrayBrush;
    NULL_BRUSH:          // Null brush (equivalent to HOLLOW_BRUSH).
      Result := FStockNullBrush;
    WHITE_BRUSH:         // White brush.
      Result := FStockWhiteBrush;

    BLACK_PEN:           // Black pen.
      Result := FStockBlackPen;
    NULL_PEN:            // Null pen.
      Result := FStockNullPen;
    WHITE_PEN:           // White pen.
      Result := FStockWhitePen;

    {System font. By default, Windows uses the system font to draw menus,
     dialog box controls, and text. In Windows versions 3.0 and later,
     the system font is a proportionally spaced font; earlier versions of
     Windows used a monospace system font.}
    DEFAULT_GUI_FONT, SYSTEM_FONT:
      begin

        If FStockSystemFont <> 0 then
        begin
          DeleteObject(FStockSystemFont);
          FStockSystemFont := 0;
        end;

        If FStockSystemFont = 0 then
          FStockSystemFont := CreateDefaultFont;
        Result := FStockSystemFont;
      end;
  end;
end;

function TGtk3WidgetSet.GetSysColor(nIndex: Integer): DWORD;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn(['TGtk3WidgetSet.GetSysColor WARNING: SOME SYSCOLORS ARE STILL HARDCODED nIndex=',nIndex]);
  {$ENDIF}
  if (nIndex = COLOR_WINDOW) or (nIndex = COLOR_WINDOWTEXT) or
    (nIndex = COLOR_HIGHLIGHT) or (nIndex = COLOR_HIGHLIGHTTEXT) then
      GetStyleWidget(lgsMemo)
  else
  if (nIndex = COLOR_MENU) or (nIndex = COLOR_MENUHILIGHT) or
    (nIndex = COLOR_MENUTEXT) then
  begin
    GetStyleWidget(lgsMenu);
    GetStyleWidget(lgsMenuitem);
  end else
  if (nIndex = COLOR_MENUBAR) then
    GetStyleWidget(lgsMenuBar)
  else
  if (nIndex = COLOR_SCROLLBAR) then
    GetStyleWidget(lgsVerticalScrollbar)
  else
  if (nIndex = COLOR_BTNFACE) or (nIndex = COLOR_BTNTEXT) or
    (nIndex = COLOR_BTNSHADOW) or (nIndex = COLOR_BTNHIGHLIGHT) then
    GetStyleWidget(lgsButton)
  else
  if (nIndex = COLOR_BACKGROUND) or (nIndex = COLOR_FORM) then
    GetStyleWidget(lgsWindow);
  Result := SysColorMap[nIndex];
end;

function TGtk3WidgetSet.GetSysColorBrush(nIndex: Integer): HBrush;
begin
  if (nIndex < 0) or (nIndex > MAX_SYS_COLORS) then
  begin
    Result := 0;
    DebugLn(Format('ERROR: [TGtk3WidgetSet.GetSysColorBrush] Bad Value: %d. Valid Range between 0 and %d', [nIndex, MAX_SYS_COLORS]));
  end else
  begin
    Result := FSysColorBrushes[nIndex];
    if Result = HBRUSH(-1) then
    begin
      InitSysColorBrushes;
      Result := FSysColorBrushes[nIndex];
      if Result = HBRUSH(-1) then
        DebugLn('WARNING: GetSysColorBrush SysColorBrushes arent''t initialized properly....');
    end;
  end;
end;

function TGtk3WidgetSet.GetSystemMetrics(nIndex: Integer): Integer;
var
  auw: guint;
  auh: guint;
  ascreen: PGdkScreen;
  ARect: TGdkRectangle;
begin
  Result := 0;
  case nIndex of
    SM_CXCURSOR,
    SM_CYCURSOR:
      begin
        // Width and height of a cursor, in pixels. For win32 system cannot create cursors of other sizes.
        // For gtk this should be maximal cursor sizes
        gdk_display_get_maximal_cursor_size(gdk_display_get_default, @auw, @auh);
        if nIndex = SM_CXCURSOR then
          Result := auw // return width
        else
          Result := auh; // return height
      end;
    SM_CXDRAG:
      begin
        Result := 2;
      end;
    SM_CYDRAG:
      begin
        Result := 2;
      end;
    SM_CXEDGE:
      begin
        Result := 2;
      end;
    SM_CYEDGE:
      begin
        Result := 2;
      end;

    SM_CXICON,
    SM_CYICON:
      // big icon size
      // gtk recommends sizes 16,32,48. optional: 64 and 128
      Result := 128;

    SM_CXMAXIMIZED:
      begin
        ascreen := gdk_screen_get_default();
        gdk_screen_get_monitor_workarea(ascreen, 0, @ARect);
        Result := ARect.width;
      end;
    SM_CYMAXIMIZED:
      begin
        ascreen := gdk_screen_get_default();
        gdk_screen_get_monitor_workarea(ascreen, 0, @ARect);
        Result := ARect.height;
      end;

    SM_CXFULLSCREEN,
    SM_CXSCREEN:
      begin
        ascreen := gdk_screen_get_default();
        gdk_screen_get_monitor_geometry(ascreen, 0, @ARect);
        Result := ARect.width;
      end;
    SM_CXVIRTUALSCREEN:
      begin
        Result := gdk_Screen_Width;
      end;
    SM_CYFULLSCREEN,
    SM_CYSCREEN:
      begin
        ascreen := gdk_screen_get_default();
        gdk_screen_get_monitor_geometry(ascreen, 0, @ARect);
        Result := ARect.height;
      end;
    SM_CYVIRTUALSCREEN:
      begin
        result := gdk_Screen_Height;
      end;
    SM_LCLHasFormAlphaBlend:
      Result := 1;
  end;
end;

function TGtk3WidgetSet.GetTextColor(DC: HDC): TColorRef;
begin
  Result := CLR_INVALID;
  if IsValidDC(DC) then
    Result := TColorRef(TGtk3DeviceContext(DC).CurrentTextColor);
end;

function TGtk3WidgetSet.GetTextExtentPoint(DC: HDC; Str: PChar; Count: Integer;
  var Size: TSize): Boolean;
begin
  Result := False;
  if not IsValidDC(DC) then
    exit;
  if (Count <= 0) or (Str = nil) or (StrPas(Str) = '') then
  begin
    FillChar(Size, SizeOf(Size), 0);
    Exit;
  end;
  TGtk3DeviceContext(DC).CurrentFont.Layout^.set_text(Str, Count);
  TGtk3DeviceContext(DC).CurrentFont.Layout^.get_pixel_size(@Size.Cx, @Size.CY);
  // DebugLn('TGtk3WidgetSet.GetTextExtentPoint pixel size is ',dbgs(Size),
  //  ' avgcharwidth ',dbgs(ACharWidth div PANGO_SCALE),' avgdigitwidth ',dbgs(ADigitWidth div PANGO_SCALE));
  Result := True;
end;

function TGtk3WidgetSet.GetTextMetrics(DC: HDC; var TM: TTextMetric): Boolean;
const
  TestString: array[boolean] of string = (
    // single byte char font
    '{ABCDEFGHIJKLMNOPQRSTUVWXYZXYZabcdefghijklmnopqrstuvwxyz|_}',
    // double byte char font
    #0'{'#0'A'#0'B'#0'C'#0'D'#0'E'#0'F'#0'G'#0'H'#0'I'#0'J'#0'K'#0'L'#0'M'#0'N'
    +#0'O'#0'P'#0'Q'#0'R'#0'S'#0'T'#0'U'#0'V'#0'W'#0'X'#0'Y'#0'Z'#0'X'#0'Y'#0'Z'
    +#0'a'#0'b'#0'c'#0'd'#0'e'#0'f'#0'g'#0'h'#0'i'#0'j'#0'k'#0'l'#0'm'#0'n'#0'o'
    +#0'p'#0'q'#0'r'#0's'#0't'#0'u'#0'v'#0'w'#0'x'#0'y'#0'z'#0'|'#0'_'#0'}'
    );
var
  AFont: TGtk3Font;
  APangoMetrics: PPangoFontMetrics;
  aRect: TPangoRectangle;
  APangoWeight: TPangoWeight;
begin
  Result := False;
  if IsValidDC(DC) then
  begin
    //TODO add metrics to cache of font, so if we have valid metrics just return.
    //or create metrics when font is created (like qt uses)
    AFont := TGtk3DeviceContext(DC).CurrentFont;

    APangoMetrics := pango_context_get_metrics(AFont.Layout^.get_context,
      AFont.Handle, AFont.Layout^.get_context^.get_language);
    if APangoMetrics = nil then
    begin
      DebugLn(['TGtk3WidgetSet.UpdateDCTextMetric WARNING: no pango metrics']);
      exit;
    end;
    FillChar(TM, SizeOf(TM), #0);

    TM.tmAveCharWidth := Max(1,
               PANGO_PIXELS(pango_font_metrics_get_approximate_char_width(APangoMetrics))
               );
    TM.tmAscent := PANGO_PIXELS(APangoMetrics^.get_ascent);
    TM.tmDescent := PANGO_PIXELS(APangoMetrics^.get_descent);

    TM.tmHeight := TM.tmAscent + TM.tmDescent;

    pango_layout_set_text(AFont.Layout, PChar(TestString[True]),
                          length(PChar(TestString[True])));
    pango_layout_get_extents(AFont.Layout, nil, @aRect);

    // lBearing := 0; // PANGO_LBEARING(aRect) div PANGO_SCALE;
    // rBearing := 0; // PANGO_RBEARING(aRect) div PANGO_SCALE;

    pango_layout_set_text(AFont.Layout, 'M', 1);
    pango_layout_get_pixel_size(AFont.Layout, @aRect.width, @aRect.height);
    TM.tmMaxCharWidth := Max(1,aRect.width);
    pango_layout_set_text(AFont.Layout, 'W', 1);
    pango_layout_get_pixel_size(AFont.Layout, @aRect.width, @aRect.height);
    TM.tmMaxCharWidth := Max(TM.tmMaxCharWidth,aRect.width);

    APangoWeight := AFont.Handle^.get_weight;

    if APangoWeight < PANGO_WEIGHT_THIN then
      APangoWeight := PANGO_WEIGHT_THIN;
    if APangoWeight > PANGO_WEIGHT_HEAVY then
      APangoWeight := PANGO_WEIGHT_HEAVY;

    TM.tmWeight := Integer(APangoWeight);

    TM.tmFirstChar := 'a';
    TM.tmLastChar := 'z';
    TM.tmDefaultChar := 'x';
    TM.tmBreakChar := '?';

    TM.tmItalic := Ord(AFont.Handle^.get_style = PANGO_STYLE_ITALIC);
    //  APangoMetrics^.get_underline_position;
    // TM.tmUnderlined :=
    // TM.tmStruckOut :=
    pango_font_metrics_unref(APangoMetrics);
    Result := True;
  end;
end;

function TGtk3WidgetSet.GetViewPortExtEx(DC: HDC; Size: PSize): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetViewportExtEx not implemented ...');
  {$ENDIF}
  Result:=inherited GetViewPortExtEx(DC, Size);
end;

function TGtk3WidgetSet.GetViewPortOrgEx(DC: HDC; P: PPoint): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetViewportOrgEx not implemented ...');
  {$ENDIF}
  Result:=inherited GetViewPortOrgEx(DC, P);
end;

function TGtk3WidgetSet.GetWindowExtEx(DC: HDC; Size: PSize): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetWindowExtEx not implemented ...');
  {$ENDIF}
  Result:=inherited GetWindowExtEx(DC, Size);
end;

function TGtk3WidgetSet.GetWindowLong(Handle: HWND; int: Integer): PtrInt;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.GetWindowLong not implemented ...');
  {$ENDIF}
  Result:=inherited GetWindowLong(Handle, int);
end;

function TGtk3WidgetSet.GetWindowOrgEx(dc: hdc; P: PPoint): Integer;
var
  Matrix: Tcairo_matrix_t;
  dx, dy: Double;
  OrigX, OrigY: LongInt;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.GetWindowOrgEx not implemented ...');
  {$ENDIF}
  Result := 0;
  if not IsValidDC(DC) and (P <> nil) then
  begin
    {$ifdef VerboseGtk3WinAPI}
      DebugLn('Trace: < [WinAPI GetWindowOrgEx] No valid DC or P is nil');
    {$endif}
    exit;
  end;
  cairo_get_matrix(TGtk3DeviceContext(DC).pcr, @Matrix);
  dx := 0;
  dy := 0;
  cairo_matrix_transform_point(@Matrix, @dx, @dy);
  if P <> nil then
  begin
    OrigX := TGtk3DeviceContext(DC).fncOrigin.X;
    OrigY := TGtk3DeviceContext(DC).fncOrigin.Y;
    //DebugLn('GetWindowOrgEx POINT ',Format('OrigX=%d, OrigY=%d, dx=%d, dy=%d',
    //                                       [OrigX, OrigY, Trunc(dx), Trunc(dy)]));
    P^.X := OrigX-Trunc(dx);
    P^.Y := OrigY-Trunc(dy);
  end;
  Result := 1;
end;

function TGtk3WidgetSet.GetWindowRect(Handle: hwnd; var ARect: TRect): Integer;
var
  AWindow: PGdkWindow;
  x, y: gint;
  //w, h: gint;
  GRect: TGdkRectangle;
  Allocation: TGtkAllocation;
begin
  Result := 0;
  if Handle <> 0 then
  begin
    AWindow := TGtk3Widget(Handle).GetWindow;
    if AWindow <> nil then
    begin
      AWindow^.get_origin(@x, @y);
      //w := AWindow^.get_width;
      //h := AWindow^.get_height;
      AWindow^.get_frame_extents(@GRect);
      // R := RectFromGdkRect(GRect);
      ARect := Bounds(x, Y, GRect.width, GRect.Height);
      Result := 1;
    end else
    begin
      TGtk3Widget(Handle).Widget^.get_allocation(@Allocation);
      ARect := Bounds(Allocation.x, Allocation.y, Allocation.width, Allocation.height);
    end;
  end;
end;

function TGtk3WidgetSet.GetWindowRelativePosition(Handle: hwnd; var Left,
  Top: integer): boolean;
var
  AWidget: TGtk3Widget;
  APos: TPoint;
begin
  Left:=0;
  Top:=0;
  if Handle = 0 then
    exit(False);
  AWidget := TGtk3Widget(Handle);
  Result := AWidget.GetPosition(APos);
  Left:=APos.X;
  Top:=APos.Y;
end;

function TGtk3WidgetSet.GetWindowSize(Handle: hwnd; var Width, Height: integer
  ): boolean;
var
  aWidget: PGtkWidget;
begin
  Result := False;
  if Handle <> 0 then
  begin
    aWidget:= TGtk3Widget(Handle).Widget;
    if aWidget<>nil then
    begin
      Width := aWidget^.get_allocated_width;
      Height := aWidget^.get_allocated_Height;
      Result := True;
    end;
  end;
end;

procedure TGtk3WidgetSet.InitializeCriticalSection(
  var CritSection: TCriticalSection);
var
  ACritSec: System.PRTLCriticalSection;
begin
  New(ACritSec);
  System.InitCriticalSection(ACritSec^);
  CritSection:=TCriticalSection(ACritSec);
end;

function TGtk3WidgetSet.InvalidateRect(aHandle: HWND; Rect: pRect;
  bErase: Boolean): Boolean;
begin
  Result := False;
  if AHandle <> 0 then
  begin
    TGtk3Widget(AHandle).Update(Rect);
    Result := True;
  end;
end;

function TGtk3WidgetSet.InvalidateRgn(Handle: HWND; Rgn: HRGN; Erase: Boolean): Boolean;
//var
//  R: TRect;
begin
  Result := False; // inherited InvalidateRgn(Handle, Rgn, Erase);
  if IsValidHandle(Handle) then
  begin
    if IsValidGDIObject(RGN) then
    begin
      gtk_widget_queue_draw_region(TGtk3Widget(Handle).GetContainerWidget,
       TGtk3Region(RGN).Handle)
    end else
      TGtk3Widget(Handle).Update(nil);
    //TODO: TGtk3Region must be implemented as Pcairo_region_t
    // GetRgnBox(Rgn, @R);
    // InvalidateRect(Handle, @R, True);
    Result := True;
    // gtk_widget_queue_draw_region();
  end;
end;

function TGtk3WidgetSet.IsIconic(handle: HWND): boolean;
begin
  Result := (handle <> 0) and TGtk3Widget(Handle).IsIconic;
end;

function TGtk3WidgetSet.IsWindow(handle: HWND): boolean;
begin
  Result := (handle <> 0) and
    Gtk3IsWidget(TGtk3Widget(Handle).Widget);
end;

function TGtk3WidgetSet.IsWindowEnabled(handle: HWND): boolean;
begin
  Result := (handle <> 0) and TGtk3Widget(Handle).Enabled and
    TGtk3Widget(Handle).Visible;
end;

function TGtk3WidgetSet.IsWindowVisible(handle: HWND): boolean;
begin
  Result := (handle <> 0) and TGtk3Widget(Handle).Visible;
end;

function TGtk3WidgetSet.IsZoomed(handle: HWND): boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.IsZoomed not implemented ...');
  {$ENDIF}
  Result:=inherited IsZoomed(handle);
end;

procedure TGtk3WidgetSet.LeaveCriticalSection(var CritSection: TCriticalSection
  );
var
  ACritSec: System.PRTLCriticalSection;
begin
  ACritSec:=System.PRTLCriticalSection(CritSection);
  System.LeaveCriticalsection(ACritSec^);
end;

function TGtk3WidgetSet.LineTo(DC: HDC; X, Y: Integer): Boolean;
begin
  if not IsValidDC(DC) then
    exit(False);
  Result := TGtk3DeviceContext(DC).LineTo(X, Y);
end;

function TGtk3WidgetSet.LPtoDP(DC: HDC; var Points; Count: Integer): BOOL;
var
  //Matrix: Tcairo_matrix_t;
  cr: PCairo_t;
  P: PPoint;
  dx, dy: Double;
  //Pt: TPoint;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.LPtoDP not implemented ...');
  {$ENDIF}
  Result := False;
  // inherited LPtoDP(DC, Points, Count);
  if not IsValidDC(DC) then
    exit;
  cr := TGtk3DeviceContext(DC).pcr;

  P := @Points;
  while Count > 0 do
  begin
    Dec(Count);
    DX := P^.X;
    DY := P^.Y;
    // DebugLn('LPTODP INPUT ',Format('dx %2.2n dy %2.2n',[dx, dy]));
    //cairo_matrix_translate(@Matrix, Dx, Dy);
    //cairo_matrix_transform_point(@Matrix, @Dx, @Dy);
    cairo_user_to_device(cr,@dx,@dy);
    // DebugLn('LPTODP Output ',Format('dx %2.2n dy %2.2n',[dx, dy]));
    P^.X := Round(DX)-TGtk3DeviceContext(DC).fncOrigin.x;
    P^.Y := Round(DY)-TGtk3DeviceContext(DC).fncOrigin.y;
    Inc(P);
  end;
  Result:=true;
end;

function TGtk3WidgetSet.MessageBox(hWnd: HWND; lpText, lpCaption: PChar;
  uType: Cardinal): integer;
var
  fact:TGtk3DialogFactory;
begin
  fact:=TGtk3DialogFactory.CreateMsgBox(hWnd,lpText,lpCaption,uType);
  try
    fact.run;
    Result:=fact.lcl_result;
  finally
    fact.Free;
  end;
end;

function TGtk3WidgetSet.MonitorFromPoint(ptScreenCoords: TPoint; dwFlags: DWord
  ): HMONITOR;
var
  pscr:PGdkScreen;
  nmon:gint;
begin
  pscr:=TGdkScreen.get_default;
  nmon:=pscr^.get_monitor_at_point(ptScreenCoords.X,ptScreenCoords.Y);
  Result:=HMONITOR(nmon+1);
end;

function TGtk3WidgetSet.MonitorFromWindow(hWnd: HWND; dwFlags: DWord): HMONITOR;
var
  pscr:PGdkScreen;
  nmon:gint;
begin
  pscr:=TGdkScreen.get_default;
  nmon:=pscr^.get_monitor_at_window(TGtk3Widget(hWnd).GetWindow);
  Result:=HMONITOR(nmon+1);
end;

function TGtk3WidgetSet.MoveToEx(DC: HDC; X, Y: Integer; OldPoint: PPoint
  ): Boolean;
begin
  if not IsValidDC(DC) then
    exit(False);
  Result := TGtk3DeviceContext(DC).MoveTo(X, Y, OldPoint);
end;

function TGtk3WidgetSet.OffsetRgn(RGN: HRGN; nXOffset, nYOffset: Integer
  ): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.OffsetRgn not implemented ...');
  {$ENDIF}
  Result:=inherited OffsetRgn(RGN, nXOffset, nYOffset);
end;

function TGtk3WidgetSet.PaintRgn(DC: HDC; RGN: HRGN): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.PaintRgn not implemented ...');
  {$ENDIF}
  Result:=inherited PaintRgn(DC, RGN);
end;

function TGtk3WidgetSet.PeekMessage(var lpMsg: TMsg; Handle: HWND;
  wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.PeekMessage not implemented ...');
  {$ENDIF}
  Result:=inherited PeekMessage(lpMsg, Handle, wMsgFilterMin, wMsgFilterMax,
    wRemoveMsg);
end;

function TGtk3WidgetSet.PolyBezier(DC: HDC; Points: PPoint; NumPts: Integer;
  Filled, Continuous: boolean): boolean;
begin
  if not IsValidDC(DC) then
    exit(False);
  TGtk3DeviceContext(DC).drawPolyBezier(Points, NumPts, Filled, Continuous);
  Result:=True;
end;

function TGtk3WidgetSet.Polygon(DC: HDC; Points: PPoint; NumPts: Integer;
  Winding: boolean): boolean;
var
  NFillRule: integer;
  bFill, bBorder: boolean;
begin
  if not IsValidDC(DC) then
    exit(False);
  if Winding then
    NFillRule := Ord(CAIRO_FILL_RULE_WINDING)
  else
    NFillRule := Ord(CAIRO_FILL_RULE_EVEN_ODD);
  bFill := TGtk3DeviceContext(DC).CurrentBrush.Style <> BS_NULL;
  bBorder := TGtk3DeviceContext(DC).CurrentPen.Style <> psClear;
  TGtk3DeviceContext(DC).drawPolygon(Points, NumPts, NFillRule, bFill, bBorder);
  Result:= True;
end;

function TGtk3WidgetSet.Polyline(DC: HDC; Points: PPoint; NumPts: Integer): boolean;
begin
  if not IsValidDC(DC) then
    exit(False);
  if TGtk3DeviceContext(DC).CurrentPen.Style = psClear then Exit;
  TGtk3DeviceContext(DC).drawPolyLine(Points, NumPts);
  Result:=True;
end;


type
  PCustomGtk3Message = ^TCustomGtk3Message;
  TCustomGtk3Message = record
    Handle: HWND;
    Msg: Cardinal;
    AwParam: WParam;
    AlParam: LParam;
    Result: LRESULT;
  end;

function Gtk3ProcessPostMessage(user_data: gpointer): gboolean; cdecl;
var
  AMsg: TCustomGtk3Message;
  AMessage: TLMessage;
begin
  Result := False;
  if user_data <> nil then
  begin
    AMsg := TCustomGtk3Message(user_data^);
    if AMsg.Handle <> 0 then
    begin
      FillChar(AMessage{%H-}, SizeOf(AMessage), #0);
      AMessage.Msg := AMsg.Msg;
      AMessage.WParam := AMsg.AwParam;
      AMessage.LParam := AMsg.AlParam;
      TGtk3Widget(AMsg.Handle).DeliverMessage(AMessage);
    end;
    g_idle_remove_by_data(user_data);
    Freemem(user_data);
    user_data := nil;
    Result := True;
  end;
end;

function TGtk3WidgetSet.PostMessage(Handle: HWND; Msg: Cardinal;
  wParam: WParam; lParam: LParam): Boolean;
var
  AEvent: PCustomGtk3Message;
begin
  Result := False;
  if Handle <> 0 then
  begin
    AEvent := GetMem(SizeOf(TCustomGtk3Message));
    AEvent^.Handle := Handle;
    AEvent^.Msg := Msg;
    AEvent^.AwParam := wParam;
    AEvent^.AlParam := lParam;
    AEvent^.Result := 0;
    g_idle_add(@Gtk3ProcessPostMessage, AEvent);
    if GetCurrentThreadId <> MainThreadID then
    begin
      // writeln('TGtk3WidgetSet.PostMessage from different thread !');
      g_main_context_wakeup(g_main_context_default);
    end;
    Result := True;
  end;
end;

function TGtk3WidgetSet.PtInRegion(RGN: HRGN; X, Y: Integer): Boolean;
begin
  Result := False;
  if IsValidGDIObject(RGN) then
    Result := TGtk3Region(RGN).ContainsPoint(Point(X, Y));
end;

function TGtk3WidgetSet.RadialArc(DC: HDC; left, top, right, bottom, sx, sy,
  ex, ey: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.RadialArc not implemented ...');
  {$ENDIF}
  Result:=inherited RadialArc(DC, left, top, right, bottom, sx, sy, ex, ey);
end;

function TGtk3WidgetSet.RadialChord(DC: HDC; x1, y1, x2, y2, sx, sy, ex,
  ey: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.RadialChord not implemented ...');
  {$ENDIF}
  Result:=inherited RadialChord(DC, x1, y1, x2, y2, sx, sy, ex, ey);
end;

function TGtk3WidgetSet.RealizePalette(DC: HDC): Cardinal;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.RealizePalette not implemented ...');
  {$ENDIF}
  Result := inherited RealizePalette(DC);
end;

function TGtk3WidgetSet.Rectangle(DC: HDC; X1, Y1, X2, Y2: Integer): Boolean;
var
  R: TRect;
  bFill, bBorder: Boolean;
begin
  if not IsValidDC(DC) then
    exit(False);
  R := NormalizeRect(Rect(X1, Y1, X2, Y2));
  if IsRectEmpty(R) then Exit(True);
  bFill := TGtk3DeviceContext(DC).CurrentBrush.Style <> BS_NULL;
  bBorder := TGtk3DeviceContext(DC).CurrentPen.Style <> psClear;
  TGtk3DeviceContext(DC).drawRect(R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top, bFill, bBorder);
  Result := True;
end;

function TGtk3WidgetSet.RectInRegion(RGN: HRGN; ARect: TRect): Boolean;
begin
  Result := False;
  if IsValidGDIObject(RGN) then
    Result := TGtk3Region(Rgn).ContainsRect(ARect);
end;

function TGtk3WidgetSet.RectVisible(dc: hdc; const ARect: TRect): Boolean;
var
  ACairoRegion: Pcairo_region_t;
  ACairoRect: Tcairo_rectangle_int_t;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.RectVisible not implemented ...');
  {$ENDIF}
  Result := False;
  if not IsValidDC(DC) then
    exit;
  if (TGtk3DeviceContext(DC).Parent <> nil) and
    Gtk3IsGdkWindow(TGtk3DeviceContext(DC).Parent^.window) then
  begin
    if not gdk_window_is_visible(TGtk3DeviceContext(DC).Parent^.window) then
      exit;
    ACairoRegion := gdk_window_get_visible_region(TGtk3DeviceContext(DC).Parent^.window);
  end else
    ACairoRegion := gdk_window_get_visible_region(gdk_get_default_root_window);
  ACairoRect.x := ARect.Left;
  ACairoRect.y := ARect.Top;
  ACairoRect.width := ARect.Right - ARect.Left;
  ACairoRect.height := ARect.Bottom - ARect.Top;
  Result := cairo_region_contains_rectangle(ACairoRegion, @ACairoRect) <> CAIRO_REGION_OVERLAP_OUT;
end;

function TGtk3WidgetSet.RegroupMenuItem(hndMenu: HMENU; GroupIndex: integer
  ): Boolean;
begin
  Result := False;
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.RegroupMenuItem not implemented ...');
  {$ENDIF}
  // inherited RegroupMenuItem(hndMenu, GroupIndex);
end;

function TGtk3WidgetSet.ReleaseCapture: Boolean;
var
  AWidget: TGtk3Widget;
begin
  {$IFDEF VerboseGtk3WinApi}
  DebugLn('TGtk3WidgetSet.ReleaseCapture');
  {$ENDIF}
  AWidget := TGtk3Widget(GetCapture);
  Result := AWidget <> nil;
  if Result then
  begin
    if AWidget.Widget = nil then exit;
    if AWidget.GetContainerWidget^.has_grab then
      gtk_grab_remove(AWidget.GetContainerWidget)
    else
    if AWidget.Widget^.has_grab then
      gtk_grab_remove(AWidget.Widget);
  end;
end;

function TGtk3WidgetSet.ReleaseDC(hWnd: HWND; DC: HDC): Integer;
begin
  Result := 0;
  if IsValidDC(DC) then
  begin
    if TGtk3DeviceContext(DC).CanRelease then
      TGtk3DeviceContext(DC).Free;
    Result := 1;
  end;
end;

function TGtk3WidgetSet.RemoveProp(Handle: hwnd; Str: PChar): TLCLHandle;
begin
  Result := 0;
  if Handle = 0 then
    exit;
  if Gtk3IsObject(TGtk3Widget(Handle).Widget) then
    g_object_set_data(TGtk3Widget(Handle).Widget, Str, nil);
  if TGtk3Widget(Handle).GetContainerWidget <> TGtk3Widget(Handle).Widget then
  begin
    if Gtk3IsObject(TGtk3Widget(Handle).GetContainerWidget) then
      g_object_set_data(TGtk3Widget(Handle).GetContainerWidget, Str, nil);
  end;
  Result := 1;
end;

function TGtk3WidgetSet.RestoreDC(DC: HDC; SavedDC: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.RestoreDC not implemented ...');
  {$ENDIF}
  Result := False;
  if not IsValidDC(DC) then
    exit;
  cairo_restore(TGtk3DeviceContext(DC).pcr);
  Result := True;
end;

function TGtk3WidgetSet.RoundRect(DC: hDC; X1, Y1, X2, Y2: Integer; RX,
  RY: Integer): Boolean;
begin
  Result := False;
  if not IsValidDC(DC) then
    exit;
  Result := TGtk3DeviceContext(DC).RoundRect(X1, Y1, X2, Y2, RX, RY);
end;

function TGtk3WidgetSet.SaveDC(DC: HDC): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.SaveDC not implemented ...');
  {$ENDIF}
  Result := 0;
  if not IsValidDC(DC) then
    exit;
  cairo_save(TGtk3DeviceContext(DC).pcr);
  Result := 1;
end;

function TGtk3WidgetSet.ScreenToClient(Handle: HWND; var P: TPoint): Integer;
begin
  Result := 0;
  {$ifdef VerboseGtk3WinApi}
  DebugLn('Trace:> [TGtk3WidgetSet.ScreenToClient] ',dbgs(P));
  {$endif}
  if not IsValidHandle(Handle) then
    exit;

  Result := TGtk3Widget(Handle).ScreenToClient(P);
end;

function TGtk3WidgetSet.ScrollWindowEx(hWnd: HWND; dx, dy: Integer; prcScroll,
  prcClip: PRect; hrgnUpdate: HRGN; prcUpdate: PRect; flags: UINT): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.ScrollWindowEx not implemented ...');
  {$ENDIF}
  Result:=inherited ScrollWindowEx(hWnd, dx, dy, prcScroll, prcClip,
    hrgnUpdate, prcUpdate, flags);
end;

function TGtk3WidgetSet.SelectClipRGN(DC: hDC; RGN: HRGN): Longint;
begin
  Result := 0;
  if IsValidDC(DC) then
  begin
    if IsValidGDIObject(RGN) then
      Result := TGtk3DeviceContext(DC).setClipRegion(TGtk3Region(RGN))
    else
      Result := TGtk3DeviceContext(DC).ResetClip;
  end;
end;

function TGtk3WidgetSet.SelectObject(DC: HDC; GDIObj: HGDIOBJ): HGDIOBJ;
begin
  Result := 0;
  if not IsValidDC(DC) then
    exit;
  if IsValidGDIObject(GDIObj) then
  begin
    Result:=HGDIOBJ(TGtk3ContextObject(GDIOBJ).Select(TGtk3DeviceContext(DC)));
  end;
end;

function TGtk3WidgetSet.SelectPalette(DC: HDC; Palette: HPALETTE;
  ForceBackground: Boolean): HPALETTE;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SelectPalette not implemented ...');
  {$ENDIF}
  Result := inherited SelectPalette(DC, Palette, ForceBackground);
end;

function TGtk3WidgetSet.SendMessage(HandleWnd: HWND; Msg: Cardinal;
  wParam: WParam; lParam: LParam): LResult;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SendMessage not implemented ...');
  {$ENDIF}
  Result := inherited SendMessage(HandleWnd, Msg, wParam, lParam);
end;

function TGtk3WidgetSet.SetActiveWindow(Handle: HWND): HWND;
begin
  Result := GetActiveWindow;
  if Handle <> 0 then
  begin
    if wtWindow in TGtk3Widget(Handle).WidgetType then
      PGtkWindow(TGtk3Window(Handle).Widget)^.present;
  end;
end;

function TGtk3WidgetSet.SetBkColor(DC: HDC; Color: TColorRef): TColorRef;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.SetBkColor not implemented ...');
  {$ENDIF}
  Result := clNone;
  if not IsValidDC(DC) then
    exit;
  Result := TGtk3DeviceContext(DC).CurrentBrush.Color;
  TGtk3DeviceContext(DC).CurrentBrush.Color := TColor(ColorToRGB(TColor(Color)));
end;

function TGtk3WidgetSet.SetBkMode(DC: HDC; bkMode: Integer): Integer;
begin
  {.$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.SetBkMode not implemented ...', dbgs(BkMode));
  {.$ENDIF}
  Result := 0;
  if not IsValidDC(DC) then
    exit;
  Result := TGtk3DeviceContext(DC).BkMode;
  TGtk3DeviceContext(DC).BkMode := bkMode;
  // if cairo_pattern_get_type(cairo_get_source(TGtk3DeviceContext(DC).Widget)) = CAIRO_PATTERN_TYPE_SURFACE then
  //  Result := TRANSPARENT;
  // we must use TGtk3Brush.Handle = Pcairo_pattern_t
  // cairo_pattern_get_type(nil).CAIRO_PATTERN_TYPE_SOLID;
  // cairo_get_source();
end;

function TGtk3WidgetSet.SetCapture(AHandle: HWND): HWND;
var
  Message: TLMessage;
begin
  {$IFDEF VerboseGtk3WinApi}
  DebugLn('TGtk3WidgetSet.SetCapture');
  {$ENDIF}
  Result := GetCapture;
  if Result <> AHandle then
  begin
    if Result <> 0 then
      ReleaseCapture;
    if IsValidHandle(AHandle) then
    begin
      TGtk3Widget(AHandle).SetCapture;
      Message.Msg := 0;
      FillChar(Message, SizeOf(Message), 0);
      Message.msg := LM_CAPTURECHANGED;
      Message.wParam := 0;
      Message.lParam := PtrInt(Result);
      LCLMessageGlue.DeliverMessage(TGtk3Widget(AHandle).LCLObject, Message);
    end;
  end;
end;

function TGtk3WidgetSet.SetCaretPos(X, Y: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetCaretPos not implemented ...');
  {$ENDIF}
  Result:=inherited SetCaretPos(X, Y);
end;

function TGtk3WidgetSet.SetCaretPosEx(Handle: HWnd; X, Y: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetCaretPosEx not implemented ...');
  {$ENDIF}
  Result:=inherited SetCaretPosEx(Handle, X, Y);
end;

function TGtk3WidgetSet.SetCaretRespondToFocus(handle: HWND;
  ShowHideOnFocus: boolean): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetCaretRespondToFocus not implemented ...');
  {$ENDIF}
  Result:=inherited SetCaretRespondToFocus(handle, ShowHideOnFocus);
end;

function TGtk3WidgetSet.SetCursor(ACursor: HCURSOR): HCURSOR;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.SetCursor not implemented ...');
  {$ENDIF}
  Result := FGlobalCursor;
  if ACursor = FGlobalCursor then Exit;
  if ACursor = Screen.Cursors[crDefault]
  then SetGlobalCursor(0)
  else SetGlobalCursor(ACursor);
  FGlobalCursor := ACursor;
end;

function TGtk3WidgetSet.SetCursorPos(X, Y: Integer): Boolean;
var
  ADeviceManager: PGdkDeviceManager;
  APointer: PGdkDevice;
begin
  ADeviceManager := gdk_display_get_device_manager(gdk_display_get_default);
  APointer := gdk_device_manager_get_client_pointer(ADeviceManager);
  // howto get what screen we are querying on ?
  // gdk_display_get_screen(gdk_display_get_default, 0);
  gdk_device_warp(APointer, gdk_screen_get_default, X, Y);
  Result := True;
end;

function TGtk3WidgetSet.SetFocus(hWnd: HWND): HWND;
begin
  Result := GetFocus;
  if hWnd <> 0 then
  begin
    {$IFDEF GTK3DEBUGFOCUS}
    if Result <> 0 then
      DebugLn('TGtk3WidgetSet.SetFocus: ',dbgsName(TGtk3Widget(HWND).LCLObject),' oldFocus ',dbgsName(TGtk3Widget(Result).LCLObject))
    else
      DebugLn('TGtk3WidgetSet.SetFocus: ',dbgsName(TGtk3Widget(HWND).LCLObject),' oldFocus 0');
    {$ENDIF}
    TGtk3Widget(HWND).setFocus;
  end;
end;

function TGtk3WidgetSet.SetForegroundWindow(hWnd: HWND): boolean;
var
  AWindow: TGtk3Window;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.SetForegroundWindow not implemented ...');
  {$ENDIF}
  if not IsValidHandle(HWnd) then
    exit(False);
  Result := wtWindow in TGtk3Widget(HWND).WidgetType;
  if Result then
  begin
    AWindow := TGtk3Window(HWND);
    if not AWindow.Visible then
      exit(False);
    // DebugLn('TGtk3WidgetSet.SetForegroundWindow ',dbgsName(AWindow.LCLObject));
    AWindow.Activate;
    Result := True;
  end;
end;

function TGtk3WidgetSet.SetMapMode(DC: HDC; fnMapMode: Integer): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetMapMode not implemented ...');
  {$ENDIF}
  Result:=inherited SetMapMode(DC, fnMapMode);
end;

function TGtk3WidgetSet.SetParent(hWndChild: HWND; hWndParent: HWND): HWND;
begin
  Result := HWND(TGtk3Widget(hWndChild).getParent);
  TGtk3Widget(hWndChild).SetParent(TGtk3Widget(hWndParent),0,0)
end;

function TGtk3WidgetSet.SetProp(Handle: hwnd; Str: PChar; Data: Pointer
  ): Boolean;
begin
  if Handle = 0 then
    exit(False);
  if Gtk3IsObject(TGtk3Widget(Handle).Widget) then
    g_object_set_data(TGtk3Widget(Handle).Widget, Str, Data);
  if TGtk3Widget(Handle).GetContainerWidget <> TGtk3Widget(Handle).Widget then
  begin
    if Gtk3IsObject(TGtk3Widget(Handle).GetContainerWidget) then
      g_object_set_data(TGtk3Widget(Handle).GetContainerWidget, Str, Data);
  end;
  Result := True;
end;

function TGtk3WidgetSet.SetRectRgn(aRGN: HRGN; X1, Y1, X2, Y2: Integer
  ): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetRectRgn not implemented ...');
  {$ENDIF}
  Result:=inherited SetRectRgn(aRGN, X1, Y1, X2, Y2);
end;

function TGtk3WidgetSet.SetROP2(DC: HDC; Mode: Integer): Integer;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetROP2 not implemented ...');
  {$ENDIF}
  Result:=inherited SetROP2(DC, Mode);
end;

function TGtk3WidgetSet.SetScrollInfo(Handle: HWND; SBStyle: Integer;
  ScrollInfo: TScrollInfo; bRedraw: Boolean): Integer;

  (*

  procedure SetRangeUpdatePolicy(Range: PGtkRange);
  var
    UpdPolicy: TGTKUpdateType;
  begin
    case ScrollInfo.nTrackPos of
      SB_POLICY_DISCONTINUOUS: UpdPolicy := GTK_UPDATE_DISCONTINUOUS;
      SB_POLICY_DELAYED:  UpdPolicy := GTK_UPDATE_DELAYED;
      else
        UpdPolicy := GTK_UPDATE_CONTINUOUS;
    end;
    !!! update policy for gtkRange does not exist anymore in gtk3
    so we must mimic that by using events. !!!
    gtk_range_set_update_policy(Range, UpdPolicy);
  end;
  procedure SetScrolledWindowUpdatePolicy(ScrolledWindow:PGTKScrolledWindow);
  var
    Range: PGtkRange;
  begin
    case SBStyle of
      SB_VERT: Range := PGtkRange(ScrolledWindow^.vscrollbar);
      SB_HORZ: Range := PGtkRange(ScrolledWindow^.hscrollbar);
      else exit;
    end;
    SetRangeUpdatePolicy(Range);
  end;
  *)

const
  POLICY: array[BOOLEAN] of TGTKPolicyType = (GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);

var
  Adjustment,hadj,vadj: PGtkAdjustment;
  AWidget: TGtk3Widget;
  ACenter:PGtkWidget;
  ARange: PGtkRange;
  AScrollWin: PGtkScrolledWindow;
  IsScrollbarVis: Boolean;
  w,h:gint;
begin
  Result := 0;
  if not IsValidHandle(Handle) then
    exit;

  AWidget := TGtk3Widget(Handle);

  Adjustment := nil;
  ARange := nil;
  AScrollWin := nil;

  if wtScrollBar in AWidget.WidgetType then
  begin
    ARange := PGtkRange(AWidget.Widget);
    Adjustment := ARange^.adjustment;
  end else
  if wtScrollingWin in AWidget.WidgetType then
  begin
    AScrollWin := TGtk3ScrollableWin(Handle).GetScrolledWindow;
    if AScrollWin = nil then
      exit;
    if not Gtk3IsScrolledWindow(AScrollWin) then
    begin
      DebugLn('ERROR: TGtk3WidgetSet.SetScrollInfo: Wrong class extracted for scrollwin ',dbgsName(TGtk3Widget(Handle).LCLObject));
      AScrollWin := nil;
    end;
    if Assigned(AScrollWin) then
    begin
      hadj := AScrollWin^.get_hadjustment;
      vadj := AScrollWin^.get_vadjustment;
    end else
    begin
      hadj:=nil;
      vadj:=nil;
    end;
  end;

  case SBStyle of
    SB_Horz:
    begin
      if not Assigned(Adjustment) and Assigned(AScrollWin) then
      begin
        Adjustment := hadj;
        if (ScrollInfo.fMask and SIF_RANGE) <> 0 then
        begin
          ACenter:=AWidget.GetContainerWidget;
          Acenter^.set_size_request(ScrollInfo.nMax - ScrollInfo.nMin,round(vadj^.upper));
        end;
      end;
    end;
    SB_Vert:
    begin
      if not Assigned(Adjustment) and Assigned(AScrollWin) then
      begin
        Adjustment := vadj;
        if (ScrollInfo.fMask and SIF_RANGE) <> 0 then
        begin
          ACenter:=AWidget.GetContainerWidget;
          Acenter^.set_size_request(round(hadj^.upper),ScrollInfo.nMax - ScrollInfo.nMin);
        end;
      end;
    end;
    SB_CTL:
    begin
      DebugLn('TGtk3WidgetSet.SetScrollInfo SB_CTL error: not implemented ',
        dbgsName(AWidget.LCLObject));
    end;
    SB_BOTH:
    begin
      DebugLn('TGtk3WidgetSet.SetScrollInfo SB_BOTH error: not implemented  ',
        dbgsName(AWidget.LCLObject));
    end;
  end;

  if Adjustment = nil then
  begin
    DebugLn('TGtk3WidgetSet.SetScrollInfo error: cannot get PGtkAdjustment from ',
      dbgsName(AWidget.LCLObject));
    exit;
  end;

  if (ScrollInfo.fMask and SIF_RANGE) <> 0 then
  begin
    Adjustment^.lower := ScrollInfo.nMin;
    Adjustment^.upper := ScrollInfo.nMax;
  end;
  if (ScrollInfo.fMask and SIF_PAGE) <> 0 then
  begin
    // 0 <= nPage <= nMax-nMin+1
    Adjustment^.page_size := ScrollInfo.nPage;
    Adjustment^.page_size := Min(Max(Adjustment^.page_size,0),
                                 Adjustment^.upper-Adjustment^.lower+1);
    Adjustment^.page_increment := (Adjustment^.page_size/6)+1;
  end;
  if (ScrollInfo.fMask and SIF_POS) <> 0 then
  begin
    // nMin <= nPos <= nMax - Max(nPage-1,0)
    Adjustment^.value := ScrollInfo.nPos;
    Adjustment^.value := Max(Adjustment^.value,Adjustment^.lower);
    Adjustment^.value := Min(Adjustment^.value,
                              Adjustment^.upper-Max(Adjustment^.page_size-1,0));
  end;

  // check if scrollbar should be hidden
  IsScrollbarVis := True;
  if ((ScrollInfo.fMask and (SIF_RANGE or SIF_PAGE)) <> 0) and
     ((SBStyle=SB_HORZ) or (SBStyle=SB_VERT)) then
  begin
    if (Adjustment^.lower >= (Adjustment^.upper-Max(adjustment^.page_size-1,0)))
    then begin
      if (ScrollInfo.fMask and SIF_DISABLENOSCROLL) = 0 then
        IsScrollbarVis := False
      else
        ;// scrollbar should look disabled (no thumbbar and grayed appearance)
         // maybe not possible in gtk
    end;
  end;
  if bRedraw then
  begin
    if (AScrollWin <> nil) then
    begin
      // DebugLn('Setting scrollstyle of ',dbgsName(AWidget.LCLObject));
      if SBStyle = SB_HORZ then
        TGtk3ScrollableWin(AWidget).HScrollBarPolicy := POLICY[IsScrollbarVis]
      else
      if SBStyle = SB_VERT then
        TGtk3ScrollableWin(AWidget).VScrollBarPolicy := POLICY[IsScrollbarVis];
    end else
      AWidget.Update(nil);

    Adjustment^.changed;

  end;
  Result := Round(Adjustment^.value);
end;

function TGtk3WidgetSet.SetSysColors(cElements: Integer; const lpaElements;
  const lpaRgbValues): Boolean;
begin
  Result:=inherited SetSysColors(cElements, lpaElements, lpaRgbValues);
end;

function TGtk3WidgetSet.SetTextCharacterExtra(DC: hdc; nCharExtra: Integer
  ): Integer;
begin
  Result:=inherited SetTextCharacterExtra(DC, nCharExtra);
end;

function TGtk3WidgetSet.SetTextColor(DC: HDC; Color: TColorRef): TColorRef;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  // DebugLn('WARNING: TGtk3WidgetSet.SetTextColor not implemented ...');
  {$ENDIF}
  Result := CLR_INVALID;
  if IsValidDC(DC) then
  begin
    Result := TGtk3DeviceContext(DC).CurrentTextColor;
    TGtk3DeviceContext(DC).CurrentTextColor := Color;
  end;
end;

function TGtk3WidgetSet.SetViewPortExtEx(DC: HDC; XExtent, YExtent: Integer;
  OldSize: PSize): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetViewPortExtEx not implemented ...');
  {$ENDIF}
  Result:=inherited SetViewPortExtEx(DC, XExtent, YExtent, OldSize);
end;

function TGtk3WidgetSet.SetViewPortOrgEx(DC: HDC; NewX, NewY: Integer;
  OldPoint: PPoint): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetViewPortOrgEx not implemented ...');
  {$ENDIF}
  Result:=inherited SetViewPortOrgEx(DC, NewX, NewY, OldPoint);
end;

function TGtk3WidgetSet.SetWindowExtEx(DC: HDC; XExtent, YExtent: Integer;
  OldSize: PSize): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetWindowExtEx not implemented ...');
  {$ENDIF}
  Result:=inherited SetWindowExtEx(DC, XExtent, YExtent, OldSize);
end;

function TGtk3WidgetSet.SetWindowLong(Handle: HWND; Idx: Integer;
  NewLong: PtrInt): PtrInt;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetWindowLong not implemented ...');
  {$ENDIF}
  Result:=inherited SetWindowLong(Handle, Idx, NewLong);
end;

function TGtk3WidgetSet.SetWindowOrgEx(dc: hdc; NewX, NewY: Integer;
  OldPoint: PPoint): Boolean;
var
  Matrix: Tcairo_matrix_t;
  //dx, dy: Double;
begin
  Result := False; // inherited SetWindowOrgEx(dc, NewX, NewY, OldPoint);
  if IsValidDC(DC) then
  begin
    GetWindowOrgEx(dc, OldPoint);
    cairo_get_matrix(TGtk3DeviceContext(DC).pcr, @Matrix);
    //dx := 0;
    //dy := 0;
    // cairo_matrix_init_translate(Matrix, -NewX, -NewY);
    cairo_matrix_translate(@Matrix,
    	-NewX - TGtk3DeviceContext(DC).fncOrigin.x,
        -NewY - TGtk3DeviceContext(DC).fncOrigin.Y);
    cairo_transform(TGtk3DeviceContext(DC).pcr, @Matrix);
    // cairo_set_matrix(TGtk3DeviceContext(DC).Widget, Matrix);
    // DebugLn('TGtk3WidgetSet.SetWindowOrgEx NewX=',dbgs(NewX),' NewY=',dbgs(NewY));
    Result := True;

  end;
end;

function TGtk3WidgetSet.SetWindowPos(hWnd: HWND; hWndInsertAfter: HWND; X, Y,
  cx, cy: Integer; uFlags: UINT): Boolean;
begin
  Result := False;
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.SetWindowPos not implemented Handle=',dbgHex(hWnd),' X=',dbgs(X),' Y=',dbgs(Y));
  {$ENDIF}
end;

function TGtk3WidgetSet.SetWindowRgn(hWnd: HWND; hRgn: HRGN; bRedraw: Boolean
  ): longint;
var
  PCR: Pcairo_region_t;
begin
  if hRgn = 0 then
    PCR := nil
  else
    PCR := TGtk3Region(hRgn).Handle;
  gtk_widget_shape_combine_region(TGtk3Widget(hWnd).Widget, PCR);
  if bRedraw then
    TGtk3Widget(hWnd).Widget^.realize;
  Result:=1;
end;

function TGtk3WidgetSet.ShowCaret(hWnd: HWND): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.ShowCaret not implemented ...');
  {$ENDIF}
  Result:=inherited ShowCaret(hWnd);
end;

function TGtk3WidgetSet.ShowScrollBar(Handle: HWND; wBar: Integer;
  bShow: Boolean): Boolean;
var
  AWidget: TGtk3Widget;
  // AScrolledWin: PGtkScrolledWindow;
  NewPolicy: TGtkPolicyType;
begin
  Result := IsValidHandle(Handle);
  if not Result then
    exit;
  AWidget := TGtk3Widget(Handle);
  if wtScrollBar in AWidget.WidgetType then
  begin
    AWidget.Visible := bShow;
  end else
  (*
  if wtWindow in AWidget.WidgetType then
  begin
    DebugLn('WARNING: TGtk3WidgetSet.ShowScrollBar cannot get scrollbar from ',dbgsName(AWidget.LCLObject),
    ' bShow ',dbgs(bShow));
  end else
  *)
  if wtScrollingWin in AWidget.WidgetType then
  begin
    // AScrolledWin :=
    if TGtk3ScrollableWin(Handle).GetScrolledWindow = nil then
      exit;
    if wBar in [SB_BOTH, SB_HORZ] then
    begin
      if bShow then
        NewPolicy := GTK_POLICY_ALWAYS
      else
        NewPolicy := GTK_POLICY_NEVER;

      // bug in gtk3
      if (wtWindow in AWidget.WidgetType) and (NewPolicy = GTK_POLICY_NEVER) then
        NewPolicy := GTK_POLICY_AUTOMATIC;

      TGtk3ScrollableWin(AWidget).HScrollBarPolicy := NewPolicy;
    end;
    if wBar in [SB_BOTH, SB_VERT] then
    begin
      if bShow then
        NewPolicy := GTK_POLICY_ALWAYS
      else
        NewPolicy := GTK_POLICY_NEVER;

      // bug in gtk3
      if (wtWindow in AWidget.WidgetType) and (NewPolicy = GTK_POLICY_NEVER) then
        NewPolicy := GTK_POLICY_AUTOMATIC;

      TGtk3ScrollableWin(AWidget).VScrollBarPolicy := NewPolicy;
    end;
  end else
    DebugLn('WARNING: TGtk3WidgetSet.ShowScrollBar cannot get scrollbar from ',dbgsName(AWidget.LCLObject));
end;

function TGtk3WidgetSet.ShowWindow(hWnd: HWND; nCmdShow: Integer): Boolean;
begin
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.ShowWindow not implemented ...');
  {$ENDIF}
  Result := IsValidHandle(Hwnd);
  if not result then exit;
  if TObject(hWnd) is TGtk3Window then
    Result:=TGtk3Window(hWnd).ShowState(nCmdShow)
  else
  begin
    TGtk3Widget(hWnd).Show;
    Result:=true;
  end;
end;

function TGtk3WidgetSet.StretchBlt(DestDC: HDC; X, Y, Width, Height: Integer;
  SrcDC: HDC; XSrc, YSrc, SrcWidth, SrcHeight: Integer; ROp: Cardinal): Boolean;
begin
  Result := StretchMaskBlt(DestDC,X,Y,Width,Height,
                          SrcDC,XSrc,YSrc,SrcWidth,SrcHeight,
                          0,0,0,
                          ROp);
end;

function TGtk3WidgetSet.StretchMaskBlt(DestDC: HDC; X, Y, Width,
  Height: Integer; SrcDC: HDC; XSrc, YSrc, SrcWidth, SrcHeight: Integer;
  Mask: HBITMAP; XMask, YMask: Integer; Rop: DWORD): Boolean;

var
  DestContext: TGtk3DeviceContext absolute DestDC;
  SrcContext: TGtk3DeviceContext absolute SrcDC;
  ATargetRect, ASrcRect: TRect;
begin
  Result := False;
  {$IFDEF GTK3DEBUGNOTIMPLEMENTED}
  DebugLn('WARNING: TGtk3WidgetSet.StretchMaskBlt not implemented ...');
  {$ENDIF}
  ATargetRect := Rect(X, Y, Width + X, Height + Y);
  ASrcRect := Rect(XSrc, YSrc, SrcWidth + XSrc, SrcHeight + YSrc);

  if (DestContext.OwnsSurface) and (SrcContext.OwnsSurface)  or (mask<>0) then
    DestContext.drawImage1(@ATargetRect, PgdkPixbuf(SrcContext.CurrentImage.Handle) , @ASrcRect, nil, nil)
  else
    DestContext.drawSurface(@ATargetRect,SrcContext.CairoSurface , @ASrcRect, nil, nil);

  Result := True;
end;

function TGtk3WidgetSet.SystemParametersInfo(uiAction: DWord; uiParam: DWord;
  pvParam: Pointer; fWinIni: DWord): LongBool;
var
  wa:TGdkRectangle;
  pdisp:PGdkDisplay;
  pmon:PgdkMonitor;
  pr:PRect;
begin
  Result:=True;
  Case uiAction of
    SPI_GETWHEELSCROLLLINES: PDword(pvParam)^ := 3;
    SPI_GETWORKAREA: begin
      pdisp:=TGdkDisplay.get_default;
      pmon:=pdisp^.get_primary_monitor();
      if not Assigned(pmon) then begin
        if pdisp^.get_n_monitors = 0 then
          Exit(False);
        pmon := pdisp^.get_monitor(0);
      end;
      pmon^.get_workarea(@wa);
      pr:=PRect(pvParam);
      pr^.Left:=wa.x;
      pr^.Top:=wa.y;
      pr^.Width:=wa.width;
      pr^.Height:=wa.height;
    end;
  else
    Result:=False;
  end;
end;

function TGtk3WidgetSet.TextOut(DC: HDC; X, Y: Integer; Str: PChar; Count: Integer): Boolean;
begin
  {$IFDEF VerboseGtk3DeviceContext}
  DebugLn('TGtk3WidgetSet.TextOut X=',dbgs(x),' Y=',dbgs(y),' Text=',dbgs(Str),' Count=',dbgs(Count));
  {$ENDIF}
  Result := False;
  if Count <= 0 then
    Exit;
  if IsValidDC(DC) then
  begin
    Result := True;
    TGtk3DeviceContext(DC).drawText(X, Y, Str, Count);
  end;
end;

function TGtk3WidgetSet.UpdateWindow(Handle: HWND): Boolean;
begin
  {$ifdef VerboseGtk3WinAPI}
  DebugLn('[Gtk3WinAPI UpdateWindow]');
  {$endif}
  Result := False;
  if IsValidHandle(Handle) then
  begin
    TGtk3Widget(Handle).Update(nil);
    if TGtk3Widget(Handle).GetContainerWidget^.get_has_window then
    begin
      if Gtk3IsGdkWindow(TGtk3Widget(Handle).GetContainerWidget^.window) then
        TGtk3Widget(Handle).GetContainerWidget^.window^.process_updates(True);
    end else
    if TGtk3Widget(Handle).Widget^.get_has_window then
    begin
      if Gtk3IsGdkWindow(TGtk3Widget(Handle).Widget^.window) then
        TGtk3Widget(Handle).Widget^.window^.process_updates(True);
    end;
    Result := True;
  end;
end;

function TGtk3WidgetSet.WindowFromPoint(APoint: TPoint): HWND;
var
  ev: TGdkEvent;
  ADeviceManager: PGdkDeviceManager;
  APointer: PGdkDevice;
  AWindow: PGdkWindow;
  AWidget: PGtkWidget;
  x: gint;
  y: gint;
begin
  //TODO: create caching mechanism. window_at_position is pretty expensive call.
  Result := 0;
  ADeviceManager := gdk_display_get_device_manager(gdk_display_get_default);
  APointer := gdk_device_manager_get_client_pointer(ADeviceManager);
  APointer^.get_position(nil, @x ,@y);
  AWindow := gdk_device_get_window_at_position(APointer, @APoint.X, @APoint.Y);
  if AWindow <> nil then
  begin
    FillChar(ev{%H-}, SizeOf(ev), 0);
    ev.any.window := AWindow;
    AWidget := gtk_get_event_widget(@ev);
    Result := HwndFromGtkWidget(AWidget);
    (*
    if Result <> 0 then
    begin
      DebugLn('TGtk3WidgetSet.WindowFromPoint ',dbgsName(TGtk3Widget(Result).LCLObject));
    end else
      DebugLn('Cannot find window under point ',dbgs(APoint));
    *)
  end;
end;
