diff options
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 | } | ||