1 //
2 // Copyright notice and author unknown.
3 // Source extracted from: http://www.mono-project.com/GtkSharpNotificationIcon
4 //
5 
6 using System;
7 using System.Runtime.InteropServices;
8 using Gtk;
9 using Gdk;
10 
11 namespace OpenVPN.Admin
12 {
13 	public class TrayIcon : Plug
14 	{
15 		int selection_atom;
16 		int system_tray_opcode_atom;
17 		IntPtr manager_window;
18 
TrayIcon(string name)19 		public TrayIcon (string name)
20 		{
21 			AddEvents ((int)EventMask.PropertyChangeMask);
22 		}
23 
OnRealized()24 		protected override void OnRealized ()
25 		{
26 			base.OnRealized ();
27 			Display display = Screen.Display;
28 			IntPtr xdisplay = gdk_x11_display_get_xdisplay (display.Handle);
29 			selection_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_S" + Screen.Number.ToString (), false);
30 
31 			XInternAtom (xdisplay, "MANAGER", false);
32 			system_tray_opcode_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_OPCODE", false);
33 
34 			XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_ORIENTATION", false);
35 			UpdateManagerWindow ();
36 		}
37 
OnUnrealized()38 		protected override void OnUnrealized ()
39 		{
40 			if (manager_window != IntPtr.Zero)
41 				Gdk.Window.LookupForDisplay (Display, (uint)manager_window);
42 
43 			base.OnUnrealized ();
44 		}
45 
UpdateManagerWindow()46 		private void UpdateManagerWindow ()
47 		{
48 			IntPtr xdisplay = gdk_x11_display_get_xdisplay (Display.Handle);
49 			if (manager_window != IntPtr.Zero)
50 				Gdk.Window.LookupForDisplay (Display, (uint)manager_window);
51 
52 			XGrabServer (xdisplay);
53 
54 			manager_window = XGetSelectionOwner (xdisplay, selection_atom);
55 			if (manager_window != IntPtr.Zero)
56 				XSelectInput (xdisplay, manager_window, EventMask.StructureNotifyMask | EventMask.PropertyChangeMask);
57 			XUngrabServer (xdisplay);
58 			XFlush (xdisplay);
59 
60 			if (manager_window != IntPtr.Zero) {
61 				Gdk.Window.LookupForDisplay (Display, (uint)manager_window);
62 				SendDockRequest ();
63 			}
64 		}
65 
SendDockRequest()66 		private void SendDockRequest ()
67 		{
68 			SendManagerMessage (SystemTrayMessage.RequestDock, manager_window, Id, 0, 0);
69 		}
70 
SendManagerMessage(SystemTrayMessage message, IntPtr window, uint data1, uint data2, uint data3)71 		private void SendManagerMessage (SystemTrayMessage message, IntPtr window, uint data1, uint data2, uint data3)
72 		{
73 			XClientMessageEvent ev = new XClientMessageEvent ();
74 			IntPtr display;
75 
76 			ev.type = XEventName.ClientMessage;
77 			ev.window = window;
78 			ev.message_type = (IntPtr)system_tray_opcode_atom;
79 			ev.format = 32;
80 			ev.ptr1 = gdk_x11_get_server_time (GdkWindow.Handle);
81 			ev.ptr2 = (IntPtr)message;
82 			ev.ptr3 = (IntPtr)data1;
83 			ev.ptr4 = (IntPtr)data2;
84 			ev.ptr5 = (IntPtr)data3;
85 
86 			display = gdk_x11_display_get_xdisplay (Display.Handle);
87 			gdk_error_trap_push ();
88 			XSendEvent (display, manager_window, false, EventMask.NoEventMask, ref ev);
89 			gdk_error_trap_pop ();
90 		}
91 
92 		[DllImport ("gdk-x11-2.0.so.0")]
gdk_x11_display_get_xdisplay(IntPtr display)93 		static extern IntPtr gdk_x11_display_get_xdisplay (IntPtr display);
94 		[DllImport ("gdk-x11-2.0.so.0")]
gdk_x11_get_server_time(IntPtr window)95 		static extern IntPtr gdk_x11_get_server_time (IntPtr window);
96 		[DllImport ("gdk-x11-2.0.so.0")]
gdk_error_trap_push()97 		static extern void gdk_error_trap_push ();
98 		[DllImport ("gdk-x11-2.0.so.0")]
gdk_error_trap_pop()99 		static extern void gdk_error_trap_pop ();
100 
101 		[DllImport ("libX11", EntryPoint="XInternAtom")]
XInternAtom(IntPtr display, string atom_name, bool only_if_exists)102 		extern static int XInternAtom(IntPtr display, string atom_name, bool only_if_exists);
103 		[DllImport ("libX11")]
XGrabServer(IntPtr display)104 		extern static void XGrabServer (IntPtr display);
105 		[DllImport ("libX11")]
XUngrabServer(IntPtr display)106 		extern static void XUngrabServer (IntPtr display);
107 		[DllImport ("libX11")]
XFlush(IntPtr display)108 		extern static int XFlush (IntPtr display);
109 		[DllImport ("libX11")]
XGetSelectionOwner(IntPtr display, int atom)110 		extern static IntPtr XGetSelectionOwner (IntPtr display, int atom);
111 		[DllImport ("libX11")]
XSelectInput(IntPtr window, IntPtr display, EventMask mask)112 		extern static IntPtr XSelectInput (IntPtr window, IntPtr display, EventMask mask);
113 		[DllImport ("libX11", EntryPoint="XSendEvent")]
XSendEvent(IntPtr display, IntPtr window, bool propagate, EventMask event_mask, ref XClientMessageEvent send_event)114 		extern static int XSendEvent(IntPtr display, IntPtr window, bool propagate, EventMask event_mask, ref XClientMessageEvent send_event);
115 	}
116 
117 	[Flags]
118 	internal enum EventMask
119 	{
120 		NoEventMask             = 0,
121 		KeyPressMask            = 1<<0,
122 		KeyReleaseMask          = 1<<1,
123 		ButtonPressMask         = 1<<2,
124 		ButtonReleaseMask       = 1<<3,
125 		EnterWindowMask         = 1<<4,
126 		LeaveWindowMask         = 1<<5,
127 		PointerMotionMask       = 1<<6,
128 		PointerMotionHintMask   = 1<<7,
129 		Button1MotionMask       = 1<<8,
130 		Button2MotionMask       = 1<<9,
131 		Button3MotionMask       = 1<<10,
132 		Button4MotionMask       = 1<<11,
133 		Button5MotionMask       = 1<<12,
134 		ButtonMotionMask        = 1<<13,
135 		KeymapStateMask         = 1<<14,
136 		ExposureMask            = 1<<15,
137 		VisibilityChangeMask    = 1<<16,
138 		StructureNotifyMask     = 1<<17,
139 		ResizeRedirectMask      = 1<<18,
140                 SubstructureNotifyMask  = 1<<19,
141 		SubstructureRedirectMask= 1<<20,
142 		FocusChangeMask         = 1<<21,
143 		PropertyChangeMask      = 1<<22,
144 		ColormapChangeMask      = 1<<23,
145 		OwnerGrabButtonMask     = 1<<24
146 	}
147 
148 	internal enum SystemTrayMessage
149 	{
150 		RequestDock,
151 		BeginMessage,
152 		CancelMessage
153 	}
154 
155 	internal enum SystemTrayOrientation
156 	{
157 		Horz,
158 		Vert
159 	}
160 
161 	[StructLayout(LayoutKind.Sequential)]
162 	internal struct XClientMessageEvent
163 	{
164 		internal XEventName     type;
165 		internal IntPtr         serial;
166 		internal bool           send_event;
167 		internal IntPtr         display;
168 		internal IntPtr         window;
169 		internal IntPtr         message_type;
170 		internal int            format;
171 		internal IntPtr         ptr1;
172 		internal IntPtr         ptr2;
173 		internal IntPtr         ptr3;
174 		internal IntPtr         ptr4;
175 		internal IntPtr         ptr5;
176 	}
177 
178 	internal enum XEventName
179 	{
180 		KeyPress                = 2,
181 		KeyRelease              = 3,
182 		ButtonPress             = 4,
183 		ButtonRelease           = 5,
184 		MotionNotify            = 6,
185 		EnterNotify             = 7,
186 		LeaveNotify             = 8,
187 		FocusIn                 = 9,
188 		FocusOut                = 10,
189 		KeymapNotify            = 11,
190 		Expose                  = 12,
191 		GraphicsExpose          = 13,
192 		NoExpose                = 14,
193 		VisibilityNotify        = 15,
194 		CreateNotify            = 16,
195 		DestroyNotify           = 17,
196 		UnmapNotify             = 18,
197 		MapNotify               = 19,
198 		MapRequest              = 20,
199 		ReparentNotify          = 21,
200 		ConfigureNotify         = 22,
201 		ConfigureRequest        = 23,
202 		GravityNotify           = 24,
203 		ResizeRequest           = 25,
204 		CirculateNotify         = 26,
205 		CirculateRequest        = 27,
206 		PropertyNotify          = 28,
207 		SelectionClear          = 29,
208 		SelectionRequest        = 30,
209 		SelectionNotify         = 31,
210 		ColormapNotify          = 32,
211 		ClientMessage           = 33,
212 		MappingNotify           = 34,
213 		TimerNotify             = 100,
214 
215 		LASTEvent
216 	}
217 }