Skip to content

Commit 3df4e74

Browse files
committed
Updated coding style and comments.
1 parent db2d000 commit 3df4e74

File tree

1 file changed

+205
-169
lines changed

1 file changed

+205
-169
lines changed

examples/pysdl2.py

100755100644
Lines changed: 205 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,182 +1,218 @@
1-
#!/usr/bin/python2
1+
"""
2+
Simple SDL2 / cefpython3 example.
23
3-
#
4-
# Simple SDL2 / cefpython3 example.
5-
#
6-
# Only handles mouse events but could be extended to handle others.
7-
#
8-
# Requires pysdl2 (and SDL2 library).
9-
#
10-
# Tested under Fedora Linux (x86_64).
11-
#
12-
# by Neil Munday (www.mundayweb.com)
13-
#
4+
Only handles mouse events but could be extended to handle others.
5+
6+
Requires pysdl2 (and SDL2 library).
7+
8+
Install instructions.
9+
10+
1. Install SDL libraries for your OS, e.g. for Fedora:
11+
12+
dnf install SDL2 SDL2_ttf SDL2_image SDL2_gfx SDL2_mixer
13+
14+
2. Install PySDL via PIP:
15+
16+
pip2 install PySDL2
17+
18+
Tested configurations:
19+
- SDL2 2.0.5 with PySDL2 0.9.3 on Fedora 25 (x86_64)
20+
"""
1421

1522
import os
1623
import sys
17-
import sdl2
18-
import sdl2.ext
19-
2024
from cefpython3 import cefpython as cef
21-
from PIL import Image
25+
try:
26+
import sdl2
27+
import sdl2.ext
28+
except ImportError:
29+
print "SDL2 module not found - please install"
30+
sys.exit(1)
31+
try:
32+
from PIL import Image
33+
except ImportError:
34+
print "PIL module not found - please install"
35+
sys.exit(1)
2236

2337
class LoadHandler(object):
24-
def OnLoadingStateChange(self, browser, is_loading, **_):
25-
if not is_loading:
26-
print "loading complete"
27-
28-
def OnLoadError(self, browser, frame, error_code, failed_url, **_):
29-
if not frame.IsMain():
30-
return
31-
print "Failed to load %s" % failed_url
32-
cef.PostTask(cef.TID_UI, exit_app, browser)
38+
"""Simple handler for loading URLs."""
39+
40+
def OnLoadingStateChange(self, browser, is_loading, **_):
41+
if not is_loading:
42+
print "loading complete"
43+
44+
def OnLoadError(self, browser, frame, error_code, failed_url, **_):
45+
if not frame.IsMain():
46+
return
47+
print "Failed to load %s" % failed_url
48+
cef.PostTask(cef.TID_UI, exit_app, browser)
3349

3450
class RenderHandler(object):
35-
36-
def __init__(self, renderer, width, height):
37-
self.__width = width
38-
self.__height = height
39-
self.__renderer = renderer
40-
self.texture = None
41-
42-
def GetViewRect(self, rect_out, **_):
43-
rect_out.extend([0, 0, self.__width, self.__height])
44-
return True
45-
46-
def GetScreenRect(self, browser, rect_out): # noqa: N802
47-
return False
48-
49-
def GetScreenPoint(self, browser, view_x, view_y, screen_coordinates_out):
50-
return False
51-
52-
def OnPaint(self, browser, element_type, paint_buffer, **_):
53-
#
54-
# Use PIL to create a set of bytes that we can turn into an SDL2 surface
55-
# and then convert this to a SDL2 texture for rendering the main program loop.
56-
#
57-
if element_type == cef.PET_VIEW:
58-
data = paint_buffer.GetString(mode="rgba", origin="top-left")
59-
image = Image.frombuffer('RGBA', (self.__width, self.__height), data, 'raw', 'BGRA')
60-
#
61-
# Following PIL to SDL2 surface code from pysdl2 source
62-
#
63-
mode = image.mode
64-
rmask = gmask = bmask = amask = 0
65-
if mode == "RGB":
66-
# 3x8-bit, 24bpp
67-
if sdl2.endian.SDL_BYTEORDER == sdl2.endian.SDL_LIL_ENDIAN:
68-
rmask = 0x0000FF
69-
gmask = 0x00FF00
70-
bmask = 0xFF0000
71-
else:
72-
rmask = 0xFF0000
73-
gmask = 0x00FF00
74-
bmask = 0x0000FF
75-
depth = 24
76-
pitch = self.__width * 3
77-
elif mode in ("RGBA", "RGBX"):
78-
# RGBX: 4x8-bit, no alpha
79-
# RGBA: 4x8-bit, alpha
80-
if sdl2.endian.SDL_BYTEORDER == sdl2.endian.SDL_LIL_ENDIAN:
81-
rmask = 0x00000000
82-
gmask = 0x0000FF00
83-
bmask = 0x00FF0000
84-
if mode == "RGBA":
85-
amask = 0xFF000000
86-
else:
87-
rmask = 0xFF000000
88-
gmask = 0x00FF0000
89-
bmask = 0x0000FF00
90-
if mode == "RGBA":
91-
amask = 0x000000FF
92-
depth = 32
93-
pitch = self.__width * 4
94-
else:
95-
print "Unsupported mode: %s" % mode
96-
exit_app()
97-
98-
pxbuf = image.tobytes()
99-
# create surface
100-
surface = sdl2.SDL_CreateRGBSurfaceFrom(pxbuf, self.__width, self.__height, depth, pitch, rmask, gmask, bmask, amask)
101-
102-
if self.texture:
103-
sdl2.SDL_DestroyTexture(self.texture)
104-
# create texture
105-
self.texture = sdl2.SDL_CreateTextureFromSurface(self.__renderer, surface)
106-
sdl2.SDL_FreeSurface(surface)
107-
else:
108-
print "Unsupport element_type in OnPaint"
51+
"""
52+
Handler for rendering web pages to the
53+
screen via SDL2.
54+
55+
The object's texture property is exposed
56+
to allow the main rendering loop to access
57+
the SDL2 texture.
58+
"""
59+
60+
def __init__(self, renderer, width, height):
61+
self.__width = width
62+
self.__height = height
63+
self.__renderer = renderer
64+
self.texture = None
65+
66+
def GetViewRect(self, rect_out, **_):
67+
rect_out.extend([0, 0, self.__width, self.__height])
68+
return True
69+
70+
def OnPaint(self, browser, element_type, paint_buffer, **_):
71+
"""
72+
Using the pixel data from CEF's offscreen rendering
73+
the data is converted by PIL into a SDL2 surface
74+
which can then be rendered as a SDL2 texture.
75+
"""
76+
if element_type == cef.PET_VIEW:
77+
image = Image.frombuffer(
78+
'RGBA',
79+
(self.__width, self.__height),
80+
paint_buffer.GetString(mode="rgba", origin="top-left"),
81+
'raw',
82+
'BGRA'
83+
)
84+
#
85+
# Following PIL to SDL2 surface code from pysdl2 source.
86+
#
87+
mode = image.mode
88+
rmask = gmask = bmask = amask = 0
89+
if mode == "RGB":
90+
# 3x8-bit, 24bpp
91+
if sdl2.endian.SDL_BYTEORDER == sdl2.endian.SDL_LIL_ENDIAN:
92+
rmask = 0x0000FF
93+
gmask = 0x00FF00
94+
bmask = 0xFF0000
95+
else:
96+
rmask = 0xFF0000
97+
gmask = 0x00FF00
98+
bmask = 0x0000FF
99+
depth = 24
100+
pitch = self.__width * 3
101+
elif mode in ("RGBA", "RGBX"):
102+
# RGBX: 4x8-bit, no alpha
103+
# RGBA: 4x8-bit, alpha
104+
if sdl2.endian.SDL_BYTEORDER == sdl2.endian.SDL_LIL_ENDIAN:
105+
rmask = 0x00000000
106+
gmask = 0x0000FF00
107+
bmask = 0x00FF0000
108+
if mode == "RGBA":
109+
amask = 0xFF000000
110+
else:
111+
rmask = 0xFF000000
112+
gmask = 0x00FF0000
113+
bmask = 0x0000FF00
114+
if mode == "RGBA":
115+
amask = 0x000000FF
116+
depth = 32
117+
pitch = self.__width * 4
118+
else:
119+
print "Unsupported mode: %s" % mode
120+
exit_app()
121+
122+
pxbuf = image.tobytes()
123+
# create surface
124+
surface = sdl2.SDL_CreateRGBSurfaceFrom(pxbuf, self.__width, self.__height, depth, pitch, rmask, gmask, bmask, amask)
125+
if self.texture:
126+
# free memory used by previous texture
127+
sdl2.SDL_DestroyTexture(self.texture)
128+
# create texture
129+
self.texture = sdl2.SDL_CreateTextureFromSurface(self.__renderer, surface)
130+
# free the surface
131+
sdl2.SDL_FreeSurface(surface)
132+
else:
133+
print "Unsupport element_type in OnPaint"
109134

110135
def exit_app():
111-
sdl2.SDL_Quit()
112-
cef.Shutdown()
113-
print "exited"
136+
"""Tidy up SDL2 and CEF before exiting."""
137+
sdl2.SDL_Quit()
138+
cef.Shutdown()
139+
print "exited"
114140

141+
def main():
142+
# the following variables control the dimensions of the window
143+
# and browser display area
144+
width = 1024
145+
height = 768
146+
headerHeight = 0 # useful if for leaving space for controls at the top of the window (future implementation?)
147+
browserHeight = height - headerHeight
148+
browserWidth = width
149+
# initialise CEF for offscreen rendering
150+
WindowUtils = cef.WindowUtils()
151+
sys.excepthook = cef.ExceptHook
152+
cef.Initialize(settings={"windowless_rendering_enabled": True})
153+
window_info = cef.WindowInfo()
154+
window_info.SetAsOffscreen(0)
155+
# initialise SDL2 for video (add other init constants if you
156+
# require other SDL2 functionality e.g. mixer,
157+
# TTF, joystick etc.
158+
sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO)
159+
# create the window
160+
window = sdl2.video.SDL_CreateWindow(
161+
'cefpython3 SDL2 Demo',
162+
sdl2.video.SDL_WINDOWPOS_UNDEFINED,
163+
sdl2.video.SDL_WINDOWPOS_UNDEFINED,
164+
width,
165+
height,
166+
0
167+
)
168+
# define default background colour (black in this case)
169+
backgroundColour = sdl2.SDL_Color(0, 0, 0)
170+
# create the renderer using hardware acceleration
171+
renderer = sdl2.SDL_CreateRenderer(window, -1, sdl2.render.SDL_RENDERER_ACCELERATED)
172+
# set-up the RenderHandler, passing in the SDL2 renderer
173+
renderHandler = RenderHandler(renderer, width, height - headerHeight)
174+
# create the browser instance
175+
browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/")
176+
browser.SetClientHandler(LoadHandler())
177+
browser.SetClientHandler(renderHandler)
178+
# must call WasResized at least once to let know CEF that
179+
# viewport size is available and that OnPaint may be called.
180+
browser.SendFocusEvent(True)
181+
browser.WasResized()
182+
# begin the main rendering loop
183+
running = True
184+
while running:
185+
# convert SDL2 events into CEF events (where appropriate)
186+
events = sdl2.ext.get_events()
187+
for event in events:
188+
if event.type == sdl2.SDL_QUIT or (event.type == sdl2.SDL_KEYDOWN and event.key.keysym.sym == sdl2.SDLK_ESCAPE):
189+
running = False
190+
break
191+
if event.type == sdl2.SDL_MOUSEBUTTONDOWN:
192+
if event.button.button == sdl2.SDL_BUTTON_LEFT:
193+
if event.button.y > headerHeight:
194+
# mouse click triggered in browser region
195+
browser.SendMouseClickEvent(event.button.x, event.button.y - headerHeight, cef.MOUSEBUTTON_LEFT, False, 1)
196+
elif event.type == sdl2.SDL_MOUSEBUTTONUP:
197+
if event.button.button == sdl2.SDL_BUTTON_LEFT:
198+
if event.button.y > headerHeight:
199+
# mouse click triggered in browser region
200+
browser.SendMouseClickEvent(event.button.x, event.button.y - headerHeight, cef.MOUSEBUTTON_LEFT, True, 1)
201+
elif event.type == sdl2.SDL_MOUSEMOTION:
202+
if event.button.y > headerHeight:
203+
# mouse click triggered in browser region
204+
browser.SendMouseMoveEvent(event.button.x, event.button.y - headerHeight, True)
205+
# clear the renderer
206+
sdl2.SDL_SetRenderDrawColor(renderer, backgroundColour.r, backgroundColour.g, backgroundColour.b, 255)
207+
sdl2.SDL_RenderClear(renderer)
208+
# tell CEF to update which will trigger the OnPaint
209+
# method of the RenderHandler instance
210+
cef.MessageLoopWork()
211+
# update display
212+
sdl2.SDL_RenderCopy(renderer, renderHandler.texture, None, sdl2.SDL_Rect(0, headerHeight, browserWidth, browserHeight))
213+
sdl2.SDL_RenderPresent(renderer)
214+
# user exited
215+
exit_app()
216+
115217
if __name__ == "__main__":
116-
117-
width = 1024
118-
height = 768
119-
headerHeight = 0 # useful if for leaving space for controls at the top of the window (future implementation?)
120-
browserHeight = height - headerHeight
121-
browserWidth = width
122-
123-
WindowUtils = cef.WindowUtils()
124-
125-
sys.excepthook = cef.ExceptHook
126-
127-
cef.Initialize(settings={"windowless_rendering_enabled": True})
128-
129-
sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO)
130-
131-
window = sdl2.video.SDL_CreateWindow('cefpython3 SDL2 Demo', sdl2.video.SDL_WINDOWPOS_UNDEFINED, sdl2.video.SDL_WINDOWPOS_UNDEFINED, width, height, 0)
132-
133-
backgroundColour = sdl2.SDL_Color(0, 0, 0)
134-
135-
renderer = sdl2.SDL_CreateRenderer(window, -1, sdl2.render.SDL_RENDERER_ACCELERATED)
136-
137-
window_info = cef.WindowInfo()
138-
window_info.SetAsOffscreen(0)
139-
140-
renderHandler = RenderHandler(renderer, width, height - headerHeight)
141-
142-
browser = cef.CreateBrowserSync(window_info, url="https://www.google.com/")
143-
browser.SetClientHandler(LoadHandler())
144-
browser.SetClientHandler(renderHandler)
145-
browser.SendFocusEvent(True)
146-
browser.WasResized()
147-
148-
running = True
149-
150-
# main loop, handle events and rendering here
151-
while running:
152-
153-
# convert SDL2 events into CEF events (where appropriate)
154-
events = sdl2.ext.get_events()
155-
for event in events:
156-
if event.type == sdl2.SDL_QUIT or (event.type == sdl2.SDL_KEYDOWN and event.key.keysym.sym == sdl2.SDLK_ESCAPE):
157-
running = False
158-
break
159-
160-
if event.type == sdl2.SDL_MOUSEBUTTONDOWN:
161-
if event.button.button == sdl2.SDL_BUTTON_LEFT:
162-
if event.button.y > headerHeight:
163-
browser.SendMouseClickEvent(event.button.x, event.button.y - headerHeight, cef.MOUSEBUTTON_LEFT, False, 1)
164-
elif event.type == sdl2.SDL_MOUSEBUTTONUP:
165-
if event.button.button == sdl2.SDL_BUTTON_LEFT:
166-
if event.button.y > headerHeight:
167-
browser.SendMouseClickEvent(event.button.x, event.button.y - headerHeight, cef.MOUSEBUTTON_LEFT, True, 1)
168-
elif event.type == sdl2.SDL_MOUSEMOTION:
169-
if event.button.y > headerHeight:
170-
browser.SendMouseMoveEvent(event.button.x, event.button.y - headerHeight, True)
171-
172-
sdl2.SDL_SetRenderDrawColor(renderer, backgroundColour.r, backgroundColour.g, backgroundColour.b, 255)
173-
sdl2.SDL_RenderClear(renderer)
174-
175-
cef.MessageLoopWork()
176-
177-
sdl2.SDL_RenderCopy(renderer, renderHandler.texture, None, sdl2.SDL_Rect(0, headerHeight, browserWidth, browserHeight))
178-
179-
sdl2.SDL_RenderPresent(renderer)
180-
181-
exit_app()
182-
218+
main()

0 commit comments

Comments
 (0)