Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/platform/xposix/gl_context.py : 92%
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.
6from ctypes import c_int, byref, cast, POINTER
7from OpenGL import GLX
8from OpenGL.GL import GL_VENDOR, GL_RENDERER, glGetString
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
17log = Logger("opengl")
20DOUBLE_BUFFERED = envbool("XPRA_OPENGL_DOUBLE_BUFFERED", True)
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 }
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)
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))
60class GLXWindowContext:
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
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
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)
83 def update_geometry(self):
84 pass
86 def swap_buffers(self):
87 assert self.valid, "GLX window context is no longer valid"
88 GLX.glXSwapBuffers(self.xdisplay, self.xid)
90 def __repr__(self):
91 return "GLXWindowContext(%#x)" % self.xid
94class GLXContext:
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)
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
196 def get_bit_depth(self):
197 return self.bit_depth
199 def is_double_buffered(self):
200 return DOUBLE_BUFFERED
202 def get_paint_context(self, gdk_window):
203 assert self.context and gdk_window
204 return GLXWindowContext(self.context, gdk_window.get_xid())
206 def destroy(self):
207 c = self.context
208 if c:
209 self.context = None
210 GLX.glXDestroyContext(self.xdisplay, c)
212 def __repr__(self):
213 return "GLXContext(%s)" % self.props
215GLContext = GLXContext
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()
224 return GLContext().check_support()