diff options
Diffstat (limited to 'util/fbcompose/XRenderScreen.cc')
-rw-r--r-- | util/fbcompose/XRenderScreen.cc | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/util/fbcompose/XRenderScreen.cc b/util/fbcompose/XRenderScreen.cc new file mode 100644 index 0000000..2450576 --- /dev/null +++ b/util/fbcompose/XRenderScreen.cc | |||
@@ -0,0 +1,356 @@ | |||
1 | /** XRenderScreen.cc file for the fluxbox compositor. */ | ||
2 | |||
3 | // Copyright (c) 2011 Gediminas Liktaras (gliktaras at gmail dot com) | ||
4 | // | ||
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
6 | // of this software and associated documentation files (the "Software"), to deal | ||
7 | // in the Software without restriction, including without limitation the rights | ||
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
9 | // copies of the Software, and to permit persons to whom the Software is | ||
10 | // furnished to do so, subject to the following conditions: | ||
11 | // | ||
12 | // The above copyright notice and this permission notice shall be included in | ||
13 | // all copies or substantial portions of the Software. | ||
14 | // | ||
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
21 | // THE SOFTWARE. | ||
22 | |||
23 | #include "XRenderScreen.hh" | ||
24 | |||
25 | #include "CompositorConfig.hh" | ||
26 | #include "Logging.hh" | ||
27 | #include "Utility.hh" | ||
28 | #include "XRenderWindow.hh" | ||
29 | |||
30 | #include <X11/extensions/shape.h> | ||
31 | #include <X11/extensions/Xcomposite.h> | ||
32 | #include <X11/extensions/Xfixes.h> | ||
33 | #include <X11/Xutil.h> | ||
34 | |||
35 | using namespace FbCompositor; | ||
36 | |||
37 | |||
38 | //--- MACROS ------------------------------------------------------------------- | ||
39 | |||
40 | // Macro for plugin iteration. | ||
41 | #define forEachPlugin(i, plugin) \ | ||
42 | (plugin) = ((pluginManager().plugins().size() > 0) \ | ||
43 | ? (dynamic_cast<XRenderPlugin*>(pluginManager().plugins()[0])) \ | ||
44 | : NULL); \ | ||
45 | for(size_t (i) = 0; \ | ||
46 | ((i) < pluginManager().plugins().size()); \ | ||
47 | (i)++, \ | ||
48 | (plugin) = (((i) < pluginManager().plugins().size()) \ | ||
49 | ? (dynamic_cast<XRenderPlugin*>(pluginManager().plugins()[(i)])) \ | ||
50 | : NULL)) | ||
51 | |||
52 | |||
53 | //--- CONSTRUCTORS AND DESTRUCTORS --------------------------------------------- | ||
54 | |||
55 | // Constructor. | ||
56 | XRenderScreen::XRenderScreen(int screen_number, const CompositorConfig &config): | ||
57 | BaseScreen(screen_number, Plugin_XRender, config), | ||
58 | m_pict_filter(config.xRenderPictFilter()) { | ||
59 | |||
60 | m_plugin_damage = XFixesCreateRegion(display(), NULL, 0); | ||
61 | |||
62 | initRenderingSurface(); | ||
63 | updateBackgroundPicture(); | ||
64 | } | ||
65 | |||
66 | // Destructor. | ||
67 | XRenderScreen::~XRenderScreen() { | ||
68 | if (m_plugin_damage) { | ||
69 | XFixesDestroyRegion(display(), m_plugin_damage); | ||
70 | } | ||
71 | |||
72 | XUnmapWindow(display(), m_rendering_window); | ||
73 | XDestroyWindow(display(), m_rendering_window); | ||
74 | } | ||
75 | |||
76 | |||
77 | //--- INITIALIZATION FUNCTIONS ------------------------------------------------- | ||
78 | |||
79 | // Initializes the rendering surface. | ||
80 | void XRenderScreen::initRenderingSurface() { | ||
81 | // Get all the elements, needed for the creation of the rendering surface. | ||
82 | Window comp_overlay = XCompositeGetOverlayWindow(display(), rootWindow().window()); | ||
83 | |||
84 | XVisualInfo visual_info; | ||
85 | if (!XMatchVisualInfo(display(), screenNumber(), 32, TrueColor, &visual_info)) { | ||
86 | throw InitException("Cannot find the required visual."); | ||
87 | } | ||
88 | |||
89 | XSetWindowAttributes wa; | ||
90 | wa.border_pixel = XBlackPixel(display(), screenNumber()); // Without this XCreateWindow gives BadMatch error. | ||
91 | wa.colormap = XCreateColormap(display(), rootWindow().window(), visual_info.visual, AllocNone); | ||
92 | long wa_mask = CWBorderPixel | CWColormap; | ||
93 | |||
94 | // Create the rendering surface. | ||
95 | m_rendering_window = XCreateWindow(display(), comp_overlay, 0, 0, rootWindow().width(), rootWindow().height(), 0, | ||
96 | visual_info.depth, InputOutput, visual_info.visual, wa_mask, &wa); | ||
97 | XmbSetWMProperties(display(), m_rendering_window, "fbcompose", "fbcompose", NULL, 0, NULL, NULL, NULL); | ||
98 | XMapWindow(display(), m_rendering_window); | ||
99 | |||
100 | // Make sure the overlays do not consume any input events. | ||
101 | XserverRegion empty_region = XFixesCreateRegion(display(), NULL, 0); | ||
102 | XFixesSetWindowShapeRegion(display(), comp_overlay, ShapeInput, 0, 0, empty_region); | ||
103 | XFixesSetWindowShapeRegion(display(), m_rendering_window, ShapeInput, 0, 0, empty_region); | ||
104 | XFixesDestroyRegion(display(), empty_region); | ||
105 | |||
106 | ignoreWindow(comp_overlay); | ||
107 | ignoreWindow(m_rendering_window); | ||
108 | |||
109 | // Create an XRender picture for the rendering window. | ||
110 | XRenderPictureAttributes pa; | ||
111 | pa.subwindow_mode = IncludeInferiors; | ||
112 | long pa_mask = CPSubwindowMode; | ||
113 | |||
114 | XRenderPictFormat *rendering_pict_format = XRenderFindVisualFormat(display(), visual_info.visual); | ||
115 | if (!rendering_pict_format) { | ||
116 | throw InitException("Cannot find the required picture format."); | ||
117 | } | ||
118 | |||
119 | m_rendering_picture = new XRenderPicture(*this, rendering_pict_format, m_pict_filter); | ||
120 | m_rendering_picture->setWindow(m_rendering_window, pa, pa_mask); | ||
121 | |||
122 | // Create the back buffer. | ||
123 | XRenderPictFormat *back_buffer_pict_format = XRenderFindStandardFormat(display(), PictStandardARGB32); | ||
124 | Pixmap back_buffer_pixmap = XCreatePixmap(display(), rootWindow().window(), rootWindow().width(), rootWindow().height(), 32); | ||
125 | |||
126 | m_back_buffer_picture = new XRenderPicture(*this, back_buffer_pict_format, m_pict_filter); | ||
127 | m_back_buffer_picture->setPixmap(back_buffer_pixmap, true, pa, pa_mask); | ||
128 | } | ||
129 | |||
130 | |||
131 | //--- SCREEN MANIPULATION ------------------------------------------------------ | ||
132 | |||
133 | // Notifies the screen of a background change. | ||
134 | void XRenderScreen::setRootPixmapChanged() { | ||
135 | BaseScreen::setRootPixmapChanged(); | ||
136 | m_root_changed = true; | ||
137 | } | ||
138 | |||
139 | // Notifies the screen of a root window change. | ||
140 | void XRenderScreen::setRootWindowSizeChanged() { | ||
141 | BaseScreen::setRootWindowSizeChanged(); | ||
142 | m_root_changed = true; | ||
143 | |||
144 | XRenderPictureAttributes pa; | ||
145 | pa.subwindow_mode = IncludeInferiors; | ||
146 | long pa_mask = CPSubwindowMode; | ||
147 | |||
148 | XResizeWindow(display(), m_rendering_window, rootWindow().width(), rootWindow().height()); | ||
149 | m_rendering_picture->setWindow(m_rendering_window, pa, pa_mask); // We need to recreate the picture. | ||
150 | |||
151 | Pixmap back_buffer_pixmap = XCreatePixmap(display(), rootWindow().window(), rootWindow().width(), rootWindow().height(), 32); | ||
152 | m_back_buffer_picture->setPixmap(back_buffer_pixmap, true, pa, pa_mask); | ||
153 | } | ||
154 | |||
155 | |||
156 | // Update the background picture. | ||
157 | void XRenderScreen::updateBackgroundPicture() { | ||
158 | XRenderPictFormat *pict_format; | ||
159 | if (wmSetRootWindowPixmap()) { | ||
160 | pict_format = XRenderFindVisualFormat(display(), rootWindow().visual()); | ||
161 | } else { | ||
162 | pict_format = XRenderFindStandardFormat(display(), PictStandardARGB32); | ||
163 | } | ||
164 | |||
165 | if (!pict_format) { | ||
166 | throw RuntimeException("Cannot find the required picture format."); | ||
167 | } | ||
168 | |||
169 | XRenderPictureAttributes pa; | ||
170 | pa.subwindow_mode = IncludeInferiors; | ||
171 | long pa_mask = CPSubwindowMode; | ||
172 | |||
173 | if (!m_root_picture) { | ||
174 | m_root_picture = new XRenderPicture(*this, pict_format, m_pict_filter); | ||
175 | } else { | ||
176 | m_root_picture->setPictFormat(pict_format); | ||
177 | } | ||
178 | m_root_picture->setPixmap(rootWindowPixmap(), false, pa, pa_mask); | ||
179 | m_root_changed = false; | ||
180 | } | ||
181 | |||
182 | |||
183 | //--- SCREEN RENDERING --------------------------------------------------------- | ||
184 | |||
185 | // Renders the screen's contents. | ||
186 | void XRenderScreen::renderScreen() { | ||
187 | clipBackBufferToDamage(); | ||
188 | |||
189 | renderBackground(); | ||
190 | |||
191 | std::list<BaseCompWindow*>::const_iterator it = allWindows().begin(); | ||
192 | while (it != allWindows().end()) { | ||
193 | if (!(*it)->isIgnored() && (*it)->isMapped()) { | ||
194 | renderWindow(*(dynamic_cast<XRenderWindow*>(*it))); | ||
195 | } | ||
196 | ++it; | ||
197 | } | ||
198 | |||
199 | if ((reconfigureRectangle().width != 0) && (reconfigureRectangle().height != 0)) { | ||
200 | renderReconfigureRect(); | ||
201 | } | ||
202 | |||
203 | renderExtraJobs(); | ||
204 | |||
205 | swapBuffers(); | ||
206 | } | ||
207 | |||
208 | // Clips the backbuffer picture to damaged area. | ||
209 | void XRenderScreen::clipBackBufferToDamage() { | ||
210 | XRenderPlugin *plugin = NULL; | ||
211 | |||
212 | m_plugin_damage_rects.clear(); | ||
213 | forEachPlugin(i, plugin) { | ||
214 | const std::vector<XRectangle> &window_damage = plugin->damagedAreas(); | ||
215 | m_plugin_damage_rects.insert(m_plugin_damage_rects.end(), window_damage.begin(), window_damage.end()); | ||
216 | } | ||
217 | XFixesSetRegion(display(), m_plugin_damage, (XRectangle*)(m_plugin_damage_rects.data()), m_plugin_damage_rects.size()); | ||
218 | |||
219 | XserverRegion all_damage = damagedScreenArea(); | ||
220 | XFixesUnionRegion(display(), all_damage, all_damage, m_plugin_damage); | ||
221 | |||
222 | XFixesSetPictureClipRegion(display(), m_back_buffer_picture->pictureHandle(), 0, 0, all_damage); | ||
223 | } | ||
224 | |||
225 | // Perform a rendering job on the back buffer picture. | ||
226 | void XRenderScreen::executeRenderingJob(const XRenderRenderingJob &job) { | ||
227 | if (job.operation != PictOpClear) { | ||
228 | Picture source = ((job.source_picture) ? (job.source_picture->pictureHandle()) : (None)); | ||
229 | Picture mask = ((job.mask_picture) ? (job.mask_picture->pictureHandle()) : (None)); | ||
230 | |||
231 | XRenderComposite(display(), job.operation, source, mask, | ||
232 | m_back_buffer_picture->pictureHandle(), job.source_x, job.source_y, | ||
233 | job.mask_x, job.mask_y, job.destination_x, job.destination_y, job.width, job.height); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | // Render the desktop wallpaper. | ||
238 | // TODO: Simply make the window transparent. | ||
239 | void XRenderScreen::renderBackground() { | ||
240 | // React to desktop background change. | ||
241 | if (m_root_changed) { | ||
242 | updateBackgroundPicture(); | ||
243 | } | ||
244 | |||
245 | // Draw the desktop. | ||
246 | XRenderComposite(display(), PictOpSrc, m_root_picture->pictureHandle(), None, m_back_buffer_picture->pictureHandle(), | ||
247 | 0, 0, 0, 0, 0, 0, rootWindow().width(), rootWindow().height()); | ||
248 | |||
249 | // Additional rendering actions. | ||
250 | XRenderPlugin *plugin = NULL; | ||
251 | XRenderRenderingJob job; | ||
252 | |||
253 | forEachPlugin(i, plugin) { | ||
254 | std::vector<XRenderRenderingJob> jobs = plugin->postBackgroundRenderingActions(); | ||
255 | for (size_t j = 0; j < jobs.size(); j++) { | ||
256 | executeRenderingJob(jobs[j]); | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | |||
261 | // Perform extra rendering jobs from plugins. | ||
262 | void XRenderScreen::renderExtraJobs() { | ||
263 | XRenderPlugin *plugin = NULL; | ||
264 | XRenderRenderingJob job; | ||
265 | |||
266 | forEachPlugin(i, plugin) { | ||
267 | std::vector<XRenderRenderingJob> jobs = plugin->extraRenderingActions(); | ||
268 | for (size_t j = 0; j < jobs.size(); j++) { | ||
269 | executeRenderingJob(jobs[j]); | ||
270 | } | ||
271 | plugin->postExtraRenderingActions(); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | // Render the reconfigure rectangle. | ||
276 | void XRenderScreen::renderReconfigureRect() { | ||
277 | XRenderPlugin *plugin = NULL; | ||
278 | XRectangle rect = reconfigureRectangle(); | ||
279 | |||
280 | XSetForeground(display(), m_back_buffer_picture->gcHandle(), XWhitePixel(display(), screenNumber())); | ||
281 | XSetFunction(display(), m_back_buffer_picture->gcHandle(), GXxor); | ||
282 | XSetLineAttributes(display(), m_back_buffer_picture->gcHandle(), 1, LineSolid, CapNotLast, JoinMiter); | ||
283 | |||
284 | forEachPlugin(i, plugin) { | ||
285 | plugin->recRectRenderingJobInit(rect, m_back_buffer_picture->gcHandle()); | ||
286 | } | ||
287 | XDrawRectangles(display(), m_back_buffer_picture->drawableHandle(), | ||
288 | m_back_buffer_picture->gcHandle(), &rect, 1); | ||
289 | } | ||
290 | |||
291 | // Render a particular window onto the screen. | ||
292 | void XRenderScreen::renderWindow(XRenderWindow &window) { | ||
293 | XRenderPlugin *plugin = NULL; | ||
294 | XRenderRenderingJob job; | ||
295 | |||
296 | // Update window contents. | ||
297 | if (window.isDamaged()) { | ||
298 | window.updateContents(); | ||
299 | } | ||
300 | |||
301 | // This might happen if the window is mapped and unmapped in the same | ||
302 | // frame, but the compositor hasn't received the unmap event yet. | ||
303 | if ((window.contentPicture()->pictureHandle() == None) | ||
304 | || (window.maskPicture()->pictureHandle() == None)) { | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | // Extra rendering actions before window is drawn. | ||
309 | forEachPlugin(i, plugin) { | ||
310 | std::vector<XRenderRenderingJob> jobs = plugin->preWindowRenderingActions(window); | ||
311 | for (size_t j = 0; j < jobs.size(); j++) { | ||
312 | executeRenderingJob(jobs[j]); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | // Draw the window. | ||
317 | job.operation = PictOpOver; | ||
318 | job.source_picture = window.contentPicture(); | ||
319 | job.mask_picture = window.maskPicture(); | ||
320 | job.source_x = 0; | ||
321 | job.source_y = 0; | ||
322 | job.mask_x = 0; | ||
323 | job.mask_y = 0; | ||
324 | job.destination_x = window.x(); | ||
325 | job.destination_y = window.y(); | ||
326 | job.width = window.realWidth(); | ||
327 | job.height = window.realHeight(); | ||
328 | |||
329 | forEachPlugin(i, plugin) { | ||
330 | plugin->windowRenderingJobInit(window, job); | ||
331 | } | ||
332 | executeRenderingJob(job); | ||
333 | |||
334 | // Extra rendering actions after window is drawn. | ||
335 | forEachPlugin(i, plugin) { | ||
336 | std::vector<XRenderRenderingJob> jobs = plugin->postWindowRenderingActions(window); | ||
337 | for (size_t j = 0; j < jobs.size(); j++) { | ||
338 | executeRenderingJob(jobs[j]); | ||
339 | } | ||
340 | } | ||
341 | } | ||
342 | |||
343 | // Swap back and front buffers. | ||
344 | void XRenderScreen::swapBuffers() { | ||
345 | XRenderComposite(display(), PictOpSrc, m_back_buffer_picture->pictureHandle(), None, m_rendering_picture->pictureHandle(), | ||
346 | 0, 0, 0, 0, 0, 0, rootWindow().width(), rootWindow().height()); | ||
347 | } | ||
348 | |||
349 | |||
350 | //--- SPECIALIZED WINDOW MANIPULATION FUNCTIONS -------------------------------- | ||
351 | |||
352 | // Creates a window object from its XID. | ||
353 | BaseCompWindow *XRenderScreen::createWindowObject(Window window) { | ||
354 | XRenderWindow *new_window = new XRenderWindow(*this, window, m_pict_filter); | ||
355 | return new_window; | ||
356 | } | ||