2173 lines
63 KiB
C++
2173 lines
63 KiB
C++
// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
|
|
// reserved. Use of this source code is governed by a BSD-style license that
|
|
// can be found in the LICENSE file.
|
|
|
|
#include "tests/cefclient/browser/browser_window_osr_gtk.h"
|
|
|
|
#include <GL/gl.h>
|
|
#include <gdk/gdk.h>
|
|
#include <gdk/gdkkeysyms.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <glib-object.h>
|
|
#include <gtk/gtk.h>
|
|
#include <gtk/gtkgl.h>
|
|
|
|
#define XK_3270 // for XK_3270_BackTab
|
|
#include <X11/XF86keysym.h>
|
|
#include <X11/Xcursor/Xcursor.h>
|
|
#include <X11/extensions/XInput2.h>
|
|
#include <X11/keysym.h>
|
|
|
|
#include "include/base/cef_logging.h"
|
|
#include "include/base/cef_macros.h"
|
|
#include "include/wrapper/cef_closure_task.h"
|
|
#include "tests/cefclient/browser/util_gtk.h"
|
|
#include "tests/shared/browser/geometry_util.h"
|
|
#include "tests/shared/browser/main_message_loop.h"
|
|
|
|
namespace client {
|
|
|
|
namespace {
|
|
|
|
// Major opcode of XInputExtension, or -1 if XInput 2.2 is not available.
|
|
int g_xinput_extension = -1;
|
|
|
|
// Static BrowserWindowOsrGtk::EventFilter needs to forward touch events
|
|
// to correct browser, so we maintain a vector of all windows.
|
|
std::vector<BrowserWindowOsrGtk*> g_browser_windows;
|
|
|
|
bool IsTouchAvailable() {
|
|
return g_xinput_extension != -1;
|
|
}
|
|
|
|
int GetCefStateModifiers(guint state) {
|
|
int modifiers = 0;
|
|
if (state & GDK_SHIFT_MASK)
|
|
modifiers |= EVENTFLAG_SHIFT_DOWN;
|
|
if (state & GDK_LOCK_MASK)
|
|
modifiers |= EVENTFLAG_CAPS_LOCK_ON;
|
|
if (state & GDK_CONTROL_MASK)
|
|
modifiers |= EVENTFLAG_CONTROL_DOWN;
|
|
if (state & GDK_MOD1_MASK)
|
|
modifiers |= EVENTFLAG_ALT_DOWN;
|
|
if (state & GDK_BUTTON1_MASK)
|
|
modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON;
|
|
if (state & GDK_BUTTON2_MASK)
|
|
modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON;
|
|
if (state & GDK_BUTTON3_MASK)
|
|
modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON;
|
|
return modifiers;
|
|
}
|
|
|
|
int GetCefStateModifiers(XIModifierState mods, XIButtonState buttons) {
|
|
guint state = mods.effective;
|
|
if (buttons.mask_len >= 1) {
|
|
if (XIMaskIsSet(buttons.mask, 1))
|
|
state |= GDK_BUTTON1_MASK;
|
|
if (XIMaskIsSet(buttons.mask, 2))
|
|
state |= GDK_BUTTON2_MASK;
|
|
if (XIMaskIsSet(buttons.mask, 3))
|
|
state |= GDK_BUTTON3_MASK;
|
|
}
|
|
|
|
return GetCefStateModifiers(state);
|
|
}
|
|
|
|
// From ui/events/keycodes/keyboard_codes_posix.h.
|
|
enum KeyboardCode {
|
|
VKEY_BACK = 0x08,
|
|
VKEY_TAB = 0x09,
|
|
VKEY_BACKTAB = 0x0A,
|
|
VKEY_CLEAR = 0x0C,
|
|
VKEY_RETURN = 0x0D,
|
|
VKEY_SHIFT = 0x10,
|
|
VKEY_CONTROL = 0x11,
|
|
VKEY_MENU = 0x12,
|
|
VKEY_PAUSE = 0x13,
|
|
VKEY_CAPITAL = 0x14,
|
|
VKEY_KANA = 0x15,
|
|
VKEY_HANGUL = 0x15,
|
|
VKEY_JUNJA = 0x17,
|
|
VKEY_FINAL = 0x18,
|
|
VKEY_HANJA = 0x19,
|
|
VKEY_KANJI = 0x19,
|
|
VKEY_ESCAPE = 0x1B,
|
|
VKEY_CONVERT = 0x1C,
|
|
VKEY_NONCONVERT = 0x1D,
|
|
VKEY_ACCEPT = 0x1E,
|
|
VKEY_MODECHANGE = 0x1F,
|
|
VKEY_SPACE = 0x20,
|
|
VKEY_PRIOR = 0x21,
|
|
VKEY_NEXT = 0x22,
|
|
VKEY_END = 0x23,
|
|
VKEY_HOME = 0x24,
|
|
VKEY_LEFT = 0x25,
|
|
VKEY_UP = 0x26,
|
|
VKEY_RIGHT = 0x27,
|
|
VKEY_DOWN = 0x28,
|
|
VKEY_SELECT = 0x29,
|
|
VKEY_PRINT = 0x2A,
|
|
VKEY_EXECUTE = 0x2B,
|
|
VKEY_SNAPSHOT = 0x2C,
|
|
VKEY_INSERT = 0x2D,
|
|
VKEY_DELETE = 0x2E,
|
|
VKEY_HELP = 0x2F,
|
|
VKEY_0 = 0x30,
|
|
VKEY_1 = 0x31,
|
|
VKEY_2 = 0x32,
|
|
VKEY_3 = 0x33,
|
|
VKEY_4 = 0x34,
|
|
VKEY_5 = 0x35,
|
|
VKEY_6 = 0x36,
|
|
VKEY_7 = 0x37,
|
|
VKEY_8 = 0x38,
|
|
VKEY_9 = 0x39,
|
|
VKEY_A = 0x41,
|
|
VKEY_B = 0x42,
|
|
VKEY_C = 0x43,
|
|
VKEY_D = 0x44,
|
|
VKEY_E = 0x45,
|
|
VKEY_F = 0x46,
|
|
VKEY_G = 0x47,
|
|
VKEY_H = 0x48,
|
|
VKEY_I = 0x49,
|
|
VKEY_J = 0x4A,
|
|
VKEY_K = 0x4B,
|
|
VKEY_L = 0x4C,
|
|
VKEY_M = 0x4D,
|
|
VKEY_N = 0x4E,
|
|
VKEY_O = 0x4F,
|
|
VKEY_P = 0x50,
|
|
VKEY_Q = 0x51,
|
|
VKEY_R = 0x52,
|
|
VKEY_S = 0x53,
|
|
VKEY_T = 0x54,
|
|
VKEY_U = 0x55,
|
|
VKEY_V = 0x56,
|
|
VKEY_W = 0x57,
|
|
VKEY_X = 0x58,
|
|
VKEY_Y = 0x59,
|
|
VKEY_Z = 0x5A,
|
|
VKEY_LWIN = 0x5B,
|
|
VKEY_COMMAND = VKEY_LWIN, // Provide the Mac name for convenience.
|
|
VKEY_RWIN = 0x5C,
|
|
VKEY_APPS = 0x5D,
|
|
VKEY_SLEEP = 0x5F,
|
|
VKEY_NUMPAD0 = 0x60,
|
|
VKEY_NUMPAD1 = 0x61,
|
|
VKEY_NUMPAD2 = 0x62,
|
|
VKEY_NUMPAD3 = 0x63,
|
|
VKEY_NUMPAD4 = 0x64,
|
|
VKEY_NUMPAD5 = 0x65,
|
|
VKEY_NUMPAD6 = 0x66,
|
|
VKEY_NUMPAD7 = 0x67,
|
|
VKEY_NUMPAD8 = 0x68,
|
|
VKEY_NUMPAD9 = 0x69,
|
|
VKEY_MULTIPLY = 0x6A,
|
|
VKEY_ADD = 0x6B,
|
|
VKEY_SEPARATOR = 0x6C,
|
|
VKEY_SUBTRACT = 0x6D,
|
|
VKEY_DECIMAL = 0x6E,
|
|
VKEY_DIVIDE = 0x6F,
|
|
VKEY_F1 = 0x70,
|
|
VKEY_F2 = 0x71,
|
|
VKEY_F3 = 0x72,
|
|
VKEY_F4 = 0x73,
|
|
VKEY_F5 = 0x74,
|
|
VKEY_F6 = 0x75,
|
|
VKEY_F7 = 0x76,
|
|
VKEY_F8 = 0x77,
|
|
VKEY_F9 = 0x78,
|
|
VKEY_F10 = 0x79,
|
|
VKEY_F11 = 0x7A,
|
|
VKEY_F12 = 0x7B,
|
|
VKEY_F13 = 0x7C,
|
|
VKEY_F14 = 0x7D,
|
|
VKEY_F15 = 0x7E,
|
|
VKEY_F16 = 0x7F,
|
|
VKEY_F17 = 0x80,
|
|
VKEY_F18 = 0x81,
|
|
VKEY_F19 = 0x82,
|
|
VKEY_F20 = 0x83,
|
|
VKEY_F21 = 0x84,
|
|
VKEY_F22 = 0x85,
|
|
VKEY_F23 = 0x86,
|
|
VKEY_F24 = 0x87,
|
|
VKEY_NUMLOCK = 0x90,
|
|
VKEY_SCROLL = 0x91,
|
|
VKEY_LSHIFT = 0xA0,
|
|
VKEY_RSHIFT = 0xA1,
|
|
VKEY_LCONTROL = 0xA2,
|
|
VKEY_RCONTROL = 0xA3,
|
|
VKEY_LMENU = 0xA4,
|
|
VKEY_RMENU = 0xA5,
|
|
VKEY_BROWSER_BACK = 0xA6,
|
|
VKEY_BROWSER_FORWARD = 0xA7,
|
|
VKEY_BROWSER_REFRESH = 0xA8,
|
|
VKEY_BROWSER_STOP = 0xA9,
|
|
VKEY_BROWSER_SEARCH = 0xAA,
|
|
VKEY_BROWSER_FAVORITES = 0xAB,
|
|
VKEY_BROWSER_HOME = 0xAC,
|
|
VKEY_VOLUME_MUTE = 0xAD,
|
|
VKEY_VOLUME_DOWN = 0xAE,
|
|
VKEY_VOLUME_UP = 0xAF,
|
|
VKEY_MEDIA_NEXT_TRACK = 0xB0,
|
|
VKEY_MEDIA_PREV_TRACK = 0xB1,
|
|
VKEY_MEDIA_STOP = 0xB2,
|
|
VKEY_MEDIA_PLAY_PAUSE = 0xB3,
|
|
VKEY_MEDIA_LAUNCH_MAIL = 0xB4,
|
|
VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5,
|
|
VKEY_MEDIA_LAUNCH_APP1 = 0xB6,
|
|
VKEY_MEDIA_LAUNCH_APP2 = 0xB7,
|
|
VKEY_OEM_1 = 0xBA,
|
|
VKEY_OEM_PLUS = 0xBB,
|
|
VKEY_OEM_COMMA = 0xBC,
|
|
VKEY_OEM_MINUS = 0xBD,
|
|
VKEY_OEM_PERIOD = 0xBE,
|
|
VKEY_OEM_2 = 0xBF,
|
|
VKEY_OEM_3 = 0xC0,
|
|
VKEY_OEM_4 = 0xDB,
|
|
VKEY_OEM_5 = 0xDC,
|
|
VKEY_OEM_6 = 0xDD,
|
|
VKEY_OEM_7 = 0xDE,
|
|
VKEY_OEM_8 = 0xDF,
|
|
VKEY_OEM_102 = 0xE2,
|
|
VKEY_OEM_103 = 0xE3, // GTV KEYCODE_MEDIA_REWIND
|
|
VKEY_OEM_104 = 0xE4, // GTV KEYCODE_MEDIA_FAST_FORWARD
|
|
VKEY_PROCESSKEY = 0xE5,
|
|
VKEY_PACKET = 0xE7,
|
|
VKEY_DBE_SBCSCHAR = 0xF3,
|
|
VKEY_DBE_DBCSCHAR = 0xF4,
|
|
VKEY_ATTN = 0xF6,
|
|
VKEY_CRSEL = 0xF7,
|
|
VKEY_EXSEL = 0xF8,
|
|
VKEY_EREOF = 0xF9,
|
|
VKEY_PLAY = 0xFA,
|
|
VKEY_ZOOM = 0xFB,
|
|
VKEY_NONAME = 0xFC,
|
|
VKEY_PA1 = 0xFD,
|
|
VKEY_OEM_CLEAR = 0xFE,
|
|
VKEY_UNKNOWN = 0,
|
|
|
|
// POSIX specific VKEYs. Note that as of Windows SDK 7.1, 0x97-9F, 0xD8-DA,
|
|
// and 0xE8 are unassigned.
|
|
VKEY_WLAN = 0x97,
|
|
VKEY_POWER = 0x98,
|
|
VKEY_BRIGHTNESS_DOWN = 0xD8,
|
|
VKEY_BRIGHTNESS_UP = 0xD9,
|
|
VKEY_KBD_BRIGHTNESS_DOWN = 0xDA,
|
|
VKEY_KBD_BRIGHTNESS_UP = 0xE8,
|
|
|
|
// Windows does not have a specific key code for AltGr. We use the unused 0xE1
|
|
// (VK_OEM_AX) code to represent AltGr, matching the behaviour of Firefox on
|
|
// Linux.
|
|
VKEY_ALTGR = 0xE1,
|
|
// Windows does not have a specific key code for Compose. We use the unused
|
|
// 0xE6 (VK_ICO_CLEAR) code to represent Compose.
|
|
VKEY_COMPOSE = 0xE6,
|
|
};
|
|
|
|
// From ui/events/keycodes/keyboard_code_conversion_x.cc.
|
|
// Gdk key codes (e.g. GDK_BackSpace) and X keysyms (e.g. XK_BackSpace) share
|
|
// the same values.
|
|
KeyboardCode KeyboardCodeFromXKeysym(unsigned int keysym) {
|
|
switch (keysym) {
|
|
case XK_BackSpace:
|
|
return VKEY_BACK;
|
|
case XK_Delete:
|
|
case XK_KP_Delete:
|
|
return VKEY_DELETE;
|
|
case XK_Tab:
|
|
case XK_KP_Tab:
|
|
case XK_ISO_Left_Tab:
|
|
case XK_3270_BackTab:
|
|
return VKEY_TAB;
|
|
case XK_Linefeed:
|
|
case XK_Return:
|
|
case XK_KP_Enter:
|
|
case XK_ISO_Enter:
|
|
return VKEY_RETURN;
|
|
case XK_Clear:
|
|
case XK_KP_Begin: // NumPad 5 without Num Lock, for crosbug.com/29169.
|
|
return VKEY_CLEAR;
|
|
case XK_KP_Space:
|
|
case XK_space:
|
|
return VKEY_SPACE;
|
|
case XK_Home:
|
|
case XK_KP_Home:
|
|
return VKEY_HOME;
|
|
case XK_End:
|
|
case XK_KP_End:
|
|
return VKEY_END;
|
|
case XK_Page_Up:
|
|
case XK_KP_Page_Up: // aka XK_KP_Prior
|
|
return VKEY_PRIOR;
|
|
case XK_Page_Down:
|
|
case XK_KP_Page_Down: // aka XK_KP_Next
|
|
return VKEY_NEXT;
|
|
case XK_Left:
|
|
case XK_KP_Left:
|
|
return VKEY_LEFT;
|
|
case XK_Right:
|
|
case XK_KP_Right:
|
|
return VKEY_RIGHT;
|
|
case XK_Down:
|
|
case XK_KP_Down:
|
|
return VKEY_DOWN;
|
|
case XK_Up:
|
|
case XK_KP_Up:
|
|
return VKEY_UP;
|
|
case XK_Escape:
|
|
return VKEY_ESCAPE;
|
|
case XK_Kana_Lock:
|
|
case XK_Kana_Shift:
|
|
return VKEY_KANA;
|
|
case XK_Hangul:
|
|
return VKEY_HANGUL;
|
|
case XK_Hangul_Hanja:
|
|
return VKEY_HANJA;
|
|
case XK_Kanji:
|
|
return VKEY_KANJI;
|
|
case XK_Henkan:
|
|
return VKEY_CONVERT;
|
|
case XK_Muhenkan:
|
|
return VKEY_NONCONVERT;
|
|
case XK_Zenkaku_Hankaku:
|
|
return VKEY_DBE_DBCSCHAR;
|
|
case XK_A:
|
|
case XK_a:
|
|
return VKEY_A;
|
|
case XK_B:
|
|
case XK_b:
|
|
return VKEY_B;
|
|
case XK_C:
|
|
case XK_c:
|
|
return VKEY_C;
|
|
case XK_D:
|
|
case XK_d:
|
|
return VKEY_D;
|
|
case XK_E:
|
|
case XK_e:
|
|
return VKEY_E;
|
|
case XK_F:
|
|
case XK_f:
|
|
return VKEY_F;
|
|
case XK_G:
|
|
case XK_g:
|
|
return VKEY_G;
|
|
case XK_H:
|
|
case XK_h:
|
|
return VKEY_H;
|
|
case XK_I:
|
|
case XK_i:
|
|
return VKEY_I;
|
|
case XK_J:
|
|
case XK_j:
|
|
return VKEY_J;
|
|
case XK_K:
|
|
case XK_k:
|
|
return VKEY_K;
|
|
case XK_L:
|
|
case XK_l:
|
|
return VKEY_L;
|
|
case XK_M:
|
|
case XK_m:
|
|
return VKEY_M;
|
|
case XK_N:
|
|
case XK_n:
|
|
return VKEY_N;
|
|
case XK_O:
|
|
case XK_o:
|
|
return VKEY_O;
|
|
case XK_P:
|
|
case XK_p:
|
|
return VKEY_P;
|
|
case XK_Q:
|
|
case XK_q:
|
|
return VKEY_Q;
|
|
case XK_R:
|
|
case XK_r:
|
|
return VKEY_R;
|
|
case XK_S:
|
|
case XK_s:
|
|
return VKEY_S;
|
|
case XK_T:
|
|
case XK_t:
|
|
return VKEY_T;
|
|
case XK_U:
|
|
case XK_u:
|
|
return VKEY_U;
|
|
case XK_V:
|
|
case XK_v:
|
|
return VKEY_V;
|
|
case XK_W:
|
|
case XK_w:
|
|
return VKEY_W;
|
|
case XK_X:
|
|
case XK_x:
|
|
return VKEY_X;
|
|
case XK_Y:
|
|
case XK_y:
|
|
return VKEY_Y;
|
|
case XK_Z:
|
|
case XK_z:
|
|
return VKEY_Z;
|
|
|
|
case XK_0:
|
|
case XK_1:
|
|
case XK_2:
|
|
case XK_3:
|
|
case XK_4:
|
|
case XK_5:
|
|
case XK_6:
|
|
case XK_7:
|
|
case XK_8:
|
|
case XK_9:
|
|
return static_cast<KeyboardCode>(VKEY_0 + (keysym - XK_0));
|
|
|
|
case XK_parenright:
|
|
return VKEY_0;
|
|
case XK_exclam:
|
|
return VKEY_1;
|
|
case XK_at:
|
|
return VKEY_2;
|
|
case XK_numbersign:
|
|
return VKEY_3;
|
|
case XK_dollar:
|
|
return VKEY_4;
|
|
case XK_percent:
|
|
return VKEY_5;
|
|
case XK_asciicircum:
|
|
return VKEY_6;
|
|
case XK_ampersand:
|
|
return VKEY_7;
|
|
case XK_asterisk:
|
|
return VKEY_8;
|
|
case XK_parenleft:
|
|
return VKEY_9;
|
|
|
|
case XK_KP_0:
|
|
case XK_KP_1:
|
|
case XK_KP_2:
|
|
case XK_KP_3:
|
|
case XK_KP_4:
|
|
case XK_KP_5:
|
|
case XK_KP_6:
|
|
case XK_KP_7:
|
|
case XK_KP_8:
|
|
case XK_KP_9:
|
|
return static_cast<KeyboardCode>(VKEY_NUMPAD0 + (keysym - XK_KP_0));
|
|
|
|
case XK_multiply:
|
|
case XK_KP_Multiply:
|
|
return VKEY_MULTIPLY;
|
|
case XK_KP_Add:
|
|
return VKEY_ADD;
|
|
case XK_KP_Separator:
|
|
return VKEY_SEPARATOR;
|
|
case XK_KP_Subtract:
|
|
return VKEY_SUBTRACT;
|
|
case XK_KP_Decimal:
|
|
return VKEY_DECIMAL;
|
|
case XK_KP_Divide:
|
|
return VKEY_DIVIDE;
|
|
case XK_KP_Equal:
|
|
case XK_equal:
|
|
case XK_plus:
|
|
return VKEY_OEM_PLUS;
|
|
case XK_comma:
|
|
case XK_less:
|
|
return VKEY_OEM_COMMA;
|
|
case XK_minus:
|
|
case XK_underscore:
|
|
return VKEY_OEM_MINUS;
|
|
case XK_greater:
|
|
case XK_period:
|
|
return VKEY_OEM_PERIOD;
|
|
case XK_colon:
|
|
case XK_semicolon:
|
|
return VKEY_OEM_1;
|
|
case XK_question:
|
|
case XK_slash:
|
|
return VKEY_OEM_2;
|
|
case XK_asciitilde:
|
|
case XK_quoteleft:
|
|
return VKEY_OEM_3;
|
|
case XK_bracketleft:
|
|
case XK_braceleft:
|
|
return VKEY_OEM_4;
|
|
case XK_backslash:
|
|
case XK_bar:
|
|
return VKEY_OEM_5;
|
|
case XK_bracketright:
|
|
case XK_braceright:
|
|
return VKEY_OEM_6;
|
|
case XK_quoteright:
|
|
case XK_quotedbl:
|
|
return VKEY_OEM_7;
|
|
case XK_ISO_Level5_Shift:
|
|
return VKEY_OEM_8;
|
|
case XK_Shift_L:
|
|
case XK_Shift_R:
|
|
return VKEY_SHIFT;
|
|
case XK_Control_L:
|
|
case XK_Control_R:
|
|
return VKEY_CONTROL;
|
|
case XK_Meta_L:
|
|
case XK_Meta_R:
|
|
case XK_Alt_L:
|
|
case XK_Alt_R:
|
|
return VKEY_MENU;
|
|
case XK_ISO_Level3_Shift:
|
|
return VKEY_ALTGR;
|
|
case XK_Multi_key:
|
|
return VKEY_COMPOSE;
|
|
case XK_Pause:
|
|
return VKEY_PAUSE;
|
|
case XK_Caps_Lock:
|
|
return VKEY_CAPITAL;
|
|
case XK_Num_Lock:
|
|
return VKEY_NUMLOCK;
|
|
case XK_Scroll_Lock:
|
|
return VKEY_SCROLL;
|
|
case XK_Select:
|
|
return VKEY_SELECT;
|
|
case XK_Print:
|
|
return VKEY_PRINT;
|
|
case XK_Execute:
|
|
return VKEY_EXECUTE;
|
|
case XK_Insert:
|
|
case XK_KP_Insert:
|
|
return VKEY_INSERT;
|
|
case XK_Help:
|
|
return VKEY_HELP;
|
|
case XK_Super_L:
|
|
return VKEY_LWIN;
|
|
case XK_Super_R:
|
|
return VKEY_RWIN;
|
|
case XK_Menu:
|
|
return VKEY_APPS;
|
|
case XK_F1:
|
|
case XK_F2:
|
|
case XK_F3:
|
|
case XK_F4:
|
|
case XK_F5:
|
|
case XK_F6:
|
|
case XK_F7:
|
|
case XK_F8:
|
|
case XK_F9:
|
|
case XK_F10:
|
|
case XK_F11:
|
|
case XK_F12:
|
|
case XK_F13:
|
|
case XK_F14:
|
|
case XK_F15:
|
|
case XK_F16:
|
|
case XK_F17:
|
|
case XK_F18:
|
|
case XK_F19:
|
|
case XK_F20:
|
|
case XK_F21:
|
|
case XK_F22:
|
|
case XK_F23:
|
|
case XK_F24:
|
|
return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_F1));
|
|
case XK_KP_F1:
|
|
case XK_KP_F2:
|
|
case XK_KP_F3:
|
|
case XK_KP_F4:
|
|
return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_KP_F1));
|
|
|
|
case XK_guillemotleft:
|
|
case XK_guillemotright:
|
|
case XK_degree:
|
|
// In the case of canadian multilingual keyboard layout, VKEY_OEM_102 is
|
|
// assigned to ugrave key.
|
|
case XK_ugrave:
|
|
case XK_Ugrave:
|
|
case XK_brokenbar:
|
|
return VKEY_OEM_102; // international backslash key in 102 keyboard.
|
|
|
|
// When evdev is in use, /usr/share/X11/xkb/symbols/inet maps F13-18 keys
|
|
// to the special XF86XK symbols to support Microsoft Ergonomic keyboards:
|
|
// https://bugs.freedesktop.org/show_bug.cgi?id=5783
|
|
// In Chrome, we map these X key symbols back to F13-18 since we don't have
|
|
// VKEYs for these XF86XK symbols.
|
|
case XF86XK_Tools:
|
|
return VKEY_F13;
|
|
case XF86XK_Launch5:
|
|
return VKEY_F14;
|
|
case XF86XK_Launch6:
|
|
return VKEY_F15;
|
|
case XF86XK_Launch7:
|
|
return VKEY_F16;
|
|
case XF86XK_Launch8:
|
|
return VKEY_F17;
|
|
case XF86XK_Launch9:
|
|
return VKEY_F18;
|
|
case XF86XK_Refresh:
|
|
case XF86XK_History:
|
|
case XF86XK_OpenURL:
|
|
case XF86XK_AddFavorite:
|
|
case XF86XK_Go:
|
|
case XF86XK_ZoomIn:
|
|
case XF86XK_ZoomOut:
|
|
// ui::AcceleratorGtk tries to convert the XF86XK_ keysyms on Chrome
|
|
// startup. It's safe to return VKEY_UNKNOWN here since ui::AcceleratorGtk
|
|
// also checks a Gdk keysym. http://crbug.com/109843
|
|
return VKEY_UNKNOWN;
|
|
// For supporting multimedia buttons on a USB keyboard.
|
|
case XF86XK_Back:
|
|
return VKEY_BROWSER_BACK;
|
|
case XF86XK_Forward:
|
|
return VKEY_BROWSER_FORWARD;
|
|
case XF86XK_Reload:
|
|
return VKEY_BROWSER_REFRESH;
|
|
case XF86XK_Stop:
|
|
return VKEY_BROWSER_STOP;
|
|
case XF86XK_Search:
|
|
return VKEY_BROWSER_SEARCH;
|
|
case XF86XK_Favorites:
|
|
return VKEY_BROWSER_FAVORITES;
|
|
case XF86XK_HomePage:
|
|
return VKEY_BROWSER_HOME;
|
|
case XF86XK_AudioMute:
|
|
return VKEY_VOLUME_MUTE;
|
|
case XF86XK_AudioLowerVolume:
|
|
return VKEY_VOLUME_DOWN;
|
|
case XF86XK_AudioRaiseVolume:
|
|
return VKEY_VOLUME_UP;
|
|
case XF86XK_AudioNext:
|
|
return VKEY_MEDIA_NEXT_TRACK;
|
|
case XF86XK_AudioPrev:
|
|
return VKEY_MEDIA_PREV_TRACK;
|
|
case XF86XK_AudioStop:
|
|
return VKEY_MEDIA_STOP;
|
|
case XF86XK_AudioPlay:
|
|
return VKEY_MEDIA_PLAY_PAUSE;
|
|
case XF86XK_Mail:
|
|
return VKEY_MEDIA_LAUNCH_MAIL;
|
|
case XF86XK_LaunchA: // F3 on an Apple keyboard.
|
|
return VKEY_MEDIA_LAUNCH_APP1;
|
|
case XF86XK_LaunchB: // F4 on an Apple keyboard.
|
|
case XF86XK_Calculator:
|
|
return VKEY_MEDIA_LAUNCH_APP2;
|
|
case XF86XK_WLAN:
|
|
return VKEY_WLAN;
|
|
case XF86XK_PowerOff:
|
|
return VKEY_POWER;
|
|
case XF86XK_MonBrightnessDown:
|
|
return VKEY_BRIGHTNESS_DOWN;
|
|
case XF86XK_MonBrightnessUp:
|
|
return VKEY_BRIGHTNESS_UP;
|
|
case XF86XK_KbdBrightnessDown:
|
|
return VKEY_KBD_BRIGHTNESS_DOWN;
|
|
case XF86XK_KbdBrightnessUp:
|
|
return VKEY_KBD_BRIGHTNESS_UP;
|
|
|
|
// TODO(sad): some keycodes are still missing.
|
|
}
|
|
return VKEY_UNKNOWN;
|
|
}
|
|
|
|
// From content/browser/renderer_host/input/web_input_event_util_posix.cc.
|
|
KeyboardCode GdkEventToWindowsKeyCode(const GdkEventKey* event) {
|
|
static const unsigned int kHardwareCodeToGDKKeyval[] = {
|
|
0, // 0x00:
|
|
0, // 0x01:
|
|
0, // 0x02:
|
|
0, // 0x03:
|
|
0, // 0x04:
|
|
0, // 0x05:
|
|
0, // 0x06:
|
|
0, // 0x07:
|
|
0, // 0x08:
|
|
0, // 0x09: GDK_Escape
|
|
GDK_1, // 0x0A: GDK_1
|
|
GDK_2, // 0x0B: GDK_2
|
|
GDK_3, // 0x0C: GDK_3
|
|
GDK_4, // 0x0D: GDK_4
|
|
GDK_5, // 0x0E: GDK_5
|
|
GDK_6, // 0x0F: GDK_6
|
|
GDK_7, // 0x10: GDK_7
|
|
GDK_8, // 0x11: GDK_8
|
|
GDK_9, // 0x12: GDK_9
|
|
GDK_0, // 0x13: GDK_0
|
|
GDK_minus, // 0x14: GDK_minus
|
|
GDK_equal, // 0x15: GDK_equal
|
|
0, // 0x16: GDK_BackSpace
|
|
0, // 0x17: GDK_Tab
|
|
GDK_q, // 0x18: GDK_q
|
|
GDK_w, // 0x19: GDK_w
|
|
GDK_e, // 0x1A: GDK_e
|
|
GDK_r, // 0x1B: GDK_r
|
|
GDK_t, // 0x1C: GDK_t
|
|
GDK_y, // 0x1D: GDK_y
|
|
GDK_u, // 0x1E: GDK_u
|
|
GDK_i, // 0x1F: GDK_i
|
|
GDK_o, // 0x20: GDK_o
|
|
GDK_p, // 0x21: GDK_p
|
|
GDK_bracketleft, // 0x22: GDK_bracketleft
|
|
GDK_bracketright, // 0x23: GDK_bracketright
|
|
0, // 0x24: GDK_Return
|
|
0, // 0x25: GDK_Control_L
|
|
GDK_a, // 0x26: GDK_a
|
|
GDK_s, // 0x27: GDK_s
|
|
GDK_d, // 0x28: GDK_d
|
|
GDK_f, // 0x29: GDK_f
|
|
GDK_g, // 0x2A: GDK_g
|
|
GDK_h, // 0x2B: GDK_h
|
|
GDK_j, // 0x2C: GDK_j
|
|
GDK_k, // 0x2D: GDK_k
|
|
GDK_l, // 0x2E: GDK_l
|
|
GDK_semicolon, // 0x2F: GDK_semicolon
|
|
GDK_apostrophe, // 0x30: GDK_apostrophe
|
|
GDK_grave, // 0x31: GDK_grave
|
|
0, // 0x32: GDK_Shift_L
|
|
GDK_backslash, // 0x33: GDK_backslash
|
|
GDK_z, // 0x34: GDK_z
|
|
GDK_x, // 0x35: GDK_x
|
|
GDK_c, // 0x36: GDK_c
|
|
GDK_v, // 0x37: GDK_v
|
|
GDK_b, // 0x38: GDK_b
|
|
GDK_n, // 0x39: GDK_n
|
|
GDK_m, // 0x3A: GDK_m
|
|
GDK_comma, // 0x3B: GDK_comma
|
|
GDK_period, // 0x3C: GDK_period
|
|
GDK_slash, // 0x3D: GDK_slash
|
|
0, // 0x3E: GDK_Shift_R
|
|
0, // 0x3F:
|
|
0, // 0x40:
|
|
0, // 0x41:
|
|
0, // 0x42:
|
|
0, // 0x43:
|
|
0, // 0x44:
|
|
0, // 0x45:
|
|
0, // 0x46:
|
|
0, // 0x47:
|
|
0, // 0x48:
|
|
0, // 0x49:
|
|
0, // 0x4A:
|
|
0, // 0x4B:
|
|
0, // 0x4C:
|
|
0, // 0x4D:
|
|
0, // 0x4E:
|
|
0, // 0x4F:
|
|
0, // 0x50:
|
|
0, // 0x51:
|
|
0, // 0x52:
|
|
0, // 0x53:
|
|
0, // 0x54:
|
|
0, // 0x55:
|
|
0, // 0x56:
|
|
0, // 0x57:
|
|
0, // 0x58:
|
|
0, // 0x59:
|
|
0, // 0x5A:
|
|
0, // 0x5B:
|
|
0, // 0x5C:
|
|
0, // 0x5D:
|
|
0, // 0x5E:
|
|
0, // 0x5F:
|
|
0, // 0x60:
|
|
0, // 0x61:
|
|
0, // 0x62:
|
|
0, // 0x63:
|
|
0, // 0x64:
|
|
0, // 0x65:
|
|
0, // 0x66:
|
|
0, // 0x67:
|
|
0, // 0x68:
|
|
0, // 0x69:
|
|
0, // 0x6A:
|
|
0, // 0x6B:
|
|
0, // 0x6C:
|
|
0, // 0x6D:
|
|
0, // 0x6E:
|
|
0, // 0x6F:
|
|
0, // 0x70:
|
|
0, // 0x71:
|
|
0, // 0x72:
|
|
GDK_Super_L, // 0x73: GDK_Super_L
|
|
GDK_Super_R, // 0x74: GDK_Super_R
|
|
};
|
|
|
|
// |windows_key_code| has to include a valid virtual-key code even when we
|
|
// use non-US layouts, e.g. even when we type an 'A' key of a US keyboard
|
|
// on the Hebrew layout, |windows_key_code| should be VK_A.
|
|
// On the other hand, |event->keyval| value depends on the current
|
|
// GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on
|
|
// the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this
|
|
// KeyboardCodeFromXKeysym() call returns 0.
|
|
// To improve compatibilty with Windows, we use |event->hardware_keycode|
|
|
// for retrieving its Windows key-code for the keys when the
|
|
// WebCore::windows_key_codeForEvent() call returns 0.
|
|
// We shouldn't use |event->hardware_keycode| for keys that GdkKeymap
|
|
// objects cannot change because |event->hardware_keycode| doesn't change
|
|
// even when we change the layout options, e.g. when we swap a control
|
|
// key and a caps-lock key, GTK doesn't swap their
|
|
// |event->hardware_keycode| values but swap their |event->keyval| values.
|
|
KeyboardCode windows_key_code = KeyboardCodeFromXKeysym(event->keyval);
|
|
if (windows_key_code)
|
|
return windows_key_code;
|
|
|
|
if (event->hardware_keycode < arraysize(kHardwareCodeToGDKKeyval)) {
|
|
int keyval = kHardwareCodeToGDKKeyval[event->hardware_keycode];
|
|
if (keyval)
|
|
return KeyboardCodeFromXKeysym(keyval);
|
|
}
|
|
|
|
// This key is one that keyboard-layout drivers cannot change.
|
|
// Use |event->keyval| to retrieve its |windows_key_code| value.
|
|
return KeyboardCodeFromXKeysym(event->keyval);
|
|
}
|
|
|
|
// From content/browser/renderer_host/input/web_input_event_util_posix.cc.
|
|
KeyboardCode GetWindowsKeyCodeWithoutLocation(KeyboardCode key_code) {
|
|
switch (key_code) {
|
|
case VKEY_LCONTROL:
|
|
case VKEY_RCONTROL:
|
|
return VKEY_CONTROL;
|
|
case VKEY_LSHIFT:
|
|
case VKEY_RSHIFT:
|
|
return VKEY_SHIFT;
|
|
case VKEY_LMENU:
|
|
case VKEY_RMENU:
|
|
return VKEY_MENU;
|
|
default:
|
|
return key_code;
|
|
}
|
|
}
|
|
|
|
// From content/browser/renderer_host/input/web_input_event_builders_gtk.cc.
|
|
// Gets the corresponding control character of a specified key code. See:
|
|
// http://en.wikipedia.org/wiki/Control_characters
|
|
// We emulate Windows behavior here.
|
|
int GetControlCharacter(KeyboardCode windows_key_code, bool shift) {
|
|
if (windows_key_code >= VKEY_A && windows_key_code <= VKEY_Z) {
|
|
// ctrl-A ~ ctrl-Z map to \x01 ~ \x1A
|
|
return windows_key_code - VKEY_A + 1;
|
|
}
|
|
if (shift) {
|
|
// following graphics chars require shift key to input.
|
|
switch (windows_key_code) {
|
|
// ctrl-@ maps to \x00 (Null byte)
|
|
case VKEY_2:
|
|
return 0;
|
|
// ctrl-^ maps to \x1E (Record separator, Information separator two)
|
|
case VKEY_6:
|
|
return 0x1E;
|
|
// ctrl-_ maps to \x1F (Unit separator, Information separator one)
|
|
case VKEY_OEM_MINUS:
|
|
return 0x1F;
|
|
// Returns 0 for all other keys to avoid inputting unexpected chars.
|
|
default:
|
|
return 0;
|
|
}
|
|
} else {
|
|
switch (windows_key_code) {
|
|
// ctrl-[ maps to \x1B (Escape)
|
|
case VKEY_OEM_4:
|
|
return 0x1B;
|
|
// ctrl-\ maps to \x1C (File separator, Information separator four)
|
|
case VKEY_OEM_5:
|
|
return 0x1C;
|
|
// ctrl-] maps to \x1D (Group separator, Information separator three)
|
|
case VKEY_OEM_6:
|
|
return 0x1D;
|
|
// ctrl-Enter maps to \x0A (Line feed)
|
|
case VKEY_RETURN:
|
|
return 0x0A;
|
|
// Returns 0 for all other keys to avoid inputting unexpected chars.
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GetWidgetRectInScreen(GtkWidget* widget, GdkRectangle* r) {
|
|
gint x, y, w, h;
|
|
GdkRectangle extents;
|
|
|
|
GdkWindow* window = gtk_widget_get_parent_window(widget);
|
|
|
|
// Get parent's left-top screen coordinates.
|
|
gdk_window_get_root_origin(window, &x, &y);
|
|
// Get parent's width and height.
|
|
gdk_drawable_get_size(window, &w, &h);
|
|
// Get parent's extents including decorations.
|
|
gdk_window_get_frame_extents(window, &extents);
|
|
|
|
// X and Y calculations assume that left, right and bottom border sizes are
|
|
// all the same.
|
|
const gint border = (extents.width - w) / 2;
|
|
r->x = x + border + widget->allocation.x;
|
|
r->y = y + (extents.height - h) - border + widget->allocation.y;
|
|
r->width = widget->allocation.width;
|
|
r->height = widget->allocation.height;
|
|
}
|
|
|
|
CefBrowserHost::DragOperationsMask GetDragOperationsMask(
|
|
GdkDragContext* drag_context) {
|
|
int allowed_ops = DRAG_OPERATION_NONE;
|
|
GdkDragAction drag_action = gdk_drag_context_get_actions(drag_context);
|
|
if (drag_action & GDK_ACTION_COPY)
|
|
allowed_ops |= DRAG_OPERATION_COPY;
|
|
if (drag_action & GDK_ACTION_MOVE)
|
|
allowed_ops |= DRAG_OPERATION_MOVE;
|
|
if (drag_action & GDK_ACTION_LINK)
|
|
allowed_ops |= DRAG_OPERATION_LINK;
|
|
if (drag_action & GDK_ACTION_PRIVATE)
|
|
allowed_ops |= DRAG_OPERATION_PRIVATE;
|
|
return static_cast<CefBrowserHost::DragOperationsMask>(allowed_ops);
|
|
}
|
|
|
|
class ScopedGLContext {
|
|
public:
|
|
ScopedGLContext(GtkWidget* widget, bool swap_buffers)
|
|
: swap_buffers_(swap_buffers) {
|
|
GdkGLContext* glcontext = gtk_widget_get_gl_context(widget);
|
|
gldrawable_ = gtk_widget_get_gl_drawable(widget);
|
|
is_valid_ = gdk_gl_drawable_gl_begin(gldrawable_, glcontext);
|
|
}
|
|
|
|
virtual ~ScopedGLContext() {
|
|
if (is_valid_) {
|
|
gdk_gl_drawable_gl_end(gldrawable_);
|
|
|
|
if (swap_buffers_) {
|
|
if (gdk_gl_drawable_is_double_buffered(gldrawable_))
|
|
gdk_gl_drawable_swap_buffers(gldrawable_);
|
|
else
|
|
glFlush();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IsValid() const { return is_valid_; }
|
|
|
|
private:
|
|
bool swap_buffers_;
|
|
GdkGLDrawable* gldrawable_;
|
|
bool is_valid_;
|
|
ScopedGdkThreadsEnter scoped_gdk_threads_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
BrowserWindowOsrGtk::BrowserWindowOsrGtk(BrowserWindow::Delegate* delegate,
|
|
const std::string& startup_url,
|
|
const OsrRendererSettings& settings)
|
|
: BrowserWindow(delegate),
|
|
xdisplay_(nullptr),
|
|
renderer_(settings),
|
|
gl_enabled_(false),
|
|
painting_popup_(false),
|
|
hidden_(false),
|
|
glarea_(NULL),
|
|
drag_trigger_event_(nullptr),
|
|
drag_data_(nullptr),
|
|
drag_operation_(DRAG_OPERATION_NONE),
|
|
drag_context_(nullptr),
|
|
drag_targets_(gtk_target_list_new(NULL, 0)),
|
|
drag_leave_(false),
|
|
drag_drop_(false),
|
|
device_scale_factor_(1.0f) {
|
|
client_handler_ = new ClientHandlerOsr(this, this, startup_url);
|
|
g_browser_windows.push_back(this);
|
|
}
|
|
|
|
BrowserWindowOsrGtk::~BrowserWindowOsrGtk() {
|
|
g_browser_windows.erase(
|
|
std::find(g_browser_windows.begin(), g_browser_windows.end(), this));
|
|
ScopedGdkThreadsEnter scoped_gdk_threads;
|
|
|
|
if (drag_trigger_event_) {
|
|
gdk_event_free(drag_trigger_event_);
|
|
}
|
|
if (drag_context_) {
|
|
g_object_unref(drag_context_);
|
|
}
|
|
gtk_target_list_unref(drag_targets_);
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::set_xdisplay(XDisplay* xdisplay) {
|
|
REQUIRE_MAIN_THREAD();
|
|
DCHECK(!xdisplay_);
|
|
xdisplay_ = xdisplay;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::CreateBrowser(
|
|
ClientWindowHandle parent_handle,
|
|
const CefRect& rect,
|
|
const CefBrowserSettings& settings,
|
|
CefRefPtr<CefDictionaryValue> extra_info,
|
|
CefRefPtr<CefRequestContext> request_context) {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
// Create the native window.
|
|
Create(parent_handle);
|
|
|
|
ScopedGdkThreadsEnter scoped_gdk_threads;
|
|
|
|
// Retrieve the X11 Window ID for the GTK parent window.
|
|
GtkWidget* window =
|
|
gtk_widget_get_ancestor(GTK_WIDGET(parent_handle), GTK_TYPE_WINDOW);
|
|
::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(window));
|
|
DCHECK(xwindow);
|
|
|
|
CefWindowInfo window_info;
|
|
window_info.SetAsWindowless(xwindow);
|
|
|
|
// Create the browser asynchronously.
|
|
CefBrowserHost::CreateBrowser(window_info, client_handler_,
|
|
client_handler_->startup_url(), settings,
|
|
extra_info, request_context);
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::GetPopupConfig(CefWindowHandle temp_handle,
|
|
CefWindowInfo& windowInfo,
|
|
CefRefPtr<CefClient>& client,
|
|
CefBrowserSettings& settings) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
windowInfo.SetAsWindowless(temp_handle);
|
|
client = client_handler_;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::ShowPopup(ClientWindowHandle parent_handle,
|
|
int x,
|
|
int y,
|
|
size_t width,
|
|
size_t height) {
|
|
REQUIRE_MAIN_THREAD();
|
|
DCHECK(browser_.get());
|
|
|
|
// Create the native window.
|
|
Create(parent_handle);
|
|
|
|
// Send resize notification so the compositor is assigned the correct
|
|
// viewport size and begins rendering.
|
|
browser_->GetHost()->WasResized();
|
|
|
|
Show();
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::Show() {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (hidden_) {
|
|
// Set the browser as visible.
|
|
browser_->GetHost()->WasHidden(false);
|
|
hidden_ = false;
|
|
}
|
|
|
|
// Give focus to the browser.
|
|
browser_->GetHost()->SendFocusEvent(true);
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::Hide() {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
if (!browser_)
|
|
return;
|
|
|
|
// Remove focus from the browser.
|
|
browser_->GetHost()->SendFocusEvent(false);
|
|
|
|
if (!hidden_) {
|
|
// Set the browser as hidden.
|
|
browser_->GetHost()->WasHidden(true);
|
|
hidden_ = true;
|
|
}
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::SetBounds(int x, int y, size_t width, size_t height) {
|
|
REQUIRE_MAIN_THREAD();
|
|
// Nothing to do here. GTK will take care of positioning in the container.
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::SetFocus(bool focus) {
|
|
REQUIRE_MAIN_THREAD();
|
|
ScopedGdkThreadsEnter scoped_gdk_threads;
|
|
if (glarea_ && focus) {
|
|
gtk_widget_grab_focus(glarea_);
|
|
}
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::SetDeviceScaleFactor(float device_scale_factor) {
|
|
REQUIRE_MAIN_THREAD();
|
|
{
|
|
base::AutoLock lock_scope(lock_);
|
|
if (device_scale_factor == device_scale_factor_)
|
|
return;
|
|
|
|
// Apply some sanity checks.
|
|
if (device_scale_factor < 1.0f || device_scale_factor > 4.0f)
|
|
return;
|
|
|
|
device_scale_factor_ = device_scale_factor;
|
|
}
|
|
|
|
if (browser_) {
|
|
browser_->GetHost()->NotifyScreenInfoChanged();
|
|
browser_->GetHost()->WasResized();
|
|
}
|
|
}
|
|
|
|
float BrowserWindowOsrGtk::GetDeviceScaleFactor() const {
|
|
REQUIRE_MAIN_THREAD();
|
|
base::AutoLock lock_scope(lock_);
|
|
return device_scale_factor_;
|
|
}
|
|
|
|
ClientWindowHandle BrowserWindowOsrGtk::GetWindowHandle() const {
|
|
REQUIRE_MAIN_THREAD();
|
|
return glarea_;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// Detach |this| from the ClientHandlerOsr.
|
|
static_cast<ClientHandlerOsr*>(client_handler_.get())->DetachOsrDelegate();
|
|
|
|
ScopedGdkThreadsEnter scoped_gdk_threads;
|
|
|
|
UnregisterDragDrop();
|
|
|
|
// Disconnect all signal handlers that reference |this|.
|
|
g_signal_handlers_disconnect_matched(glarea_, G_SIGNAL_MATCH_DATA, 0, 0, NULL,
|
|
NULL, this);
|
|
|
|
DisableGL();
|
|
}
|
|
|
|
bool BrowserWindowOsrGtk::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
|
|
CefRect& rect) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
return false;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::GetViewRect(CefRefPtr<CefBrowser> browser,
|
|
CefRect& rect) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
rect.x = rect.y = 0;
|
|
|
|
if (!glarea_) {
|
|
// Never return an empty rectangle.
|
|
rect.width = rect.height = 1;
|
|
return;
|
|
}
|
|
|
|
float device_scale_factor;
|
|
{
|
|
base::AutoLock lock_scope(lock_);
|
|
device_scale_factor = device_scale_factor_;
|
|
}
|
|
|
|
// The simulated screen and view rectangle are the same. This is necessary
|
|
// for popup menus to be located and sized inside the view.
|
|
rect.width = DeviceToLogical(glarea_->allocation.width, device_scale_factor);
|
|
if (rect.width == 0)
|
|
rect.width = 1;
|
|
rect.height =
|
|
DeviceToLogical(glarea_->allocation.height, device_scale_factor);
|
|
if (rect.height == 0)
|
|
rect.height = 1;
|
|
}
|
|
|
|
bool BrowserWindowOsrGtk::GetScreenPoint(CefRefPtr<CefBrowser> browser,
|
|
int viewX,
|
|
int viewY,
|
|
int& screenX,
|
|
int& screenY) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
float device_scale_factor;
|
|
{
|
|
base::AutoLock lock_scope(lock_);
|
|
device_scale_factor = device_scale_factor_;
|
|
}
|
|
|
|
GdkRectangle screen_rect;
|
|
GetWidgetRectInScreen(glarea_, &screen_rect);
|
|
screenX = screen_rect.x + LogicalToDevice(viewX, device_scale_factor);
|
|
screenY = screen_rect.y + LogicalToDevice(viewY, device_scale_factor);
|
|
return true;
|
|
}
|
|
|
|
bool BrowserWindowOsrGtk::GetScreenInfo(CefRefPtr<CefBrowser> browser,
|
|
CefScreenInfo& screen_info) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
CefRect view_rect;
|
|
GetViewRect(browser, view_rect);
|
|
|
|
float device_scale_factor;
|
|
{
|
|
base::AutoLock lock_scope(lock_);
|
|
device_scale_factor = device_scale_factor_;
|
|
}
|
|
|
|
screen_info.device_scale_factor = device_scale_factor;
|
|
|
|
// The screen info rectangles are used by the renderer to create and position
|
|
// popups. Keep popups inside the view rectangle.
|
|
screen_info.rect = view_rect;
|
|
screen_info.available_rect = view_rect;
|
|
return true;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::OnPopupShow(CefRefPtr<CefBrowser> browser,
|
|
bool show) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!show) {
|
|
renderer_.ClearPopupRects();
|
|
browser->GetHost()->Invalidate(PET_VIEW);
|
|
}
|
|
renderer_.OnPopupShow(browser, show);
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::OnPopupSize(CefRefPtr<CefBrowser> browser,
|
|
const CefRect& rect) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
float device_scale_factor;
|
|
{
|
|
base::AutoLock lock_scope(lock_);
|
|
device_scale_factor = device_scale_factor_;
|
|
}
|
|
|
|
renderer_.OnPopupSize(browser, LogicalToDevice(rect, device_scale_factor));
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::OnPaint(CefRefPtr<CefBrowser> browser,
|
|
CefRenderHandler::PaintElementType type,
|
|
const CefRenderHandler::RectList& dirtyRects,
|
|
const void* buffer,
|
|
int width,
|
|
int height) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (width <= 2 && height <= 2) {
|
|
// Ignore really small buffer sizes while the widget is starting up.
|
|
return;
|
|
}
|
|
|
|
if (painting_popup_) {
|
|
renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
|
|
return;
|
|
}
|
|
|
|
if (!gl_enabled_)
|
|
EnableGL();
|
|
|
|
ScopedGLContext scoped_gl_context(glarea_, true);
|
|
if (!scoped_gl_context.IsValid())
|
|
return;
|
|
|
|
renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
|
|
if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) {
|
|
painting_popup_ = true;
|
|
browser->GetHost()->Invalidate(PET_POPUP);
|
|
painting_popup_ = false;
|
|
}
|
|
renderer_.Render();
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::OnCursorChange(
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefCursorHandle cursor,
|
|
cef_cursor_type_t type,
|
|
const CefCursorInfo& custom_cursor_info) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// Retrieve the X11 display shared with Chromium.
|
|
CHECK(xdisplay_ != 0);
|
|
|
|
ScopedGdkThreadsEnter scoped_gdk_threads;
|
|
|
|
// Retrieve the X11 window handle for the GTK widget.
|
|
::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(glarea_));
|
|
|
|
// Set the cursor.
|
|
XDefineCursor(xdisplay_, xwindow, cursor);
|
|
}
|
|
|
|
bool BrowserWindowOsrGtk::StartDragging(
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefDragData> drag_data,
|
|
CefRenderHandler::DragOperationsMask allowed_ops,
|
|
int x,
|
|
int y) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!drag_data->HasImage()) {
|
|
LOG(ERROR) << "Drag image representation not available";
|
|
return false;
|
|
}
|
|
|
|
DragReset();
|
|
drag_data_ = drag_data;
|
|
|
|
ScopedGdkThreadsEnter scoped_gdk_threads;
|
|
|
|
// Begin drag.
|
|
if (drag_trigger_event_) {
|
|
LOG(ERROR) << "Dragging started, but last mouse event is missing";
|
|
DragReset();
|
|
return false;
|
|
}
|
|
drag_context_ = gtk_drag_begin(glarea_, drag_targets_, GDK_ACTION_COPY,
|
|
1, // left mouse button
|
|
drag_trigger_event_);
|
|
if (!drag_context_) {
|
|
LOG(ERROR) << "GTK drag begin failed";
|
|
DragReset();
|
|
return false;
|
|
}
|
|
g_object_ref(drag_context_);
|
|
|
|
// Send drag enter event.
|
|
CefMouseEvent ev;
|
|
ev.x = x;
|
|
ev.y = y;
|
|
ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
|
|
browser->GetHost()->DragTargetDragEnter(drag_data, ev, allowed_ops);
|
|
|
|
return true;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::UpdateDragCursor(
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRenderHandler::DragOperation operation) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
drag_operation_ = operation;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::OnImeCompositionRangeChanged(
|
|
CefRefPtr<CefBrowser> browser,
|
|
const CefRange& selection_range,
|
|
const CefRenderHandler::RectList& character_bounds) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::UpdateAccessibilityLocation(
|
|
CefRefPtr<CefValue> value) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
|
|
REQUIRE_MAIN_THREAD();
|
|
DCHECK(!glarea_);
|
|
|
|
ScopedGdkThreadsEnter scoped_gdk_threads;
|
|
|
|
glarea_ = gtk_drawing_area_new();
|
|
DCHECK(glarea_);
|
|
|
|
GdkGLConfig* glconfig =
|
|
gdk_gl_config_new_by_mode(static_cast<GdkGLConfigMode>(
|
|
GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE));
|
|
DCHECK(glconfig);
|
|
|
|
gtk_widget_set_gl_capability(glarea_, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE);
|
|
|
|
gtk_widget_set_can_focus(glarea_, TRUE);
|
|
|
|
g_signal_connect(G_OBJECT(glarea_), "size_allocate",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::SizeAllocation), this);
|
|
|
|
gtk_widget_set_events(
|
|
glarea_, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
|
|
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
|
|
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
|
|
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
|
|
GDK_SCROLL_MASK | GDK_FOCUS_CHANGE_MASK);
|
|
g_signal_connect(G_OBJECT(glarea_), "button_press_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::ClickEvent), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "button_release_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::ClickEvent), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "key_press_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::KeyEvent), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "key_release_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::KeyEvent), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "enter_notify_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "leave_notify_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "motion_notify_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "scroll_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::ScrollEvent), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "focus_in_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "focus_out_event",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
|
|
|
|
RegisterDragDrop();
|
|
|
|
gtk_container_add(GTK_CONTAINER(parent_handle), glarea_);
|
|
|
|
// Make the GlArea visible in the parent container.
|
|
gtk_widget_show_all(parent_handle);
|
|
|
|
InitializeXinput(xdisplay_);
|
|
|
|
if (IsTouchAvailable())
|
|
RegisterTouch();
|
|
}
|
|
|
|
// static
|
|
gint BrowserWindowOsrGtk::SizeAllocation(GtkWidget* widget,
|
|
GtkAllocation* allocation,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (self->browser_.get()) {
|
|
// Results in a call to GetViewRect().
|
|
self->browser_->GetHost()->WasResized();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// static
|
|
gint BrowserWindowOsrGtk::ClickEvent(GtkWidget* widget,
|
|
GdkEventButton* event,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!self->browser_.get())
|
|
return TRUE;
|
|
|
|
CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
|
|
|
|
CefBrowserHost::MouseButtonType button_type = MBT_LEFT;
|
|
switch (event->button) {
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
button_type = MBT_MIDDLE;
|
|
break;
|
|
case 3:
|
|
button_type = MBT_RIGHT;
|
|
break;
|
|
default:
|
|
// Other mouse buttons are not handled here.
|
|
return FALSE;
|
|
}
|
|
|
|
float device_scale_factor;
|
|
{
|
|
base::AutoLock lock_scope(self->lock_);
|
|
device_scale_factor = self->device_scale_factor_;
|
|
}
|
|
|
|
CefMouseEvent mouse_event;
|
|
mouse_event.x = event->x;
|
|
mouse_event.y = event->y;
|
|
self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
|
|
DeviceToLogical(mouse_event, device_scale_factor);
|
|
mouse_event.modifiers = GetCefStateModifiers(event->state);
|
|
|
|
bool mouse_up = (event->type == GDK_BUTTON_RELEASE);
|
|
if (!mouse_up) {
|
|
gtk_widget_grab_focus(widget);
|
|
}
|
|
|
|
int click_count = 1;
|
|
switch (event->type) {
|
|
case GDK_2BUTTON_PRESS:
|
|
click_count = 2;
|
|
break;
|
|
case GDK_3BUTTON_PRESS:
|
|
click_count = 3;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
host->SendMouseClickEvent(mouse_event, button_type, mouse_up, click_count);
|
|
|
|
// Save mouse event that can be a possible trigger for drag.
|
|
if (!self->drag_context_ && button_type == MBT_LEFT) {
|
|
if (self->drag_trigger_event_) {
|
|
gdk_event_free(self->drag_trigger_event_);
|
|
}
|
|
self->drag_trigger_event_ =
|
|
gdk_event_copy(reinterpret_cast<GdkEvent*>(event));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// static
|
|
gint BrowserWindowOsrGtk::KeyEvent(GtkWidget* widget,
|
|
GdkEventKey* event,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!self->browser_.get())
|
|
return TRUE;
|
|
|
|
CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
|
|
|
|
// Based on WebKeyboardEventBuilder::Build from
|
|
// content/browser/renderer_host/input/web_input_event_builders_gtk.cc.
|
|
CefKeyEvent key_event;
|
|
KeyboardCode windows_key_code = GdkEventToWindowsKeyCode(event);
|
|
key_event.windows_key_code =
|
|
GetWindowsKeyCodeWithoutLocation(windows_key_code);
|
|
key_event.native_key_code = event->hardware_keycode;
|
|
|
|
key_event.modifiers = GetCefStateModifiers(event->state);
|
|
if (event->keyval >= GDK_KP_Space && event->keyval <= GDK_KP_9)
|
|
key_event.modifiers |= EVENTFLAG_IS_KEY_PAD;
|
|
if (key_event.modifiers & EVENTFLAG_ALT_DOWN)
|
|
key_event.is_system_key = true;
|
|
|
|
if (windows_key_code == VKEY_RETURN) {
|
|
// We need to treat the enter key as a key press of character \r. This
|
|
// is apparently just how webkit handles it and what it expects.
|
|
key_event.unmodified_character = '\r';
|
|
} else {
|
|
// FIXME: fix for non BMP chars
|
|
key_event.unmodified_character =
|
|
static_cast<int>(gdk_keyval_to_unicode(event->keyval));
|
|
}
|
|
|
|
// If ctrl key is pressed down, then control character shall be input.
|
|
if (key_event.modifiers & EVENTFLAG_CONTROL_DOWN) {
|
|
key_event.character = GetControlCharacter(
|
|
windows_key_code, key_event.modifiers & EVENTFLAG_SHIFT_DOWN);
|
|
} else {
|
|
key_event.character = key_event.unmodified_character;
|
|
}
|
|
|
|
if (event->type == GDK_KEY_PRESS) {
|
|
key_event.type = KEYEVENT_RAWKEYDOWN;
|
|
host->SendKeyEvent(key_event);
|
|
key_event.type = KEYEVENT_CHAR;
|
|
host->SendKeyEvent(key_event);
|
|
} else {
|
|
key_event.type = KEYEVENT_KEYUP;
|
|
host->SendKeyEvent(key_event);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// static
|
|
gint BrowserWindowOsrGtk::MoveEvent(GtkWidget* widget,
|
|
GdkEventMotion* event,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!self->browser_.get())
|
|
return TRUE;
|
|
|
|
CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
|
|
|
|
gint x, y;
|
|
GdkModifierType state;
|
|
|
|
if (event->is_hint) {
|
|
gdk_window_get_pointer(event->window, &x, &y, &state);
|
|
} else {
|
|
x = (gint)event->x;
|
|
y = (gint)event->y;
|
|
state = (GdkModifierType)event->state;
|
|
if (x == 0 && y == 0) {
|
|
// Invalid coordinates of (0,0) appear from time to time in
|
|
// enter-notify-event and leave-notify-event events. Sending them may
|
|
// cause StartDragging to never get called, so just ignore these.
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
float device_scale_factor;
|
|
{
|
|
base::AutoLock lock_scope(self->lock_);
|
|
device_scale_factor = self->device_scale_factor_;
|
|
}
|
|
|
|
CefMouseEvent mouse_event;
|
|
mouse_event.x = x;
|
|
mouse_event.y = y;
|
|
self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
|
|
DeviceToLogical(mouse_event, device_scale_factor);
|
|
mouse_event.modifiers = GetCefStateModifiers(state);
|
|
|
|
bool mouse_leave = (event->type == GDK_LEAVE_NOTIFY);
|
|
host->SendMouseMoveEvent(mouse_event, mouse_leave);
|
|
|
|
// Save mouse event that can be a possible trigger for drag.
|
|
if (!self->drag_context_ &&
|
|
(mouse_event.modifiers & EVENTFLAG_LEFT_MOUSE_BUTTON)) {
|
|
if (self->drag_trigger_event_) {
|
|
gdk_event_free(self->drag_trigger_event_);
|
|
}
|
|
self->drag_trigger_event_ =
|
|
gdk_event_copy(reinterpret_cast<GdkEvent*>(event));
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// static
|
|
gint BrowserWindowOsrGtk::ScrollEvent(GtkWidget* widget,
|
|
GdkEventScroll* event,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!self->browser_.get())
|
|
return TRUE;
|
|
|
|
CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
|
|
|
|
float device_scale_factor;
|
|
{
|
|
base::AutoLock lock_scope(self->lock_);
|
|
device_scale_factor = self->device_scale_factor_;
|
|
}
|
|
|
|
CefMouseEvent mouse_event;
|
|
mouse_event.x = event->x;
|
|
mouse_event.y = event->y;
|
|
self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
|
|
DeviceToLogical(mouse_event, device_scale_factor);
|
|
mouse_event.modifiers = GetCefStateModifiers(event->state);
|
|
|
|
static const int scrollbarPixelsPerGtkTick = 40;
|
|
int deltaX = 0;
|
|
int deltaY = 0;
|
|
switch (event->direction) {
|
|
case GDK_SCROLL_UP:
|
|
deltaY = scrollbarPixelsPerGtkTick;
|
|
break;
|
|
case GDK_SCROLL_DOWN:
|
|
deltaY = -scrollbarPixelsPerGtkTick;
|
|
break;
|
|
case GDK_SCROLL_LEFT:
|
|
deltaX = scrollbarPixelsPerGtkTick;
|
|
break;
|
|
case GDK_SCROLL_RIGHT:
|
|
deltaX = -scrollbarPixelsPerGtkTick;
|
|
break;
|
|
}
|
|
|
|
host->SendMouseWheelEvent(mouse_event, deltaX, deltaY);
|
|
return TRUE;
|
|
}
|
|
|
|
// static
|
|
gint BrowserWindowOsrGtk::FocusEvent(GtkWidget* widget,
|
|
GdkEventFocus* event,
|
|
BrowserWindowOsrGtk* self) {
|
|
// May be called on the main thread and the UI thread.
|
|
if (self->browser_.get())
|
|
self->browser_->GetHost()->SendFocusEvent(event->in == TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::TouchEvent(CefXIDeviceEvent event) {
|
|
if (!browser_.get())
|
|
return;
|
|
|
|
XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(event);
|
|
CefTouchEvent cef_event;
|
|
switch (ev->evtype) {
|
|
case XI_TouchBegin:
|
|
cef_event.type = CEF_TET_PRESSED;
|
|
break;
|
|
case XI_TouchUpdate:
|
|
cef_event.type = CEF_TET_MOVED;
|
|
break;
|
|
case XI_TouchEnd:
|
|
cef_event.type = CEF_TET_RELEASED;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
cef_event.id = ev->detail;
|
|
cef_event.x = ev->event_x;
|
|
cef_event.y = ev->event_y;
|
|
cef_event.radius_x = 0;
|
|
cef_event.radius_y = 0;
|
|
cef_event.rotation_angle = 0;
|
|
cef_event.pressure = 0;
|
|
cef_event.modifiers = GetCefStateModifiers(ev->mods, ev->buttons);
|
|
|
|
browser_->GetHost()->SendTouchEvent(cef_event);
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::RegisterTouch() {
|
|
GdkWindow* glwindow = gtk_widget_get_window(glarea_);
|
|
::Window xwindow = GDK_WINDOW_XID(glwindow);
|
|
uint32_t bitMask = XI_TouchBeginMask | XI_TouchUpdateMask | XI_TouchEndMask;
|
|
|
|
XIEventMask mask;
|
|
mask.deviceid = XIAllMasterDevices;
|
|
mask.mask = reinterpret_cast<unsigned char*>(&bitMask);
|
|
mask.mask_len = sizeof(bitMask);
|
|
XISelectEvents(xdisplay_, xwindow, &mask, 1);
|
|
}
|
|
|
|
// static
|
|
GdkFilterReturn BrowserWindowOsrGtk::EventFilter(GdkXEvent* gdk_xevent,
|
|
GdkEvent* event,
|
|
gpointer data) {
|
|
XEvent* xevent = static_cast<XEvent*>(gdk_xevent);
|
|
if (xevent->type == GenericEvent &&
|
|
xevent->xgeneric.extension == g_xinput_extension) {
|
|
XGetEventData(xevent->xcookie.display, &xevent->xcookie);
|
|
XIDeviceEvent* ev = static_cast<XIDeviceEvent*>(xevent->xcookie.data);
|
|
|
|
if (!ev)
|
|
return GDK_FILTER_REMOVE;
|
|
|
|
for (BrowserWindowOsrGtk* browser_window : g_browser_windows) {
|
|
GtkWidget* widget = browser_window->GetWindowHandle();
|
|
::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(widget));
|
|
if (xwindow == ev->event) {
|
|
browser_window->TouchEvent(ev);
|
|
break;
|
|
}
|
|
}
|
|
|
|
XFreeEventData(xevent->xcookie.display, &xevent->xcookie);
|
|
// Even if we didn't find a consumer for this event, we will make sure Gdk
|
|
// doesn't attempt to process the event, since it can't parse GenericEvents
|
|
return GDK_FILTER_REMOVE;
|
|
}
|
|
|
|
return GDK_FILTER_CONTINUE;
|
|
}
|
|
|
|
// static
|
|
void BrowserWindowOsrGtk::InitializeXinput(XDisplay* xdisplay) {
|
|
static bool initialized = false;
|
|
if (initialized)
|
|
return;
|
|
initialized = true;
|
|
|
|
int firstEvent, firstError;
|
|
if (XQueryExtension(xdisplay, "XInputExtension", &g_xinput_extension,
|
|
&firstEvent, &firstError)) {
|
|
int major = 2, minor = 2;
|
|
// X Input Extension 2.2 is needed for multitouch events.
|
|
if (XIQueryVersion(xdisplay, &major, &minor) == Success) {
|
|
// Ideally we would add an event filter for each glarea_ window
|
|
// separately, but unfortunately GDK can't parse X GenericEvents
|
|
// which have the target window stored in different way compared
|
|
// to other X events. That is why we add this global event filter
|
|
// just once, and dispatch the event to correct BrowserWindowOsrGtk
|
|
// manually.
|
|
gdk_window_add_filter(nullptr, &BrowserWindowOsrGtk::EventFilter,
|
|
nullptr);
|
|
} else {
|
|
g_xinput_extension = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool BrowserWindowOsrGtk::IsOverPopupWidget(int x, int y) const {
|
|
const CefRect& rc = renderer_.popup_rect();
|
|
int popup_right = rc.x + rc.width;
|
|
int popup_bottom = rc.y + rc.height;
|
|
return (x >= rc.x) && (x < popup_right) && (y >= rc.y) && (y < popup_bottom);
|
|
}
|
|
|
|
int BrowserWindowOsrGtk::GetPopupXOffset() const {
|
|
return renderer_.original_popup_rect().x - renderer_.popup_rect().x;
|
|
}
|
|
|
|
int BrowserWindowOsrGtk::GetPopupYOffset() const {
|
|
return renderer_.original_popup_rect().y - renderer_.popup_rect().y;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::ApplyPopupOffset(int& x, int& y) const {
|
|
if (IsOverPopupWidget(x, y)) {
|
|
x += GetPopupXOffset();
|
|
y += GetPopupYOffset();
|
|
}
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::EnableGL() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (gl_enabled_)
|
|
return;
|
|
|
|
ScopedGLContext scoped_gl_context(glarea_, false);
|
|
if (!scoped_gl_context.IsValid())
|
|
return;
|
|
|
|
renderer_.Initialize();
|
|
|
|
gl_enabled_ = true;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::DisableGL() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (!gl_enabled_)
|
|
return;
|
|
|
|
ScopedGLContext scoped_gl_context(glarea_, false);
|
|
if (!scoped_gl_context.IsValid())
|
|
return;
|
|
|
|
renderer_.Cleanup();
|
|
|
|
gl_enabled_ = false;
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::RegisterDragDrop() {
|
|
REQUIRE_MAIN_THREAD();
|
|
|
|
ScopedGdkThreadsEnter scoped_gdk_threads;
|
|
|
|
// Succession of CEF d&d calls:
|
|
// 1. DragTargetDragEnter
|
|
// 2. DragTargetDragOver
|
|
// 3. DragTargetDragLeave - optional
|
|
// 4. DragSourceSystemDragEnded - optional, to cancel dragging
|
|
// 5. DragTargetDrop
|
|
// 6. DragSourceEndedAt
|
|
// 7. DragSourceSystemDragEnded
|
|
|
|
// Succession of GTK d&d events:
|
|
// 1. drag-begin-event, drag-data-get
|
|
// 2. drag-motion
|
|
// 3. drag-leave
|
|
// 4. drag-failed
|
|
// 5. drag-drop, drag-data-received
|
|
// 6. 7. drag-end-event
|
|
|
|
// Using gtk_drag_begin in StartDragging instead of calling
|
|
// gtk_drag_source_set here. Doing so because when using gtk_drag_source_set
|
|
// then StartDragging is being called very late, about ten DragMotion events
|
|
// after DragBegin, and drag icon can be set only when beginning drag.
|
|
// Default values for drag threshold are set to 8 pixels in both GTK and
|
|
// Chromium, but doesn't work as expected.
|
|
// --OFF--
|
|
// gtk_drag_source_set(glarea_, GDK_BUTTON1_MASK, NULL, 0, GDK_ACTION_COPY);
|
|
|
|
// Source widget events.
|
|
g_signal_connect(G_OBJECT(glarea_), "drag_begin",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::DragBegin), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "drag_data_get",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::DragDataGet), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "drag_end",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::DragEnd), this);
|
|
|
|
// Destination widget and its events.
|
|
gtk_drag_dest_set(glarea_, (GtkDestDefaults)0, (GtkTargetEntry*)NULL, 0,
|
|
(GdkDragAction)GDK_ACTION_COPY);
|
|
g_signal_connect(G_OBJECT(glarea_), "drag_motion",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::DragMotion), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "drag_leave",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::DragLeave), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "drag_failed",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::DragFailed), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "drag_drop",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::DragDrop), this);
|
|
g_signal_connect(G_OBJECT(glarea_), "drag_data_received",
|
|
G_CALLBACK(&BrowserWindowOsrGtk::DragDataReceived), this);
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::UnregisterDragDrop() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
ScopedGdkThreadsEnter scoped_gdk_threads;
|
|
gtk_drag_dest_unset(glarea_);
|
|
// Drag events are unregistered in OnBeforeClose by calling
|
|
// g_signal_handlers_disconnect_matched.
|
|
}
|
|
|
|
void BrowserWindowOsrGtk::DragReset() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (drag_trigger_event_) {
|
|
gdk_event_free(drag_trigger_event_);
|
|
drag_trigger_event_ = nullptr;
|
|
}
|
|
drag_data_ = nullptr;
|
|
drag_operation_ = DRAG_OPERATION_NONE;
|
|
if (drag_context_) {
|
|
g_object_unref(drag_context_);
|
|
drag_context_ = nullptr;
|
|
}
|
|
drag_leave_ = false;
|
|
drag_drop_ = false;
|
|
}
|
|
|
|
// static
|
|
void BrowserWindowOsrGtk::DragBegin(GtkWidget* widget,
|
|
GdkDragContext* drag_context,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// Load drag icon.
|
|
if (!self->drag_data_->HasImage()) {
|
|
LOG(ERROR) << "Failed to set drag icon, drag image not available";
|
|
return;
|
|
}
|
|
|
|
float device_scale_factor;
|
|
{
|
|
base::AutoLock lock_scope(self->lock_);
|
|
device_scale_factor = self->device_scale_factor_;
|
|
}
|
|
|
|
int pixel_width = 0;
|
|
int pixel_height = 0;
|
|
CefRefPtr<CefBinaryValue> image_binary =
|
|
self->drag_data_->GetImage()->GetAsPNG(device_scale_factor, true,
|
|
pixel_width, pixel_height);
|
|
if (!image_binary) {
|
|
LOG(ERROR) << "Failed to set drag icon, drag image error";
|
|
return;
|
|
}
|
|
|
|
size_t image_size = image_binary->GetSize();
|
|
guint8* image_buffer = (guint8*)malloc(image_size); // must free
|
|
image_binary->GetData((void*)image_buffer, image_size, 0);
|
|
GdkPixbufLoader* loader = nullptr; // must unref
|
|
GError* error = nullptr; // must free
|
|
GdkPixbuf* pixbuf = nullptr; // owned by loader
|
|
gboolean success = FALSE;
|
|
loader = gdk_pixbuf_loader_new_with_type("png", &error);
|
|
if (error == nullptr && loader) {
|
|
success = gdk_pixbuf_loader_write(loader, image_buffer, image_size, NULL);
|
|
if (success) {
|
|
success = gdk_pixbuf_loader_close(loader, NULL);
|
|
if (success) {
|
|
pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
|
|
if (pixbuf) {
|
|
CefPoint image_hotspot = self->drag_data_->GetImageHotspot();
|
|
int hotspot_x = image_hotspot.x;
|
|
int hotspot_y = image_hotspot.y;
|
|
gtk_drag_set_icon_pixbuf(drag_context, pixbuf, hotspot_x, hotspot_y);
|
|
} else {
|
|
LOG(ERROR) << "Failed to set drag icon, pixbuf error";
|
|
}
|
|
} else {
|
|
LOG(ERROR) << "Failed to set drag icon, loader close error";
|
|
}
|
|
} else {
|
|
LOG(ERROR) << "Failed to set drag icon, loader write error";
|
|
}
|
|
} else {
|
|
LOG(ERROR) << "Failed to set drag icon, loader creation error";
|
|
}
|
|
if (loader) {
|
|
g_object_unref(loader); // unref
|
|
}
|
|
if (error) {
|
|
g_error_free(error); // free
|
|
}
|
|
free(image_buffer); // free
|
|
}
|
|
|
|
// static
|
|
void BrowserWindowOsrGtk::DragDataGet(GtkWidget* widget,
|
|
GdkDragContext* drag_context,
|
|
GtkSelectionData* data,
|
|
guint info,
|
|
guint time,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
// No drag targets are set so this callback is never called.
|
|
}
|
|
|
|
// static
|
|
void BrowserWindowOsrGtk::DragEnd(GtkWidget* widget,
|
|
GdkDragContext* drag_context,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
if (self->browser_) {
|
|
// Sometimes there is DragEnd event generated without prior DragDrop.
|
|
// Maybe related to drag-leave bug described in comments in DragLeave.
|
|
if (!self->drag_drop_) {
|
|
// Real coordinates not available.
|
|
self->browser_->GetHost()->DragSourceEndedAt(-1, -1,
|
|
self->drag_operation_);
|
|
}
|
|
self->browser_->GetHost()->DragSourceSystemDragEnded();
|
|
}
|
|
|
|
self->DragReset();
|
|
}
|
|
|
|
// static
|
|
gboolean BrowserWindowOsrGtk::DragMotion(GtkWidget* widget,
|
|
GdkDragContext* drag_context,
|
|
gint x,
|
|
gint y,
|
|
guint time,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
float device_scale_factor;
|
|
{
|
|
base::AutoLock lock_scope(self->lock_);
|
|
device_scale_factor = self->device_scale_factor_;
|
|
}
|
|
|
|
// MoveEvent is never called during drag & drop, so must call
|
|
// SendMouseMoveEvent here.
|
|
CefMouseEvent mouse_event;
|
|
mouse_event.x = x;
|
|
mouse_event.y = y;
|
|
mouse_event.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
|
|
self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
|
|
DeviceToLogical(mouse_event, device_scale_factor);
|
|
if (self->browser_) {
|
|
bool mouse_leave = self->drag_leave_;
|
|
self->browser_->GetHost()->SendMouseMoveEvent(mouse_event, mouse_leave);
|
|
}
|
|
|
|
// Mouse event.
|
|
CefMouseEvent ev;
|
|
ev.x = x;
|
|
ev.y = y;
|
|
ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
|
|
|
|
CefBrowserHost::DragOperationsMask allowed_ops =
|
|
GetDragOperationsMask(drag_context);
|
|
|
|
// Send drag enter event if needed.
|
|
if (self->drag_leave_ && self->browser_) {
|
|
self->browser_->GetHost()->DragTargetDragEnter(self->drag_data_, ev,
|
|
allowed_ops);
|
|
}
|
|
|
|
// Send drag over event.
|
|
if (self->browser_) {
|
|
self->browser_->GetHost()->DragTargetDragOver(ev, allowed_ops);
|
|
}
|
|
|
|
// Update GTK drag status.
|
|
if (widget == self->glarea_) {
|
|
gdk_drag_status(drag_context, GDK_ACTION_COPY, time);
|
|
if (self->drag_leave_) {
|
|
self->drag_leave_ = false;
|
|
}
|
|
return TRUE;
|
|
} else {
|
|
LOG(WARNING) << "Invalid drag destination widget";
|
|
gdk_drag_status(drag_context, (GdkDragAction)0, time);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// static
|
|
void BrowserWindowOsrGtk::DragLeave(GtkWidget* widget,
|
|
GdkDragContext* drag_context,
|
|
guint time,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// There is no drag-enter event in GTK. The first drag-motion event
|
|
// after drag-leave will be a drag-enter event.
|
|
|
|
// There seems to be a bug during GTK drop, drag-leave event is generated
|
|
// just before drag-drop. A solution is to call DragTargetDragEnter
|
|
// and DragTargetDragOver in DragDrop when drag_leave_ is true.
|
|
|
|
// Send drag leave event.
|
|
if (self->browser_) {
|
|
self->browser_->GetHost()->DragTargetDragLeave();
|
|
}
|
|
|
|
self->drag_leave_ = true;
|
|
}
|
|
|
|
// static
|
|
gboolean BrowserWindowOsrGtk::DragFailed(GtkWidget* widget,
|
|
GdkDragContext* drag_context,
|
|
GtkDragResult result,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// Send drag end coordinates and system drag ended event.
|
|
if (self->browser_) {
|
|
// Real coordinates not available.
|
|
self->browser_->GetHost()->DragSourceEndedAt(-1, -1, self->drag_operation_);
|
|
self->browser_->GetHost()->DragSourceSystemDragEnded();
|
|
}
|
|
|
|
self->DragReset();
|
|
return TRUE;
|
|
}
|
|
|
|
// static
|
|
gboolean BrowserWindowOsrGtk::DragDrop(GtkWidget* widget,
|
|
GdkDragContext* drag_context,
|
|
gint x,
|
|
gint y,
|
|
guint time,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
|
|
// Finish GTK drag.
|
|
gtk_drag_finish(drag_context, TRUE, FALSE, time);
|
|
|
|
// Mouse event.
|
|
CefMouseEvent ev;
|
|
ev.x = x;
|
|
ev.y = y;
|
|
ev.modifiers = EVENTFLAG_LEFT_MOUSE_BUTTON;
|
|
|
|
CefBrowserHost::DragOperationsMask allowed_ops =
|
|
GetDragOperationsMask(drag_context);
|
|
|
|
// Send drag enter/over events if needed (read comment in DragLeave).
|
|
if (self->drag_leave_ && self->browser_) {
|
|
self->browser_->GetHost()->DragTargetDragEnter(self->drag_data_, ev,
|
|
allowed_ops);
|
|
self->browser_->GetHost()->DragTargetDragOver(ev, allowed_ops);
|
|
}
|
|
|
|
// Send drag drop event.
|
|
if (self->browser_) {
|
|
self->browser_->GetHost()->DragTargetDrop(ev);
|
|
}
|
|
|
|
// Send drag end coordinates.
|
|
if (self->browser_) {
|
|
self->browser_->GetHost()->DragSourceEndedAt(x, y, self->drag_operation_);
|
|
}
|
|
|
|
self->drag_drop_ = true;
|
|
return TRUE;
|
|
}
|
|
|
|
// static
|
|
void BrowserWindowOsrGtk::DragDataReceived(GtkWidget* widget,
|
|
GdkDragContext* drag_context,
|
|
gint x,
|
|
gint y,
|
|
GtkSelectionData* data,
|
|
guint info,
|
|
guint time,
|
|
BrowserWindowOsrGtk* self) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
// This callback is never called because DragDrop does not call
|
|
// gtk_drag_get_data, as only dragging inside web view is supported.
|
|
}
|
|
|
|
} // namespace client
|