X Tutup
Skip to content

Commit 8c81f4c

Browse files
committed
Added keyboard handling to the Kivy example.
Fixed the renderer process crash when js bindings bindToFrames option was set to True.
1 parent 4c2efa8 commit 8c81f4c

File tree

4 files changed

+160
-49
lines changed

4 files changed

+160
-49
lines changed

cefpython/browser.pyx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
# Website: http://code.google.com/p/cefpython/
44

55
IF CEF_VERSION == 1:
6-
# cef_key_type_t, SendKeyEvent().
7-
KEYTYPE_KEYUP = cef_types.KT_KEYUP
6+
# In CEF 1 there are both KT_KEYDOWN and KEYEVENT_KEYDOWN, and
7+
# these are different constants, making a bit of confusion.
8+
# In CEF 1 KT_ is for SendKeyEvent, KEYEVENT_ is for OnKeyEvent().
9+
# In CEF 3 there are only KEYEVENT_* constants.
810
KEYTYPE_KEYDOWN = cef_types.KT_KEYDOWN
11+
KEYTYPE_KEYUP = cef_types.KT_KEYUP
912
KEYTYPE_CHAR = cef_types.KT_CHAR
10-
ELIF CEF_VERSION == 3:
11-
# cef_key_type_t, SendKeyEvent().
12-
KEYTYPE_KEYUP = cef_types.KEYEVENT_RAWKEYDOWN
13-
KEYTYPE_KEYDOWN = cef_types.KEYEVENT_KEYDOWN
14-
KEYTYPE_CHAR = cef_types.KEYEVENT_CHAR
1513

1614
# Both CEF 1 and CEF 3.
1715
# cef_mouse_button_type_t, SendMouseClickEvent().

cefpython/cef3/client_handler/client_handler.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@ void ClientHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
562562
void ClientHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
563563
cef_termination_status_t status) {
564564
REQUIRE_UI_THREAD();
565+
DebugLog("Browser: OnRenderProcessTerminated()");
565566
LoadHandler_OnRendererProcessTerminated(browser, status);
566567
}
567568

cefpython/cef3/linux/binaries_64bit/kivy_.py

Lines changed: 137 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from kivy.core.window import Window
1515
from kivy.lang import Builder
1616
from kivy.uix.boxlayout import BoxLayout
17+
from kivy.base import EventLoop
1718

1819
####CEF IMPORT ####
1920
import ctypes, os, sys
@@ -59,6 +60,7 @@ def __init__(self, **kwargs):
5960

6061

6162
class CefBrowser(Widget):
63+
6264
'''Represent a browser widget for kivy, which can be used like a normal widget.
6365
'''
6466
def __init__(self, start_url='http://www.google.com', **kwargs):
@@ -105,8 +107,9 @@ def start_cef(self, start_url='http://google.com'):
105107

106108
#configure cef
107109
cefpython.g_debug = True
110+
cefpython.g_debugFile = "debug.log"
108111
settings = {"log_severity": cefpython.LOGSEVERITY_INFO,
109-
#"log_file": GetApplicationPath("debug.log"),
112+
"log_file": "debug.log",
110113
"release_dcheck_enabled": True, # Enable only when debugging.
111114
# This directories must be set on Linux
112115
"locales_dir_path": cefpython.GetModuleDirectory()+"/locales",
@@ -137,43 +140,98 @@ def start_cef(self, start_url='http://google.com'):
137140
# --
138141
# Do not use "about:blank" as navigateUrl - this will cause
139142
# the GoBack() and GoForward() methods to not work.
140-
# --
141-
# TODO: get rid of the hard-coded path. Use the __file__ variable
142-
# to get the current directory. Using a path to a non-existent
143-
# local html file seems to not cause any problems, so it can
144-
# stay for a moment.
145143
self.browser = cefpython.CreateBrowserSync(windowInfo, browserSettings,
146-
navigateUrl="file:///home/czarek/cefpython/cefpython/cef3/linux/binaries_64bit/empty2.html")
144+
navigateUrl=start_url)
147145

148146
#set focus
149147
self.browser.SendFocusEvent(True)
150148

151149
#Create RenderHandler (in ClientHandler)
152150
CH = ClientHandler(self.texture, self)
153151
self.browser.SetClientHandler(CH)
152+
153+
jsBindings = cefpython.JavascriptBindings(
154+
bindToFrames=True, bindToPopups=True)
155+
jsBindings.SetFunction("__kivy__request_keyboard",
156+
self.request_keyboard)
157+
jsBindings.SetFunction("__kivy__release_keyboard",
158+
self.release_keyboard)
159+
self.browser.SetJavascriptBindings(jsBindings)
154160

155161
#Call WasResized() => force cef to call GetViewRect() and OnPaint afterwards
156162
self.browser.WasResized()
157-
158-
# Load desired start URL
159-
# TODO: let the local html file "empty.html" to finish loading,
160-
# call the LoadUrl() method with a 100ms delay. In the
161-
# empty.html you could add a "Loading.." text, this would
162-
# event useful, user would see some message instead of the
163-
# blank page. Sometimes the lag can cause the website to
164-
# load for a few seconds and user would be seeing only a
165-
# white screen and wonder of what is happening. The other
166-
# solution would be to change the mouse cursor to loading
167-
# state - but this won't work in touch screen devices? As
168-
# there is no cursor there. In wxpython there is the
169-
# wx.CallLater() method, is there anything similar in Kivy?
170-
self.browser.GetMainFrame().LoadUrl(start_url)
171-
172-
#Clock.schedule_interval(self.press_key, 5)
173-
self._keyboard = Window.request_keyboard(self._keyboard_closed, self)
174-
self._keyboard.bind(on_key_down=self.press_key)
175-
163+
164+
# The browserWidget instance is required in OnLoadingStateChange().
165+
self.browser.SetUserData("browserWidget", self)
166+
167+
168+
def request_keyboard(self):
169+
print("request_keyboard()")
170+
self._keyboard = EventLoop.window.request_keyboard(
171+
self.release_keyboard, self)
172+
self._keyboard.bind(on_key_down=self.on_key_down)
173+
self._keyboard.bind(on_key_up=self.on_key_up)
174+
175+
176+
def release_keyboard(self):
177+
if not self._keyboard:
178+
return
179+
print("release_keyboard()")
180+
self._keyboard.unbind(on_key_down=self.on_key_down)
181+
self._keyboard.unbind(on_key_up=self.on_key_up)
182+
self._keyboard.release()
183+
176184

185+
def on_key_down(self, keyboard, keycode, text, modifiers):
186+
# Notes:
187+
# - right alt modifier is not sent by Kivy
188+
# (Polish characters don't work)
189+
print("on_key_down(): keycode = %s modifiers = %s" % (
190+
keycode, modifiers))
191+
if keycode[0] == 27:
192+
# On escape release the keyboard, see the injected
193+
# javascript in OnLoadStart().
194+
self.browser.GetFocusedFrame().ExecuteJavascript(
195+
"__kivy__on_escape()")
196+
return
197+
cefModifiers = cefpython.EVENTFLAG_NONE
198+
if "shift" in modifiers:
199+
cefModifiers |= cefpython.EVENTFLAG_SHIFT_DOWN
200+
if "ctrl" in modifiers:
201+
cefModifiers |= cefpython.EVENTFLAG_CONTROL_DOWN
202+
if "alt" in modifiers:
203+
cefModifiers |= cefpython.EVENTFLAG_ALT_DOWN
204+
if "capslock" in modifiers:
205+
cefModifiers |= cefpython.EVENTFLAG_CAPS_LOCK_ON
206+
# print("on_key_down(): cefModifiers = %s" % cefModifiers)
207+
keyEvent = {
208+
"type": cefpython.KEYEVENT_RAWKEYDOWN,
209+
"native_key_code": keycode[0],
210+
"modifiers": cefModifiers
211+
}
212+
# print("keydown keyEvent: %s" % keyEvent)
213+
self.browser.SendKeyEvent(keyEvent)
214+
215+
216+
def on_key_up(self, keyboard, keycode):
217+
# print("on_key_up(): keycode = %s" % (keycode,))
218+
cefModifiers = cefpython.EVENTFLAG_NONE
219+
keyEvent = {
220+
"type": cefpython.KEYEVENT_KEYUP,
221+
"native_key_code": keycode[0],
222+
"modifiers": cefModifiers
223+
}
224+
# print("keyup keyEvent: %s" % keyEvent)
225+
self.browser.SendKeyEvent(keyEvent)
226+
keyEvent = {
227+
"type": cefpython.KEYEVENT_CHAR,
228+
"native_key_code": keycode[0],
229+
"modifiers": cefModifiers
230+
}
231+
# print("char keyEvent: %s" % keyEvent)
232+
self.browser.SendKeyEvent(keyEvent)
233+
234+
177235
def go_forward(self):
178236
'''Going to forward in browser history
179237
'''
@@ -188,15 +246,6 @@ def go_back(self):
188246
self.browser.GoBack()
189247

190248

191-
def _keyboard_closed(self, *kwargs):
192-
pass
193-
194-
195-
def press_key(self, wid, key, *kwargs):
196-
#print "key pressed", key[0]
197-
self.browser.SendKeyEvent(cefpython.KEYTYPE_CHAR, key[0])
198-
199-
200249
def on_touch_down(self, touch, *kwargs):
201250
if not self.collide_point(*touch.pos):
202251
return
@@ -225,16 +274,66 @@ def on_touch_up(self, touch, *kwargs):
225274
touch.ungrab(self)
226275

227276

228-
229277
class ClientHandler:
230278

231279
def __init__(self, texture, parent):
232280
self.texture = texture
233281
self.parent = parent
234-
235282

283+
284+
def OnLoadStart(self, browser, frame):
285+
print("OnLoadStart(): injecting focus listeners for text controls")
286+
# The logic is similar to the one found in kivy-berkelium:
287+
# https://github.com/kivy/kivy-berkelium/blob/master/berkelium/__init__.py
288+
jsCode = """
289+
var __kivy__keyboard_requested = false;
290+
function __kivy__keyboard_interval() {
291+
var element = document.activeElement;
292+
if (!element) {
293+
return;
294+
}
295+
var tag = element.tagName;
296+
var type = element.type;
297+
// <input> with an empty type is a text type by default!
298+
if (tag == "INPUT" && (type == "" || type == "text"
299+
|| type == "password") || tag == "TEXTAREA") {
300+
if (!__kivy__keyboard_requested) {
301+
__kivy__request_keyboard();
302+
__kivy__keyboard_requested = true;
303+
}
304+
return;
305+
}
306+
if (__kivy__keyboard_requested) {
307+
__kivy__release_keyboard();
308+
__kivy__keyboard_requested = false;
309+
}
310+
}
311+
function __kivy__on_escape() {
312+
if (document.activeElement) {
313+
document.activeElement.blur();
314+
}
315+
if (__kivy__keyboard_requested) {
316+
__kivy__release_keyboard();
317+
__kivy__keyboard_requested = false;
318+
}
319+
}
320+
setInterval(__kivy__keyboard_interval, 13);
321+
"""
322+
frame.ExecuteJavascript(jsCode,
323+
"kivy_.py > ClientHandler > OnLoadStart")
324+
325+
326+
def OnLoadingStateChange(self, browser, isLoading, canGoBack,
327+
canGoForward):
328+
print("OnLoadingStateChange(): isLoading = %s" % isLoading)
329+
if isLoading and browser.GetUserData("browserWidget"):
330+
# Release keyboard when navigating to a new page.
331+
browser.GetUserData("browserWidget").release_keyboard()
332+
pass
333+
334+
236335
def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height):
237-
print "OnPaint()"
336+
# print "OnPaint()"
238337
if paintElementType != cefpython.PET_VIEW:
239338
print "Popups aren't implemented yet"
240339
return
@@ -250,16 +349,12 @@ def OnPaint(self, browser, paintElementType, dirtyRects, buffer, width, height):
250349

251350

252351
def GetViewRect(self, browser, rect):
253-
print "GetViewRect()"
254352
width, height = self.texture.size
255353
rect.append(0)
256354
rect.append(0)
257355
rect.append(width)
258356
rect.append(height)
259-
print width
260-
print height
261357
return True
262-
263358

264359

265360
if __name__ == '__main__':

cefpython/cef3/subprocess/cefpython_app.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,25 @@ void CefPythonApp::DoJavascriptBindingsForBrowser(
362362
| for (std::vector<int64>::iterator it = v.begin(); it != v.end(); ++it) {
363363
| CefRefPtr<CefFrame> frame = browser->GetFrame(*it);
364364
*/
365+
if (!frameIds.size()) {
366+
DebugLog("Renderer: DoJavascriptBindingsForBrowser() FAILED: " \
367+
"frameIds.size() == 0");
368+
return;
369+
}
365370
for (std::vector<int>::size_type i = 0; i != frameIds.size(); i++) {
371+
if (frameIds[i] <= 0) {
372+
// Frame not yet created, race condition / bug in CEF.
373+
DebugLog("Renderer: DoJavascriptBindingsForBrowser() WARNING: " \
374+
"frameId <= 0");
375+
printf("cefpython: Renderer: frameId = %li\n", frameIds[i]);
376+
continue;
377+
}
366378
CefRefPtr<CefFrame> frame = browser->GetFrame(frameIds[i]);
379+
if (!frame.get()) {
380+
DebugLog("Renderer: DoJavascriptBindingsForBrowser() WARNING: " \
381+
"GetFrame() failed");
382+
continue;
383+
}
367384
CefRefPtr<CefV8Context> context = frame->GetV8Context();
368385
CefRefPtr<CefTaskRunner> taskRunner = context->GetTaskRunner();
369386
taskRunner->PostTask(NewCefRunnableMethod(

0 commit comments

Comments
 (0)
X Tutup