File indexing completed on 2024-04-21 04:20:22
0001 /******************************************************************** 0002 KolorServer - color server based on the X Color Management Specification 0003 This file is part of the KDE project. 0004 0005 Copyright (C) 2012 Casian Andrei <skeletk13@gmail.com> 0006 0007 Redistribution and use in source and binary forms, with or without 0008 modification, are permitted provided that the following conditions 0009 are met: 0010 0011 1. Redistributions of source code must retain the above copyright 0012 notice, this list of conditions and the following disclaimer. 0013 2. Redistributions in binary form must reproduce the above copyright 0014 notice, this list of conditions and the following disclaimer in the 0015 documentation and/or other materials provided with the distribution. 0016 0017 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 0018 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 0019 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 0020 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 0021 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 0022 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 0023 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 0024 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 0025 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 0026 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 0027 *********************************************************************/ 0028 0029 #include <KDebug> 0030 #include <QTimer> 0031 0032 #include <unistd.h> 0033 0034 #include "color-lookup-table.h" 0035 #include "screen.h" 0036 0037 #include "display.h" 0038 0039 #include <oyranos_devices.h> 0040 #include <X11/Xcm/XcmEvents.h> 0041 0042 namespace KolorServer 0043 { 0044 0045 static const int X11_EVENTS_POLL_INTERVAL = 4000; //ms 0046 0047 /* 0048 * Display 0049 */ 0050 0051 Display *s_display = 0; 0052 0053 Display *Display::getInstance() 0054 { 0055 if (!s_display) { 0056 s_display = new Display; 0057 s_display->initialize(); 0058 } 0059 return s_display; 0060 } 0061 0062 void Display::cleanup() 0063 { 0064 delete s_display; 0065 } 0066 0067 Display::Display() 0068 : m_screen(0) 0069 , m_xcmeContext(0) 0070 , m_colorDesktopActivated(true) 0071 , m_oyCache(0) 0072 { 0073 // Determine the display name 0074 QByteArray displayName = qgetenv("DISPLAY"); 0075 if (displayName.isEmpty()) 0076 displayName = ":0"; 0077 0078 // Open connection to the X Server 0079 m_display = X11::XOpenDisplay(displayName.constData()); 0080 if (!m_display) { 0081 kFatal() << "Cannot connect to X server" << displayName; 0082 activateColorDesktop(false); 0083 return; 0084 } 0085 } 0086 0087 Display::~Display() 0088 { 0089 if (!m_display) 0090 return; 0091 0092 // Remove desktop colour management service mark 0093 X11::changeProperty(m_display, iccColorDesktop, XA_STRING, (unsigned char*) NULL, 0); 0094 0095 // Delete default screen 0096 delete m_screen; 0097 0098 // Uninit X11 Events monitoring 0099 if (m_xcmeContext) 0100 X11::XcmeContext_Release(&m_xcmeContext); 0101 0102 // Delete cache 0103 oyStructList_Clear(m_oyCache); 0104 oyStructList_Release(&m_oyCache); 0105 0106 // Close connection to the X Sever 0107 X11::XCloseDisplay(m_display); 0108 } 0109 0110 void Display::initialize() 0111 { 0112 // Setup X11 event monitoring 0113 m_xcmeContext = X11::XcmeContext_New(); 0114 if (m_xcmeContext) { 0115 X11::XcmeContext_DisplaySet(m_xcmeContext, m_display); 0116 if (X11::XcmeContext_Setup(m_xcmeContext, "")) 0117 kWarning() << "Unable to setup X11 event monitor"; 0118 } else 0119 kWarning() << "Unable to create X11 event monitor"; 0120 0121 // Setup a timer for polling for X11 events 0122 QTimer *eventTimer = new QTimer(this); 0123 connect(eventTimer, SIGNAL(timeout()), this, SLOT(checkX11Events())); 0124 eventTimer->start(X11_EVENTS_POLL_INTERVAL); 0125 0126 iccColorProfiles = X11::XInternAtom(m_display, XCM_COLOR_PROFILES, False); 0127 iccColorRegions = X11::XInternAtom(m_display, XCM_COLOR_REGIONS, False); 0128 iccColorOutputs = X11::XInternAtom(m_display, XCM_COLOR_OUTPUTS, False); 0129 iccColorDesktop = X11::XInternAtom(m_display, XCM_COLOR_DESKTOP, False); 0130 netDesktopGeometry = X11::XInternAtom(m_display, "_NET_DESKTOP_GEOMETRY", False); 0131 iccDisplayAdvanced = X11::XInternAtom(m_display, XCM_COLOUR_DESKTOP_ADVANCED, False); 0132 0133 // Get the default screen 0134 int screenNumber = X11::defaultScreen(m_display); 0135 m_screen = new Screen(m_display, screenNumber, this); 0136 m_screen->setupOutputs(); 0137 m_screen->updateOutputConfiguration(true); 0138 0139 // Initialize Oyranos Cache 0140 m_oyCache = oyStructList_New(0); 0141 } 0142 0143 Screen * Display::screen() const 0144 { 0145 return m_screen; 0146 } 0147 0148 oyStructList_s* Display::cache() 0149 { 0150 return m_oyCache; 0151 } 0152 0153 bool Display::colorDesktopActivated() const 0154 { 0155 return m_colorDesktopActivated; 0156 } 0157 0158 void Display::activateColorDesktop (bool activate) 0159 { 0160 m_colorDesktopActivated = activate; 0161 } 0162 0163 void Display::clean() 0164 { 0165 kDebug(); 0166 0167 int error; 0168 oyOptions_s *options = 0; 0169 oyConfigs_s *devices = 0; 0170 0171 // Get display name 0172 QByteArray displayName = X11::XDisplayString(m_display); 0173 QByteArray t; 0174 if (displayName.contains('.')) 0175 displayName.resize(displayName.indexOf('.')); 0176 if (displayName.isEmpty()) 0177 displayName = ":0"; 0178 0179 // Clean up old displays 0180 error = oyOptions_SetFromText(&options, "//"OY_TYPE_STD"/config/command", "unset", OY_CREATE_NEW); 0181 for (int screen = 0; screen < 200; ++screen) 0182 { 0183 QByteArray screenName = displayName + "." + QByteArray::number(screen); 0184 error = oyOptions_SetFromText(&options, "//"OY_TYPE_STD"/config/device_name", 0185 screenName.data(), OY_CREATE_NEW); 0186 error = oyDevicesGet(OY_TYPE_STD, "monitor", options, &devices); 0187 if (error) 0188 break; 0189 oyConfigs_Release(&devices); 0190 } 0191 oyOptions_Release(&options); 0192 0193 0194 /* get number of connected devices */ 0195 error = oyOptions_SetFromText(&options, "//"OY_TYPE_STD"/config/command", "list", OY_CREATE_NEW); 0196 error = oyOptions_SetFromText(&options, "//"OY_TYPE_STD"/config/display_name", displayName.constData(), OY_CREATE_NEW); 0197 error = oyDevicesGet(OY_TYPE_STD, "monitor", options, &devices); 0198 oyConfigs_Release(&devices); 0199 oyOptions_Release(&options); 0200 0201 /* 0202 * Monitor hotplugs can easily mess up the ICC profile to device assigment. 0203 * So first we erase the _ICC_PROFILE(_xxx) to get a clean state. 0204 * We setup the EDID atoms and ICC profiles new. 0205 * The ICC profiles are moved to the right places through the 0206 * PropertyChange events recieved by the colour server. 0207 */ 0208 0209 // Refresh EDID 0210 QByteArray screen0Name = displayName + ".0"; 0211 error = oyOptions_SetFromText(&options, "//"OY_TYPE_STD"/config/command", "list", OY_CREATE_NEW); 0212 error = oyOptions_SetFromText(&options, "//"OY_TYPE_STD"/config/device_name", screen0Name.constData(), OY_CREATE_NEW); 0213 error = oyOptions_SetFromText(&options, "//"OY_TYPE_STD"/config/edid", "refresh", OY_CREATE_NEW); 0214 error = oyDevicesGet(OY_TYPE_STD, "monitor", options, &devices); 0215 oyConfigs_Release(&devices); 0216 oyOptions_Release(&options); 0217 } 0218 0219 int Display::updateNetColorDesktopAtom(bool init) 0220 { 0221 static time_t lastUpdateTime = 0; 0222 0223 enum status_e { 0224 statusOk = 0, 0225 statusInactive = 1, 0226 statusActive = 2, 0227 statusError = 3 0228 } status = statusOk; 0229 const QByteArray myID = "kolorserver"; 0230 const QByteArray myCaps = "|ICM|V0.4|"; // TODO add other capabilities? 0231 0232 if (!colorDesktopActivated()) 0233 return (int) statusInactive; 0234 0235 kDebug() << init; 0236 0237 X11::Window rootWindow = X11::rootWindow(m_display, 0); 0238 unsigned long n = 0; 0239 char *data = (char*) X11::fetchProperty(m_display, rootWindow, iccColorDesktop, XA_STRING, &n, False); 0240 0241 pid_t pid = getpid(); 0242 const char *oldData = 0; 0243 int oldPid = 0; 0244 long atomTime = 0; 0245 time_t currentTime = time(NULL); 0246 QByteArray colorServerAtomName(1024, '\0'); 0247 QByteArray capabilitiesAtomText(1024, '\0'); 0248 0249 if (n && data && strlen(data)) { 0250 sscanf((const char*) data, "%d %ld %s %s", 0251 &oldPid, 0252 &atomTime, 0253 capabilitiesAtomText.data(), 0254 colorServerAtomName.data()); 0255 oldData = data; 0256 } 0257 0258 const QByteArray otherSrv = oldData ? oldData : "????"; 0259 0260 if (n && data && oldPid != (int) pid) { 0261 if (oldData && currentTime - atomTime > 60) { 0262 kWarning() << "Found old _ICC_COLOR_DESKTOP:" << otherSrv; 0263 kWarning() << "Either there was a previous crash or your setup may be double color corrected"; 0264 } 0265 0266 // Check for taking over of colour service 0267 if (colorServerAtomName != myID) { 0268 if (atomTime < lastUpdateTime || init) { 0269 kDebug() << "Taking over color service from old _ICC_COLOR_DESKTOP:" << otherSrv; 0270 } else { 0271 if (atomTime > lastUpdateTime) { 0272 kDebug() << "Giving color service to _ICC_COLOR_DESKTOP:" << otherSrv; 0273 activateColorDesktop(false); 0274 } 0275 } 0276 } else { 0277 kDebug() << "Taking over color service from old _ICC_COLOR_DESKTOP:" << otherSrv; 0278 } 0279 } 0280 0281 if (lastUpdateTime - atomTime > 10 || init) { 0282 QByteArray newData(1024, '\0'); 0283 snprintf(newData.data(), 1024, "%d %ld %s %s", (int) pid, (long) currentTime, myCaps.constData(), myID.constData()); 0284 0285 // Set the colour management desktop service activity atom 0286 if (m_screen->profileCount() > 0) 0287 X11::changeProperty(m_display, iccColorDesktop, XA_STRING, (const unsigned char*) newData.constData(), newData.size()); 0288 else if (oldData) { 0289 /* switch off the plugin */ 0290 X11::changeProperty(m_display, iccColorDesktop, XA_STRING, (const unsigned char*) NULL, 0); 0291 activateColorDesktop(false); 0292 } 0293 } 0294 0295 lastUpdateTime = currentTime; 0296 0297 return status; 0298 } 0299 0300 bool Display::isAdvancedIccDisplay() 0301 { 0302 unsigned long nBytes; 0303 char *opt = 0; 0304 bool advanced = false; 0305 X11::Window rootWindow = X11::rootWindow(m_display, 0); 0306 0307 // Optionally set advanced options from Oyranos 0308 opt = (char*) X11::fetchProperty(m_display, rootWindow, iccDisplayAdvanced, XA_STRING, &nBytes, False); 0309 kDebug() << "iccDisplayAdvanced, nBytes:" << nBytes; 0310 if (opt && nBytes && atoi(opt) > 0) 0311 advanced = atoi(opt) != 0; 0312 if (opt) 0313 X11::XFree(opt); 0314 0315 return advanced; 0316 } 0317 0318 void Display::handleEvent(X11::XEvent* event) 0319 { 0320 const char *atomName = 0; 0321 0322 if (!colorDesktopActivated()) 0323 return; 0324 0325 switch (event->type) { 0326 case PropertyNotify: 0327 atomName = X11::XGetAtomName(event->xany.display, event->xproperty.atom); 0328 0329 if (event->xproperty.atom == iccColorProfiles) { 0330 kDebug() << "ICC Color Profiles atom changed"; 0331 m_screen->updateProfiles(); 0332 } else if (event->xproperty.atom == iccColorRegions) { 0333 kDebug() << "ICC Color Regions atom changed"; 0334 // CompWindow *w = findWindowAtDisplay(d, event->xproperty.window); 0335 // updateWindowRegions(w); 0336 // colour_desktop_region_count = -1; 0337 // TODO 0338 } else if (event->xproperty.atom == iccColorOutputs) { 0339 kDebug() << "ICC Color Outputs atom changed"; 0340 // CompWindow *w = findWindowAtDisplay(d, event->xproperty.window); 0341 // updateWindowOutput(w); 0342 // TODO 0343 } else if (event->xproperty.atom == iccColorDesktop && atomName) { 0344 // Possibly let others take over the colour server 0345 kDebug() << "ICC Color Desktop atom changed"; 0346 updateNetColorDesktopAtom(false); 0347 } else if (strstr(atomName, XCM_ICC_V0_3_TARGET_PROFILE_IN_X_BASE) != 0) { 0348 // Update for a changing monitor profile 0349 kDebug() << "ICC Output Profile atom changed"; 0350 m_screen->updateProfileForAtom(atomName, event->xproperty.atom); 0351 } else if (event->xproperty.atom == netDesktopGeometry) { 0352 // Update for changing geometry 0353 kDebug() << "Desktop geometry atom changed"; 0354 m_screen->setupOutputs(); 0355 m_screen->updateOutputConfiguration(true); 0356 } else if (event->xproperty.atom == iccDisplayAdvanced) { 0357 kDebug() << "ICC Display Advanced atom changed"; 0358 m_screen->updateOutputConfiguration(false); 0359 } 0360 0361 break; 0362 0363 case RRNotify: { 0364 X11::XRRNotifyEvent *rrn = (X11::XRRNotifyEvent *) event; 0365 if (rrn->subtype == RRNotify_OutputChange) { 0366 kDebug() << "XRandR outputs changed"; 0367 m_screen->setupOutputs(); 0368 m_screen->updateOutputConfiguration(true); 0369 } 0370 break; 0371 } 0372 0373 default: 0374 break; 0375 } 0376 } 0377 0378 void Display::checkX11Events() 0379 { 0380 X11::XEvent event; 0381 long eventMask = ExposureMask | PropertyChangeMask; 0382 0383 while (X11::XCheckMaskEvent(m_display, eventMask, &event) == True) { 0384 X11::XcmeContext_InLoop(m_xcmeContext, &event); 0385 handleEvent(&event); 0386 } 0387 } 0388 0389 0390 } // KolorServer namespace 0391 0392 #include "moc_display.cpp"