1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
4
/* ***** BEGIN LICENSE BLOCK *****
5
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17
* The Original Code is EGE Color Profile Tracker.
19
* The Initial Developer of the Original Code is
21
* Portions created by the Initial Developer are Copyright (C) 2007
22
* the Initial Developer. All Rights Reserved.
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.
38
* ***** END LICENSE BLOCK ***** */
40
/* Note: this file should be kept compilable as both .cpp and .c */
44
#include <gtk/gtkwidget.h>
45
#include <gtk/gtkwindow.h>
46
#include <gtk/gtkmarshal.h>
48
#ifdef GDK_WINDOWING_X11
50
#include <X11/Xatom.h>
53
#endif /* GDK_WINDOWING_X11 */
55
#include "ege-color-prof-tracker.h"
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,
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 {
104
#ifdef GDK_WINDOWING_X11
107
#endif /* GDK_WINDOWING_X11 */
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
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;
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 ),
157
(GInstanceInitFunc)ege_color_prof_tracker_init,
161
myType = g_type_register_static( G_TYPE_OBJECT, "EgeColorProfTracker", &myInfo, (GTypeFlags)0 );
167
void ege_color_prof_tracker_class_init( EgeColorProfTrackerClass* 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;
177
signals[CHANGED] = g_signal_new( "changed",
178
G_TYPE_FROM_CLASS(klass),
180
G_STRUCT_OFFSET(EgeColorProfTrackerClass, changed),
182
g_cclosure_marshal_VOID__VOID,
185
signals[ADDED] = g_signal_new( "added",
186
G_TYPE_FROM_CLASS(klass),
190
gtk_marshal_VOID__INT_INT,
195
signals[REMOVED] = g_signal_new( "removed",
196
G_TYPE_FROM_CLASS(klass),
200
gtk_marshal_VOID__INT_INT,
205
signals[MODIFIED] = g_signal_new( "modified",
206
G_TYPE_FROM_CLASS(klass),
210
gtk_marshal_VOID__INT_INT,
215
g_type_class_add_private( klass, sizeof(EgeColorProfTrackerClass) );
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,
232
EgeColorProfTracker* tracker = EGE_COLOR_PROF_TRACKER( obj );
233
tracker->private_data->_target = 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 );
244
abstract_trackers = g_slist_append( abstract_trackers, obj );
246
GSList* curr = tracked_screens;
248
ScreenTrack* track = (ScreenTrack*)curr->data;
249
gint screenNum = gdk_screen_get_number(track->screen);
251
for ( monitor = 0; monitor < (gint)track->profiles->len; monitor++ ) {
252
g_signal_emit( G_OBJECT(tracker), signals[MODIFIED], 0, screenNum, monitor );
255
curr = g_slist_next(curr);
263
void ege_color_prof_tracker_get_profile( EgeColorProfTracker const * tracker, gpointer* ptr, guint* len )
265
gpointer dataPos = 0;
267
if ( tracker && tracker->private_data->_target ) {
268
GdkScreen* screen = gtk_widget_get_screen(tracker->private_data->_target);
269
GSList* curr = tracked_screens;
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 );
280
g_warning("No profile data tracked for the specified item.");
284
curr = g_slist_next(curr);
296
void ege_color_prof_tracker_get_profile_for( guint screenNum, guint monitor, gpointer* ptr, guint* len )
298
gpointer dataPos = 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;
305
GSList* curr = tracked_screens;
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 );
316
g_warning("No profile data tracked for the specified item.");
321
curr = g_slist_next(curr);
333
void ege_color_prof_tracker_get_property( GObject* obj, guint propId, GValue* value, GParamSpec * pspec )
335
EgeColorProfTracker* tracker = EGE_COLOR_PROF_TRACKER( obj );
341
G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
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 );
352
G_OBJECT_WARN_INVALID_PROPERTY_ID( obj, propId, pspec );
357
void track_screen( GdkScreen* screen, EgeColorProfTracker* tracker )
359
GSList* curr = tracked_screens;
360
/* First remove the tracker from different screens */
362
ScreenTrack* screenTrack = (ScreenTrack*)curr->data;
363
if ( screenTrack->screen != screen ) {
364
screenTrack->trackers = g_slist_remove_all(screenTrack->trackers, tracker);
366
curr = g_slist_next(curr);
369
curr = tracked_screens;
370
while ( curr && ((ScreenTrack*)curr->data)->screen != screen ) {
371
curr = g_slist_next(curr);
375
/* We found the screen already being tracked */
376
ScreenTrack* screenTrack = (ScreenTrack*)curr->data;
377
GSList* trackHook = g_slist_find( screenTrack->trackers, tracker );
379
screenTrack->trackers = g_slist_append( screenTrack->trackers, tracker );
382
ScreenTrack* newTrack = g_new(ScreenTrack, 1);
383
gint numMonitors = gdk_screen_get_n_monitors(screen);
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 );
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
406
void target_finalized( gpointer data, GObject* where_the_object_was )
409
GSList* curr = tracked_screens;
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 );
420
trackHook = g_slist_next( trackHook );
424
curr = g_slist_next( curr );
428
void window_finalized( gpointer data, GObject* where_the_object_was )
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 );
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 );
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) );
470
void screen_size_changed_cb(GdkScreen* screen, gpointer user_data)
472
GSList* curr = tracked_screens;
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), */
478
while ( curr && ((ScreenTrack*)curr->data)->screen != screen ) {
479
curr = g_slist_next(curr);
482
ScreenTrack* track = (ScreenTrack*)curr->data;
483
gint numMonitors = gdk_screen_get_n_monitors(screen);
484
if ( numMonitors > (gint)track->profiles->len ) {
486
for ( i = track->profiles->len; i < (guint)numMonitors; i++ ) {
487
g_ptr_array_add( track->profiles, 0 );
488
#ifdef GDK_WINDOWING_X11
490
gchar* name = g_strdup_printf( "_ICC_PROFILE_%d", i );
491
handle_property_change( screen, name );
494
#endif /* GDK_WINDOWING_X11 */
496
} else if ( numMonitors < (gint)track->profiles->len ) {
497
/* g_message("The count of monitors decreased, remove some"); */
502
void fire(GdkScreen* screen, gint monitor)
504
GSList* curr = tracked_screens;
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 );
514
trackHook = g_slist_next(trackHook);
517
curr = g_slist_next(curr);
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);
528
ScreenTrack* track = (ScreenTrack*)curr->data;
530
GByteArray* previous = 0;
531
for ( i = track->profiles->len; i <= monitor; i++ ) {
532
g_ptr_array_add( track->profiles, 0 );
534
previous = (GByteArray*)g_ptr_array_index( track->profiles, monitor );
536
g_byte_array_free( previous, TRUE );
539
track->profiles->pdata[monitor] = 0;
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);
550
/* Something happened to a screen being tracked. */
551
ScreenTrack* track = (ScreenTrack*)curr->data;
552
gint screenNum = gdk_screen_get_number(screen);
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 );
560
previous = (GByteArray*)g_ptr_array_index( track->profiles, monitor );
562
g_byte_array_free( previous, TRUE );
566
GByteArray* newBytes = g_byte_array_sized_new( len );
567
newBytes = g_byte_array_append( newBytes, data, len );
568
track->profiles->pdata[monitor] = newBytes;
570
track->profiles->pdata[monitor] = 0;
573
for ( abstracts = abstract_trackers; abstracts; abstracts = g_slist_next(abstracts) ) {
574
g_signal_emit( G_OBJECT(abstracts->data), signals[MODIFIED], 0, screenNum, monitor );
579
#ifdef GDK_WINDOWING_X11
580
GdkFilterReturn x11_win_filter(GdkXEvent *xevent,
584
XEvent* x11 = (XEvent*)xevent;
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 );
598
GdkDisplay* display = gdk_x11_lookup_xdisplay(native->xproperty.display);
600
gint screenCount = gdk_display_get_n_screens(display);
601
GdkScreen* targetScreen = 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) ) {
610
handle_property_change( targetScreen, name );
613
/* g_message("%d failed XGetWindowAttributes with %d", GPOINTER_TO_INT(data), stat); */
618
return GDK_FILTER_CONTINUE;
621
void handle_property_change(GdkScreen* screen, const gchar* name)
623
Display* xdisplay = GDK_SCREEN_XDISPLAY(screen);
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 ) {
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;
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 );
660
g_warning("Problem reading profile from root window");
664
set_profile( screen, monitor, 0, 0 );
667
g_warning("error loading profile property");
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);
678
Window rootWin = GDK_WINDOW_XID(root);
679
Atom baseAtom = XInternAtom(xdisplay, "_ICC_PROFILE", True);
681
Atom* propArray = XListProperties(xdisplay, rootWin, &numWinProps);
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 */
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" );
700
/* g_message("Base atom not found"); */
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 );
721
#endif /* GDK_WINDOWING_X11 */