File indexing completed on 2024-04-28 12:07:01
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 <QApplication> 0031 #include <QDesktopWidget> 0032 0033 #include "screen.h" 0034 0035 #include "display.h" 0036 #include "output.h" 0037 0038 #include <oyranos_devices.h> 0039 #include <oyFilterNode_s.h> 0040 0041 /** 0042 * Helper function to convert a MD5 into a readable string. 0043 */ 0044 static const char *md5string(const uint8_t md5[16]) 0045 { 0046 static char buffer[33] = { 0 }; 0047 const uint32_t *h = (const uint32_t*) md5; 0048 0049 snprintf(buffer, 33, "%x%x%x%x", h[0], h[1], h[2], h[3]); 0050 0051 return buffer; 0052 } 0053 0054 0055 namespace KolorServer 0056 { 0057 0058 /* 0059 * Screen 0060 */ 0061 0062 Screen::Screen(X11::Display *display, int number, Display *parent) 0063 : QObject(parent) 0064 , m_parent(parent) 0065 , m_display(display) 0066 , m_screen(number) 0067 { 0068 X11::setupXRandR(display, number); 0069 0070 /* select profiles matching actual capabilities */ 0071 icc_profile_flags = oyICCProfileSelectionFlagsFromOptions( OY_CMM_STD, "//" OY_TYPE_STD "/icc_color", NULL, 0 ); 0072 } 0073 0074 Screen::~Screen() 0075 { 0076 cleanOutputs(); 0077 } 0078 0079 X11::Display *Screen::display() const 0080 { 0081 return m_display; 0082 } 0083 0084 int Screen::screenNumber() const 0085 { 0086 return m_screen; 0087 } 0088 0089 X11::Window Screen::rootWindow() const 0090 { 0091 return X11::rootWindow(m_display, m_screen); 0092 } 0093 0094 const ClutList& Screen::outputCluts() const 0095 { 0096 return m_outputCluts; 0097 } 0098 0099 const RegionalClutMap& Screen::regionCluts() const 0100 { 0101 return m_regionCluts; 0102 } 0103 0104 void Screen::cleanProfileAtoms() 0105 { 0106 kDebug(); 0107 for (int i = 0; i < m_outputs.size(); ++i) 0108 m_outputs[i]->cleanProfileAtom(); 0109 } 0110 0111 void Screen::cleanOutputs() 0112 { 0113 kDebug(); 0114 for (int i = 0; i < m_outputs.size(); ++i) 0115 delete m_outputs[i]; 0116 m_outputs.clear(); 0117 m_outputCluts.clear(); 0118 } 0119 0120 void Screen::setupOutputs() 0121 { 0122 kDebug(); 0123 0124 // Cleanup old outputs 0125 cleanOutputs(); 0126 0127 int n = QApplication::desktop()->screenCount(); 0128 m_outputs.clear(); 0129 for (int i = 0; i < n; ++i) 0130 m_outputs << new ColorOutput(this, i); 0131 0132 // Allow Oyranos to see modifications made to the process's Xlib context 0133 X11::XFlush(m_display); 0134 0135 m_parent->clean(); // does not seem to be too nice 0136 } 0137 0138 void Screen::updateOutputConfiguration(bool init) 0139 { 0140 kDebug() << init; 0141 0142 int error; 0143 oyOptions_s *options = 0; 0144 oyConfigs_s *devices = 0; 0145 oyConfig_s *device = 0; 0146 0147 // Allow Oyranos to see modifications made to the process's Xlib context 0148 X11::XFlush(m_display); 0149 0150 /* 0151 * Obtain device information, including geometry and ICC profiles 0152 * from the according Oyranos module 0153 */ 0154 oyOptions_SetFromText(&options, "//" OY_TYPE_STD "/config/command", "list", OY_CREATE_NEW); 0155 oyOptions_SetFromText(&options, "//" OY_TYPE_STD "/config/device_rectangle", "true", OY_CREATE_NEW); 0156 error = oyDevicesGet(OY_TYPE_STD, "monitor", options, &devices); 0157 if(error > 0) 0158 kError() << "Unable to get devices, error" << error; 0159 oyOptions_Release(&options); 0160 0161 if (Display::getInstance()->colorDesktopActivated()) { 0162 for (int i = 0; i < m_outputs.size(); ++i) { 0163 device = oyConfigs_Get(devices, i); 0164 m_outputs[i]->updateConfiguration(device, init); 0165 oyConfig_Release(&device); 0166 } 0167 } 0168 0169 oyConfigs_Release(&devices); 0170 0171 // Recreate output clut list 0172 m_outputCluts.clear(); 0173 for (int i = 0; i < m_outputs.size(); ++i) 0174 m_outputCluts << m_outputs[i]->colorLookupTable(); 0175 0176 emit outputClutsChanged(); 0177 } 0178 0179 void Screen::updateProfiles() 0180 { 0181 kDebug(); 0182 0183 /* Fetch the profiles */ 0184 unsigned long nBytes; 0185 int screen = X11::defaultScreen(m_display); 0186 X11::Window rootWindow = X11::rootWindow(m_display, screen); 0187 void *data = X11::fetchProperty(m_display, rootWindow, m_parent->iccColorProfiles, XA_CARDINAL, &nBytes, True); 0188 if (!data) { 0189 kDebug() << "No profiles from the applications to the color server"; 0190 return; 0191 } 0192 0193 uint32_t exact_hash_size = 0; 0194 oyHash_s *entry; 0195 oyProfile_s *oyProfile = NULL; 0196 oyStructList_s *cache = Display::getInstance()->cache(); 0197 int n = 0; 0198 0199 // Grow or shrink the array as needed 0200 unsigned long count = X11::XcolorProfileCount(data, nBytes); 0201 0202 // Copy the profiles into the array, and create the Oyranos handles 0203 XcolorProfile *xcmProfile = (XcolorProfile*) data; 0204 for (unsigned long i = 0; i < count; ++i) { 0205 const char *hash_text = md5string(xcmProfile->md5); 0206 entry = oyStructList_GetHash(cache, exact_hash_size, hash_text); 0207 oyProfile = (oyProfile_s *) oyHash_GetPointer(entry, oyOBJECT_PROFILE_S); 0208 0209 /* XcolorProfile::length == 0 means the clients wants to delete the xcmProfile. */ 0210 if (ntohl(xcmProfile->length)) { 0211 if (!oyProfile) { 0212 // Not found in cache, get from the data 0213 oyProfile = oyProfile_FromMem(htonl(xcmProfile->length), xcmProfile + 1, 0, NULL); 0214 if (!oyProfile) { 0215 /* 0216 * If creating the Oyranos xcmProfile fails, don't try to parse 0217 * any further profiles and just quit. 0218 */ 0219 kWarning() << "Could not create Oyranos xcmProfile" << hash_text; 0220 goto updateProfiles_out; 0221 } 0222 0223 oyHash_SetPointer(entry, (oyStruct_s*) oyProfile); 0224 ++ n; 0225 } 0226 } 0227 0228 xcmProfile = X11::XcolorProfileNext(xcmProfile); 0229 } 0230 0231 kDebug() << "Added" << n << "of" << count << "screen profiles"; 0232 0233 updateProfiles_out: 0234 X11::XFree(data); 0235 } 0236 0237 void Screen::updateProfileForAtom(const char *atomName, X11::Atom atom) 0238 { 0239 int screen = 0; 0240 bool ignoreProfile = false; 0241 unsigned long n = 0; 0242 0243 Atom csAtom; 0244 QByteArray colorServerProfileAtom = XCM_DEVICE_PROFILE; 0245 if (strlen(atomName) > (size_t) strlen(XCM_ICC_V0_3_TARGET_PROFILE_IN_X_BASE) + 1) 0246 sscanf((const char*) atomName, XCM_ICC_V0_3_TARGET_PROFILE_IN_X_BASE"_%d", &screen); 0247 if (screen) { 0248 colorServerProfileAtom += "_"; 0249 colorServerProfileAtom += QByteArray::number(screen); 0250 } 0251 0252 csAtom = X11::XInternAtom(m_display, colorServerProfileAtom.constData(), False); 0253 if (csAtom) { 0254 void *data = X11::fetchProperty(m_display, X11::rootWindow(m_display, 0), atom, XA_CARDINAL, &n, False); 0255 if (data && n) { 0256 oyProfile_s *baseProfile = oyProfile_FromMem(n, data, 0,0); 0257 oyProfile_s *dummyProfile = oyProfile_FromStd(oyASSUMED_WEB, icc_profile_flags, 0); // sRGB 0258 0259 /* The distinction of sRGB profiles set by the server and ones 0260 * coming from outside the colour server is rather fragile. 0261 * So we ignore any sRGB profiles set into _ICC_PROFILE(_xxx). 0262 * The correct way to omit colour correction is to tag window 0263 * regions. As a last resort the colour server can be switched off. 0264 */ 0265 if (oyProfile_Equal(baseProfile, dummyProfile)) { 0266 oyProfile_Release(&baseProfile); 0267 ignoreProfile = true; 0268 } 0269 oyProfile_Release(&dummyProfile); 0270 0271 if (baseProfile) { 0272 if (screen < m_outputs.count()) { 0273 m_outputs[screen]->setProfile(baseProfile); 0274 } else 0275 kWarning() << "Contexts not ready for screen" << screen; 0276 0277 X11::changeProperty(m_display, csAtom, XA_CARDINAL, (unsigned char *) 0, 0); 0278 } 0279 0280 baseProfile = 0; 0281 X11::XFree(data); 0282 } 0283 } 0284 0285 // Change only existing profiles, ignore removed ones 0286 if (!ignoreProfile && n) { 0287 updateOutputConfiguration(false); 0288 } 0289 } 0290 0291 int Screen::profileCount() const 0292 { 0293 int c = 0; 0294 for (int i = 0; i < m_outputs.size(); ++i) 0295 if (m_outputs[i]->profile()) 0296 c ++; 0297 return c; 0298 } 0299 0300 } // KolorServer namespace 0301 0302 #include "moc_screen.cpp" 0303