aboutsummaryrefslogtreecommitdiff
path: root/util/fbcompose/Compositor.cc
diff options
context:
space:
mode:
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}