mirror of
				https://gitee.com/csharpui/CPF.git
				synced 2025-11-01 00:46:56 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			378 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			378 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using CPF.Controls;
 | |
| using CPF.Drawing;
 | |
| using CPF.Input;
 | |
| using CPF.Platform;
 | |
| using System;
 | |
| using System.Linq;
 | |
| using System.Collections.Generic;
 | |
| using System.Text;
 | |
| using static CPF.Linux.XLib;
 | |
| 
 | |
| namespace CPF.Linux
 | |
| {
 | |
|     class NotifyIcon : XWindow, INotifyIconImpl
 | |
|     {
 | |
|         public NotifyIcon()
 | |
|         {
 | |
|             DisplayHandle = LinuxPlatform.Platform.Display;
 | |
|         }
 | |
|         LinuxPlatform x11info;
 | |
| 
 | |
|         XSetWindowAttributes attr = new XSetWindowAttributes();
 | |
|         protected override void OnCreateWindw()
 | |
|         {
 | |
|             x11info = LinuxPlatform.Platform;
 | |
|             var valueMask = default(SetWindowValuemask);
 | |
|             attr.background_pixel = IntPtr.Zero;
 | |
|             attr.border_pixel = IntPtr.Zero;
 | |
|             attr.backing_store = 1;
 | |
|             attr.bit_gravity = Gravity.NorthWestGravity;
 | |
|             attr.win_gravity = Gravity.NorthWestGravity;
 | |
|             attr.override_redirect = true;
 | |
|             valueMask |= SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel | SetWindowValuemask.BackingStore | SetWindowValuemask.OverrideRedirect
 | |
|                          //| SetWindowValuemask.BackPixmap 
 | |
|                          | SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity;
 | |
|             valueMask |= SetWindowValuemask.SaveUnder;
 | |
| 
 | |
|             attr.colormap = XLib.XCreateColormap(x11info.Info.Display, x11info.Info.RootWindow, x11info.Info.TransparentVisualInfo.visual, 0);
 | |
|             valueMask |= SetWindowValuemask.ColorMap;
 | |
| 
 | |
|             Handle = XLib.XCreateWindow(x11info.Display, x11info.Info.DefaultRootWindow, 0, 0, 16, 16, 0, 32, (int)CreateWindowArgs.InputOutput, x11info.Info.TransparentVisualInfo.visual, new UIntPtr((uint)valueMask), ref attr);
 | |
|             //Handle= XCreateSimpleWindow(x11info.Display, x11info.Info.DefaultRootWindow, 0, 0, 16, 16, 0, IntPtr.Zero, XWhitePixel(x11info.Display,x11info.Info.DefaultScreen)); 
 | |
| 
 | |
|             XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask | XEventMask.PointerMotionHintMask;
 | |
|             var mask = new IntPtr(0xffffff ^ (int)ignoredMask);
 | |
|             XLib.XSelectInput(x11info.Display, Handle, mask);
 | |
|             EventAction = OnXEvent;
 | |
|             popup.Children.Add(textBlock);
 | |
|         }
 | |
|         Bitmap bitmap;
 | |
|         void OnXEvent(ref XEvent ev)
 | |
|         {
 | |
|             if (ev.type == XEventName.Expose)
 | |
|             {
 | |
|                 XWindowAttributes Attr = new XWindowAttributes();
 | |
|                 XGetWindowAttributes(DisplayHandle, Handle, ref Attr);
 | |
| 
 | |
|                 if (icon != null)
 | |
|                 {
 | |
|                     if (bitmap == null || bitmap.Width != Attr.width || bitmap.Height != Attr.height)
 | |
|                     {
 | |
|                         if (bitmap != null)
 | |
|                         {
 | |
|                             bitmap.Dispose();
 | |
|                         }
 | |
|                         bitmap = new Bitmap(Attr.width, Attr.height);
 | |
|                     }
 | |
|                     using (var dc = DrawingContext.FromBitmap(bitmap))
 | |
|                     {
 | |
|                         dc.AntialiasMode = AntialiasMode.AntiAlias;
 | |
|                         dc.Clear(Color.Transparent);
 | |
|                         dc.DrawImage(icon, new Rect(0, 0, bitmap.Width, bitmap.Height), new Rect(0, 0, icon.Width, icon.Height));
 | |
|                     }
 | |
|                 }
 | |
|                 if (bitmap != null)
 | |
|                 {
 | |
|                     using (var l = bitmap.Lock())
 | |
|                     {
 | |
|                         var gc = XCreateGC(x11info.Display, Handle, 0, IntPtr.Zero);
 | |
|                         //XLockDisplay(x11info.Display);
 | |
|                         var img = new XImage();
 | |
|                         int bitsPerPixel = 32;
 | |
|                         img.width = bitmap.Width;
 | |
|                         img.height = bitmap.Height;
 | |
|                         img.format = 2; //ZPixmap;
 | |
|                         img.data = l.DataPointer;
 | |
|                         img.byte_order = 0;// LSBFirst;
 | |
|                         img.bitmap_unit = bitsPerPixel;
 | |
|                         img.bitmap_bit_order = 0;// LSBFirst;
 | |
|                         img.bitmap_pad = bitsPerPixel;
 | |
|                         img.depth = 32;
 | |
|                         img.bytes_per_line = bitmap.Width * 4;
 | |
|                         img.bits_per_pixel = bitsPerPixel;
 | |
|                         XInitImage(ref img);
 | |
| 
 | |
|                         XPutImage(x11info.Display, Handle, gc, ref img, 0, 0, 0, 0, (uint)Attr.width, (uint)Attr.height);
 | |
|                         //XSync(x11info.Display, false);
 | |
|                         //XUnlockDisplay(x11info.Display);
 | |
|                         XFreeGC(x11info.Display, gc);
 | |
|                         //XFlush(x11info.Display);
 | |
|                         //Console.WriteLine(Attr.width + "," + Attr.height);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else if (ev.type == XEventName.ButtonPress)
 | |
|             {
 | |
|                 if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
 | |
|                 {
 | |
|                     MouseButton mouseButton = MouseButton.None;
 | |
|                     switch (ev.ButtonEvent.button)
 | |
|                     {
 | |
|                         case 1:
 | |
|                             mouseButton = MouseButton.Left;
 | |
|                             break;
 | |
|                         case 2:
 | |
|                             mouseButton = MouseButton.Middle;
 | |
|                             break;
 | |
|                         case 3:
 | |
|                             mouseButton = MouseButton.Right;
 | |
|                             break;
 | |
|                         case 8:
 | |
|                             mouseButton = MouseButton.XButton1;
 | |
|                             break;
 | |
|                         case 9:
 | |
|                             mouseButton = MouseButton.XButton2;
 | |
|                             break;
 | |
|                     }
 | |
|                     if (MouseDown != null)
 | |
|                     {
 | |
|                         MouseDown(this, new NotifyIconMouseEventArgs(mouseButton));
 | |
|                     }
 | |
|                     if (mouseButton == MouseButton.Left)
 | |
|                     {
 | |
|                         var time = DateTime.Now;
 | |
|                         if (mouseDownTime.HasValue)
 | |
|                         {
 | |
|                             if (time - mouseDownTime <= LinuxPlatform.Platform.DoubleClickTime)
 | |
|                             {
 | |
|                                 if (DoubleClick != null)
 | |
|                                 {
 | |
|                                     DoubleClick(this, EventArgs.Empty);
 | |
|                                 }
 | |
|                                 mouseDownTime = null;
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 mouseDownTime = time;
 | |
|                             }
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             mouseDownTime = time;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else if (ev.type == XEventName.ButtonRelease)
 | |
|             {
 | |
|                 if (ev.ButtonEvent.button < 4 || ev.ButtonEvent.button == 8 || ev.ButtonEvent.button == 9)
 | |
|                 {
 | |
|                     MouseButton mouseButton = MouseButton.None;
 | |
|                     switch (ev.ButtonEvent.button)
 | |
|                     {
 | |
|                         case 1:
 | |
|                             mouseButton = MouseButton.Left;
 | |
|                             break;
 | |
|                         case 2:
 | |
|                             mouseButton = MouseButton.Middle;
 | |
|                             break;
 | |
|                         case 3:
 | |
|                             mouseButton = MouseButton.Right;
 | |
|                             break;
 | |
|                         case 8:
 | |
|                             mouseButton = MouseButton.XButton1;
 | |
|                             break;
 | |
|                         case 9:
 | |
|                             mouseButton = MouseButton.XButton2;
 | |
|                             break;
 | |
|                     }
 | |
|                     if (MouseUp != null)
 | |
|                     {
 | |
|                         MouseUp(this, new NotifyIconMouseEventArgs(mouseButton));
 | |
|                     }
 | |
|                     if (mouseButton == MouseButton.Left)
 | |
|                     {
 | |
|                         if (Click != null)
 | |
|                         {
 | |
|                             Click(this, EventArgs.Empty);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else if (ev.type == XEventName.MotionNotify)
 | |
|             {
 | |
|                 if (!isMouseEnter)
 | |
|                 {
 | |
|                     isMouseEnter = true;
 | |
|                     if (!string.IsNullOrWhiteSpace(Text))
 | |
|                     {
 | |
|                         textBlock.Text = Text;
 | |
|                         popup.Placement = PlacementMode.Absolute;
 | |
|                         var p = MouseDevice.Location;
 | |
|                         popup.MarginLeft = p.X / popup.LayoutScaling + 10;
 | |
|                         if (p.X < 100)
 | |
|                         {
 | |
|                             popup.MarginTop = p.Y / popup.LayoutScaling + 10;
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             popup.MarginTop = p.Y / popup.LayoutScaling - 30;
 | |
|                         }
 | |
|                         popup.LoadStyle(Window.Windows.FirstOrDefault(a => a.IsMain));
 | |
|                         popup.Show();
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             else if (ev.type == XEventName.LeaveNotify)
 | |
|             {
 | |
|                 isMouseEnter = false;
 | |
|                 if (popup.Visibility == Visibility.Visible)
 | |
|                 {
 | |
|                     popup.Width = "auto";
 | |
|                     popup.Height = "auto";
 | |
|                     popup.Hide();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         TextBlock textBlock = new TextBlock { Margin = "2" };
 | |
|         Popup popup = new Popup
 | |
|         {
 | |
|             CanActivate = false,
 | |
|             StaysOpen = false,
 | |
|             BorderFill = "#aaa",
 | |
|             Background = "#fff",
 | |
|             BorderStroke = "1",
 | |
|         };
 | |
|         bool isMouseEnter;
 | |
|         IntPtr DisplayHandle;
 | |
|         IntPtr SystrayMgrWindow;
 | |
| 
 | |
|         public string Text { get; set; }
 | |
|         Image icon;
 | |
|         public Image Icon
 | |
|         {
 | |
|             get { return icon; }
 | |
|             set
 | |
|             {
 | |
|                 icon = value;
 | |
|                 Invalidate();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void Invalidate()
 | |
|         {
 | |
|             XWindowAttributes Attr = new XWindowAttributes();
 | |
|             XGetWindowAttributes(DisplayHandle, Handle, ref Attr);
 | |
|             var xev = new XEvent
 | |
|             {
 | |
|                 ExposeEvent =
 | |
|                     {
 | |
|                         type = XEventName.Expose,
 | |
|                         send_event = true,
 | |
|                         window = Handle,
 | |
|                         count=1,
 | |
|                         display=x11info.Display,
 | |
|                         height=Attr.height,
 | |
|                         width= Attr.width,
 | |
|                     }
 | |
|             };
 | |
|             lock (XlibLock)
 | |
|             {
 | |
|                 XSendEvent(x11info.Display, Handle, false,
 | |
|                     new IntPtr((int)(EventMask.ExposureMask)), ref xev);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         //public ContextMenu ContextMenu { get; set; }
 | |
| 
 | |
|         bool visible;
 | |
|         DateTime? mouseDownTime;
 | |
|         public event EventHandler Click;
 | |
|         public event EventHandler DoubleClick;
 | |
|         public event EventHandler<NotifyIconMouseEventArgs> MouseDown;
 | |
|         public event EventHandler<NotifyIconMouseEventArgs> MouseUp;
 | |
| 
 | |
|         bool maped;
 | |
|         public bool Visible
 | |
|         {
 | |
|             get { return visible; }
 | |
|             set
 | |
|             {
 | |
|                 //System.Threading.Thread.Sleep(10000);
 | |
|                 visible = value;
 | |
|                 if (visible)
 | |
|                 {
 | |
|                     XGrabServer(DisplayHandle);
 | |
|                     SystrayMgrWindow = XGetSelectionOwner(DisplayHandle, LinuxPlatform.Platform.Info.Atoms._NET_SYSTEM_TRAY_S);
 | |
|                     XUngrabServer(DisplayHandle);
 | |
|                     XFlush(DisplayHandle);
 | |
| 
 | |
| 
 | |
|                     if (SystrayMgrWindow != IntPtr.Zero)
 | |
|                     {
 | |
|                         //XSelectInput(DisplayHandle, SystrayMgrWindow, (IntPtr)XEventMask.StructureNotifyMask);
 | |
| 
 | |
|                         XSizeHints size_hints;
 | |
| 
 | |
|                         // We are going to be directly mapped by the system tray, so mark as mapped
 | |
|                         // so we can later properly unmap it.
 | |
|                         //XWindowAttributes attributes = new XWindowAttributes();
 | |
|                         //XGetWindowAttributes(x11info.Display, XDefaultRootWindow(x11info.Display), ref attributes);
 | |
|                         //Console.WriteLine(attributes.width + "," + attributes.height);
 | |
| 
 | |
|                         size_hints = new XSizeHints();
 | |
| 
 | |
|                         size_hints.flags = (IntPtr)(XSizeHintsFlags.PMinSize | XSizeHintsFlags.PMaxSize | XSizeHintsFlags.PBaseSize);
 | |
| 
 | |
|                         size_hints.min_width = 16;
 | |
|                         size_hints.min_height = 16;
 | |
|                         size_hints.max_width = 16;
 | |
|                         size_hints.max_height = 16;
 | |
|                         size_hints.base_width = 16;
 | |
|                         size_hints.base_height = 16;
 | |
| 
 | |
|                         XSetWMNormalHints(DisplayHandle, Handle, ref size_hints);
 | |
| 
 | |
|                         IntPtr[] atoms = new IntPtr[2];
 | |
|                         atoms[0] = (IntPtr)1;           // Version 1
 | |
|                         atoms[1] = (IntPtr)1;           // we want to be mapped
 | |
| 
 | |
|                         XChangeProperty(DisplayHandle, Handle, LinuxPlatform.Platform.Info.Atoms._XEMBED_INFO, LinuxPlatform.Platform.Info.Atoms._XEMBED_INFO, 32, PropertyMode.Replace, atoms, 2);
 | |
| 
 | |
|                         SendNetClientMessage(SystrayMgrWindow, LinuxPlatform.Platform.Info.Atoms._NET_SYSTEM_TRAY_OPCODE, IntPtr.Zero, (IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK, Handle);
 | |
|                         //XReparentWindow(DisplayHandle, Handle, SystrayMgrWindow, 0, 0);
 | |
|                         Invalidate();
 | |
|                         if (maped)
 | |
|                         {
 | |
|                             XMapWindow(x11info.Display, Handle);
 | |
|                         }
 | |
|                         maped = true;
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     XUnmapWindow(DisplayHandle, Handle);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void SendNetClientMessage(IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2)
 | |
|         {
 | |
|             XEvent xev;
 | |
| 
 | |
|             xev = new XEvent();
 | |
|             xev.ClientMessageEvent.type = XEventName.ClientMessage;
 | |
|             xev.ClientMessageEvent.send_event = true;
 | |
|             xev.ClientMessageEvent.window = window;
 | |
|             xev.ClientMessageEvent.message_type = message_type;
 | |
|             xev.ClientMessageEvent.format = 32;
 | |
|             xev.ClientMessageEvent.ptr1 = l0;
 | |
|             xev.ClientMessageEvent.ptr2 = l1;
 | |
|             xev.ClientMessageEvent.ptr3 = l2;
 | |
|             XSendEvent(DisplayHandle, window, false, new IntPtr((int)EventMask.NoEventMask), ref xev);
 | |
|             //XSync(DisplayHandle, false);
 | |
|         }
 | |
| 
 | |
|         protected override void Dispose(bool disposing)
 | |
|         {
 | |
|             if (bitmap != null)
 | |
|             {
 | |
|                 bitmap.Dispose();
 | |
|                 bitmap = null;
 | |
|             }
 | |
|             base.Dispose(disposing);
 | |
|         }
 | |
| 
 | |
|     }
 | |
| }
 | 
