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 
0031 #include "color-lookup-table.h"
0032 #include "display.h"
0033 #include "output.h"
0034 #include "screen.h"
0035 
0036 #include <oyranos_devices.h>
0037 #include <oyFilterNode_s.h>
0038 #include <oyRectangle_s.h>
0039 
0040 namespace KolorServer
0041 {
0042 
0043 /*
0044  * Color Output
0045  */
0046 
0047 ColorOutput::ColorOutput(Screen *parent, int index)
0048     : m_parent(parent)
0049     , m_index(index)
0050 {
0051     /* select profiles matching actual capabilities */
0052     icc_profile_flags = oyICCProfileSelectionFlagsFromOptions( OY_CMM_STD, "//" OY_TYPE_STD "/icc_color", NULL, 0 );
0053 }
0054 
0055 ColorOutput::~ColorOutput()
0056 {
0057     if (profile()) {
0058         // Move profile atoms back
0059         moveProfileAtoms(false);
0060     }
0061 
0062     // Clean profile atoms
0063     if (hasProfileAtom())
0064         cleanProfileAtom();
0065 }
0066 
0067 oyProfile_s *ColorOutput::profile()
0068 {
0069     return m_cc.destinationProfile();
0070 }
0071 
0072 void ColorOutput::setProfile(oyProfile_s* profile)
0073 {
0074     m_cc.setDestinationProfile(profile);
0075 }
0076 
0077 const QString& ColorOutput::name()
0078 {
0079     return m_name;
0080 }
0081 
0082 const Clut& ColorOutput::colorLookupTable() const
0083 {
0084     return m_cc.colorLookupTable();
0085 }
0086 
0087 bool ColorOutput::hasProfileAtom()
0088 {
0089     X11::Display *display = m_parent->display();
0090     X11::Window rootWindow = m_parent->rootWindow();
0091     X11::Atom a = iccProfileAtom(display, m_index, true);
0092     long unsigned int n;
0093 
0094     void *data = X11::fetchProperty(display, rootWindow, a, XA_CARDINAL, &n, False);
0095     if (data)
0096         X11::XFree(data);
0097 
0098     return n != 0;
0099 }
0100 
0101 void ColorOutput::cleanProfileAtom()
0102 {
0103     kDebug();
0104     X11::Display *display = m_parent->display();
0105     X11::Atom a = iccProfileAtom(display, m_index, true);
0106     X11::XFlush(display);
0107     X11::deleteProperty(display, a);
0108 }
0109 
0110 void ColorOutput::updateConfiguration(oyConfig_s *device, bool init)
0111 {
0112     kDebug() << device << init;
0113 
0114     if (init) {
0115         if (!m_cc.getDeviceProfile(device))
0116             kError() << "Unable to get device profile";
0117     }
0118 
0119     if (profile()) {
0120         moveProfileAtoms(true); // TODO really true?
0121     } else {
0122         kDebug() << "No profile found for output" << m_index << m_name;
0123     }
0124 
0125     setup();
0126 }
0127 
0128 X11::Atom ColorOutput::iccProfileAtom(X11::Display *display, int num, bool forServer)
0129 {
0130     QByteArray atomName;
0131 
0132     if (forServer)
0133         atomName = XCM_DEVICE_PROFILE;
0134     else
0135         atomName = XCM_ICC_V0_3_TARGET_PROFILE_IN_X_BASE;
0136 
0137     if (num) {
0138         atomName += "_";
0139         atomName += QByteArray::number(num);
0140     }
0141 
0142     return X11::XInternAtom(display, atomName.constData(), False);
0143 }
0144 
0145 bool ColorOutput::getDeviceProfile(oyConfig_s *device)
0146 {
0147     kDebug() << device;
0148 
0149     oyOption_s *o = 0;
0150     oyRectangle_s *r = 0;
0151     const char *device_name = 0;
0152     int error = 0;
0153 
0154     o = oyConfig_Find(device, "device_rectangle");
0155     if (!o) {
0156         kWarning() << "Request for monitor rectangle failed";
0157         return false;
0158     }
0159     r = (oyRectangle_s*) oyOption_GetStruct(o, oyOBJECT_RECTANGLE_S);
0160     if (!r) {
0161         kWarning() << "Request for monitor rectangle failed";
0162         return false;
0163     }
0164     oyOption_Release(&o);
0165 
0166     m_rect = QRect(oyRectangle_GetGeo1( r, 0),
0167                    oyRectangle_GetGeo1( r, 1),
0168                    oyRectangle_GetGeo1( r, 2),
0169                    oyRectangle_GetGeo1( r, 3));
0170 
0171     device_name = oyConfig_FindString(device, "device_name", 0);
0172     if (device_name && device_name[0]) {
0173         m_name = device_name;
0174     } else {
0175         kWarning() << "Unable to get device name";
0176         m_name = QString::number(m_parent->screenNumber());
0177     }
0178 
0179     error = m_cc.getDeviceProfile(device);
0180 
0181     return !error;
0182 }
0183 
0184 void ColorOutput::setup()
0185 {
0186     kDebug();
0187     m_cc.setup(m_name);
0188 }
0189 
0190 /*
0191 The atom will hold a native ICC profile with the exposed device
0192 characteristics at the compositing window manager level.
0193 The colour server shall if no _ICC_DEVICE_PROFILE(_xxx) is set, copy the
0194 _ICC_PROFILE(_xxx) profiles to each equivalent _ICC_DEVICE_PROFILE(_xxx) atom.
0195 The _ICC_PROFILE(_xxx) profiles shall be replaced by a sRGB ICC profile.
0196 The counting in the atoms (_xxx) name section follows the rules outlined in
0197 the ICC Profile in X recommendation. After finishing the session the old
0198 state has to be recovered by copying any _ICC_DEVICE_PROFILE(_xxx) atoms
0199 content into the appropriate _ICC_PROFILE(_xxx) atoms and removing all
0200 _ICC_DEVICE_PROFILE(_xxx) atoms.
0201 The colour server must be aware about change property events indicating that
0202 a _ICC_PROFILE(_xxx) atom has changed by a external application and needs to
0203 move that profile to the appropriate _ICC_DEVICE_PROFILE(_xxx) atom and set
0204 the _ICC_PROFILE(_xxx) atom to sRGB as well.
0205  */
0206 void ColorOutput::moveProfileAtoms(bool init)
0207 {
0208     kDebug() << "init: " << init;
0209 
0210     X11::Display *display = m_parent->display();
0211     X11::Window rootWindow = m_parent->rootWindow();
0212     X11::Atom a1, a2, sourceAtom, targetAtom;
0213 
0214     a1 = iccProfileAtom(display, m_index, false);
0215     a2 = iccProfileAtom(display, m_index, true);
0216 
0217     // Select the atoms (source -> target)
0218     if (init) {
0219         sourceAtom = a1;
0220         targetAtom = a2;
0221     } else {
0222         sourceAtom = a2;
0223         targetAtom = a1;
0224     }
0225 
0226     void *source;
0227     unsigned long sourceSize = 0, targetSize = 0;
0228     bool sourceExists = false, targetExists = false;
0229     bool updatedColorDesktopAtom = false;
0230 
0231     X11::fetchProperty(display, rootWindow, targetAtom, XA_CARDINAL, &targetSize, False);
0232     targetExists = targetSize > 0;
0233 
0234     if (!targetExists ||
0235         ( targetExists && !init)) {
0236         // Copy the real device atom
0237         source = X11::fetchProperty(display, rootWindow, sourceAtom, XA_CARDINAL, &sourceSize, False);
0238         sourceExists = sourceSize > 0;
0239 
0240         // _ICC_COLOR_DESKTOP atom is set before any _ICC_PROFILE(_xxx) changes.
0241         if (init) {
0242             Display::getInstance()->updateNetColorDesktopAtom(true);
0243             updatedColorDesktopAtom = true;
0244         }
0245 
0246         if (sourceExists)
0247             X11::changeProperty(display, targetAtom, XA_CARDINAL, (const unsigned char *) source, sourceSize);
0248         if (source)
0249             X11::XFree(source);
0250         source = 0;
0251         sourceSize = 0;
0252         sourceExists = false;
0253 
0254         if (init) {
0255             /* setup the OY_ICC_V0_3_TARGET_PROFILE_IN_X_BASE(_xxx) atom as document colour space */
0256             size_t size = 0;
0257             oyProfile_s *screenDocumentProfile = oyProfile_FromStd(oyASSUMED_WEB, icc_profile_flags, 0);
0258 
0259             if (screenDocumentProfile) {
0260                 // Make sure the profile is ignored
0261                 source = oyProfile_GetMem(screenDocumentProfile, &size, 0, malloc);
0262                 oyProfile_Release(&screenDocumentProfile);
0263                 sourceSize = size;
0264                 sourceExists = sourceSize > 0;
0265             } else {
0266                 kWarning() << "Could not get oyASSUMED_WEB profile";
0267                 source = 0;
0268                 sourceSize = 0;
0269                 sourceExists = false;
0270             }
0271 
0272             if (!updatedColorDesktopAtom) {
0273                 Display::getInstance()->updateNetColorDesktopAtom(true); // FIXME or false?
0274                 updatedColorDesktopAtom = true;
0275             }
0276 
0277             if (sourceExists)
0278                 X11::changeProperty(display, sourceAtom, XA_CARDINAL, (const unsigned char *) source, sourceSize);
0279             if (source)
0280                 free(source);
0281             source = 0;
0282             sourceSize = 0;
0283             sourceExists = false;
0284         } else {
0285             // Clear / erase the _ICC_DEVICE_PROFILE(_xxx) atom
0286             X11::deleteProperty(display, sourceAtom);
0287         }
0288     } else
0289         if (targetAtom && init)
0290             kWarning() << "ICC Color Server Atom already present:" << targetAtom << "size" << targetSize;
0291 }
0292 
0293 } // KolorServer namespace