File indexing completed on 2024-04-14 04:16:23

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