1 // Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
5 #include "tests/cefclient/browser/browser_window_osr_gtk.h"
8 #include <gdk/gdkkeysyms.h>
10 #include <glib-object.h>
12 #include <gtk/gtkgl.h>
15 #define XK_3270 // for XK_3270_BackTab
16 #include <X11/keysym.h>
17 #include <X11/XF86keysym.h>
18 #include <X11/Xcursor/Xcursor.h>
20 #include "include/base/cef_logging.h"
21 #include "include/wrapper/cef_closure_task.h"
22 #include "tests/shared/browser/geometry_util.h"
23 #include "tests/shared/browser/main_message_loop.h"
29 int GetCefStateModifiers(guint state) {
31 if (state & GDK_SHIFT_MASK)
32 modifiers |= EVENTFLAG_SHIFT_DOWN;
33 if (state & GDK_LOCK_MASK)
34 modifiers |= EVENTFLAG_CAPS_LOCK_ON;
35 if (state & GDK_CONTROL_MASK)
36 modifiers |= EVENTFLAG_CONTROL_DOWN;
37 if (state & GDK_MOD1_MASK)
38 modifiers |= EVENTFLAG_ALT_DOWN;
39 if (state & GDK_BUTTON1_MASK)
40 modifiers |= EVENTFLAG_LEFT_MOUSE_BUTTON;
41 if (state & GDK_BUTTON2_MASK)
42 modifiers |= EVENTFLAG_MIDDLE_MOUSE_BUTTON;
43 if (state & GDK_BUTTON3_MASK)
44 modifiers |= EVENTFLAG_RIGHT_MOUSE_BUTTON;
48 // From ui/events/keycodes/keyboard_codes_posix.h.
68 VKEY_NONCONVERT = 0x1D,
70 VKEY_MODECHANGE = 0x1F,
124 VKEY_COMMAND = VKEY_LWIN, // Provide the Mac name for convenience.
138 VKEY_MULTIPLY = 0x6A,
140 VKEY_SEPARATOR = 0x6C,
141 VKEY_SUBTRACT = 0x6D,
172 VKEY_LCONTROL = 0xA2,
173 VKEY_RCONTROL = 0xA3,
176 VKEY_BROWSER_BACK = 0xA6,
177 VKEY_BROWSER_FORWARD = 0xA7,
178 VKEY_BROWSER_REFRESH = 0xA8,
179 VKEY_BROWSER_STOP = 0xA9,
180 VKEY_BROWSER_SEARCH = 0xAA,
181 VKEY_BROWSER_FAVORITES = 0xAB,
182 VKEY_BROWSER_HOME = 0xAC,
183 VKEY_VOLUME_MUTE = 0xAD,
184 VKEY_VOLUME_DOWN = 0xAE,
185 VKEY_VOLUME_UP = 0xAF,
186 VKEY_MEDIA_NEXT_TRACK = 0xB0,
187 VKEY_MEDIA_PREV_TRACK = 0xB1,
188 VKEY_MEDIA_STOP = 0xB2,
189 VKEY_MEDIA_PLAY_PAUSE = 0xB3,
190 VKEY_MEDIA_LAUNCH_MAIL = 0xB4,
191 VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5,
192 VKEY_MEDIA_LAUNCH_APP1 = 0xB6,
193 VKEY_MEDIA_LAUNCH_APP2 = 0xB7,
195 VKEY_OEM_PLUS = 0xBB,
196 VKEY_OEM_COMMA = 0xBC,
197 VKEY_OEM_MINUS = 0xBD,
198 VKEY_OEM_PERIOD = 0xBE,
207 VKEY_OEM_103 = 0xE3, // GTV KEYCODE_MEDIA_REWIND
208 VKEY_OEM_104 = 0xE4, // GTV KEYCODE_MEDIA_FAST_FORWARD
209 VKEY_PROCESSKEY = 0xE5,
211 VKEY_DBE_SBCSCHAR = 0xF3,
212 VKEY_DBE_DBCSCHAR = 0xF4,
221 VKEY_OEM_CLEAR = 0xFE,
224 // POSIX specific VKEYs. Note that as of Windows SDK 7.1, 0x97-9F, 0xD8-DA,
225 // and 0xE8 are unassigned.
228 VKEY_BRIGHTNESS_DOWN = 0xD8,
229 VKEY_BRIGHTNESS_UP = 0xD9,
230 VKEY_KBD_BRIGHTNESS_DOWN = 0xDA,
231 VKEY_KBD_BRIGHTNESS_UP = 0xE8,
233 // Windows does not have a specific key code for AltGr. We use the unused 0xE1
234 // (VK_OEM_AX) code to represent AltGr, matching the behaviour of Firefox on
237 // Windows does not have a specific key code for Compose. We use the unused
238 // 0xE6 (VK_ICO_CLEAR) code to represent Compose.
242 // From ui/events/keycodes/keyboard_code_conversion_x.cc.
243 // Gdk key codes (e.g. GDK_BackSpace) and X keysyms (e.g. XK_BackSpace) share
245 KeyboardCode KeyboardCodeFromXKeysym(unsigned int keysym) {
254 case XK_ISO_Left_Tab:
255 case XK_3270_BackTab:
263 case XK_KP_Begin: // NumPad 5 without Num Lock, for crosbug.com/29169.
275 case XK_KP_Page_Up: // aka XK_KP_Prior
278 case XK_KP_Page_Down: // aka XK_KP_Next
299 case XK_Hangul_Hanja:
306 return VKEY_NONCONVERT;
307 case XK_Zenkaku_Hankaku:
308 return VKEY_DBE_DBCSCHAR;
398 return static_cast<KeyboardCode>(VKEY_0 + (keysym - XK_0));
431 return static_cast<KeyboardCode>(VKEY_NUMPAD0 + (keysym - XK_KP_0));
435 return VKEY_MULTIPLY;
438 case XK_KP_Separator:
439 return VKEY_SEPARATOR;
441 return VKEY_SUBTRACT;
449 return VKEY_OEM_PLUS;
452 return VKEY_OEM_COMMA;
455 return VKEY_OEM_MINUS;
458 return VKEY_OEM_PERIOD;
474 case XK_bracketright:
480 case XK_ISO_Level5_Shift:
493 case XK_ISO_Level3_Shift:
546 return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_F1));
551 return static_cast<KeyboardCode>(VKEY_F1 + (keysym - XK_KP_F1));
553 case XK_guillemotleft:
554 case XK_guillemotright:
556 // In the case of canadian multilingual keyboard layout, VKEY_OEM_102 is
557 // assigned to ugrave key.
561 return VKEY_OEM_102; // international backslash key in 102 keyboard.
563 // When evdev is in use, /usr/share/X11/xkb/symbols/inet maps F13-18 keys
564 // to the special XF86XK symbols to support Microsoft Ergonomic keyboards:
565 // https://bugs.freedesktop.org/show_bug.cgi?id=5783
566 // In Chrome, we map these X key symbols back to F13-18 since we don't have
567 // VKEYs for these XF86XK symbols.
583 case XF86XK_AddFavorite:
587 // ui::AcceleratorGtk tries to convert the XF86XK_ keysyms on Chrome
588 // startup. It's safe to return VKEY_UNKNOWN here since ui::AcceleratorGtk
589 // also checks a Gdk keysym. http://crbug.com/109843
591 // For supporting multimedia buttons on a USB keyboard.
593 return VKEY_BROWSER_BACK;
595 return VKEY_BROWSER_FORWARD;
597 return VKEY_BROWSER_REFRESH;
599 return VKEY_BROWSER_STOP;
601 return VKEY_BROWSER_SEARCH;
602 case XF86XK_Favorites:
603 return VKEY_BROWSER_FAVORITES;
604 case XF86XK_HomePage:
605 return VKEY_BROWSER_HOME;
606 case XF86XK_AudioMute:
607 return VKEY_VOLUME_MUTE;
608 case XF86XK_AudioLowerVolume:
609 return VKEY_VOLUME_DOWN;
610 case XF86XK_AudioRaiseVolume:
611 return VKEY_VOLUME_UP;
612 case XF86XK_AudioNext:
613 return VKEY_MEDIA_NEXT_TRACK;
614 case XF86XK_AudioPrev:
615 return VKEY_MEDIA_PREV_TRACK;
616 case XF86XK_AudioStop:
617 return VKEY_MEDIA_STOP;
618 case XF86XK_AudioPlay:
619 return VKEY_MEDIA_PLAY_PAUSE;
621 return VKEY_MEDIA_LAUNCH_MAIL;
622 case XF86XK_LaunchA: // F3 on an Apple keyboard.
623 return VKEY_MEDIA_LAUNCH_APP1;
624 case XF86XK_LaunchB: // F4 on an Apple keyboard.
625 case XF86XK_Calculator:
626 return VKEY_MEDIA_LAUNCH_APP2;
629 case XF86XK_PowerOff:
631 case XF86XK_MonBrightnessDown:
632 return VKEY_BRIGHTNESS_DOWN;
633 case XF86XK_MonBrightnessUp:
634 return VKEY_BRIGHTNESS_UP;
635 case XF86XK_KbdBrightnessDown:
636 return VKEY_KBD_BRIGHTNESS_DOWN;
637 case XF86XK_KbdBrightnessUp:
638 return VKEY_KBD_BRIGHTNESS_UP;
640 // TODO(sad): some keycodes are still missing.
645 // From content/browser/renderer_host/input/web_input_event_util_posix.cc.
646 KeyboardCode GdkEventToWindowsKeyCode(const GdkEventKey* event) {
647 static const unsigned int kHardwareCodeToGDKKeyval[] = {
657 0, // 0x09: GDK_Escape
658 GDK_1, // 0x0A: GDK_1
659 GDK_2, // 0x0B: GDK_2
660 GDK_3, // 0x0C: GDK_3
661 GDK_4, // 0x0D: GDK_4
662 GDK_5, // 0x0E: GDK_5
663 GDK_6, // 0x0F: GDK_6
664 GDK_7, // 0x10: GDK_7
665 GDK_8, // 0x11: GDK_8
666 GDK_9, // 0x12: GDK_9
667 GDK_0, // 0x13: GDK_0
668 GDK_minus, // 0x14: GDK_minus
669 GDK_equal, // 0x15: GDK_equal
670 0, // 0x16: GDK_BackSpace
672 GDK_q, // 0x18: GDK_q
673 GDK_w, // 0x19: GDK_w
674 GDK_e, // 0x1A: GDK_e
675 GDK_r, // 0x1B: GDK_r
676 GDK_t, // 0x1C: GDK_t
677 GDK_y, // 0x1D: GDK_y
678 GDK_u, // 0x1E: GDK_u
679 GDK_i, // 0x1F: GDK_i
680 GDK_o, // 0x20: GDK_o
681 GDK_p, // 0x21: GDK_p
682 GDK_bracketleft, // 0x22: GDK_bracketleft
683 GDK_bracketright, // 0x23: GDK_bracketright
684 0, // 0x24: GDK_Return
685 0, // 0x25: GDK_Control_L
686 GDK_a, // 0x26: GDK_a
687 GDK_s, // 0x27: GDK_s
688 GDK_d, // 0x28: GDK_d
689 GDK_f, // 0x29: GDK_f
690 GDK_g, // 0x2A: GDK_g
691 GDK_h, // 0x2B: GDK_h
692 GDK_j, // 0x2C: GDK_j
693 GDK_k, // 0x2D: GDK_k
694 GDK_l, // 0x2E: GDK_l
695 GDK_semicolon, // 0x2F: GDK_semicolon
696 GDK_apostrophe, // 0x30: GDK_apostrophe
697 GDK_grave, // 0x31: GDK_grave
698 0, // 0x32: GDK_Shift_L
699 GDK_backslash, // 0x33: GDK_backslash
700 GDK_z, // 0x34: GDK_z
701 GDK_x, // 0x35: GDK_x
702 GDK_c, // 0x36: GDK_c
703 GDK_v, // 0x37: GDK_v
704 GDK_b, // 0x38: GDK_b
705 GDK_n, // 0x39: GDK_n
706 GDK_m, // 0x3A: GDK_m
707 GDK_comma, // 0x3B: GDK_comma
708 GDK_period, // 0x3C: GDK_period
709 GDK_slash, // 0x3D: GDK_slash
710 0, // 0x3E: GDK_Shift_R
763 GDK_Super_L, // 0x73: GDK_Super_L
764 GDK_Super_R, // 0x74: GDK_Super_R
767 // |windows_key_code| has to include a valid virtual-key code even when we
768 // use non-US layouts, e.g. even when we type an 'A' key of a US keyboard
769 // on the Hebrew layout, |windows_key_code| should be VK_A.
770 // On the other hand, |event->keyval| value depends on the current
771 // GdkKeymap object, i.e. when we type an 'A' key of a US keyboard on
772 // the Hebrew layout, |event->keyval| becomes GDK_hebrew_shin and this
773 // KeyboardCodeFromXKeysym() call returns 0.
774 // To improve compatibilty with Windows, we use |event->hardware_keycode|
775 // for retrieving its Windows key-code for the keys when the
776 // WebCore::windows_key_codeForEvent() call returns 0.
777 // We shouldn't use |event->hardware_keycode| for keys that GdkKeymap
778 // objects cannot change because |event->hardware_keycode| doesn't change
779 // even when we change the layout options, e.g. when we swap a control
780 // key and a caps-lock key, GTK doesn't swap their
781 // |event->hardware_keycode| values but swap their |event->keyval| values.
782 KeyboardCode windows_key_code =
783 KeyboardCodeFromXKeysym(event->keyval);
784 if (windows_key_code)
785 return windows_key_code;
787 if (event->hardware_keycode < arraysize(kHardwareCodeToGDKKeyval)) {
788 int keyval = kHardwareCodeToGDKKeyval[event->hardware_keycode];
790 return KeyboardCodeFromXKeysym(keyval);
793 // This key is one that keyboard-layout drivers cannot change.
794 // Use |event->keyval| to retrieve its |windows_key_code| value.
795 return KeyboardCodeFromXKeysym(event->keyval);
798 // From content/browser/renderer_host/input/web_input_event_util_posix.cc.
799 KeyboardCode GetWindowsKeyCodeWithoutLocation(KeyboardCode key_code) {
815 // From content/browser/renderer_host/input/web_input_event_builders_gtk.cc.
816 // Gets the corresponding control character of a specified key code. See:
817 // http://en.wikipedia.org/wiki/Control_characters
818 // We emulate Windows behavior here.
819 int GetControlCharacter(KeyboardCode windows_key_code, bool shift) {
820 if (windows_key_code >= VKEY_A && windows_key_code <= VKEY_Z) {
821 // ctrl-A ~ ctrl-Z map to \x01 ~ \x1A
822 return windows_key_code - VKEY_A + 1;
825 // following graphics chars require shift key to input.
826 switch (windows_key_code) {
827 // ctrl-@ maps to \x00 (Null byte)
830 // ctrl-^ maps to \x1E (Record separator, Information separator two)
833 // ctrl-_ maps to \x1F (Unit separator, Information separator one)
836 // Returns 0 for all other keys to avoid inputting unexpected chars.
841 switch (windows_key_code) {
842 // ctrl-[ maps to \x1B (Escape)
845 // ctrl-\ maps to \x1C (File separator, Information separator four)
848 // ctrl-] maps to \x1D (Group separator, Information separator three)
851 // ctrl-Enter maps to \x0A (Line feed)
854 // Returns 0 for all other keys to avoid inputting unexpected chars.
861 void GetWidgetRectInScreen(GtkWidget* widget, GdkRectangle* r) {
863 GdkRectangle extents;
865 GdkWindow* window = gtk_widget_get_parent_window(widget);
867 // Get parent's left-top screen coordinates.
868 gdk_window_get_root_origin(window, &x, &y);
869 // Get parent's width and height.
870 gdk_drawable_get_size(window, &w, &h);
871 // Get parent's extents including decorations.
872 gdk_window_get_frame_extents(window, &extents);
874 // X and Y calculations assume that left, right and bottom border sizes are
876 const gint border = (extents.width - w) / 2;
877 r->x = x + border + widget->allocation.x;
878 r->y = y + (extents.height - h) - border + widget->allocation.y;
879 r->width = widget->allocation.width;
880 r->height = widget->allocation.height;
883 class ScopedGLContext {
885 ScopedGLContext(GtkWidget* widget, bool swap_buffers)
886 : swap_buffers_(swap_buffers) {
887 GdkGLContext* glcontext = gtk_widget_get_gl_context(widget);
888 gldrawable_ = gtk_widget_get_gl_drawable(widget);
889 is_valid_ = gdk_gl_drawable_gl_begin(gldrawable_, glcontext);
892 virtual ~ScopedGLContext() {
894 gdk_gl_drawable_gl_end(gldrawable_);
897 if (gdk_gl_drawable_is_double_buffered(gldrawable_))
898 gdk_gl_drawable_swap_buffers(gldrawable_);
905 bool IsValid() const { return is_valid_; }
909 GdkGLDrawable* gldrawable_;
915 BrowserWindowOsrGtk::BrowserWindowOsrGtk(BrowserWindow::Delegate* delegate,
916 const std::string& startup_url,
917 const OsrRenderer::Settings& settings)
918 : BrowserWindow(delegate),
923 painting_popup_(false),
924 device_scale_factor_(1.0f) {
925 client_handler_ = new ClientHandlerOsr(this, this, startup_url);
928 void BrowserWindowOsrGtk::CreateBrowser(
929 ClientWindowHandle parent_handle,
931 const CefBrowserSettings& settings,
932 CefRefPtr<CefRequestContext> request_context) {
933 REQUIRE_MAIN_THREAD();
935 // Create the native window.
936 Create(parent_handle);
938 // Retrieve the X11 Window ID for the GTK parent window.
939 GtkWidget* window = gtk_widget_get_ancestor(
940 GTK_WIDGET(parent_handle), GTK_TYPE_WINDOW);
941 ::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(window));
944 CefWindowInfo window_info;
945 window_info.SetAsWindowless(xwindow, renderer_.IsTransparent());
947 // Create the browser asynchronously.
948 CefBrowserHost::CreateBrowser(window_info, client_handler_,
949 client_handler_->startup_url(),
950 settings, request_context);
953 void BrowserWindowOsrGtk::GetPopupConfig(CefWindowHandle temp_handle,
954 CefWindowInfo& windowInfo,
955 CefRefPtr<CefClient>& client,
956 CefBrowserSettings& settings) {
957 // Note: This method may be called on any thread.
958 windowInfo.SetAsWindowless(temp_handle, renderer_.IsTransparent());
959 client = client_handler_;
962 void BrowserWindowOsrGtk::ShowPopup(ClientWindowHandle parent_handle,
963 int x, int y, size_t width, size_t height) {
964 REQUIRE_MAIN_THREAD();
965 DCHECK(browser_.get());
967 // Create the native window.
968 Create(parent_handle);
970 // Send resize notification so the compositor is assigned the correct
971 // viewport size and begins rendering.
972 browser_->GetHost()->WasResized();
977 void BrowserWindowOsrGtk::Show() {
978 REQUIRE_MAIN_THREAD();
981 // Set the browser as visible.
982 browser_->GetHost()->WasHidden(false);
986 // Give focus to the browser.
987 browser_->GetHost()->SendFocusEvent(true);
990 void BrowserWindowOsrGtk::Hide() {
991 REQUIRE_MAIN_THREAD();
996 // Remove focus from the browser.
997 browser_->GetHost()->SendFocusEvent(false);
1000 // Set the browser as hidden.
1001 browser_->GetHost()->WasHidden(true);
1006 void BrowserWindowOsrGtk::SetBounds(int x, int y, size_t width, size_t height) {
1007 REQUIRE_MAIN_THREAD();
1008 // Nothing to do here. GTK will take care of positioning in the container.
1011 void BrowserWindowOsrGtk::SetFocus(bool focus) {
1012 REQUIRE_MAIN_THREAD();
1013 if (glarea_ && focus)
1014 gtk_widget_grab_focus(glarea_);
1017 void BrowserWindowOsrGtk::SetDeviceScaleFactor(float device_scale_factor) {
1018 REQUIRE_MAIN_THREAD();
1019 if (device_scale_factor == device_scale_factor_)
1022 // Apply some sanity checks.
1023 if (device_scale_factor < 1.0f || device_scale_factor > 4.0f)
1026 device_scale_factor_ = device_scale_factor;
1029 browser_->GetHost()->NotifyScreenInfoChanged();
1030 browser_->GetHost()->WasResized();
1034 float BrowserWindowOsrGtk::GetDeviceScaleFactor() const {
1035 REQUIRE_MAIN_THREAD();
1036 return device_scale_factor_;
1039 ClientWindowHandle BrowserWindowOsrGtk::GetWindowHandle() const {
1040 REQUIRE_MAIN_THREAD();
1044 void BrowserWindowOsrGtk::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
1045 CEF_REQUIRE_UI_THREAD();
1048 void BrowserWindowOsrGtk::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
1049 CEF_REQUIRE_UI_THREAD();
1050 REQUIRE_MAIN_THREAD();
1052 // Detach |this| from the ClientHandlerOsr.
1053 static_cast<ClientHandlerOsr*>(client_handler_.get())->DetachOsrDelegate();
1055 // Disconnect all signal handlers that reference |this|.
1056 g_signal_handlers_disconnect_matched(glarea_, G_SIGNAL_MATCH_DATA, 0, 0,
1062 bool BrowserWindowOsrGtk::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
1064 CEF_REQUIRE_UI_THREAD();
1068 bool BrowserWindowOsrGtk::GetViewRect(CefRefPtr<CefBrowser> browser,
1070 CEF_REQUIRE_UI_THREAD();
1071 REQUIRE_MAIN_THREAD();
1076 // The simulated screen and view rectangle are the same. This is necessary
1077 // for popup menus to be located and sized inside the view.
1078 rect.x = rect.y = 0;
1079 rect.width = DeviceToLogical(glarea_->allocation.width, device_scale_factor_);
1080 rect.height = DeviceToLogical(glarea_->allocation.height,
1081 device_scale_factor_);
1085 bool BrowserWindowOsrGtk::GetScreenPoint(CefRefPtr<CefBrowser> browser,
1090 CEF_REQUIRE_UI_THREAD();
1091 REQUIRE_MAIN_THREAD();
1093 GdkRectangle screen_rect;
1094 GetWidgetRectInScreen(glarea_, &screen_rect);
1095 screenX = screen_rect.x + LogicalToDevice(viewX, device_scale_factor_);
1096 screenY = screen_rect.y + LogicalToDevice(viewY, device_scale_factor_);
1100 bool BrowserWindowOsrGtk::GetScreenInfo(CefRefPtr<CefBrowser> browser,
1101 CefScreenInfo& screen_info) {
1102 CEF_REQUIRE_UI_THREAD();
1105 GetViewRect(browser, view_rect);
1107 screen_info.device_scale_factor = device_scale_factor_;
1109 // The screen info rectangles are used by the renderer to create and position
1110 // popups. Keep popups inside the view rectangle.
1111 screen_info.rect = view_rect;
1112 screen_info.available_rect = view_rect;
1116 void BrowserWindowOsrGtk::OnPopupShow(CefRefPtr<CefBrowser> browser,
1118 CEF_REQUIRE_UI_THREAD();
1119 REQUIRE_MAIN_THREAD();
1122 renderer_.ClearPopupRects();
1123 browser->GetHost()->Invalidate(PET_VIEW);
1125 renderer_.OnPopupShow(browser, show);
1128 void BrowserWindowOsrGtk::OnPopupSize(CefRefPtr<CefBrowser> browser,
1129 const CefRect& rect) {
1130 CEF_REQUIRE_UI_THREAD();
1131 REQUIRE_MAIN_THREAD();
1133 renderer_.OnPopupSize(browser, LogicalToDevice(rect, device_scale_factor_));
1136 void BrowserWindowOsrGtk::OnPaint(
1137 CefRefPtr<CefBrowser> browser,
1138 CefRenderHandler::PaintElementType type,
1139 const CefRenderHandler::RectList& dirtyRects,
1143 CEF_REQUIRE_UI_THREAD();
1144 REQUIRE_MAIN_THREAD();
1146 if (width <= 2 && height <= 2) {
1147 // Ignore really small buffer sizes while the widget is starting up.
1151 if (painting_popup_) {
1152 renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
1159 ScopedGLContext scoped_gl_context(glarea_, true);
1160 if (!scoped_gl_context.IsValid())
1163 renderer_.OnPaint(browser, type, dirtyRects, buffer, width, height);
1164 if (type == PET_VIEW && !renderer_.popup_rect().IsEmpty()) {
1165 painting_popup_ = true;
1166 browser->GetHost()->Invalidate(PET_POPUP);
1167 painting_popup_ = false;
1172 void BrowserWindowOsrGtk::OnCursorChange(
1173 CefRefPtr<CefBrowser> browser,
1174 CefCursorHandle cursor,
1175 CefRenderHandler::CursorType type,
1176 const CefCursorInfo& custom_cursor_info) {
1177 CEF_REQUIRE_UI_THREAD();
1178 REQUIRE_MAIN_THREAD();
1180 // Retrieve the X11 display shared with Chromium.
1181 ::Display* xdisplay = cef_get_xdisplay();
1184 // Retrieve the X11 window handle for the GTK widget.
1185 ::Window xwindow = GDK_WINDOW_XID(gtk_widget_get_window(glarea_));
1188 XDefineCursor(xdisplay, xwindow, cursor);
1191 bool BrowserWindowOsrGtk::StartDragging(
1192 CefRefPtr<CefBrowser> browser,
1193 CefRefPtr<CefDragData> drag_data,
1194 CefRenderHandler::DragOperationsMask allowed_ops,
1196 CEF_REQUIRE_UI_THREAD();
1197 // TODO(port): Implement drag&drop support.
1201 void BrowserWindowOsrGtk::UpdateDragCursor(
1202 CefRefPtr<CefBrowser> browser,
1203 CefRenderHandler::DragOperation operation) {
1204 CEF_REQUIRE_UI_THREAD();
1207 void BrowserWindowOsrGtk::OnImeCompositionRangeChanged(
1208 CefRefPtr<CefBrowser> browser,
1209 const CefRange& selection_range,
1210 const CefRenderHandler::RectList& character_bounds) {
1211 CEF_REQUIRE_UI_THREAD();
1214 void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
1215 REQUIRE_MAIN_THREAD();
1218 glarea_ = gtk_drawing_area_new();
1221 GdkGLConfig* glconfig = gdk_gl_config_new_by_mode(
1222 static_cast<GdkGLConfigMode>(GDK_GL_MODE_RGB |
1224 GDK_GL_MODE_DOUBLE));
1227 gtk_widget_set_gl_capability(glarea_, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE);
1229 gtk_widget_set_can_focus(glarea_, TRUE);
1231 g_signal_connect(G_OBJECT(glarea_), "size_allocate",
1232 G_CALLBACK(&BrowserWindowOsrGtk::SizeAllocation), this);
1234 gtk_widget_set_events(glarea_,
1235 GDK_BUTTON_PRESS_MASK |
1236 GDK_BUTTON_RELEASE_MASK |
1237 GDK_KEY_PRESS_MASK |
1238 GDK_KEY_RELEASE_MASK |
1239 GDK_ENTER_NOTIFY_MASK |
1240 GDK_LEAVE_NOTIFY_MASK |
1241 GDK_POINTER_MOTION_MASK |
1242 GDK_POINTER_MOTION_HINT_MASK |
1244 GDK_FOCUS_CHANGE_MASK);
1245 g_signal_connect(G_OBJECT(glarea_), "button_press_event",
1246 G_CALLBACK(&BrowserWindowOsrGtk::ClickEvent), this);
1247 g_signal_connect(G_OBJECT(glarea_), "button_release_event",
1248 G_CALLBACK(&BrowserWindowOsrGtk::ClickEvent), this);
1249 g_signal_connect(G_OBJECT(glarea_), "key_press_event",
1250 G_CALLBACK(&BrowserWindowOsrGtk::KeyEvent), this);
1251 g_signal_connect(G_OBJECT(glarea_), "key_release_event",
1252 G_CALLBACK(&BrowserWindowOsrGtk::KeyEvent), this);
1253 g_signal_connect(G_OBJECT(glarea_), "enter_notify_event",
1254 G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
1255 g_signal_connect(G_OBJECT(glarea_), "leave_notify_event",
1256 G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
1257 g_signal_connect(G_OBJECT(glarea_), "motion_notify_event",
1258 G_CALLBACK(&BrowserWindowOsrGtk::MoveEvent), this);
1259 g_signal_connect(G_OBJECT(glarea_), "scroll_event",
1260 G_CALLBACK(&BrowserWindowOsrGtk::ScrollEvent), this);
1261 g_signal_connect(G_OBJECT(glarea_), "focus_in_event",
1262 G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
1263 g_signal_connect(G_OBJECT(glarea_), "focus_out_event",
1264 G_CALLBACK(&BrowserWindowOsrGtk::FocusEvent), this);
1266 gtk_container_add(GTK_CONTAINER(parent_handle), glarea_);
1268 // Make the GlArea visible in the parent container.
1269 gtk_widget_show_all(parent_handle);
1273 gint BrowserWindowOsrGtk::SizeAllocation(GtkWidget* widget,
1274 GtkAllocation* allocation,
1275 BrowserWindowOsrGtk* self) {
1276 REQUIRE_MAIN_THREAD();
1278 if (self->browser_.get()) {
1279 // Results in a call to GetViewRect().
1280 self->browser_->GetHost()->WasResized();
1286 gint BrowserWindowOsrGtk::ClickEvent(GtkWidget* widget,
1287 GdkEventButton* event,
1288 BrowserWindowOsrGtk* self) {
1289 REQUIRE_MAIN_THREAD();
1291 if (!self->browser_.get())
1294 CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
1296 CefBrowserHost::MouseButtonType button_type = MBT_LEFT;
1297 switch (event->button) {
1301 button_type = MBT_MIDDLE;
1304 button_type = MBT_RIGHT;
1307 // Other mouse buttons are not handled here.
1311 CefMouseEvent mouse_event;
1312 mouse_event.x = event->x;
1313 mouse_event.y = event->y;
1314 self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
1315 DeviceToLogical(mouse_event, self->device_scale_factor_);
1316 mouse_event.modifiers = GetCefStateModifiers(event->state);
1318 bool mouse_up = (event->type == GDK_BUTTON_RELEASE);
1320 gtk_widget_grab_focus(widget);
1322 int click_count = 1;
1323 switch (event->type) {
1324 case GDK_2BUTTON_PRESS:
1327 case GDK_3BUTTON_PRESS:
1334 host->SendMouseClickEvent(mouse_event, button_type, mouse_up, click_count);
1339 gint BrowserWindowOsrGtk::KeyEvent(GtkWidget* widget,
1341 BrowserWindowOsrGtk* self) {
1342 REQUIRE_MAIN_THREAD();
1344 if (!self->browser_.get())
1347 CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
1349 // Based on WebKeyboardEventBuilder::Build from
1350 // content/browser/renderer_host/input/web_input_event_builders_gtk.cc.
1351 CefKeyEvent key_event;
1352 KeyboardCode windows_key_code = GdkEventToWindowsKeyCode(event);
1353 key_event.windows_key_code =
1354 GetWindowsKeyCodeWithoutLocation(windows_key_code);
1355 key_event.native_key_code = event->hardware_keycode;
1357 key_event.modifiers = GetCefStateModifiers(event->state);
1358 if (event->keyval >= GDK_KP_Space && event->keyval <= GDK_KP_9)
1359 key_event.modifiers |= EVENTFLAG_IS_KEY_PAD;
1360 if (key_event.modifiers & EVENTFLAG_ALT_DOWN)
1361 key_event.is_system_key = true;
1363 if (windows_key_code == VKEY_RETURN) {
1364 // We need to treat the enter key as a key press of character \r. This
1365 // is apparently just how webkit handles it and what it expects.
1366 key_event.unmodified_character = '\r';
1368 // FIXME: fix for non BMP chars
1369 key_event.unmodified_character =
1370 static_cast<int>(gdk_keyval_to_unicode(event->keyval));
1373 // If ctrl key is pressed down, then control character shall be input.
1374 if (key_event.modifiers & EVENTFLAG_CONTROL_DOWN) {
1375 key_event.character =
1376 GetControlCharacter(windows_key_code,
1377 key_event.modifiers & EVENTFLAG_SHIFT_DOWN);
1379 key_event.character = key_event.unmodified_character;
1382 if (event->type == GDK_KEY_PRESS) {
1383 key_event.type = KEYEVENT_RAWKEYDOWN;
1384 host->SendKeyEvent(key_event);
1385 key_event.type = KEYEVENT_CHAR;
1386 host->SendKeyEvent(key_event);
1388 key_event.type = KEYEVENT_KEYUP;
1389 host->SendKeyEvent(key_event);
1396 gint BrowserWindowOsrGtk::MoveEvent(GtkWidget* widget,
1397 GdkEventMotion* event,
1398 BrowserWindowOsrGtk* self) {
1399 REQUIRE_MAIN_THREAD();
1401 if (!self->browser_.get())
1404 CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
1407 GdkModifierType state;
1409 if (event->is_hint) {
1410 gdk_window_get_pointer(event->window, &x, &y, &state);
1414 state = (GdkModifierType)event->state;
1417 CefMouseEvent mouse_event;
1420 self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
1421 DeviceToLogical(mouse_event, self->device_scale_factor_);
1422 mouse_event.modifiers = GetCefStateModifiers(state);
1424 bool mouse_leave = (event->type == GDK_LEAVE_NOTIFY);
1426 host->SendMouseMoveEvent(mouse_event, mouse_leave);
1431 gint BrowserWindowOsrGtk::ScrollEvent(GtkWidget* widget,
1432 GdkEventScroll* event,
1433 BrowserWindowOsrGtk* self) {
1434 REQUIRE_MAIN_THREAD();
1436 if (!self->browser_.get())
1439 CefRefPtr<CefBrowserHost> host = self->browser_->GetHost();
1441 CefMouseEvent mouse_event;
1442 mouse_event.x = event->x;
1443 mouse_event.y = event->y;
1444 self->ApplyPopupOffset(mouse_event.x, mouse_event.y);
1445 DeviceToLogical(mouse_event, self->device_scale_factor_);
1446 mouse_event.modifiers = GetCefStateModifiers(event->state);
1448 static const int scrollbarPixelsPerGtkTick = 40;
1451 switch (event->direction) {
1453 deltaY = scrollbarPixelsPerGtkTick;
1455 case GDK_SCROLL_DOWN:
1456 deltaY = -scrollbarPixelsPerGtkTick;
1458 case GDK_SCROLL_LEFT:
1459 deltaX = scrollbarPixelsPerGtkTick;
1461 case GDK_SCROLL_RIGHT:
1462 deltaX = -scrollbarPixelsPerGtkTick;
1466 host->SendMouseWheelEvent(mouse_event, deltaX, deltaY);
1471 gint BrowserWindowOsrGtk::FocusEvent(GtkWidget* widget,
1472 GdkEventFocus* event,
1473 BrowserWindowOsrGtk* self) {
1474 REQUIRE_MAIN_THREAD();
1476 if (self->browser_.get())
1477 self->browser_->GetHost()->SendFocusEvent(event->in == TRUE);
1481 bool BrowserWindowOsrGtk::IsOverPopupWidget(int x, int y) const {
1482 const CefRect& rc = renderer_.popup_rect();
1483 int popup_right = rc.x + rc.width;
1484 int popup_bottom = rc.y + rc.height;
1485 return (x >= rc.x) && (x < popup_right) &&
1486 (y >= rc.y) && (y < popup_bottom);
1489 int BrowserWindowOsrGtk::GetPopupXOffset() const {
1490 return renderer_.original_popup_rect().x - renderer_.popup_rect().x;
1493 int BrowserWindowOsrGtk::GetPopupYOffset() const {
1494 return renderer_.original_popup_rect().y - renderer_.popup_rect().y;
1497 void BrowserWindowOsrGtk::ApplyPopupOffset(int& x, int& y) const {
1498 if (IsOverPopupWidget(x, y)) {
1499 x += GetPopupXOffset();
1500 y += GetPopupYOffset();
1504 void BrowserWindowOsrGtk::EnableGL() {
1505 REQUIRE_MAIN_THREAD();
1510 ScopedGLContext scoped_gl_context(glarea_, false);
1511 if (!scoped_gl_context.IsValid())
1514 renderer_.Initialize();
1519 void BrowserWindowOsrGtk::DisableGL() {
1520 REQUIRE_MAIN_THREAD();
1525 ScopedGLContext scoped_gl_context(glarea_, false);
1526 if (!scoped_gl_context.IsValid())
1529 renderer_.Cleanup();
1531 gl_enabled_ = false;
1534 } // namespace client