diff options
author | Gediminas Liktaras <gliktaras@gmail.com> | 2011-12-08 13:34:09 (GMT) |
---|---|---|
committer | Paul Tagliamonte <paultag@fluxbox.org> | 2011-12-10 16:13:19 (GMT) |
commit | cd339169d1961eb508ea89cee2609ec6d0fc0c15 (patch) | |
tree | 01acd158a03fb17a72e067ff0b36701da75e49dc /util/fbcompose/Compositor.cc | |
parent | 85ac5c4b2c6a526992f483a6e91867dc2f82a19e (diff) | |
download | fluxbox_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.cc | 440 |
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 | |||
67 | using namespace FbCompositor; | ||
68 | |||
69 | |||
70 | //--- CONSTANTS ---------------------------------------------------------------- | ||
71 | |||
72 | /** Length of the error buffer in X error handler. */ | ||
73 | const int ERROR_BUFFER_LENGTH = 128; | ||
74 | |||
75 | /** Name of the relevant error database with X error messages in X error handler. */ | ||
76 | const char ERROR_DB_TEXT_NAME[] = "XRequest"; | ||
77 | |||
78 | /** Default name for unknown requests in X error handler. */ | ||
79 | const char REQUEST_NAME_UNKNOWN_MESSAGE[] = "<UNKNOWN>"; | ||
80 | |||
81 | /** How many microseconds to sleep before restarting the event loop. */ | ||
82 | const int SLEEP_TIME = 5000; | ||
83 | |||
84 | |||
85 | //--- CONSTRUCTORS AND DESTRUCTORS --------------------------------------------- | ||
86 | |||
87 | // The constructor. | ||
88 | Compositor::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. | ||
149 | Compositor::~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. | ||
161 | Window 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. | ||
178 | void 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. | ||
203 | void 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. | ||
242 | void 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. | ||
267 | void 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. | ||
383 | XRectangle 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. | ||
389 | int 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. | ||
408 | void 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). | ||
416 | int FbCompositor::ignoreXError(Display * /*display*/, XErrorEvent * /*error*/) { | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | // Custom X error handler (print, continue). | ||
421 | int 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 | } | ||