aboutsummaryrefslogtreecommitdiff
path: root/util/fbcompose/Compositor.cc
diff options
context:
space:
mode:
authorGediminas Liktaras <gliktaras@gmail.com>2011-12-08 13:34:09 (GMT)
committerPaul Tagliamonte <paultag@fluxbox.org>2011-12-10 16:13:19 (GMT)
commitcd339169d1961eb508ea89cee2609ec6d0fc0c15 (patch)
tree01acd158a03fb17a72e067ff0b36701da75e49dc /util/fbcompose/Compositor.cc
parent85ac5c4b2c6a526992f483a6e91867dc2f82a19e (diff)
downloadfluxbox_paul-cd339169d1961eb508ea89cee2609ec6d0fc0c15.zip
fluxbox_paul-cd339169d1961eb508ea89cee2609ec6d0fc0c15.tar.bz2
fbcompose - A compositing addon for fluxbox window manager.
fbcompose(1) is an optional compositing addon for fluxbox window manager. It augments fluxbox with a number of graphical features. Most notably, fbcompose allows fluxbox to properly display applications that require compositing (docky, for example), adds support for true window transparency (as opposed to fluxbox's pseudo transparency) and provides a plugin framework to extend the compositor's functionality. As this is still a beta version of the compositor, the bugs are likely.
Diffstat (limited to 'util/fbcompose/Compositor.cc')
-rw-r--r--util/fbcompose/Compositor.cc440
1 files changed, 440 insertions, 0 deletions
diff --git a/util/fbcompose/Compositor.cc b/util/fbcompose/Compositor.cc
new file mode 100644
index 0000000..48fd64f
--- /dev/null
+++ b/util/fbcompose/Compositor.cc
@@ -0,0 +1,440 @@
1/** Compositor.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
24#ifdef HAVE_CONFIG_H
25 #include "config.h"
26#endif // HAVE_CONFIG_H
27
28#include "Compositor.hh"
29
30#include "Atoms.hh"
31#include "BaseScreen.hh"
32#include "CompositorConfig.hh"
33#include "Constants.hh"
34#include "Logging.hh"
35#include "OpenGLScreen.hh"
36#include "XRenderScreen.hh"
37
38#ifdef USE_OPENGL_COMPOSITING
39 #include <GL/glxew.h>
40 #include <GL/glx.h>
41#endif // USE_OPENGL_COMPOSITING
42
43#include <X11/extensions/shape.h>
44#include <X11/extensions/Xcomposite.h>
45#include <X11/extensions/Xdamage.h>
46#include <X11/extensions/Xfixes.h>
47#ifdef XINERAMA
48 #include <X11/extensions/Xinerama.h>
49#endif // XINERAMA
50#ifdef USE_XRENDER_COMPOSITING
51 #include <X11/extensions/Xrender.h>
52#endif // USE_XRENDER_COMPOSITING
53#include <X11/Xutil.h>
54
55#include <sstream>
56
57#ifdef HAVE_CTIME
58 #include <ctime>
59#else
60#ifdef HAVE_TIME_H
61 #include <time.h>
62#endif
63#endif
64
65#include <csignal>
66
67using namespace FbCompositor;
68
69
70//--- CONSTANTS ----------------------------------------------------------------
71
72/** Length of the error buffer in X error handler. */
73const int ERROR_BUFFER_LENGTH = 128;
74
75/** Name of the relevant error database with X error messages in X error handler. */
76const char ERROR_DB_TEXT_NAME[] = "XRequest";
77
78/** Default name for unknown requests in X error handler. */
79const char REQUEST_NAME_UNKNOWN_MESSAGE[] = "<UNKNOWN>";
80
81/** How many microseconds to sleep before restarting the event loop. */
82const int SLEEP_TIME = 5000;
83
84
85//--- CONSTRUCTORS AND DESTRUCTORS ---------------------------------------------
86
87// The constructor.
88Compositor::Compositor(const CompositorConfig &config) :
89 App(config.displayName().c_str()) {
90
91 if (config.synchronize()) {
92 XSynchronize(display(), True);
93 }
94
95 if (config.renderingMode() == RM_ServerAuto) {
96 throw InitException("Compositor class does not provide the serverauto renderer.");
97 }
98 m_rendering_mode = config.renderingMode();
99
100 if (config.showXErrors()) {
101 XSetErrorHandler(&printXError);
102 } else {
103 XSetErrorHandler(&ignoreXError);
104 }
105
106 initAllExtensions();
107
108 int screen_count = XScreenCount(display());
109 m_screens.reserve(screen_count);
110
111 for (int i = 0; i < screen_count; i++) {
112 Window cm_selection_owner = getCMSelectionOwnership(i);
113
114 switch (m_rendering_mode) {
115#ifdef USE_OPENGL_COMPOSITING
116 case RM_OpenGL :
117 m_screens.push_back(new OpenGLScreen(i, config));
118 break;
119#endif // USE_OPENGL_COMPOSITING
120#ifdef USE_XRENDER_COMPOSITING
121 case RM_XRender :
122 m_screens.push_back(new XRenderScreen(i, config));
123 break;
124#endif // USE_XRENDER_COMPOSITING
125 default :
126 throw InitException("Unknown rendering mode selected.");
127 break;
128 }
129
130 m_screens[i]->ignoreWindow(cm_selection_owner);
131 }
132
133 initHeads();
134 for (size_t i = 0; i < m_screens.size(); i++) {
135 m_screens[i]->initPlugins(config);
136 m_screens[i]->initWindows();
137 }
138
139 m_timer.setTickSize(1000000 / config.framesPerSecond());
140 m_timer.start();
141
142 XFlush(display());
143
144 signal(SIGINT, handleSignal);
145 signal(SIGTERM, handleSignal);
146}
147
148// Destructor.
149Compositor::~Compositor() {
150 std::vector<BaseScreen*>::iterator it = m_screens.begin();
151 while (it != m_screens.end()) {
152 delete *it;
153 ++it;
154 }
155}
156
157
158//--- INITIALIZATION FUNCTIONS -----------------------------------------
159
160// Acquires the ownership of compositing manager selections.
161Window Compositor::getCMSelectionOwnership(int screen_number) {
162 Atom cm_atom = Atoms::compositingSelectionAtom(screen_number);
163
164 Window cur_owner = XGetSelectionOwner(display(), cm_atom);
165 if (cur_owner != None) {
166 // TODO: More detailed message - what is the other program?
167 throw InitException("Another compositing manager is running.");
168 }
169
170 cur_owner = XCreateSimpleWindow(display(), XRootWindow(display(), screen_number), -10, -10, 1, 1, 0, None, None);
171 XmbSetWMProperties(display(), cur_owner, APP_NAME, APP_NAME, NULL, 0, NULL, NULL, NULL);
172 XSetSelectionOwner(display(), cm_atom, cur_owner, CurrentTime);
173
174 return cur_owner;
175}
176
177// Initializes X's extensions.
178void Compositor::initAllExtensions() {
179#ifdef USE_OPENGL_COMPOSITING
180 if (m_rendering_mode == RM_OpenGL) {
181 initExtension("GLX", &glXQueryExtension, &glXQueryVersion, 1, 3, m_glx_event_base, m_glx_error_base);
182 initExtension("XComposite", &XCompositeQueryExtension, &XCompositeQueryVersion, 0, 4, m_composite_event_base, m_composite_error_base);
183 initExtension("XDamage", &XDamageQueryExtension, &XDamageQueryVersion, 1, 0, m_damage_event_base, m_damage_error_base);
184 initExtension("XFixes", &XFixesQueryExtension, &XFixesQueryVersion, 2, 0, m_fixes_event_base, m_fixes_error_base);
185 initExtension("XShape", &XShapeQueryExtension, &XShapeQueryVersion, 1, 1, m_shape_event_base, m_shape_error_base);
186 } else
187#endif // USE_OPENGL_COMPOSITING
188
189#ifdef USE_XRENDER_COMPOSITING
190 if (m_rendering_mode == RM_XRender) {
191 initExtension("XComposite", &XCompositeQueryExtension, &XCompositeQueryVersion, 0, 4, m_composite_event_base, m_composite_error_base);
192 initExtension("XDamage", &XDamageQueryExtension, &XDamageQueryVersion, 1, 0, m_damage_event_base, m_damage_error_base);
193 initExtension("XFixes", &XFixesQueryExtension, &XFixesQueryVersion, 2, 0, m_fixes_event_base, m_fixes_error_base);
194 initExtension("XRender", &XRenderQueryExtension, &XRenderQueryVersion, 0, 1, m_render_event_base, m_render_error_base);
195 initExtension("XShape", &XShapeQueryExtension, &XShapeQueryVersion, 1, 1, m_shape_event_base, m_shape_error_base);
196 } else
197#endif // USE_XRENDER_COMPOSITING
198
199 { }
200}
201
202// Initializes a particular X server extension.
203void Compositor::initExtension(const char *extension_name, QueryExtensionFunction extension_func,
204 QueryVersionFunction version_func, const int min_major_ver, const int min_minor_ver,
205 int &event_base, int &error_base) {
206 int major_ver;
207 int minor_ver;
208
209 // Check that the extension exists.
210 if (!(*extension_func)(display(), &event_base, &error_base)) {
211 event_base = -1;
212 error_base = -1;
213
214 std::stringstream ss;
215 ss << extension_name << " extension not found.";
216 throw InitException(ss.str());
217 }
218
219 // Get extension version.
220 if (!(*version_func)(display(), &major_ver, &minor_ver)) {
221 event_base = -1;
222 error_base = -1;
223
224 std::stringstream ss;
225 ss << "Could not query the version of " << extension_name << " extension.";
226 throw InitException(ss.str());
227 }
228
229 // Make sure the extension version is at least what we require.
230 if ((major_ver < min_major_ver) || ((major_ver == min_major_ver) && (minor_ver < min_minor_ver))) {
231 event_base = -1;
232 error_base = -1;
233
234 std::stringstream ss;
235 ss << "Unsupported " << extension_name << " extension version found (required >=" << min_major_ver
236 << "." << min_minor_ver << ", got " << major_ver << "." << minor_ver << ").";
237 throw InitException(ss.str());
238 }
239}
240
241// Initializes monitor heads on every screen.
242void Compositor::initHeads() {
243 HeadMode head_mode = Heads_One;
244
245#ifdef XINERAMA
246 try {
247 initExtension("Xinerama", &XineramaQueryExtension, &XCompositeQueryVersion, 0, 0, m_xinerama_event_base, m_xinerama_error_base);
248 if (XineramaIsActive(display())) {
249 head_mode = Heads_Xinerama;
250 }
251 } catch (const InitException &e) { }
252
253 if (head_mode != Heads_Xinerama) {
254 fbLog_warn << "Could not initialize Xinerama." << std::endl;
255 }
256#endif // XINERAMA
257
258 for (size_t i = 0; i < m_screens.size(); i++) {
259 m_screens[i]->updateHeads(head_mode);
260 }
261}
262
263
264//--- EVENT LOOP ---------------------------------------------------------------
265
266// The event loop.
267void Compositor::eventLoop() {
268 union {
269 XEvent event_x;
270 XDamageNotifyEvent event_x_damage_notify;
271 XShapeEvent event_x_shape;
272 } event_union;
273 XEvent &event = event_union.event_x;
274
275 int event_screen;
276 timespec sleep_timespec = { 0, SLEEP_TIME * 1000 };
277
278 while (!done()) {
279 while (XPending(display())) {
280 XNextEvent(display(), &event);
281
282 event_screen = screenOfEvent(event);
283 if (event_screen < 0) {
284 fbLog_info << "Event " << std::dec << event.xany.serial << " (window " << std::hex << event.xany.window
285 << ", type " << std::dec << event.xany.type << ") does not affect any managed windows, skipping."
286 << std::endl;
287 continue;
288 }
289
290 switch (event.type) {
291 case CirculateNotify :
292 m_screens[event_screen]->circulateWindow(event.xcirculate.window, event.xcirculate.place);
293 fbLog_debug << "CirculateNotify on " << std::hex << event.xcirculate.window << std::endl;
294 break;
295
296 case ConfigureNotify :
297 m_screens[event_screen]->reconfigureWindow(event.xconfigure);
298 fbLog_debug << "ConfigureNotify on " << std::hex << event.xconfigure.window << std::endl;
299 break;
300
301 case CreateNotify :
302 m_screens[event_screen]->createWindow(event.xcreatewindow.window);
303 fbLog_debug << "CreateNotify on " << std::hex << event.xcreatewindow.window << std::endl;
304 break;
305
306 case DestroyNotify :
307 m_screens[event_screen]->destroyWindow(event.xdestroywindow.window);
308 fbLog_debug << "DestroyNotify on " << std::hex << event.xdestroywindow.window << std::endl;
309 break;
310
311 case Expose :
312 m_screens[event_screen]->damageWindow(event.xexpose.window, getExposedRect(event.xexpose));
313 fbLog_debug << "Expose on " << std::hex << event.xexpose.window << std::endl;
314 break;
315
316 case GravityNotify :
317 fbLog_debug << "GravityNotify on " << std::hex << event.xgravity.window << std::endl;
318 break;
319
320 case MapNotify :
321 m_screens[event_screen]->mapWindow(event.xmap.window);
322 fbLog_debug << "MapNotify on " << std::hex << event.xmap.window << std::endl;
323 break;
324
325 case PropertyNotify :
326 m_screens[event_screen]->updateWindowProperty(event.xproperty.window, event.xproperty.atom, event.xproperty.state);
327 fbLog_debug << "PropertyNotify on " << std::hex << event.xproperty.window << " ("
328 << XGetAtomName(display(), event.xproperty.atom) << ")" << std::endl;
329 break;
330
331 case ReparentNotify :
332 m_screens[event_screen]->reparentWindow(event.xreparent.window, event.xreparent.parent);
333 fbLog_debug << "ReparentNotify on " << std::hex << event.xreparent.window << " (parent "
334 << event.xreparent.parent << ")" << std::endl;
335 break;
336
337 case UnmapNotify :
338 m_screens[event_screen]->unmapWindow(event.xunmap.window);
339 fbLog_debug << "UnmapNotify on " << std::hex << event.xunmap.window << std::endl;
340 break;
341
342 default :
343 if (event.type == (m_damage_event_base + XDamageNotify)) {
344 XDamageNotifyEvent damageEvent = event_union.event_x_damage_notify;
345 m_screens[event_screen]->damageWindow(damageEvent.drawable, damageEvent.area);
346 fbLog_debug << "DamageNotify on " << std::hex << damageEvent.drawable << std::endl;
347
348 } else if (event.type == (m_shape_event_base + ShapeNotify)) {
349 XShapeEvent shapeEvent = event_union.event_x_shape;
350 m_screens[event_screen]->updateShape(shapeEvent.window);
351 fbLog_debug << "ShapeNotify on " << std::hex << shapeEvent.window << std::endl;
352
353 } else {
354 fbLog_debug << "Other event " << std::dec << event.xany.type << " received on screen "
355 << event_screen << " and window " << std::hex << event.xany.window << std::endl;
356 }
357 break;
358 }
359 }
360
361 if (m_timer.newElapsedTicks()) {
362 for (size_t i = 0; i < m_screens.size(); i++) {
363 m_screens[i]->renderScreen();
364 m_screens[i]->clearScreenDamage();
365 }
366 XSync(display(), False);
367
368 fbLog_debugDump << m_screens.size() << " screen(s) available." << std::endl;
369 for (size_t i = 0; i < m_screens.size(); i++) {
370 fbLog_debugDump << *m_screens[i];
371 }
372 fbLog_debugDump << "======================================" << std::endl;
373 } else {
374 nanosleep(&sleep_timespec, NULL);
375 }
376 }
377}
378
379
380//--- INTERNAL FUNCTIONS -----------------------------------------------
381
382// Returns the exposed area in a XExposeEvent as an XRectangle.
383XRectangle Compositor::getExposedRect(const XExposeEvent &event) {
384 XRectangle rect = { event.x, event.y, event.width, event.height };
385 return rect;
386}
387
388// Locates the screen an event affects. Returns -1 on failure.
389int Compositor::screenOfEvent(const XEvent &event) {
390 if (m_screens.size() == 1) {
391 return 0;
392 } else {
393 for (size_t i = 0; i < m_screens.size(); i++) {
394 if ((event.xany.window == m_screens[i]->rootWindow().window())
395 || (m_screens[i]->isWindowManaged(event.xany.window))) {
396 return i;
397 }
398 }
399 }
400
401 return -1;
402}
403
404
405//--- VARIOUS HANDLERS ---------------------------------------------------------
406
407// Custom signal handler.
408void FbCompositor::handleSignal(int sig) {
409 if ((sig == SIGINT) || (sig == SIGTERM)) {
410 FbTk::App::instance()->end();
411 }
412}
413
414
415// Custom X error handler (ignore).
416int FbCompositor::ignoreXError(Display * /*display*/, XErrorEvent * /*error*/) {
417 return 0;
418}
419
420// Custom X error handler (print, continue).
421int FbCompositor::printXError(Display *display, XErrorEvent *error) {
422 static std::stringstream ss;
423 ss.str("");
424
425 char error_text[ERROR_BUFFER_LENGTH];
426 XGetErrorText(display, error->error_code, error_text, ERROR_BUFFER_LENGTH);
427
428 ss << int(error->request_code);
429
430 char request_name[ERROR_BUFFER_LENGTH];
431 XGetErrorDatabaseText(display, (char*)(ERROR_DB_TEXT_NAME), (char*)(ss.str().c_str()),
432 (char*)(REQUEST_NAME_UNKNOWN_MESSAGE), request_name, ERROR_BUFFER_LENGTH);
433
434 fbLog_warn << "X Error: " << error_text << " in " << request_name << " request, errorCode="
435 << std::dec << int(error->error_code) << ", majorOpCode=" << int(error->request_code)
436 << ", minorOpCode=" << int(error->minor_code) << ", resourceId=" << std::hex
437 << error->resourceid << "." << std::endl;
438
439 return 0;
440}