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
125 {
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 )
145 {
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;
165 }
167 void ege_color_prof_tracker_class_init( EgeColorProfTrackerClass* klass )
168 {
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 }
217 }
220 void ege_color_prof_tracker_init( EgeColorProfTracker* tracker )
221 {
222 tracker->private_data = EGE_GET_PRIVATE( tracker );
223 tracker->private_data->_target = 0;
224 tracker->private_data->_monitor = 0;
225 }
227 EgeColorProfTracker* ege_color_prof_tracker_new( GtkWidget* target )
228 {
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;
261 }
263 void ege_color_prof_tracker_get_profile( EgeColorProfTracker const * tracker, gpointer* ptr, guint* len )
264 {
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 }
294 }
296 void ege_color_prof_tracker_get_profile_for( guint screenNum, guint monitor, gpointer* ptr, guint* len )
297 {
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 }
331 }
333 void ege_color_prof_tracker_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec )
334 {
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 }
343 }
345 void ege_color_prof_tracker_set_property( GObject* obj, guint propId, const GValue *value, GParamSpec* pspec )
346 {
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 }
354 }
357 void track_screen( GdkScreen* screen, EgeColorProfTracker* tracker )
358 {
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 }
403 }
406 void target_finalized( gpointer data, GObject* where_the_object_was )
407 {
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 }
426 }
428 void window_finalized( gpointer data, GObject* where_the_object_was )
429 {
430 (void)data;
431 (void)where_the_object_was;
432 /* g_message("Window at %p is now going away", where_the_object_was); */
433 }
435 void event_after_cb( GtkWidget* widget, GdkEvent* event, gpointer user_data )
436 {
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 }
447 }
449 void target_hierarchy_changed_cb(GtkWidget* widget, GtkWidget* prev_top, gpointer user_data)
450 {
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 }
459 }
461 void target_screen_changed_cb(GtkWidget* widget, GdkScreen* prev_screen, gpointer user_data)
462 {
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 }
468 }
470 void screen_size_changed_cb(GdkScreen* screen, gpointer user_data)
471 {
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 }
500 }
502 void fire(GdkScreen* screen, gint monitor)
503 {
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 }
519 }
521 static void clear_profile( GdkScreen* screen, guint monitor )
522 {
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 }
541 }
543 static void set_profile( GdkScreen* screen, guint monitor, const guint8* data, guint len )
544 {
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 }
577 }
579 #ifdef GDK_WINDOWING_X11
580 GdkFilterReturn x11_win_filter(GdkXEvent *xevent,
581 GdkEvent *event,
582 gpointer data)
583 {
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;
619 }
621 void handle_property_change(GdkScreen* screen, const gchar* name)
622 {
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);
671 }
673 void add_x11_tracking_for_screen(GdkScreen* screen, ScreenTrack* screenTrack)
674 {
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 }
720 }
721 #endif /* GDK_WINDOWING_X11 */