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 Beagle.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;