2 // NotificationAreaIcon.cs
4 // Copyright (C) 2005 Todd Berman <tberman@off.net>
5 // Copyright (C) 2005 Ed Catmur <ed@catmur.co.uk>
6 // Copyright (C) 2005 Novell, Inc. (Miguel de Icaza, Aaron Bockover)
9 // NOTE: throughout IntPtr is used for the Xlib long type, as this tends to
10 // have the correct width and does not require any configure checks.
13 using System
.Runtime
.InteropServices
;
18 namespace Search
.Tray
{
20 public class NotificationArea
: Plug
23 private Orientation orientation
;
25 private IntPtr selection_atom
;
26 private IntPtr manager_atom
;
27 private IntPtr system_tray_opcode_atom
;
28 private IntPtr orientation_atom
;
29 private IntPtr message_data_atom
;
30 private IntPtr manager_window
;
31 private FilterFunc filter
;
33 public NotificationArea (string name
)
39 public NotificationArea (string name
, Gdk
.Screen screen
)
46 [DllImport ("libc.so.6")]
47 private static extern IntPtr
memcpy (ref XClientMessageEvent
.DataUnion dest
, IntPtr src
, IntPtr len
);
49 public uint SendMessage (uint timeout
, string message
)
51 if (manager_window
== IntPtr
.Zero
) {
55 byte[] arr
= System
.Text
.Encoding
.UTF8
.GetBytes (message
);
56 IntPtr unmanaged_arr
= Marshal
.AllocHGlobal (arr
.Length
);
57 Marshal
.Copy (arr
, 0, unmanaged_arr
, arr
.Length
);
59 SendManagerMessage (SystemTrayMessage
.BeginMessage
, (IntPtr
) Id
, timeout
, (uint) arr
.Length
, ++stamp
);
61 gdk_error_trap_push ();
63 for (int index
= 0; index
< message
.Length
; index
+= 20) {
64 XClientMessageEvent ev
= new XClientMessageEvent ();
66 IntPtr display
= gdk_x11_display_get_xdisplay (Display
.Handle
);
68 ev
.type
= XEventName
.ClientMessage
;
69 ev
.window
= (IntPtr
) Id
;
71 ev
.message_type
= message_data_atom
;
73 int len
= Math
.Min (arr
.Length
- index
, 20);
74 memcpy (ref ev
.data
, (IntPtr
)((int)unmanaged_arr
+ index
), (IntPtr
)len
);
76 XSendEvent (display
, manager_window
, false, (IntPtr
) EventMask
.StructureNotifyMask
, ref ev
);
77 XSync (display
, false);
80 gdk_error_trap_pop ();
85 public void CancelMessage (uint id
)
91 SendManagerMessage (SystemTrayMessage
.CancelMessage
, (IntPtr
) Id
, id
, 0, 0);
97 orientation
= Orientation
.Horizontal
;
98 AddEvents ((int)EventMask
.PropertyChangeMask
);
99 filter
= new FilterFunc (ManagerFilter
);
103 private void TransparentExposeEvent (object obj
, Gtk
.ExposeEventArgs args
)
105 Gtk
.Widget widget
= (Gtk
.Widget
)obj
;
106 Gdk
.Rectangle area
= args
.Event
.Area
;
108 widget
.GdkWindow
.ClearArea (area
.X
, area
.Y
, area
.Width
, area
.Height
);
111 private void MakeTransparentAgain (object obj
, Gtk
.StyleSetArgs args
)
113 Gtk
.Widget widget
= (Gtk
.Widget
)obj
;
115 widget
.GdkWindow
.SetBackPixmap (null, true);
118 private void MakeTransparent (object obj
, EventArgs args
)
120 Gtk
.Widget widget
= (Gtk
.Widget
)obj
;
121 if (widget
.IsNoWindow
|| widget
.IsAppPaintable
)
124 widget
.AppPaintable
= true;
125 widget
.DoubleBuffered
= false;
126 widget
.GdkWindow
.SetBackPixmap (null, true);
127 widget
.ExposeEvent
+= TransparentExposeEvent
;
128 widget
.StyleSet
+= MakeTransparentAgain
;
131 protected override void OnRealized ()
134 MakeTransparent (this, EventArgs
.Empty
);
135 Display display
= Screen
.Display
;
136 IntPtr xdisplay
= gdk_x11_display_get_xdisplay (display
.Handle
);
137 selection_atom
= XInternAtom (xdisplay
, "_NET_SYSTEM_TRAY_S" + Screen
.Number
.ToString (), false);
138 manager_atom
= XInternAtom (xdisplay
, "MANAGER", false);
139 system_tray_opcode_atom
= XInternAtom (xdisplay
, "_NET_SYSTEM_TRAY_OPCODE", false);
140 orientation_atom
= XInternAtom (xdisplay
, "_NET_SYSTEM_TRAY_ORIENTATION", false);
141 message_data_atom
= XInternAtom (xdisplay
, "_NET_SYSTEM_TRAY_MESSAGE_DATA", false);
142 UpdateManagerWindow (false);
144 Screen
.RootWindow
.AddFilter (filter
);
147 protected override void OnAdded (Gtk
.Widget child
)
149 child
.Realized
+= MakeTransparent
;
150 base.OnAdded (child
);
153 protected override void OnUnrealized ()
155 if (manager_window
!= IntPtr
.Zero
) {
156 Gdk
.Window gdkwin
= Gdk
.Window
.ForeignNewForDisplay (Display
, (uint)manager_window
);
157 if (gdkwin
!= null) {
158 gdkwin
.RemoveFilter (filter
);
162 Screen
.RootWindow
.RemoveFilter (filter
);
163 base.OnUnrealized ();
166 private void UpdateManagerWindow (bool dock_if_realized
)
168 IntPtr xdisplay
= gdk_x11_display_get_xdisplay (Display
.Handle
);
170 if (manager_window
!= IntPtr
.Zero
) {
174 XGrabServer (xdisplay
);
176 manager_window
= XGetSelectionOwner (xdisplay
, selection_atom
);
177 if (manager_window
!= IntPtr
.Zero
) {
178 XSelectInput (xdisplay
, manager_window
, (IntPtr
) (EventMask
.StructureNotifyMask
| EventMask
.PropertyChangeMask
));
181 XUngrabServer (xdisplay
);
184 if (manager_window
!= IntPtr
.Zero
) {
185 Gdk
.Window gdkwin
= Gdk
.Window
.ForeignNewForDisplay (Display
, (uint)manager_window
);
186 if (gdkwin
!= null) {
187 gdkwin
.AddFilter (filter
);
190 if (dock_if_realized
&& IsRealized
) {
194 GetOrientationProperty ();
198 private void SendDockRequest ()
200 SendManagerMessage (SystemTrayMessage
.RequestDock
, manager_window
, Id
, 0, 0);
203 private void SendManagerMessage (SystemTrayMessage message
, IntPtr window
, uint data1
, uint data2
, uint data3
)
205 XClientMessageEvent ev
= new XClientMessageEvent ();
208 ev
.type
= XEventName
.ClientMessage
;
210 ev
.message_type
= system_tray_opcode_atom
;
212 ev
.data
.ptr1
= (IntPtr
)gdk_x11_get_server_time (GdkWindow
.Handle
);
213 ev
.data
.ptr2
= (IntPtr
)message
;
214 ev
.data
.ptr3
= (IntPtr
)data1
;
215 ev
.data
.ptr4
= (IntPtr
)data2
;
216 ev
.data
.ptr5
= (IntPtr
)data3
;
218 display
= gdk_x11_display_get_xdisplay (Display
.Handle
);
219 gdk_error_trap_push ();
220 XSendEvent (display
, manager_window
, false, (IntPtr
) EventMask
.NoEventMask
, ref ev
);
221 XSync (display
, false);
222 gdk_error_trap_pop ();
225 private FilterReturn
ManagerFilter (IntPtr xevent
, Event evnt
)
227 XAnyEvent xev
= (XAnyEvent
) Marshal
.PtrToStructure (xevent
, typeof(XAnyEvent
));
229 if (xev
.type
== XEventName
.ClientMessage
){
230 XClientMessageEvent xclient
= (XClientMessageEvent
) Marshal
.PtrToStructure (xevent
, typeof(XClientMessageEvent
));
232 if (xclient
.message_type
== manager_atom
&& xclient
.data
.ptr2
== selection_atom
) {
233 UpdateManagerWindow (true);
234 return FilterReturn
.Continue
;
238 if (xev
.window
== manager_window
) {
239 if (xev
.type
== XEventName
.PropertyNotify
){
240 XPropertyEvent xproperty
= (XPropertyEvent
) Marshal
.PtrToStructure (xevent
, typeof(XPropertyEvent
));
241 if (xproperty
.atom
== orientation_atom
) {
242 GetOrientationProperty();
243 return FilterReturn
.Continue
;
247 if (xev
.type
== XEventName
.DestroyNotify
) {
248 ManagerWindowDestroyed();
252 return FilterReturn
.Continue
;
255 private void ManagerWindowDestroyed ()
257 if (manager_window
!= IntPtr
.Zero
) {
258 Gdk
.Window gdkwin
= Gdk
.Window
.ForeignNewForDisplay (Display
, (uint) manager_window
);
260 if (gdkwin
!= null) {
261 gdkwin
.RemoveFilter (filter
);
264 manager_window
= IntPtr
.Zero
;
265 UpdateManagerWindow (true);
269 private void GetOrientationProperty ()
275 IntPtr nitems
, bytes_after
;
278 if (manager_window
== IntPtr
.Zero
) {
282 display
= gdk_x11_display_get_xdisplay (Display
.Handle
);
284 gdk_error_trap_push ();
287 result
= XGetWindowProperty (display
, manager_window
, orientation_atom
, (IntPtr
) 0,
288 (IntPtr
) System
.Int32
.MaxValue
, false, (IntPtr
) XAtom
.Cardinal
, out type
, out format
,
289 out nitems
, out bytes_after
, out prop_return
);
291 error
= gdk_error_trap_pop ();
293 if (error
!= 0 || result
!= 0) {
297 if (type
== (IntPtr
) XAtom
.Cardinal
) {
298 orientation
= ((SystemTrayOrientation
) Marshal
.ReadInt32 (prop_return
) == SystemTrayOrientation
.Horz
)
299 ? Orientation
.Horizontal
300 : Orientation
.Vertical
;
301 if (OrientationChanged
!= null)
302 OrientationChanged (this, orientation
);
305 if (prop_return
!= IntPtr
.Zero
) {
310 public Orientation Orientation
{
316 public delegate void OrientationChangedHandler (NotificationArea area
, Orientation orientation
);
317 public event OrientationChangedHandler OrientationChanged
;
319 [DllImport ("libgdk-x11-2.0.so.0")]
320 private static extern IntPtr
gdk_x11_display_get_xdisplay (IntPtr display
);
322 [DllImport ("libgdk-x11-2.0.so.0")]
323 private static extern int gdk_x11_get_server_time (IntPtr window
);
325 [DllImport ("libgdk-x11-2.0.so.0")]
326 private static extern void gdk_error_trap_push ();
328 [DllImport ("libgdk-x11-2.0.so.0")]
329 private static extern int gdk_error_trap_pop ();
331 [DllImport ("libX11.so.6")]
332 private extern static IntPtr
XInternAtom(IntPtr display
, string atom_name
, bool only_if_exists
);
334 [DllImport ("libX11.so.6")]
335 private extern static void XGrabServer (IntPtr display
);
337 [DllImport ("libX11.so.6")]
338 private extern static void XUngrabServer (IntPtr display
);
340 [DllImport ("libX11.so.6")]
341 private extern static int XFlush (IntPtr display
);
343 [DllImport ("libX11.so.6")]
344 private extern static int XSync (IntPtr display
, bool discard
);
346 [DllImport ("libX11.so.6")]
347 private extern static int XFree (IntPtr display
);
349 [DllImport ("libX11.so.6")]
350 private extern static IntPtr
XGetSelectionOwner (IntPtr display
, IntPtr atom
);
352 [DllImport ("libX11.so.6")]
353 private extern static IntPtr
XSelectInput (IntPtr display
, IntPtr window
, IntPtr mask
);
355 [DllImport ("libX11.so.6")]
356 private extern static int XSendEvent(IntPtr display
, IntPtr window
, bool propagate
, IntPtr event_mask
,
357 ref XClientMessageEvent send_event
);
359 [DllImport("libX11.so.6")]
360 private extern static int XGetWindowProperty(IntPtr display
, IntPtr w
, IntPtr property
, IntPtr long_offset
,
361 IntPtr long_length
, bool deleteProp
, IntPtr req_type
,
362 out IntPtr actual_type_return
, out int actual_format_return
,
363 out IntPtr nitems_return
, out IntPtr bytes_after_return
,
364 out IntPtr prop_return
);
367 private enum EventMask
{
369 KeyPressMask
= 1 << 0,
370 KeyReleaseMask
= 1 << 1,
371 ButtonPressMask
= 1 << 2,
372 ButtonReleaseMask
= 1 << 3,
373 EnterWindowMask
= 1 << 4,
374 LeaveWindowMask
= 1 << 5,
375 PointerMotionMask
= 1 << 6,
376 PointerMotionHintMask
= 1 << 7,
377 Button1MotionMask
= 1 << 8,
378 Button2MotionMask
= 1 << 9,
379 Button3MotionMask
= 1 << 10,
380 Button4MotionMask
= 1 << 11,
381 Button5MotionMask
= 1 << 12,
382 ButtonMotionMask
= 1 << 13,
383 KeymapStateMask
= 1 << 14,
384 ExposureMask
= 1 << 15,
385 VisibilityChangeMask
= 1 << 16,
386 StructureNotifyMask
= 1 << 17,
387 ResizeRedirectMask
= 1 << 18,
388 SubstructureNotifyMask
= 1 << 19,
389 SubstructureRedirectMask
= 1 << 20,
390 FocusChangeMask
= 1 << 21,
391 PropertyChangeMask
= 1 << 22,
392 ColormapChangeMask
= 1 << 23,
393 OwnerGrabButtonMask
= 1 << 24
396 private enum SystemTrayMessage
{
402 private enum SystemTrayOrientation
{
407 private enum XEventName
{
421 VisibilityNotify
= 15,
428 ConfigureNotify
= 22,
429 ConfigureRequest
= 23,
432 CirculateNotify
= 26,
433 CirculateRequest
= 27,
436 SelectionRequest
= 30,
437 SelectionNotify
= 31,
450 [StructLayout(LayoutKind
.Sequential
)]
451 private struct XAnyEvent
453 internal XEventName type
;
454 internal IntPtr serial
;
455 internal bool send_event
;
456 internal IntPtr display
;
457 internal IntPtr window
;
460 [StructLayout(LayoutKind
.Sequential
)]
461 private struct XPropertyEvent
463 internal XEventName type
;
464 internal IntPtr serial
;
465 internal bool send_event
;
466 internal IntPtr display
;
467 internal IntPtr window
;
468 internal IntPtr atom
;
469 internal IntPtr time
;
473 [StructLayout(LayoutKind
.Sequential
)]
474 private struct XClientMessageEvent
476 internal XEventName type
;
477 internal IntPtr serial
;
478 internal bool send_event
;
479 internal IntPtr display
;
480 internal IntPtr window
;
481 internal IntPtr message_type
;
484 [StructLayout(LayoutKind
.Sequential
)]
485 internal struct DataUnion
487 internal IntPtr ptr1
;
488 internal IntPtr ptr2
;
489 internal IntPtr ptr3
;
490 internal IntPtr ptr4
;
491 internal IntPtr ptr5
;
494 internal DataUnion data
;