Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of Xpra. 

2# Copyright (C) 2017-2019 Antoine Martin <antoine@xpra.org> 

3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any 

4# later version. See the file COPYING for details. 

5 

6from ctypes import c_int, byref, cast, POINTER 

7from OpenGL import GLX 

8from OpenGL.GL import GL_VENDOR, GL_RENDERER, glGetString 

9 

10from xpra.util import envbool 

11from xpra.client.gl.gl_check import check_PyOpenGL_support 

12from xpra.x11.bindings.display_source import get_display_ptr #@UnresolvedImport 

13from xpra.gtk_common.error import xsync 

14from xpra.gtk_common.gtk_util import set_visual 

15from xpra.log import Logger 

16 

17log = Logger("opengl") 

18 

19 

20DOUBLE_BUFFERED = envbool("XPRA_OPENGL_DOUBLE_BUFFERED", True) 

21 

22 

23GLX_ATTRIBUTES = { 

24 GLX.GLX_ACCUM_RED_SIZE : "accum-red-size", 

25 GLX.GLX_ACCUM_GREEN_SIZE : "accum-green-size", 

26 GLX.GLX_ACCUM_BLUE_SIZE : "accum-blue-size", 

27 GLX.GLX_ACCUM_ALPHA_SIZE : "accum-alpha-size", 

28 GLX.GLX_RED_SIZE : "red-size", 

29 GLX.GLX_GREEN_SIZE : "green-size", 

30 GLX.GLX_BLUE_SIZE : "blue-size", 

31 GLX.GLX_ALPHA_SIZE : "alpha-size", 

32 GLX.GLX_DEPTH_SIZE : "depth-size", 

33 GLX.GLX_STENCIL_SIZE : "stencil-size", 

34 GLX.GLX_BUFFER_SIZE : "buffer-size", 

35 GLX.GLX_AUX_BUFFERS : "aux-buffers", 

36 GLX.GLX_DOUBLEBUFFER : "double-buffered", 

37 GLX.GLX_LEVEL : "level", 

38 GLX.GLX_STEREO : "stereo", 

39 GLX.GLX_RGBA : "rgba", 

40 } 

41 

42 

43def c_attrs(props): 

44 attrs = [] 

45 for k,v in props.items(): 

46 if v is None: 

47 attrs += [k] 

48 else: 

49 attrs += [k, v] 

50 attrs += [0, 0] 

51 return (c_int * len(attrs))(*attrs) 

52 

53def get_xdisplay(): 

54 ptr = get_display_ptr() 

55 assert ptr, "no X11 display registered" 

56 from OpenGL.raw.GLX._types import struct__XDisplay 

57 return cast(ptr, POINTER(struct__XDisplay)) 

58 

59 

60class GLXWindowContext: 

61 

62 def __init__(self, glx_context, xid): 

63 self.context = glx_context 

64 self.xid = xid 

65 self.xdisplay = get_xdisplay() 

66 self.valid = False 

67 

68 def __enter__(self): 

69 log("glXMakeCurrent: xid=%#x, context=%s", self.xid, self.context) 

70 with xsync: 

71 if not GLX.glXMakeCurrent(self.xdisplay, self.xid, self.context): 

72 raise Exception("glXMakeCurrent failed") 

73 self.valid = True 

74 

75 def __exit__(self, *_args): 

76 self.valid = False 

77 if self.context: 

78 context_type = type(self.context) 

79 null_context = cast(0, context_type) 

80 log("glXMakeCurrent: NULL for xid=%#x", self.xid) 

81 GLX.glXMakeCurrent(self.xdisplay, 0, null_context) 

82 

83 def update_geometry(self): 

84 pass 

85 

86 def swap_buffers(self): 

87 assert self.valid, "GLX window context is no longer valid" 

88 GLX.glXSwapBuffers(self.xdisplay, self.xid) 

89 

90 def __repr__(self): 

91 return "GLXWindowContext(%#x)" % self.xid 

92 

93 

94class GLXContext: 

95 

96 def __init__(self, alpha=False): 

97 self.props = {} 

98 self.xdisplay = None 

99 self.context = None 

100 self.bit_depth = 0 

101 from gi.repository import Gdk 

102 display = Gdk.Display.get_default() 

103 if not display: 

104 log.warn("Warning: GLXContext: no default display") 

105 return 

106 screen = display.get_default_screen() 

107 bpc = 8 

108 pyattrs = { 

109 GLX.GLX_RGBA : None, 

110 GLX.GLX_RED_SIZE : bpc, 

111 GLX.GLX_GREEN_SIZE : bpc, 

112 GLX.GLX_BLUE_SIZE : bpc, 

113 } 

114 if alpha: 

115 pyattrs[GLX.GLX_ALPHA_SIZE] = int(alpha)*bpc 

116 if DOUBLE_BUFFERED: 

117 pyattrs[GLX.GLX_DOUBLEBUFFER] = None 

118 attrs = c_attrs(pyattrs) 

119 self.xdisplay = get_xdisplay() 

120 xvinfo = GLX.glXChooseVisual(self.xdisplay, screen.get_number(), attrs) 

121 def getconfig(attrib): 

122 value = c_int() 

123 r = GLX.glXGetConfig(self.xdisplay, xvinfo, attrib, byref(value)) 

124 assert r==0, "glXGetConfig returned %i" % r 

125 return value.value 

126 assert getconfig(GLX.GLX_USE_GL), "OpenGL is not supported by this visual!" 

127 major = c_int() 

128 minor = c_int() 

129 assert GLX.glXQueryVersion(self.xdisplay, byref(major), byref(minor)) 

130 log("found GLX version %i.%i", major.value, minor.value) 

131 self.props["GLX"] = (major.value, minor.value) 

132 self.bit_depth = sum(getconfig(x) for x in ( 

133 GLX.GLX_RED_SIZE, GLX.GLX_GREEN_SIZE, GLX.GLX_BLUE_SIZE, GLX.GLX_ALPHA_SIZE)) 

134 self.props["depth"] = self.bit_depth 

135 #hide those because we don't use them 

136 #and because they're misleading: 'has-alpha' may be False 

137 #even when we have RGBA support (and therefore very likely to have alpha..) 

138 #self.props["has-depth-buffer"] = getconfig(GLX.GLX_DEPTH_SIZE)>0 

139 #self.props["has-stencil-buffer"] = getconfig(GLX.GLX_STENCIL_SIZE)>0 

140 #self.props["has-alpha"] = getconfig(GLX.GLX_ALPHA_SIZE)>0 

141 for attrib,name in GLX_ATTRIBUTES.items(): 

142 v = getconfig(attrib) 

143 if name in ("stereo", "double-buffered", "rgba"): 

144 v = bool(v) 

145 self.props[name] = v 

146 #attribute names matching gtkgl: 

147 display_mode = [] 

148 if getconfig(GLX.GLX_RGBA): 

149 #this particular context may not have alpha channel support... 

150 #but if we have RGBA then it's almost guaranteed that we can do ALPHA 

151 display_mode.append("ALPHA") 

152 if getconfig(GLX.GLX_DOUBLEBUFFER): 

153 display_mode.append("DOUBLE") 

154 else: # pragma: no cover 

155 display_mode.append("SINGLE") 

156 self.props["display_mode"] = display_mode 

157 self.context = GLX.glXCreateContext(self.xdisplay, xvinfo, None, True) 

158 self.props["direct"] = bool(GLX.glXIsDirect(self.xdisplay, self.context)) 

159 def getstr(k): 

160 try: 

161 return glGetString(k) 

162 except Exception as e: # pragma: no cover 

163 self.props["safe"] = False 

164 result = getattr(e, "result", None) 

165 if result and isinstance(result, str): 

166 return result 

167 raise 

168 self.props["vendor"] = getstr(GL_VENDOR) 

169 self.props["renderer"] = getstr(GL_RENDERER) 

170 log("GLXContext(%s) context=%s, props=%s", alpha, self.context, self.props) 

171 

172 def check_support(self, force_enable=False): 

173 i = self.props 

174 if not self.xdisplay: 

175 return { 

176 "success" : False, 

177 "safe" : False, 

178 "enabled" : False, 

179 "message" : "cannot access X11 display", 

180 } 

181 from gi.repository import Gtk 

182 tmp = Gtk.Window(Gtk.WindowType.TOPLEVEL) 

183 tmp.resize(1, 1) 

184 tmp.set_decorated(False) 

185 tmp.realize() 

186 da = Gtk.DrawingArea() 

187 tmp.add(da) 

188 set_visual(da, True) 

189 win = tmp.get_window() 

190 log("check_support(%s) using temporary window=%s", force_enable, tmp) 

191 with self.get_paint_context(win): 

192 i.update(check_PyOpenGL_support(force_enable)) 

193 tmp.destroy() 

194 return i 

195 

196 def get_bit_depth(self): 

197 return self.bit_depth 

198 

199 def is_double_buffered(self): 

200 return DOUBLE_BUFFERED 

201 

202 def get_paint_context(self, gdk_window): 

203 assert self.context and gdk_window 

204 return GLXWindowContext(self.context, gdk_window.get_xid()) 

205 

206 def destroy(self): 

207 c = self.context 

208 if c: 

209 self.context = None 

210 GLX.glXDestroyContext(self.xdisplay, c) 

211 

212 def __repr__(self): 

213 return "GLXContext(%s)" % self.props 

214 

215GLContext = GLXContext 

216 

217 

218def check_support(): 

219 ptr = get_display_ptr() 

220 if not ptr: 

221 from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source #@UnresolvedImport, @UnusedImport 

222 init_gdk_display_source() 

223 

224 return GLContext().check_support()