1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ 
   3  * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org> 
   5  * This library is free software; you can redistribute it and/or 
   6  * modify it under the terms of the GNU Lesser General Public 
   7  * License as published by the Free Software Foundation; either 
   8  * version 2 of the License, or (at your option) any later version. 
  10  * This library is distributed in the hope that it will be useful, 
  11  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  13  * Lesser General Public License for more details. 
  15  * You should have received a copy of the GNU Lesser General Public 
  16  * License along with this library; if not, write to the 
  17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
  18  * Boston, MA 02111-1307, USA. 
  23 Permission to use this file under wxWindows licence given by 
  26 From andersca@gnu.org Tue Dec  9 13:01:56 2003 
  27 Return-path: <andersca@gnu.org> 
  28 Envelope-to: vasek@localhost 
  29 Delivery-date: Tue, 09 Dec 2003 13:04:35 +0100 
  30 Received: from localhost 
  31         ([127.0.0.1] helo=amavis ident=amavis) 
  32         by armitage with esmtp (Exim 3.35 #1) 
  34         for vasek@localhost; Tue, 09 Dec 2003 13:04:35 +0100 
  35 Received: from armitage ([127.0.0.1]) 
  36         by amavis (armitage [127.0.0.1]) (amavisd-new, port 10024) with ESMTP 
  37         id 04227-09 for <vasek@localhost>; 
  38         Tue, 9 Dec 2003 13:04:11 +0100 (CET) 
  39 Received: from localhost ([127.0.0.1] ident=fetchmail) 
  40         by armitage with esmtp (Exim 3.35 #1) 
  42         for vasek@localhost; Tue, 09 Dec 2003 13:04:11 +0100 
  43 Delivered-To: alias-email-slavikvaclav@seznam.cz 
  44 Received: from pop3.seznam.cz [212.80.76.45] 
  45         by localhost with POP3 (fetchmail-5.9.11) 
  46         for vasek@localhost (single-drop); Tue, 09 Dec 2003 13:04:11 +0100 (CET) 
  47 Received: (qmail 9861 invoked from network); 9 Dec 2003 12:02:17 -0000 
  48 Received: from unknown (HELO maxipes.logix.cz) (81.0.234.97) 
  49   by buster.go.seznam.cz with SMTP; 9 Dec 2003 12:02:17 -0000 
  50 Received: by maxipes.logix.cz (Postfix, from userid 604) 
  51         id 37E6D29A51; Tue,  9 Dec 2003 13:02:16 +0100 (CET) 
  52 X-Original-To: vaclav.slavik@matfyz.cz 
  53 Received: from mail.csbnet.se (glutus.csbnet.se [193.11.248.2]) 
  54         by maxipes.logix.cz (Postfix) with ESMTP id 90D6A29A51 
  55         for <vaclav.slavik@matfyz.cz>; Tue,  9 Dec 2003 13:02:15 +0100 (CET) 
  56 Received: by mail.csbnet.se (Postfix, from userid 8) 
  57         id 7AA7F10A6D7; Tue,  9 Dec 2003 13:02:14 +0100 (CET) 
  58 Received: from carbon.csbnet.se (carbon.csbnet.se [193.11.248.180]) 
  59         by mail.csbnet.se (Postfix) with ESMTP id A190F10A71D 
  60         for <vaclav.slavik@matfyz.cz>; Tue,  9 Dec 2003 13:01:56 +0100 (CET) 
  61 Subject: Re: eggtrayicon.{c,h} licensing 
  62 From: Anders Carlsson <andersca@gnu.org> 
  63 To: Vaclav Slavik <vaclav.slavik@matfyz.cz> 
  64 In-Reply-To: <200312091142.54542.vaclav.slavik@matfyz.cz> 
  65 References: <200312091142.54542.vaclav.slavik@matfyz.cz> 
  66 Content-Type: text/plain 
  67 Message-Id: <1070971316.30989.0.camel@carbon.csbnet.se> 
  69 X-Mailer: Ximian Evolution 1.5  
  70 Date: Tue, 09 Dec 2003 13:01:56 +0100 
  71 Content-Transfer-Encoding: 7bit 
  72 X-Scanned-By: CLAM (openantivirus DB) antivirus scanner at mail.csbnet.se 
  73 X-Virus-Scanned: by amavisd-new-20030616-p5 (Debian) at armitage 
  74 X-Spam-Status: No, hits=-4.9 tagged_above=-999.0 required=6.3 tests=BAYES_00 
  78 X-KMail-EncryptionState:   
  79 X-KMail-SignatureState:   
  81 On tis, 2003-12-09 at 11:42 +0100, Vaclav Slavik wrote: 
  84 > I'm working on the wxWindows cross-platform GUI toolkit 
  85 > (http://www.wxwindows.org) which uses GTK+ and it would save me a lot 
  86 > of time if I could use your eggtrayicon code to implement tray icons 
  87 > on X11. Unfortunately I can't use it right now because it is not part 
  88 > of any library we could depend on (as we do depend on GTK+) and would 
  89 > have to be included in our sources and it is under the LGPL license. 
  90 > The problem is that wxWindows' license is more permissive (see 
  91 > http://www.opensource.org/licenses/wxwindows.php for details) and so 
  92 > I can't take your code and put it under wxWindows License. And I 
  93 > can't put code that can't be used under the terms of wxWindows 
  94 > License into wxWindows either. Do you think it would be possible to 
  95 > get permission to include eggtrayicon under wxWindows license? 
 101 Sure, that's fine by me. 
 108 #include "wx/platform.h" 
 110 #if wxUSE_TASKBARICON 
 112 #include <gtk/gtkversion.h> 
 113 #if GTK_CHECK_VERSION(2, 1, 0) 
 116 #include "eggtrayicon.h" 
 118 #include <gdkconfig.h> 
 119 #if defined (GDK_WINDOWING_X11) 
 120 #include <gdk/gdkx.h> 
 121 #include <X11/Xatom.h> 
 122 #elif defined (GDK_WINDOWING_WIN32) 
 123 #include <gdk/gdkwin32.h> 
 127 #define SYSTEM_TRAY_REQUEST_DOCK    0 
 128 #define SYSTEM_TRAY_BEGIN_MESSAGE   1 
 129 #define SYSTEM_TRAY_CANCEL_MESSAGE  2 
 131 #define SYSTEM_TRAY_ORIENTATION_HORZ 0 
 132 #define SYSTEM_TRAY_ORIENTATION_VERT 1 
 139 static GtkPlugClass 
*parent_class 
= NULL
; 
 141 static void egg_tray_icon_init (EggTrayIcon 
*icon
); 
 142 static void egg_tray_icon_class_init (EggTrayIconClass 
*klass
); 
 144 static void egg_tray_icon_get_property (GObject    
*object
, 
 149 static void egg_tray_icon_realize   (GtkWidget 
*widget
); 
 150 static void egg_tray_icon_unrealize (GtkWidget 
*widget
); 
 152 static void egg_tray_icon_add (GtkContainer 
*container
, 
 155 #ifdef GDK_WINDOWING_X11 
 156 static void egg_tray_icon_update_manager_window    (EggTrayIcon 
*icon
, 
 157                                                     gboolean     dock_if_realized
); 
 158 static void egg_tray_icon_manager_window_destroyed (EggTrayIcon 
*icon
); 
 162 egg_tray_icon_get_type (void) 
 164   static GType our_type 
= 0; 
 168       static const GTypeInfo our_info 
= 
 170         sizeof (EggTrayIconClass
), 
 171         (GBaseInitFunc
) NULL
, 
 172         (GBaseFinalizeFunc
) NULL
, 
 173         (GClassInitFunc
) egg_tray_icon_class_init
, 
 174         NULL
, /* class_finalize */ 
 175         NULL
, /* class_data */ 
 176         sizeof (EggTrayIcon
), 
 178         (GInstanceInitFunc
) egg_tray_icon_init
 
 181       our_type 
= g_type_register_static (GTK_TYPE_PLUG
, "EggTrayIcon", &our_info
, 0); 
 188 egg_tray_icon_init (EggTrayIcon 
*icon
) 
 191   icon
->orientation 
= GTK_ORIENTATION_HORIZONTAL
; 
 193   gtk_widget_add_events (GTK_WIDGET (icon
), GDK_PROPERTY_CHANGE_MASK
); 
 197 egg_tray_icon_class_init (EggTrayIconClass 
*klass
) 
 199   GObjectClass 
*gobject_class 
= (GObjectClass 
*)klass
; 
 200   GtkWidgetClass 
*widget_class 
= (GtkWidgetClass 
*)klass
; 
 201   GtkContainerClass 
*container_class 
= (GtkContainerClass 
*)klass
; 
 203   parent_class 
= g_type_class_peek_parent (klass
); 
 205   gobject_class
->get_property 
= egg_tray_icon_get_property
; 
 207   widget_class
->realize   
= egg_tray_icon_realize
; 
 208   widget_class
->unrealize 
= egg_tray_icon_unrealize
; 
 210   container_class
->add 
= egg_tray_icon_add
; 
 212   g_object_class_install_property (gobject_class
, 
 214                                    g_param_spec_enum ("orientation", 
 216                                                       "The orientation of the tray.", 
 217                                                       GTK_TYPE_ORIENTATION
, 
 218                                                       GTK_ORIENTATION_HORIZONTAL
, 
 223 egg_tray_icon_get_property (GObject    
*object
, 
 228   EggTrayIcon 
*icon 
= EGG_TRAY_ICON (object
); 
 232     case PROP_ORIENTATION
: 
 233       g_value_set_enum (value
, icon
->orientation
); 
 236       G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
); 
 241 #ifdef GDK_WINDOWING_X11 
 244 egg_tray_icon_get_orientation_property (EggTrayIcon 
*icon
) 
 257   g_assert (icon
->manager_window 
!= None
); 
 259   xdisplay 
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
))); 
 261   gdk_error_trap_push (); 
 263   result 
= XGetWindowProperty (xdisplay
, 
 264                                icon
->manager_window
, 
 265                                icon
->orientation_atom
, 
 268                                &type
, &format
, &nitems
, 
 269                                &bytes_after
, &(prop
.prop_ch
)); 
 270   error 
= gdk_error_trap_pop (); 
 272   if (error 
|| result 
!= Success
) 
 275   if (type 
== XA_CARDINAL
) 
 277       GtkOrientation orientation
; 
 279       orientation 
= (prop
.prop 
[0] == SYSTEM_TRAY_ORIENTATION_HORZ
) ? 
 280                                         GTK_ORIENTATION_HORIZONTAL 
: 
 281                                         GTK_ORIENTATION_VERTICAL
; 
 283       if (icon
->orientation 
!= orientation
) 
 285           icon
->orientation 
= orientation
; 
 287           g_object_notify (G_OBJECT (icon
), "orientation"); 
 295 static GdkFilterReturn
 
 296 egg_tray_icon_manager_filter (GdkXEvent 
*xevent
, GdkEvent 
*event
, gpointer user_data
) 
 298   EggTrayIcon 
*icon 
= user_data
; 
 299   XEvent 
*xev 
= (XEvent 
*)xevent
; 
 301   if (xev
->xany
.type 
== ClientMessage 
&& 
 302       xev
->xclient
.message_type 
== icon
->manager_atom 
&& 
 303       xev
->xclient
.data
.l
[1] == icon
->selection_atom
) 
 305       egg_tray_icon_update_manager_window (icon
, TRUE
); 
 307   else if (xev
->xany
.window 
== icon
->manager_window
) 
 309       if (xev
->xany
.type 
== PropertyNotify 
&& 
 310           xev
->xproperty
.atom 
== icon
->orientation_atom
) 
 312           egg_tray_icon_get_orientation_property (icon
); 
 314       if (xev
->xany
.type 
== DestroyNotify
) 
 316           egg_tray_icon_manager_window_destroyed (icon
); 
 319   return GDK_FILTER_CONTINUE
; 
 325 egg_tray_icon_unrealize (GtkWidget 
*widget
) 
 327 #ifdef GDK_WINDOWING_X11 
 328   EggTrayIcon 
*icon 
= EGG_TRAY_ICON (widget
); 
 329   GdkWindow 
*root_window
; 
 331   if (icon
->manager_window 
!= None
) 
 335       gdkwin 
= gdk_window_lookup_for_display (gtk_widget_get_display (widget
), 
 336                                               icon
->manager_window
); 
 338       gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
); 
 341   root_window 
= gdk_screen_get_root_window (gtk_widget_get_screen (widget
)); 
 343   gdk_window_remove_filter (root_window
, egg_tray_icon_manager_filter
, icon
); 
 345   if (GTK_WIDGET_CLASS (parent_class
)->unrealize
) 
 346     (* GTK_WIDGET_CLASS (parent_class
)->unrealize
) (widget
); 
 350 #ifdef GDK_WINDOWING_X11 
 353 egg_tray_icon_send_manager_message (EggTrayIcon 
*icon
, 
 360   XClientMessageEvent ev
; 
 363   ev
.type 
= ClientMessage
; 
 365   ev
.message_type 
= icon
->system_tray_opcode_atom
; 
 367   ev
.data
.l
[0] = gdk_x11_get_server_time (GTK_WIDGET (icon
)->window
); 
 368   ev
.data
.l
[1] = message
; 
 369   ev
.data
.l
[2] = data1
; 
 370   ev
.data
.l
[3] = data2
; 
 371   ev
.data
.l
[4] = data3
; 
 373   display 
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
))); 
 375   gdk_error_trap_push (); 
 377               icon
->manager_window
, False
, NoEventMask
, (XEvent 
*)&ev
); 
 378   XSync (display
, False
); 
 379   gdk_error_trap_pop (); 
 383 egg_tray_icon_send_dock_request (EggTrayIcon 
*icon
) 
 385   egg_tray_icon_send_manager_message (icon
, 
 386                                       SYSTEM_TRAY_REQUEST_DOCK
, 
 387                                       icon
->manager_window
, 
 388                                       gtk_plug_get_id (GTK_PLUG (icon
)), 
 393 egg_tray_icon_update_manager_window (EggTrayIcon 
*icon
, 
 394                                      gboolean     dock_if_realized
) 
 398   if (icon
->manager_window 
!= None
) 
 401   xdisplay 
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
))); 
 403   XGrabServer (xdisplay
); 
 405   icon
->manager_window 
= XGetSelectionOwner (xdisplay
, 
 406                                              icon
->selection_atom
); 
 408   if (icon
->manager_window 
!= None
) 
 409     XSelectInput (xdisplay
, 
 410                   icon
->manager_window
, StructureNotifyMask
|PropertyChangeMask
); 
 412   XUngrabServer (xdisplay
); 
 415   if (icon
->manager_window 
!= None
) 
 419       gdkwin 
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)), 
 420                                               icon
->manager_window
); 
 422       gdk_window_add_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
); 
 424       if (dock_if_realized 
&& GTK_WIDGET_REALIZED (icon
)) 
 425         egg_tray_icon_send_dock_request (icon
); 
 427       egg_tray_icon_get_orientation_property (icon
); 
 432 egg_tray_icon_manager_window_destroyed (EggTrayIcon 
*icon
) 
 436   g_return_if_fail (icon
->manager_window 
!= None
); 
 438   gdkwin 
= gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon
)), 
 439                                           icon
->manager_window
); 
 441   gdk_window_remove_filter (gdkwin
, egg_tray_icon_manager_filter
, icon
); 
 443   icon
->manager_window 
= None
; 
 445   egg_tray_icon_update_manager_window (icon
, TRUE
); 
 451 transparent_expose_event (GtkWidget 
*widget
, GdkEventExpose 
*event
, gpointer user_data
) 
 453   gdk_window_clear_area (widget
->window
, event
->area
.x
, event
->area
.y
, 
 454                          event
->area
.width
, event
->area
.height
); 
 459 make_transparent_again (GtkWidget 
*widget
, GtkStyle 
*previous_style
, 
 462   gdk_window_set_back_pixmap (widget
->window
, NULL
, TRUE
); 
 466 make_transparent (GtkWidget 
*widget
, gpointer user_data
) 
 468   if (GTK_WIDGET_NO_WINDOW (widget
) || GTK_WIDGET_APP_PAINTABLE (widget
)) 
 471   gtk_widget_set_app_paintable (widget
, TRUE
); 
 472   gtk_widget_set_double_buffered (widget
, FALSE
); 
 473   gdk_window_set_back_pixmap (widget
->window
, NULL
, TRUE
); 
 474   g_signal_connect (widget
, "expose_event", 
 475                     G_CALLBACK (transparent_expose_event
), NULL
); 
 476   g_signal_connect_after (widget
, "style_set", 
 477                           G_CALLBACK (make_transparent_again
), NULL
); 
 481 egg_tray_icon_realize (GtkWidget 
*widget
) 
 483 #ifdef GDK_WINDOWING_X11 
 484   EggTrayIcon 
*icon 
= EGG_TRAY_ICON (widget
); 
 489   GdkWindow 
*root_window
; 
 491   if (GTK_WIDGET_CLASS (parent_class
)->realize
) 
 492     GTK_WIDGET_CLASS (parent_class
)->realize (widget
); 
 494   make_transparent (widget
, NULL
); 
 496   screen 
= gtk_widget_get_screen (widget
); 
 497   display 
= gdk_screen_get_display (screen
); 
 498   xdisplay 
= gdk_x11_display_get_xdisplay (display
); 
 500   /* Now see if there's a manager window around */ 
 501   g_snprintf (buffer
, sizeof (buffer
), 
 502               "_NET_SYSTEM_TRAY_S%d", 
 503               gdk_screen_get_number (screen
)); 
 505   icon
->selection_atom 
= XInternAtom (xdisplay
, buffer
, False
); 
 507   icon
->manager_atom 
= XInternAtom (xdisplay
, "MANAGER", False
); 
 509   icon
->system_tray_opcode_atom 
= XInternAtom (xdisplay
, 
 510                                                    "_NET_SYSTEM_TRAY_OPCODE", 
 513   icon
->orientation_atom 
= XInternAtom (xdisplay
, 
 514                                         "_NET_SYSTEM_TRAY_ORIENTATION", 
 517   egg_tray_icon_update_manager_window (icon
, FALSE
); 
 518   egg_tray_icon_send_dock_request (icon
); 
 520   root_window 
= gdk_screen_get_root_window (screen
); 
 522   /* Add a root window filter so that we get changes on MANAGER */ 
 523   gdk_window_add_filter (root_window
, 
 524                          egg_tray_icon_manager_filter
, icon
); 
 529 egg_tray_icon_add (GtkContainer 
*container
, GtkWidget 
*widget
) 
 531   g_signal_connect (widget
, "realize", 
 532                     G_CALLBACK (make_transparent
), NULL
); 
 533   GTK_CONTAINER_CLASS (parent_class
)->add (container
, widget
); 
 537 egg_tray_icon_new_for_screen (GdkScreen 
*screen
, const char *name
) 
 539   g_return_val_if_fail (GDK_IS_SCREEN (screen
), NULL
); 
 541   return g_object_new (EGG_TYPE_TRAY_ICON
, "screen", screen
, "title", name
, NULL
); 
 545 egg_tray_icon_new (const gchar 
*name
) 
 547   return g_object_new (EGG_TYPE_TRAY_ICON
, "title", name
, NULL
); 
 551 egg_tray_icon_send_message (EggTrayIcon 
*icon
, 
 553                             const gchar 
*message
, 
 558   g_return_val_if_fail (EGG_IS_TRAY_ICON (icon
), 0); 
 559   g_return_val_if_fail (timeout 
>= 0, 0); 
 560   g_return_val_if_fail (message 
!= NULL
, 0); 
 562 #ifdef GDK_WINDOWING_X11 
 563   if (icon
->manager_window 
== None
) 
 568     len 
= strlen (message
); 
 570   stamp 
= icon
->stamp
++; 
 572 #ifdef GDK_WINDOWING_X11 
 573   /* Get ready to send the message */ 
 574   egg_tray_icon_send_manager_message (icon
, SYSTEM_TRAY_BEGIN_MESSAGE
, 
 575                                       icon
->manager_window
, 
 576                                       timeout
, len
, stamp
); 
 578   /* Now to send the actual message */ 
 579   gdk_error_trap_push (); 
 582       XClientMessageEvent ev
; 
 585       xdisplay 
= GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon
))); 
 587       ev
.type 
= ClientMessage
; 
 588       ev
.window 
= icon
->manager_window
; 
 590       ev
.message_type 
= XInternAtom (xdisplay
, 
 591                                      "_NET_SYSTEM_TRAY_MESSAGE_DATA", False
); 
 594           memcpy (&ev
.data
, message
, 20); 
 600           memcpy (&ev
.data
, message
, len
); 
 604       XSendEvent (xdisplay
, 
 605                   icon
->manager_window
, False
, StructureNotifyMask
, (XEvent 
*)&ev
); 
 606       XSync (xdisplay
, False
); 
 608   gdk_error_trap_pop (); 
 615 egg_tray_icon_cancel_message (EggTrayIcon 
*icon
, 
 618   g_return_if_fail (EGG_IS_TRAY_ICON (icon
)); 
 619   g_return_if_fail (id 
> 0); 
 620 #ifdef GDK_WINDOWING_X11 
 621   egg_tray_icon_send_manager_message (icon
, SYSTEM_TRAY_CANCEL_MESSAGE
, 
 622                                       (Window
)gtk_plug_get_id (GTK_PLUG (icon
)), 
 628 egg_tray_icon_get_orientation (EggTrayIcon 
*icon
) 
 630   g_return_val_if_fail (EGG_IS_TRAY_ICON (icon
), GTK_ORIENTATION_HORIZONTAL
); 
 632   return icon
->orientation
; 
 639 #endif /* GTK_CHECK_VERSION(2, 1, 0) */ 
 640 #endif /* wxUSE_TASKBARICON */