unit uCEFLinuxFunctions;

{$IFDEF FPC}
  {$MODE OBJFPC}{$H+}
{$ENDIF}

{$I cef.inc}

{$IFNDEF TARGET_64BITS}{$ALIGN ON}{$ENDIF}
{$MINENUMSIZE 4}

interface

{$IFDEF LINUX}
uses
  {$IFDEF FPC}
    ctypes, keysym, xf86keysym, x, xlib, LCLVersion,
    {$IFDEF LCLGTK2}gdk2, gtk2proc, gtk2int, Gtk2Def, gdk2x, Gtk2Extra,{$ENDIF}
    {$IFDEF LCLGTK3}LazGdk3, LazGtk3, LazGLib2, gtk3procs,{$ENDIF}
    {$IFDEF LCLQT}qt4,{$ENDIF}
    {$IFDEF LCLQT5}qt5,{$ENDIF}
    {$IFDEF LCLQT6}qt6,{$ENDIF}
  {$ELSE}
    uCEFLinuxTypes,
  {$ENDIF}
  uCEFTypes;
{$ENDIF}

{$IFNDEF FPC}
const
  // We define this constant only to avoid warnings in Delphi
  LCL_FULLVERSION = 3000001;
{$ENDIF}

{$IFDEF LINUX}
function  KeyboardCodeFromXKeysym(keysym : uint32) : integer;
{$IF DEFINED(LINUXFMX) or DEFINED(LCLGTK2) or (DEFINED(LCLGTK3) and (LCL_FULLVERSION<3000000))}
function  GetCefStateModifiers(state : uint32) : integer;
{$IFEND}
{$IF DEFINED(LCLGTK3) and (LCL_FULLVERSION>3000000)}
function  GetCefStateModifiers(state : TGdkModifierType) : integer;
{$IFEND}
function  GetWindowsKeyCodeWithoutLocation(key_code : integer) : integer;
function  GetControlCharacter(windows_key_code : integer; shift : boolean) : integer;
{$IF DEFINED(LINUXFMX) or DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
procedure GdkEventKeyToCEFKeyEvent(GdkEvent: PGdkEventKey; var aCEFKeyEvent : TCEFKeyEvent);
function  GdkEventToWindowsKeyCode(Event: PGdkEventKey) : integer;
{$IFEND}
{$IF DEFINED(LCLQT) OR DEFINED(LCLQT5) OR DEFINED(LCLQT6)}
function  GetCefStateModifiers(KeyboardModifiers : QtKeyboardModifiers; NativeModifiers : LongWord) : TCefEventFlags;
function  GetCefWindowsKeyCode(key : QtKey) : integer;
procedure QTKeyEventToCEFKeyEvent(Event_ : QKeyEventH; var aCEFKeyEvent : TCEFKeyEvent);
function  AdjustCefKeyCharEvent(Event_ : QKeyEventH; var aCEFKeyEvent : TCEFKeyEvent): boolean;
{$IFEND}

{$IFDEF FMX}
type
   TXErrorHandler   = function (para1:PDisplay; para2:PXErrorEvent):longint; cdecl;
   TXIOErrorHandler = function (para1:PDisplay):longint; cdecl;

function XSetErrorHandler(para1:TXErrorHandler):TXErrorHandler; cdecl; external 'libX11.so';
function XSetIOErrorHandler(para1:TXIOErrorHandler):TXIOErrorHandler; cdecl; external 'libX11.so';

function gdk_keyval_to_unicode(keyval: guint): guint32; cdecl; external 'libgdk-3.so';
function g_signal_connect_data(instance: gpointer; detailed_signal: Pgchar; c_handler: TGCallback; data: gpointer; destroy_data: TGClosureNotify; connect_flags: TGConnectFlags): gulong; cdecl; external 'libgobject-2.0.so';
function g_signal_connect(instance: gpointer; detailed_signal: Pgchar; c_handler: TGCallback; data: gpointer): gulong; overload;
function g_signal_connect(instance: gpointer; const detailed_signal: AnsiString; c_handler: TGCallback; data: gpointer): gulong; overload;
function gdk_screen_width:gint; cdecl; external 'libgdk-3.so';
function gdk_screen_width_mm:gint; cdecl; external 'libgdk-3.so';
function gdk_screen_get_default:PGdkScreen; cdecl; external 'libgdk-3.so';
function gdk_screen_get_resolution(screen:PGdkScreen):gdouble; cdecl; external 'libgdk-3.so';
{$ENDIF}
{$IFDEF FPC}
{$IFDEF LCLGTK3}
function gdk_x11_window_get_xid(window: PGdkWindow): TXID; cdecl; external 'libgdk-3.so.0';
function gdk_x11_get_default_xdisplay: PDisplay; cdecl; external 'libgdk-3.so.0';
procedure gdk_set_allowed_backends(const backends: PGchar); cdecl; external 'libgdk-3.so.0';
function gdk_x11_display_get_xdisplay(display: PGdkDisplay): PDisplay; cdecl; external 'libgdk-3.so.0';
function gdk_x11_screen_get_screen_number(screen: PGdkScreen): longint; cdecl; external 'libgdk-3.so.0';
function gdk_x11_visual_get_xvisual(visual: PGdkVisual): PVisual; cdecl; external 'libgdk-3.so.0';
procedure UseDefaultX11VisualForGtk(widget : PGtkWidget); 
procedure FlushDisplay(widget : PGtkWidget); 
{$ENDIF}
procedure ShowX11Message(const aMessage : string);
{$ENDIF}{$ENDIF}

implementation

{$IFDEF LINUX}
uses
  {$IFDEF DELPHI16_UP}
  System.SysUtils,
  {$ELSE}
  SysUtils,
  {$ENDIF}                   
  uCEFLinuxConstants, uCEFConstants;
{$ENDIF}

{$IFDEF LINUX}
function KeyboardCodeFromXKeysym(keysym : uint32) : integer;
begin
  case keysym of
    XK_BackSpace:
      Result := VKEY_BACK;
    XK_Delete,
    XK_KP_Delete:
      Result := VKEY_DELETE;
    XK_Tab,
    XK_KP_Tab,
    XK_ISO_Left_Tab,
    XK_3270_BackTab:
      Result := VKEY_TAB;
    XK_Linefeed,
    XK_Return,
    XK_KP_Enter,
    XK_ISO_Enter:
      Result := VKEY_Return;
    XK_Clear,
    XK_KP_Begin:
      Result := VKEY_CLEAR;
    XK_KP_Space,
    XK_space:
      Result := VKEY_SPACE;
    XK_Home,
    XK_KP_Home:
      Result := VKEY_HOME;
    XK_End,
    XK_KP_End:
      Result := VKEY_END;
    XK_Page_Up,
    XK_KP_Page_Up:
      Result := VKEY_PRIOR;
    XK_Page_Down,
    XK_KP_Page_Down:
      Result := VKEY_NEXT;
    XK_Left,
    XK_KP_Left:
      Result := VKEY_LEFT;
    XK_Right,
    XK_KP_Right:
      Result := VKEY_RIGHT;
    XK_Down,
    XK_KP_Down:
      Result := VKEY_DOWN;
    XK_Up,
    XK_KP_Up:
      Result := VKEY_UP;
    XK_Escape:
      Result := VKEY_ESCAPE;
    XK_Kana_Lock,
    XK_Kana_Shift:
      Result := VKEY_KANA;
    XK_Hangul:
      Result := VKEY_HANGUL;
    XK_Hangul_Hanja:
      Result := VKEY_HANJA;
    XK_Kanji:
      Result := VKEY_KANJI;
    XK_Henkan:
      Result := VKEY_CONVERT;
    XK_Muhenkan:
      Result := VKEY_NONCONVERT;
    XK_Zenkaku_Hankaku:
      Result := VKEY_DBE_DBCSCHAR;
    XKc_A,
    XK_a:
      Result := VKEY_A;
    XKc_B,
    XK_b:
      Result :=  VKEY_B;
    XKc_C,
    XK_c:
      Result :=  VKEY_C;
    XKc_D,
    XK_d:
      Result :=  VKEY_D;
    XKc_E,
    XK_e:
      Result :=  VKEY_E;
    XKc_F,
    XK_f:
      Result :=  VKEY_F;
    XKc_G,
    XK_g:
      Result :=  VKEY_G;
    XKc_H,
    XK_h:
      Result :=  VKEY_H;
    XKc_I,
    XK_i:
      Result :=  VKEY_I;
    XKc_J,
    XK_j:
      Result :=  VKEY_J;
    XKc_K,
    XK_k:
      Result :=  VKEY_K;
    XKc_L,
    XK_l:
      Result :=  VKEY_L;
    XKc_M,
    XK_m:
      Result :=  VKEY_M;
    XKc_N,
    XK_n:
      Result :=  VKEY_N;
    XKc_O,
    XK_o:
      Result :=  VKEY_O;
    XKc_P,
    XK_p:
      Result :=  VKEY_P;
    XKc_Q,
    XK_q:
      Result :=  VKEY_Q;
    XKc_R,
    XK_r:
      Result :=  VKEY_R;
    XKc_S,
    XK_s:
      Result :=  VKEY_S;
    XKc_T,
    XK_t:
      Result :=  VKEY_T;
    XKc_U,
    XK_u:
      Result :=  VKEY_U;
    XKc_V,
    XK_v:
      Result :=  VKEY_V;
    XKc_W,
    XK_w:
      Result :=  VKEY_W;
    XKc_X,
    XK_x:
      Result :=  VKEY_X;
    XKc_Y,
    XK_y:
      Result :=  VKEY_Y;
    XKc_Z,
    XK_z:
      Result :=  VKEY_Z;
    XK_0,
    XK_1,
    XK_2,
    XK_3,
    XK_4,
    XK_5,
    XK_6,
    XK_7,
    XK_8,
    XK_9:
      Result := VKEY_0 + (keysym - XK_0);
    XK_parenright:
      Result :=  VKEY_0;
    XK_exclam:
      Result :=  VKEY_1;
    XK_at:
      Result :=  VKEY_2;
    XK_numbersign:
      Result :=  VKEY_3;
    XK_dollar:
      Result :=  VKEY_4;
    XK_percent:
      Result :=  VKEY_5;
    XK_asciicircum:
      Result :=  VKEY_6;
    XK_ampersand:
      Result :=  VKEY_7;
    XK_asterisk:
      Result :=  VKEY_8;
    XK_parenleft:
      Result :=  VKEY_9;
    XK_KP_0,
    XK_KP_1,
    XK_KP_2,
    XK_KP_3,
    XK_KP_4,
    XK_KP_5,
    XK_KP_6,
    XK_KP_7,
    XK_KP_8,
    XK_KP_9:
      Result :=  VKEY_NUMPAD0 + (keysym - XK_KP_0);
    XK_multiply,
    XK_KP_Multiply:
      Result :=  VKEY_MULTIPLY;
    XK_KP_Add:
      Result :=  VKEY_ADD;
    XK_KP_Separator:
      Result :=  VKEY_SEPARATOR;
    XK_KP_Subtract:
      Result :=  VKEY_SUBTRACT;
    XK_KP_Decimal:
      Result :=  VKEY_DECIMAL;
    XK_KP_Divide:
      Result :=  VKEY_DIVIDE;
    XK_KP_Equal,
    XK_equal,
    XK_plus:
      Result :=  VKEY_OEM_PLUS;
    XK_comma,
    XK_less:
      Result :=  VKEY_OEM_COMMA;
    XK_minus,
    XK_underscore:
      Result :=  VKEY_OEM_MINUS;
    XK_greater,
    XK_period:
      Result :=  VKEY_OEM_PERIOD;
    XK_colon,
    XK_semicolon:
      Result :=  VKEY_OEM_1;
    XK_question,
    XK_slash:
      Result :=  VKEY_OEM_2;
    XK_asciitilde,
    XK_quoteleft:
      Result :=  VKEY_OEM_3;
    XK_bracketleft,
    XK_braceleft:
      Result :=  VKEY_OEM_4;
    XK_backslash,
    XK_bar:
      Result :=  VKEY_OEM_5;
    XK_bracketright,
    XK_braceright:
      Result :=  VKEY_OEM_6;
    XK_quoteright,
    XK_quotedbl:
      Result :=  VKEY_OEM_7;
    XK_ISO_Level5_Shift:
      Result :=  VKEY_OEM_8;
    XK_Shift_L,
    XK_Shift_R:
      Result :=  VKEY_SHIFT;
    XK_Control_L,
    XK_Control_R:
      Result :=  VKEY_CONTROL;
    XK_Meta_L,
    XK_Meta_R,
    XK_Alt_L,
    XK_Alt_R:
      Result :=  VKEY_MENU;
    XK_ISO_Level3_Shift:
      Result :=  VKEY_ALTGR;
    XK_Multi_key:
      Result :=  VKEY_COMPOSE;
    XK_Pause:
      Result :=  VKEY_PAUSE;
    XK_Caps_Lock:
      Result :=  VKEY_CAPITAL;
    XK_Num_Lock:
      Result :=  VKEY_NUMLOCK;
    XK_Scroll_Lock:
      Result :=  VKEY_SCROLL;
    XK_Select:
      Result :=  VKEY_SELECT;
    XK_Print:
      Result :=  VKEY_PRINT;
    XK_Execute:
      Result :=  VKEY_EXECUTE;
    XK_Insert,
    XK_KP_Insert:
      Result :=  VKEY_INSERT;
    XK_Help:
      Result :=  VKEY_HELP;
    XK_Super_L:
      Result :=  VKEY_LWIN;
    XK_Super_R:
      Result :=  VKEY_RWIN;
    XK_Menu:
      Result :=  VKEY_APPS;
    XK_F1,
    XK_F2,
    XK_F3,
    XK_F4,
    XK_F5,
    XK_F6,
    XK_F7,
    XK_F8,
    XK_F9,
    XK_F10,
    XK_F11,
    XK_F12,
    XK_F13,
    XK_F14,
    XK_F15,
    XK_F16,
    XK_F17,
    XK_F18,
    XK_F19,
    XK_F20,
    XK_F21,
    XK_F22,
    XK_F23,
    XK_F24:
      Result := VKEY_F1 + (keysym - XK_F1);
    XK_KP_F1,
    XK_KP_F2,
    XK_KP_F3,
    XK_KP_F4:
      Result := VKEY_F1 + (keysym - XK_KP_F1);
    XK_guillemotleft,
    XK_guillemotright,
    XK_degree,
    XK_ugrave,
    XKc_Ugrave,
    XK_brokenbar:
      Result :=  VKEY_OEM_102;
    XF86XK_Tools:
      Result :=  VKEY_F13;
    XF86XK_Launch5:
      Result :=  VKEY_F14;
    XF86XK_Launch6:
      Result :=  VKEY_F15;
    XF86XK_Launch7:
      Result :=  VKEY_F16;
    XF86XK_Launch8:
      Result :=  VKEY_F17;
    XF86XK_Launch9:
      Result :=  VKEY_F18;
    XF86XK_Refresh,
    XF86XK_History,
    XF86XK_OpenURL,
    XF86XK_AddFavorite,
    XF86XK_Go,
    XF86XK_ZoomIn,
    XF86XK_ZoomOut:
      Result :=  VKEY_UNKNOWN;
    XF86XK_Back:
      Result :=  VKEY_BROWSER_BACK;
    XF86XK_Forward:
      Result :=  VKEY_BROWSER_FORWARD;
    XF86XK_Reload:
      Result :=  VKEY_BROWSER_REFRESH;
    XF86XK_Stop:
      Result :=  VKEY_BROWSER_STOP;
    XF86XK_Search:
      Result :=  VKEY_BROWSER_SEARCH;
    XF86XK_Favorites:
      Result :=  VKEY_BROWSER_FAVORITES;
    XF86XK_HomePage:
      Result :=  VKEY_BROWSER_HOME;
    XF86XK_AudioMute:
      Result :=  VKEY_VOLUME_MUTE;
    XF86XK_AudioLowerVolume:
      Result :=  VKEY_VOLUME_DOWN;
    XF86XK_AudioRaiseVolume:
      Result :=  VKEY_VOLUME_UP;
    XF86XK_AudioNext:
      Result :=  VKEY_MEDIA_NEXT_TRACK;
    XF86XK_AudioPrev:
      Result :=  VKEY_MEDIA_PREV_TRACK;
    XF86XK_AudioStop:
      Result :=  VKEY_MEDIA_STOP;
    XF86XK_AudioPlay:
      Result :=  VKEY_MEDIA_PLAY_PAUSE;
    XF86XK_Mail:
      Result :=  VKEY_MEDIA_LAUNCH_MAIL;
    XF86XK_LaunchA:
      Result :=  VKEY_MEDIA_LAUNCH_APP1;
    XF86XK_LaunchB,
    XF86XK_Calculator:
      Result :=  VKEY_MEDIA_LAUNCH_APP2;
    XF86XK_WLAN:
      Result :=  VKEY_WLAN;
    XF86XK_PowerOff:
      Result :=  VKEY_POWER;
    XF86XK_MonBrightnessDown:
      Result :=  VKEY_BRIGHTNESS_DOWN;
    XF86XK_MonBrightnessUp:
      Result :=  VKEY_BRIGHTNESS_UP;
    XF86XK_KbdBrightnessDown:
      Result :=  VKEY_KBD_BRIGHTNESS_DOWN;
    XF86XK_KbdBrightnessUp:
      Result :=  VKEY_KBD_BRIGHTNESS_UP;
    else Result :=  VKEY_UNKNOWN;
  end;
end;

{$IF DEFINED(LINUXFMX) or DEFINED(LCLGTK2) or (DEFINED(LCLGTK3) and (LCL_FULLVERSION<3000000))}
function GetCefStateModifiers(state : uint32) : integer;
begin
  Result := EVENTFLAG_NONE;

  if ((state and GDK_SHIFT_MASK) <> 0) then
    Result := Result or EVENTFLAG_SHIFT_DOWN;

  if ((state and GDK_LOCK_MASK) <> 0) then
    Result := Result or EVENTFLAG_CAPS_LOCK_ON;

  if ((state and GDK_CONTROL_MASK) <> 0) then
    Result := Result or EVENTFLAG_CONTROL_DOWN;

  if ((state and GDK_MOD1_MASK) <> 0) then
    Result := Result or EVENTFLAG_ALT_DOWN;

  if ((state and GDK_BUTTON1_MASK) <> 0) then
    Result := Result or EVENTFLAG_LEFT_MOUSE_BUTTON;

  if ((state and GDK_BUTTON2_MASK) <> 0) then
    Result := Result or EVENTFLAG_MIDDLE_MOUSE_BUTTON;

  if ((state and GDK_BUTTON3_MASK) <> 0) then
    Result := Result or EVENTFLAG_RIGHT_MOUSE_BUTTON;
end;
{$IFEND}
{$IF DEFINED(LCLGTK3) and (LCL_FULLVERSION>3000000)}
function GetCefStateModifiers(state : TGdkModifierType) : integer;
begin
  Result := EVENTFLAG_NONE;

  if (GDK_SHIFT_MASK in state) then
    Result := Result or EVENTFLAG_SHIFT_DOWN;

  if (GDK_LOCK_MASK in state) then
    Result := Result or EVENTFLAG_CAPS_LOCK_ON;

  if (GDK_CONTROL_MASK in state) then
    Result := Result or EVENTFLAG_CONTROL_DOWN;

  if (GDK_MOD1_MASK in state) then
    Result := Result or EVENTFLAG_ALT_DOWN;

  if (GDK_BUTTON1_MASK in state) then
    Result := Result or EVENTFLAG_LEFT_MOUSE_BUTTON;

  if (GDK_BUTTON2_MASK in state) then
    Result := Result or EVENTFLAG_MIDDLE_MOUSE_BUTTON;

  if (GDK_BUTTON3_MASK in state) then
    Result := Result or EVENTFLAG_RIGHT_MOUSE_BUTTON;
end;
{$IFEND}

function GetWindowsKeyCodeWithoutLocation(key_code : integer) : integer;
begin
  case key_code of
    VKEY_LCONTROL, VKEY_RCONTROL : Result := VKEY_CONTROL;
    VKEY_LSHIFT, VKEY_RSHIFT     : Result := VKEY_SHIFT;
    VKEY_LMENU, VKEY_RMENU       : Result := VKEY_MENU;
    else                           Result := key_code;
  end;
end;

function GetControlCharacter(windows_key_code : integer; shift : boolean) : integer;
begin
  if (windows_key_code >= VKEY_A) and (windows_key_code <= VKEY_Z) then
    Result := windows_key_code - VKEY_A + 1
   else
    if shift then
      case windows_key_code of
        VKEY_2         : Result := 0;
        VKEY_6         : Result := $1E;
        VKEY_OEM_MINUS : Result := $1F;
        else             Result := 0;
      end
     else
      case windows_key_code of
        VKEY_OEM_4  : Result := $1B;
        VKEY_OEM_5  : Result := $1C;
        VKEY_OEM_6  : Result := $1D;
        VKEY_RETURN : Result := $0A;
        else          Result := 0;
      end;
end;

{$IF DEFINED(LINUXFMX) or DEFINED(LCLGTK2) or DEFINED(LCLGTK3)}
procedure GdkEventKeyToCEFKeyEvent(GdkEvent: PGdkEventKey; var aCEFKeyEvent : TCEFKeyEvent);
var
  windows_key_code : integer;
begin
  windows_key_code                     := GdkEventToWindowsKeyCode(GdkEvent);
  aCEFKeyEvent.size	               := SizeOf(TCEFKeyEvent);
  aCEFKeyEvent.windows_key_code        := GetWindowsKeyCodeWithoutLocation(windows_key_code);
  aCEFKeyEvent.native_key_code         := GdkEvent^.hardware_keycode;
  aCEFKeyEvent.modifiers               := GetCefStateModifiers(GdkEvent^.state);
  aCEFKeyEvent.focus_on_editable_field := 0;

  if (GdkEvent^.keyval >= GDK_KP_Space) and (GdkEvent^.keyval <= GDK_KP_9) then
    aCEFKeyEvent.modifiers := aCEFKeyEvent.modifiers or EVENTFLAG_IS_KEY_PAD;

  aCEFKeyEvent.is_system_key := ord((aCEFKeyEvent.modifiers and EVENTFLAG_ALT_DOWN) <> 0);

  if (windows_key_code = VKEY_RETURN) then
    aCEFKeyEvent.unmodified_character := #13
   else
    aCEFKeyEvent.unmodified_character := WideChar(gdk_keyval_to_unicode(GdkEvent^.keyval));

  if ((aCEFKeyEvent.modifiers and EVENTFLAG_CONTROL_DOWN) <> 0) then
    aCEFKeyEvent.character := WideChar(GetControlCharacter(windows_key_code, ((aCEFKeyEvent.modifiers and EVENTFLAG_SHIFT_DOWN) <> 0)))
   else
    aCEFKeyEvent.character := aCEFKeyEvent.unmodified_character;
end;

function GdkEventToWindowsKeyCode(event: PGdkEventKey) : integer;
var
  windows_key_code, keyval : integer;
begin
  windows_key_code := KeyboardCodeFromXKeysym(event^.keyval);
  if (windows_key_code <> 0) then
    begin
      Result := windows_key_code;
      exit;
    end;

  if (event^.hardware_keycode < length(kHardwareCodeToGDKKeyval)) then
    begin
      keyval := kHardwareCodeToGDKKeyval[event^.hardware_keycode];
      if (keyval <> 0) then
        begin
          Result := KeyboardCodeFromXKeysym(keyval);
          exit;
        end;
    end;

  Result := KeyboardCodeFromXKeysym(event^.keyval);
end;
{$IFEND}

{$IF DEFINED(LCLQT) OR DEFINED(LCLQT5) OR DEFINED(LCLQT6)}
function  GetCefStateModifiers(KeyboardModifiers : QtKeyboardModifiers; NativeModifiers : LongWord) : TCefEventFlags;
Const
  GDK_SHIFT_MASK   = 1 shl 0;
  GDK_LOCK_MASK    = 1 shl 1;
  GDK_CONTROL_MASK = 1 shl 2;
  GDK_MOD1_MASK    = 1 shl 3;
  GDK_BUTTON1_MASK = 1 shl 8;
  GDK_BUTTON2_MASK = 1 shl 9;
  GDK_BUTTON3_MASK = 1 shl 10;
begin
  Result := EVENTFLAG_NONE;

  if (KeyboardModifiers and QtShiftModifier)   <> 0 then Result := Result or EVENTFLAG_SHIFT_DOWN;
  if (KeyboardModifiers and QtControlModifier) <> 0 then Result := Result or EVENTFLAG_CONTROL_DOWN;
  if (KeyboardModifiers and QtAltModifier)     <> 0 then Result := Result or EVENTFLAG_ALT_DOWN;
  if (KeyboardModifiers and QtKeypadModifier)  <> 0 then Result := Result or EVENTFLAG_IS_KEY_PAD;

  if (NativeModifiers   and GDK_SHIFT_MASK)    <> 0 then Result := Result or EVENTFLAG_SHIFT_DOWN;
  if (NativeModifiers   and GDK_LOCK_MASK)     <> 0 then Result := Result or EVENTFLAG_CAPS_LOCK_ON;
  if (NativeModifiers   and GDK_CONTROL_MASK)  <> 0 then Result := Result or EVENTFLAG_CONTROL_DOWN;
  if (NativeModifiers   and GDK_MOD1_MASK)     <> 0 then Result := Result or EVENTFLAG_ALT_DOWN;
  if (NativeModifiers   and GDK_BUTTON1_MASK)  <> 0 then Result := Result or EVENTFLAG_LEFT_MOUSE_BUTTON;
  if (NativeModifiers   and GDK_BUTTON2_MASK)  <> 0 then Result := Result or EVENTFLAG_MIDDLE_MOUSE_BUTTON;
  if (NativeModifiers   and GDK_BUTTON3_MASK)  <> 0 then Result := Result or EVENTFLAG_RIGHT_MOUSE_BUTTON;
end;

function GetCefWindowsKeyCode(key : QtKey): integer;
begin
  case key of
    QtKey_Escape        : Result := VKEY_ESCAPE;
    QtKey_Tab,
    QtKey_Backtab       : Result := VKEY_TAB;
    QtKey_Backspace     : Result := VKEY_BACK;
    QtKey_Return,
    QtKey_Enter         : Result := VKEY_RETURN;
    QtKey_Insert        : Result := VKEY_INSERT;
    QtKey_Delete        : Result := VKEY_DELETE;
    QtKey_Pause         : Result := VKEY_PAUSE;
    QtKey_Print         : Result := VKEY_PRINT;
    QtKey_Clear         : Result := VKEY_CLEAR;
    QtKey_Home          : Result := VKEY_HOME;
    QtKey_End           : Result := VKEY_END;
    QtKey_Left          : Result := VKEY_LEFT;
    QtKey_Up            : Result := VKEY_UP;
    QtKey_Right         : Result := VKEY_RIGHT;
    QtKey_Down          : Result := VKEY_DOWN;
    QtKey_PageUp        : Result := VKEY_PRIOR;
    QtKey_PageDown      : Result := VKEY_NEXT;
    QtKey_Shift         : Result := VKEY_SHIFT;
    QtKey_Control       : Result := VKEY_CONTROL;
    QtKey_Alt           : Result := VKEY_MENU;
    QtKey_CapsLock      : Result := VKEY_CAPITAL;
    QtKey_NumLock       : Result := VKEY_NUMLOCK;
    QtKey_ScrollLock    : Result := VKEY_SCROLL;
    QtKey_F1..QtKey_F24 : Result := key - QtKey_F1 + VKEY_F1;
    QtKey_Help          : Result := VKEY_HELP;
    QtKey_Space         : Result := VKEY_SPACE;
    QtKey_Exclam        : Result := VKEY_1;
    QtKey_QuoteDbl      : Result := VKEY_OEM_7;
    QtKey_NumberSign    : Result := VKEY_3;
    QtKey_Dollar        : Result := VKEY_4;
    QtKey_Percent       : Result := VKEY_5;
    QtKey_Ampersand     : Result := VKEY_7;
    QtKey_Apostrophe    : Result := VKEY_OEM_7;
    QtKey_ParenLeft     : Result := VKEY_9;
    QtKey_ParenRight    : Result := VKEY_0;
    QtKey_Asterisk      : Result := VKEY_8;
    QtKey_Plus          : Result := VKEY_OEM_PLUS;
    QtKey_Comma         : Result := VKEY_OEM_COMMA;
    QtKey_Minus         : Result := VKEY_OEM_MINUS;
    QtKey_Period        : Result := VKEY_OEM_PERIOD;
    QtKey_Slash         : Result := VKEY_OEM_2;
    QtKey_0..QtKey_9    : Result := key;
    QtKey_Colon,
    QtKey_Semicolon     : Result := VKEY_OEM_1;
    QtKey_Less          : Result := VKEY_OEM_COMMA;
    QtKey_Equal         : Result := VKEY_OEM_PLUS;
    QtKey_Greater       : Result := VKEY_OEM_PERIOD;
    QtKey_Question      : Result := VKEY_OEM_2;
    QtKey_At            : Result := VKEY_2;
    QtKey_A..QtKey_Z    : Result := key;
    QtKey_BracketLeft   : Result := VKEY_OEM_4;
    QtKey_Backslash     : Result := VKEY_OEM_5;
    QtKey_BracketRight  : Result := VKEY_OEM_6;
    QtKey_AsciiCircum   : Result := VKEY_6;
    QtKey_Underscore    : Result := VKEY_OEM_MINUS;
    QtKey_QuoteLeft     : Result := VKEY_OEM_3;
    QtKey_BraceLeft     : Result := VKEY_OEM_4;
    QtKey_Bar           : Result := VKEY_OEM_5;
    QtKey_BraceRight    : Result := VKEY_OEM_6;
    QtKey_AsciiTilde    : Result := VKEY_OEM_3;
    QtKey_multiply      : Result := VKEY_MULTIPLY;
    QtKey_VolumeDown    : Result := VKEY_VOLUME_DOWN;
    QtKey_VolumeMute    : Result := VKEY_VOLUME_MUTE;
    QtKey_VolumeUp      : Result := VKEY_VOLUME_UP;
    QtKey_MediaPlay     : Result := VKEY_MEDIA_PLAY_PAUSE;
    QtKey_MediaStop     : Result := VKEY_MEDIA_STOP;
    QtKey_Select        : Result := VKEY_SELECT;
    QtKey_Printer       : Result := VKEY_SNAPSHOT;
    QtKey_Execute       : Result := VKEY_EXECUTE;
    else                  Result := 0;
  end;
end;

procedure QTKeyEventToCEFKeyEvent(Event_: QKeyEventH; var aCEFKeyEvent : TCEFKeyEvent);  
var
  windows_key_code : integer;
begin                                                 
  windows_key_code                     := GetCefWindowsKeyCode(QKeyEvent_key(Event_));

  aCEFKeyEvent.size	               := SizeOf(TCEFKeyEvent);
  aCEFKeyEvent.modifiers               := GetCefStateModifiers(QKeyEvent_modifiers(Event_), QKeyEvent_nativeModifiers(Event_));
  aCEFKeyEvent.windows_key_code        := GetWindowsKeyCodeWithoutLocation(windows_key_code);
  aCEFKeyEvent.native_key_code         := QKeyEvent_nativeScanCode(Event_);
  aCEFKeyEvent.is_system_key           := ord((aCEFKeyEvent.modifiers and EVENTFLAG_ALT_DOWN) <> 0);
  aCEFKeyEvent.unmodified_character    := WideChar(QKeyEvent_nativeVirtualKey(Event_));
  aCEFKeyEvent.focus_on_editable_field := ord(False);

  if ((aCEFKeyEvent.modifiers and EVENTFLAG_CONTROL_DOWN) <> 0) then
    aCEFKeyEvent.character := WideChar(GetControlCharacter(windows_key_code, ((aCEFKeyEvent.modifiers and EVENTFLAG_SHIFT_DOWN) <> 0)))
   else
    aCEFKeyEvent.character := aCEFKeyEvent.unmodified_character;
end;

function AdjustCefKeyCharEvent(Event_ : QKeyEventH; var aCEFKeyEvent : TCEFKeyEvent): boolean;
var
  TempKey : WideString;
begin
  Result := False;

  QKeyEvent_text(Event_, @TempKey);
  if (length(TempKey) > 0) then
    begin
      aCEFKeyEvent.windows_key_code     := ord(TempKey[1]);
      aCEFKeyEvent.unmodified_character := TempKey[1];
      aCEFKeyEvent.character            := aCEFKeyEvent.unmodified_character;
      Result                            := True;
    end;
end;

{$IFEND}

{$IFDEF FMX}
function g_signal_connect(instance: gpointer; detailed_signal: Pgchar; c_handler: TGCallback; data: gpointer): gulong;
begin
  Result := g_signal_connect_data(instance, detailed_signal, c_handler, data, nil, TGConnectFlags(0));
end;

function g_signal_connect(instance: gpointer; const detailed_signal: AnsiString; c_handler: TGCallback; data: gpointer): gulong;
begin
  Result := g_signal_connect(instance, @detailed_signal[1], c_handler, data);
end;
{$ENDIF}

{$IFDEF FPC}
// This function is almost an identical copy of "ModalShowX11Window" available
// at https://wiki.lazarus.freepascal.org/X11
procedure ShowX11Message(const aMessage : string);
var
  TempDisplay : PDisplay;
  TempWindow  : TWindow;
  TempEvent   : TXEvent;
  TempMessage : PChar;
  TempScreen  : cint;
begin
  TempMessage := PChar(trim(copy(aMessage, 1, pred(pos(#13, aMessage)))));
  TempDisplay := XOpenDisplay(nil);

  if (TempDisplay = nil) then
    begin
      WriteLn(aMessage);
      exit;
    end;

  TempScreen := DefaultScreen(TempDisplay);
  TempWindow := XCreateSimpleWindow(TempDisplay,
                                    RootWindow(TempDisplay, TempScreen),
                                    10, 10, 200, 100, 1,
                                    BlackPixel(TempDisplay, TempScreen),
                                    WhitePixel(TempDisplay, TempScreen));

  XSelectInput(TempDisplay, TempWindow, ExposureMask or KeyPressMask);

  XMapWindow(TempDisplay, TempWindow);

  while (True) do
    begin
      XNextEvent(TempDisplay, @TempEvent);

      if (TempEvent._type = Expose) then
        XDrawString(TempDisplay,
                    TempWindow,
                    DefaultGC(TempDisplay, TempScreen),
                    40, 50,
                    TempMessage,
                    strlen(TempMessage));

      if (TempEvent._type = KeyPress) then Break;
    end;

  XCloseDisplay(TempDisplay);
end;
{$ENDIF}

{$IFDEF LCLGTK3}
procedure UseDefaultX11VisualForGtk(widget : PGtkWidget);
type
  PGdkX11Screen = type PGdkScreen;
var
  screen : PGdkScreen;
  visuals, cursor : PGList;
  x11_screen : PGdkX11Screen;
  default_xvisual : PVisual;
  visual : PGdkVisual;

  function GDK_X11_VISUAL(obj : pointer) : PGdkVisual;
  begin
    Result := PGdkVisual(obj);
  end;

  function GDK_SCREEN_X11(obj : pointer) : PGdkX11Screen;
  begin
     GDK_SCREEN_X11 := PGdkX11Screen(obj);
  end;

  function GDK_SCREEN_XDISPLAY(screen : PGdkScreen) : PDisplay;
  begin
     GDK_SCREEN_XDISPLAY := gdk_x11_display_get_xdisplay(gdk_screen_get_display(screen));
  end;

  function GDK_SCREEN_XNUMBER(screen : PGdkScreen) : longint;
  begin
     GDK_SCREEN_XNUMBER := gdk_x11_screen_get_screen_number(screen);
  end;

begin
  // GTK+ > 3.15.1 uses an X11 visual optimized for GTK+'s OpenGL stuff
  // since revid dae447728d: https://github.com/GNOME/gtk/commit/dae447728d
  // However, it breaks CEF: https://github.com/cztomczak/cefcapi/issues/9
  // Let's use the default X11 visual instead of the GTK's blessed one.
  // Copied from: https://github.com/cztomczak/cefcapi.
  screen     := gdk_screen_get_default();
  visuals    := gdk_screen_list_visuals(screen);
  x11_screen := GDK_SCREEN_X11(screen);

  if (x11_screen <> nil) then
    begin
      default_xvisual := DefaultVisual(GDK_SCREEN_XDISPLAY(x11_screen),
                                       GDK_SCREEN_XNUMBER(x11_screen));

      if (default_xvisual <> nil) then
        begin
          cursor := visuals;

          while (cursor <> nil) do
            begin
              visual := GDK_X11_VISUAL(cursor^.data);

              if (default_xvisual^.visualid = gdk_x11_visual_get_xvisual(visual)^.visualid) then
                begin
                  gtk_widget_set_visual(widget, visual);
                  break;
                end;

              cursor := cursor^.next;
            end;
        end;
    end;

  g_list_free(visuals);
end;

procedure FlushDisplay(widget : PGtkWidget);
var
  gdk_window : PGdkWindow;
  display : PGdkDisplay;
begin
  gdk_window := gtk_widget_get_window(widget);
  display    := gdk_window_get_display(gdk_window);
  gdk_display_flush(display);
end;
{$ENDIF}
{$ENDIF}

end.
