Code

Store cached icons to disk between runs, and invalidate/purge as needed.
[inkscape.git] / src / ege-color-prof-tracker.cpp
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  */
4 /* ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is EGE Color Profile Tracker.
18  *
19  * The Initial Developer of the Original Code is
20  * Jon A. Cruz.
21  * Portions created by the Initial Developer are Copyright (C) 2007
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either the GNU General Public License Version 2 or later (the "GPL"), or
28  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 /* Note: this file should be kept compilable as both .cpp and .c */
42 #include <string.h>
44 #include <gtk/gtkwidget.h>
45 #include <gtk/gtkwindow.h>
46 #include <gtk/gtkmarshal.h>
48 #ifdef GDK_WINDOWING_X11
49 #include <X11/Xlib.h>
50 #include <X11/Xatom.h>
52 #include <gdk/gdkx.h>
53 #endif /* GDK_WINDOWING_X11 */
55 #include "ege-color-prof-tracker.h"
57 /*
58 #define GDK_ROOT_WINDOW()             (gdk_x11_get_default_root_xwindow ())
59 #define GDK_DISPLAY()                 gdk_display
60 #define             GDK_WINDOW_XDISPLAY(win)
61 #define             GDK_WINDOW_XID(win)
62 #define             GDK_DISPLAY_XDISPLAY(display)
63 #define             GDK_SCREEN_XDISPLAY(screen)
64 #define             GDK_SCREEN_XNUMBER(screen)
65 #define             GDK_SCREEN_XSCREEN(screen)
67 #define             GDK_WINDOW_XWINDOW
68 #define             GDK_DRAWABLE_XID(win)
70 GdkWindow*          gdk_window_lookup                   (GdkNativeWindow anid);
71 GdkWindow*          gdk_window_lookup_for_display       (GdkDisplay *display,
72                                                          GdkNativeWindow anid);
74 GdkDisplay*         gdk_x11_lookup_xdisplay             (Display *xdisplay);
76 Display*            gdk_x11_display_get_xdisplay        (GdkDisplay *display);
78 Window              gdk_x11_get_default_root_xwindow    (void);
79 gint                gdk_x11_get_default_screen          (void);
80 Display*            gdk_x11_get_default_xdisplay        (void);
81 int                 gdk_x11_screen_get_screen_number    (GdkScreen *screen);
82 Screen*             gdk_x11_screen_get_xscreen          (GdkScreen *screen);
84 const gchar*        gdk_x11_get_xatom_name              (Atom xatom);
85 const gchar*        gdk_x11_get_xatom_name_for_display  (GdkDisplay *display,
86                                                          Atom xatom);
87  */
89 enum {
90     CHANGED = 0,
91     ADDED,
92     REMOVED,
93     MODIFIED,
94     LAST_SIGNAL};
97 static void ege_color_prof_tracker_class_init( EgeColorProfTrackerClass* klass );
98 static void ege_color_prof_tracker_init( EgeColorProfTracker* tracker );
99 static void ege_color_prof_tracker_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec );
100 static void ege_color_prof_tracker_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec );
102 typedef struct _ScreenTrack {
103     GdkScreen* screen;
104 #ifdef GDK_WINDOWING_X11
105     gboolean zeroSeen;
106     gboolean otherSeen;
107 #endif /* GDK_WINDOWING_X11 */
108     GSList* trackers;
109     GPtrArray* profiles;
110 } ScreenTrack;
112 #ifdef GDK_WINDOWING_X11
113 GdkFilterReturn x11_win_filter(GdkXEvent *xevent, GdkEvent *event, gpointer data);
114 void handle_property_change(GdkScreen* screen, const gchar* name);
115 void add_x11_tracking_for_screen(GdkScreen* screen, ScreenTrack* screenTrack);
116 #endif /* GDK_WINDOWING_X11 */
118 static GObjectClass* gParentClass = 0;
119 static guint signals[LAST_SIGNAL] = {0};
121 static GSList* tracked_screens = 0;
122 static GSList* abstract_trackers = 0;
124 struct _EgeColorProfTrackerPrivate
126     GtkWidget* _target;
127     gint _monitor;
128 };
130 #define EGE_GET_PRIVATE( o ) ( G_TYPE_INSTANCE_GET_PRIVATE( (o), EGE_COLOR_PROF_TRACKER_TYPE, EgeColorProfTrackerPrivate ) )
133 static void target_finalized( gpointer data, GObject* where_the_object_was );
134 static void window_finalized( gpointer data, GObject* where_the_object_was );
135 static void event_after_cb( GtkWidget* widget, GdkEvent* event, gpointer user_data );
136 static void target_hierarchy_changed_cb(GtkWidget* widget, GtkWidget* prev_top, gpointer user_data);
137 static void target_screen_changed_cb(GtkWidget* widget, GdkScreen* prev_screen, gpointer user_data);
138 static void screen_size_changed_cb(GdkScreen* screen, gpointer user_data);
139 static void fire(GdkScreen* screen, gint monitor);
140 static void clear_profile( GdkScreen* screen, guint monitor );
141 static void set_profile( GdkScreen* screen, guint monitor, const guint8* data, guint len );
142 static void track_screen( GdkScreen* screen, EgeColorProfTracker* tracker );
144 GType ege_color_prof_tracker_get_type( void )
146     static GType myType = 0;
147     if ( !myType ) {
148         static const GTypeInfo myInfo = {
149             sizeof( EgeColorProfTrackerClass ),
150             NULL, /* base_init */
151             NULL, /* base_finalize */
152             (GClassInitFunc)ege_color_prof_tracker_class_init,
153             NULL, /* class_finalize */
154             NULL, /* class_data */
155             sizeof( EgeColorProfTracker ),
156             0, /* n_preallocs */
157             (GInstanceInitFunc)ege_color_prof_tracker_init,
158             NULL
159         };
161         myType = g_type_register_static( G_TYPE_OBJECT, "EgeColorProfTracker", &myInfo, (GTypeFlags)0 );
162     }
164     return myType;
167 void ege_color_prof_tracker_class_init( EgeColorProfTrackerClass* klass )
169     if ( klass ) {
170         gParentClass = G_OBJECT_CLASS( g_type_class_peek_parent( klass ) );
171         GObjectClass* objClass = G_OBJECT_CLASS( klass );
173         objClass->get_property = ege_color_prof_tracker_get_property;
174         objClass->set_property = ege_color_prof_tracker_set_property;
175         klass->changed = 0;
177         signals[CHANGED] = g_signal_new( "changed",
178                                          G_TYPE_FROM_CLASS(klass),
179                                          G_SIGNAL_RUN_FIRST,
180                                          G_STRUCT_OFFSET(EgeColorProfTrackerClass, changed),
181                                          NULL, NULL,
182                                          g_cclosure_marshal_VOID__VOID,
183                                          G_TYPE_NONE, 0 );
185         signals[ADDED] = g_signal_new( "added",
186                                        G_TYPE_FROM_CLASS(klass),
187                                        G_SIGNAL_RUN_FIRST,
188                                        0,
189                                        NULL, NULL,
190                                        gtk_marshal_VOID__INT_INT,
191                                        G_TYPE_NONE, 2,
192                                        G_TYPE_INT,
193                                        G_TYPE_INT);
195         signals[REMOVED] = g_signal_new( "removed",
196                                          G_TYPE_FROM_CLASS(klass),
197                                          G_SIGNAL_RUN_FIRST,
198                                          0,
199                                          NULL, NULL,
200                                          gtk_marshal_VOID__INT_INT,
201                                          G_TYPE_NONE, 2,
202                                          G_TYPE_INT,
203                                          G_TYPE_INT);
205         signals[MODIFIED] = g_signal_new( "modified",
206                                           G_TYPE_FROM_CLASS(klass),
207                                           G_SIGNAL_RUN_FIRST,
208                                           0,
209                                           NULL, NULL,
210                                           gtk_marshal_VOID__INT_INT,
211                                           G_TYPE_NONE, 2,
212                                           G_TYPE_INT,
213                                           G_TYPE_INT);
215         g_type_class_add_private( klass, sizeof(EgeColorProfTrackerClass) );
216     }
220 void ege_color_prof_tracker_init( EgeColorProfTracker* tracker )
222     tracker->private_data = EGE_GET_PRIVATE( tracker );
223     tracker->private_data->_target = 0;
224     tracker->private_data->_monitor = 0;
227 EgeColorProfTracker* ege_color_prof_tracker_new( GtkWidget* target )
229     GObject* obj = (GObject*)g_object_new( EGE_COLOR_PROF_TRACKER_TYPE,
230                                            NULL );
232     EgeColorProfTracker* tracker = EGE_COLOR_PROF_TRACKER( obj );
233     tracker->private_data->_target = target;
235     if ( target ) {
236         g_object_weak_ref( G_OBJECT(target), target_finalized, obj );
237         g_signal_connect( G_OBJECT(target), "hierarchy-changed", G_CALLBACK( target_hierarchy_changed_cb ), obj );
238         g_signal_connect( G_OBJECT(target), "screen-changed", G_CALLBACK( target_screen_changed_cb ), obj );
240         /* invoke the callbacks now to connect if the widget is already visible */
241         target_hierarchy_changed_cb( target, 0, obj );
242         target_screen_changed_cb( target, 0, obj );
243     } else {
244         abstract_trackers = g_slist_append( abstract_trackers, obj );
246         GSList* curr = tracked_screens;
247         while ( curr ) {
248             ScreenTrack* track = (ScreenTrack*)curr->data;
249             gint screenNum = gdk_screen_get_number(track->screen);
250             gint monitor = 0;
251             for ( monitor = 0; monitor < (gint)track->profiles->len; monitor++ ) {
252                 g_signal_emit( G_OBJECT(tracker), signals[MODIFIED], 0, screenNum, monitor );
253             }
255             curr = g_slist_next(curr);
256         }
258     }
260     return tracker;
263 void ege_color_prof_tracker_get_profile( EgeColorProfTracker const * tracker, gpointer* ptr, guint* len )
265     gpointer dataPos = 0;
266     guint dataLen = 0;
267     if ( tracker && tracker->private_data->_target ) {
268         GdkScreen* screen = gtk_widget_get_screen(tracker->private_data->_target);
269         GSList* curr = tracked_screens;
270         while ( curr ) {
271             ScreenTrack* screenTrack = (ScreenTrack*)curr->data;
272             if ( screenTrack->screen == screen ) {
273                 if ( tracker->private_data->_monitor >= 0 && tracker->private_data->_monitor < (gint)screenTrack->profiles->len ) {
274                     GByteArray* gba = (GByteArray*)g_ptr_array_index( screenTrack->profiles, tracker->private_data->_monitor );
275                     if ( gba ) {
276                         dataPos = gba->data;
277                         dataLen = gba->len;
278                     }
279                 } else {
280                     g_warning("No profile data tracked for the specified item.");
281                 }
282                 break;
283             }
284             curr = g_slist_next(curr);
285         }
287     }
288     if ( ptr ) {
289         *ptr = dataPos;
290     }
291     if ( len ) {
292         *len = dataLen;
293     }
296 void ege_color_prof_tracker_get_profile_for( guint screenNum, guint monitor, gpointer* ptr, guint* len )
298     gpointer dataPos = 0;
299     guint dataLen = 0;
300     GdkDisplay* display = gdk_display_get_default();
301     gint numScreens = gdk_display_get_n_screens(display);
302     GdkScreen* screen = (screenNum < (guint)numScreens) ? gdk_display_get_screen(display, screenNum) : 0;
304     if ( screen ) {
305         GSList* curr = tracked_screens;
306         while ( curr ) {
307             ScreenTrack* screenTrack = (ScreenTrack*)curr->data;
308             if ( screenTrack->screen == screen ) {
309                 if ( monitor < screenTrack->profiles->len ) {
310                     GByteArray* gba = (GByteArray*)g_ptr_array_index( screenTrack->profiles, monitor );
311                     if ( gba ) {
312                         dataPos = gba->data;
313                         dataLen = gba->len;
314                     }
315                 } else {
316                     g_warning("No profile data tracked for the specified item.");
317                 }
318                 break;
319             }
321             curr = g_slist_next(curr);
322         }
323     }
325     if ( ptr ) {
326         *ptr = dataPos;
327     }
328     if ( len ) {
329         *len = dataLen;
330     }
333 void ege_color_prof_tracker_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec )
335     EgeColorProfTracker* tracker = EGE_COLOR_PROF_TRACKER( obj );
336     (void)tracker;
337     (void)value;
339     switch ( propId ) {
340         default:
341             G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
342     }
345 void ege_color_prof_tracker_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec )
347     EgeColorProfTracker* tracker = EGE_COLOR_PROF_TRACKER( obj );
348     (void)tracker;
349     (void)value;
350     switch ( propId ) {
351         default:
352             G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
353     }
357 void track_screen( GdkScreen* screen, EgeColorProfTracker* tracker )
359     GSList* curr = tracked_screens;
360     /* First remove the tracker from different screens */
361     while ( curr ) {
362         ScreenTrack* screenTrack = (ScreenTrack*)curr->data;
363         if ( screenTrack->screen != screen ) {
364             screenTrack->trackers = g_slist_remove_all(screenTrack->trackers, tracker);
365         }
366         curr = g_slist_next(curr);
367     }
369     curr = tracked_screens;
370     while ( curr && ((ScreenTrack*)curr->data)->screen != screen ) {
371         curr = g_slist_next(curr);
372     }
374     if ( curr ) {
375         /* We found the screen already being tracked */
376         ScreenTrack* screenTrack = (ScreenTrack*)curr->data;
377         GSList* trackHook = g_slist_find( screenTrack->trackers, tracker );
378         if ( !trackHook ) {
379             screenTrack->trackers = g_slist_append( screenTrack->trackers, tracker );
380         }
381     } else {
382         ScreenTrack* newTrack = g_new(ScreenTrack, 1);
383         gint numMonitors = gdk_screen_get_n_monitors(screen);
384         int i = 0;
385         newTrack->screen = screen;
386 #ifdef GDK_WINDOWING_X11
387         newTrack->zeroSeen = FALSE;
388         newTrack->otherSeen = FALSE;
389 #endif /* GDK_WINDOWING_X11 */
390         newTrack->trackers = g_slist_append( 0, tracker );
391         newTrack->profiles = g_ptr_array_new();
392         for ( i = 0; i < numMonitors; i++ ) {
393             g_ptr_array_add( newTrack->profiles, 0 );
394         }
395         tracked_screens = g_slist_append( tracked_screens, newTrack );
397         g_signal_connect( G_OBJECT(screen), "size-changed", G_CALLBACK( screen_size_changed_cb ), tracker );
399 #ifdef GDK_WINDOWING_X11
400         add_x11_tracking_for_screen(screen, newTrack);
401 #endif // GDK_WINDOWING_X11
402     }
406 void target_finalized( gpointer data, GObject* where_the_object_was )
408     (void)data;
409     GSList* curr = tracked_screens;
410     while ( curr ) {
411         ScreenTrack* track = (ScreenTrack*)curr->data;
412         GSList* trackHook = track->trackers;
413         while ( trackHook ) {
414             if ( (void*)(((EgeColorProfTracker*)(trackHook->data))->private_data->_target) == (void*)where_the_object_was ) {
415                 /* The tracked widget is now gone, remove it */
416                 ((EgeColorProfTracker*)trackHook->data)->private_data->_target = 0;
417                 track->trackers = g_slist_remove( track->trackers, trackHook );
418                 trackHook = 0;
419             } else {
420                 trackHook = g_slist_next( trackHook );
421             }
422         }
424         curr = g_slist_next( curr );
425     }
428 void window_finalized( gpointer data, GObject* where_the_object_was )
430     (void)data;
431     (void)where_the_object_was;
432 /*     g_message("Window at %p is now going away", where_the_object_was); */
435 void event_after_cb( GtkWidget* widget, GdkEvent* event, gpointer user_data )
437     if ( event->type == GDK_CONFIGURE ) {
438         GdkScreen* screen = gtk_widget_get_screen(widget);
439         GdkWindow* window = widget->window;
440         EgeColorProfTracker* tracker = (EgeColorProfTracker*)user_data;
441         gint monitorNum = gdk_screen_get_monitor_at_window(screen, window);
442         if ( monitorNum != tracker->private_data->_monitor ) {
443             tracker->private_data->_monitor = monitorNum;
444             g_signal_emit( G_OBJECT(tracker), signals[CHANGED], 0 );
445         }
446     }
449 void target_hierarchy_changed_cb(GtkWidget* widget, GtkWidget* prev_top, gpointer user_data)
451     if ( !prev_top && gtk_widget_get_toplevel(widget) ) {
452         GtkWidget* top = gtk_widget_get_toplevel(widget);
453         if ( GTK_WIDGET_TOPLEVEL(top) ) {
454             GtkWindow* win = GTK_WINDOW(top);
455             g_signal_connect( G_OBJECT(win), "event-after", G_CALLBACK( event_after_cb ), user_data );
456             g_object_weak_ref( G_OBJECT(win), window_finalized, user_data );
457         }
458     }
461 void target_screen_changed_cb(GtkWidget* widget, GdkScreen* prev_screen, gpointer user_data)
463     GdkScreen* screen = gtk_widget_get_screen(widget);
465     if ( screen && (screen != prev_screen) ) {
466         track_screen( screen, EGE_COLOR_PROF_TRACKER(user_data) );
467     }
470 void screen_size_changed_cb(GdkScreen* screen, gpointer user_data)
472     GSList* curr = tracked_screens;
473     (void)user_data;
474 /*     g_message("screen size changed to (%d, %d) with %d monitors for obj:%p", */
475 /*               gdk_screen_get_width(screen), gdk_screen_get_height(screen), */
476 /*               gdk_screen_get_n_monitors(screen), */
477 /*               user_data); */
478     while ( curr && ((ScreenTrack*)curr->data)->screen != screen ) {
479         curr = g_slist_next(curr);
480     }
481     if ( curr ) {
482         ScreenTrack* track = (ScreenTrack*)curr->data;
483         gint numMonitors = gdk_screen_get_n_monitors(screen);
484         if ( numMonitors > (gint)track->profiles->len ) {
485             guint i = 0;
486             for ( i = track->profiles->len; i < (guint)numMonitors; i++ ) {
487                 g_ptr_array_add( track->profiles, 0 );
488 #ifdef GDK_WINDOWING_X11
489                 {
490                     gchar* name = g_strdup_printf( "_ICC_PROFILE_%d", i );
491                     handle_property_change( screen, name );
492                     g_free(name);
493                 }
494 #endif /* GDK_WINDOWING_X11 */
495             }
496         } else if ( numMonitors < (gint)track->profiles->len ) {
497 /*             g_message("The count of monitors decreased, remove some"); */
498         }
499     }
502 void fire(GdkScreen* screen, gint monitor)
504     GSList* curr = tracked_screens;
505     while ( curr ) {
506         ScreenTrack* track = (ScreenTrack*)curr->data;
507         if ( track->screen == screen) {
508             GSList* trackHook = track->trackers;
509             while ( trackHook ) {
510                 EgeColorProfTracker* tracker = (EgeColorProfTracker*)(trackHook->data);
511                 if ( (monitor == -1) || (tracker->private_data->_monitor == monitor) ) {
512                     g_signal_emit( G_OBJECT(tracker), signals[CHANGED], 0 );
513                 }
514                 trackHook = g_slist_next(trackHook);
515             }
516         }
517         curr = g_slist_next(curr);
518     }
521 static void clear_profile( GdkScreen* screen, guint monitor )
523     GSList* curr = tracked_screens;
524     while ( curr && ((ScreenTrack*)curr->data)->screen != screen ) {
525         curr = g_slist_next(curr);
526     }
527     if ( curr ) {
528         ScreenTrack* track = (ScreenTrack*)curr->data;
529         guint i = 0;
530         GByteArray* previous = 0;
531         for ( i = track->profiles->len; i <= monitor; i++ ) {
532             g_ptr_array_add( track->profiles, 0 );
533         }
534         previous = (GByteArray*)g_ptr_array_index( track->profiles, monitor );
535         if ( previous ) {
536             g_byte_array_free( previous, TRUE );
537         }
539         track->profiles->pdata[monitor] = 0;
540     }
543 static void set_profile( GdkScreen* screen, guint monitor, const guint8* data, guint len )
545     GSList* curr = tracked_screens;
546     while ( curr && ((ScreenTrack*)curr->data)->screen != screen ) {
547         curr = g_slist_next(curr);
548     }
549     if ( curr ) {
550         /* Something happened to a screen being tracked. */
551         ScreenTrack* track = (ScreenTrack*)curr->data;
552         gint screenNum = gdk_screen_get_number(screen);
553         guint i = 0;
554         GByteArray* previous = 0;
555         GSList* abstracts = 0;
557         for ( i = track->profiles->len; i <= monitor; i++ ) {
558             g_ptr_array_add( track->profiles, 0 );
559         }
560         previous = (GByteArray*)g_ptr_array_index( track->profiles, monitor );
561         if ( previous ) {
562             g_byte_array_free( previous, TRUE );
563         }
565         if ( data && len ) {
566             GByteArray* newBytes = g_byte_array_sized_new( len );
567             newBytes = g_byte_array_append( newBytes, data, len );
568             track->profiles->pdata[monitor] = newBytes;
569         } else {
570             track->profiles->pdata[monitor] = 0;
571         }
573         for ( abstracts = abstract_trackers; abstracts; abstracts = g_slist_next(abstracts) ) {
574             g_signal_emit( G_OBJECT(abstracts->data), signals[MODIFIED], 0, screenNum, monitor );
575         }
576     }
579 #ifdef GDK_WINDOWING_X11
580 GdkFilterReturn x11_win_filter(GdkXEvent *xevent,
581                                GdkEvent *event,
582                                gpointer data)
584     XEvent* x11 = (XEvent*)xevent;
585     (void)event;
586     (void)data;
588     if ( x11->type == PropertyNotify ) {
589         XPropertyEvent* note = (XPropertyEvent*)x11;
590         /*GdkAtom gatom = gdk_x11_xatom_to_atom(note->atom);*/
591         const gchar* name = gdk_x11_get_xatom_name(note->atom);
592         if ( strncmp("_ICC_PROFILE", name, 12 ) == 0 ) {
593             XEvent* native = (XEvent*)xevent;
594             Status stat = Success;
595             XWindowAttributes tmp;
596             stat = XGetWindowAttributes( native->xproperty.display, native->xproperty.window, &tmp );
597             if ( stat ) {
598                 GdkDisplay* display = gdk_x11_lookup_xdisplay(native->xproperty.display);
599                 if ( display ) {
600                     gint screenCount = gdk_display_get_n_screens(display);
601                     GdkScreen* targetScreen = 0;
602                     gint i = 0;
603                     for ( i = 0; i < screenCount; i++ ) {
604                         GdkScreen* sc = gdk_display_get_screen( display, i );
605                         if ( tmp.screen == GDK_SCREEN_XSCREEN(sc) ) {
606                             targetScreen = sc;
607                         }
608                     }
610                     handle_property_change( targetScreen, name );
611                 }
612             } else {
613 /*                 g_message("%d           failed XGetWindowAttributes with %d", GPOINTER_TO_INT(data), stat); */
614             }
615         }
616     }
618     return GDK_FILTER_CONTINUE;
621 void handle_property_change(GdkScreen* screen, const gchar* name)
623     Display* xdisplay = GDK_SCREEN_XDISPLAY(screen);
624     gint monitor = 0;
625     Atom atom = XInternAtom(xdisplay, name, True);
626     if ( strncmp("_ICC_PROFILE_", name, 13 ) == 0 ) {
627         gint64 tmp = g_ascii_strtoll(name + 13, NULL, 10);
628         if ( tmp != 0 && tmp != G_MAXINT64 && tmp != G_MININT64 ) {
629             monitor = (gint)tmp;
630         }
631     }
632     if ( atom != None ) {
633         Atom actualType = None;
634         int actualFormat = 0;
635         unsigned long size = 128 * 1042;
636         unsigned long nitems = 0;
637         unsigned long bytesAfter = 0;
638         unsigned char* prop = 0;
640         clear_profile( screen, monitor );
642         if ( XGetWindowProperty( xdisplay, GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
643                                  atom, 0, size, False, AnyPropertyType,
644                                  &actualType, &actualFormat, &nitems, &bytesAfter, &prop ) == Success ) {
645             if ( (actualType != None) && (bytesAfter + nitems) ) {
646                 size = nitems + bytesAfter;
647                 bytesAfter = 0;
648                 nitems = 0;
649                 if ( prop ) {
650                     XFree(prop);
651                     prop = 0;
652                 }
653                 if ( XGetWindowProperty( xdisplay, GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
654                                          atom, 0, size, False, AnyPropertyType,
655                                          &actualType, &actualFormat, &nitems, &bytesAfter, &prop ) == Success ) {
656                     gpointer profile = g_memdup( prop, nitems );
657                     set_profile( screen, monitor, (const guint8*)profile, nitems );
658                     XFree(prop);
659                 } else {
660                     g_warning("Problem reading profile from root window");
661                 }
662             } else {
663                 /* clear it */
664                 set_profile( screen, monitor, 0, 0 );
665             }
666         } else {
667             g_warning("error loading profile property");
668         }
669     }
670     fire(screen, monitor);
673 void add_x11_tracking_for_screen(GdkScreen* screen, ScreenTrack* screenTrack)
675     Display* xdisplay = GDK_SCREEN_XDISPLAY(screen);
676     GdkWindow* root = gdk_screen_get_root_window(screen);
677     if ( root ) {
678         Window rootWin = GDK_WINDOW_XID(root);
679         Atom baseAtom = XInternAtom(xdisplay, "_ICC_PROFILE", True);
680         int numWinProps = 0;
681         Atom* propArray = XListProperties(xdisplay, rootWin, &numWinProps);
682         gint i;
684         gdk_window_set_events(root, (GdkEventMask)(gdk_window_get_events(root) | GDK_PROPERTY_CHANGE_MASK));
685         gdk_window_add_filter(root, x11_win_filter, GINT_TO_POINTER(1));
687         /* Look for any profiles attached to this root window */
688         if ( propArray ) {
689             int j = 0;
690             gint numMonitors = gdk_screen_get_n_monitors(screen);
692             if ( baseAtom != None ) {
693                 for ( i = 0; i < numWinProps; i++ ) {
694                     if ( baseAtom == propArray[i] ) {
695                         screenTrack->zeroSeen = TRUE;
696                         handle_property_change( screen, "_ICC_PROFILE" );
697                     }
698                 }
699             } else {
700 /*                 g_message("Base atom not found"); */
701             }
703             for ( i = 1; i < numMonitors; i++ ) {
704                 gchar* name = g_strdup_printf("_ICC_PROFILE_%d", i);
705                 Atom atom = XInternAtom(xdisplay, name, True);
706                 if ( atom != None ) {
707                     for ( j = 0; j < numWinProps; j++ ) {
708                         if ( atom == propArray[j] ) {
709                             screenTrack->otherSeen = TRUE;
710                             handle_property_change( screen, name );
711                         }
712                     }
713                 }
714                 g_free(name);
715             }
716             XFree(propArray);
717             propArray = 0;
718         }
719     }
721 #endif /* GDK_WINDOWING_X11 */