using System; using System.Collections; using System.Collections.Generic; using System.Text; using CPF.Controls; using CPF.Drawing; using CPF.Platform; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using CPF.Input; using static CPF.Linux.XLib; using CPF.OpenGL; namespace CPF.Linux { public class XWindow : IDisposable { internal static object XlibLock = new object(); public XWindow() { OnCreateWindw(); LinuxPlatform.Platform.windows.Add(Handle, this); } protected virtual void OnCreateWindw() { Handle = XCreateSimpleWindow(LinuxPlatform.Platform.Display, LinuxPlatform.Platform.Info.DefaultRootWindow, 0, 0, 1, 1, 0, IntPtr.Zero, IntPtr.Zero); } public IntPtr Handle { get; protected set; } public OnEventHandler EventAction { get; set; } public virtual void OnEvent(ref XEvent e) { if (EventAction != null) { EventAction(ref e); } } #region IDisposable Support private bool disposedValue = false; // 要检测冗余调用 protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: 释放托管状态(托管对象)。 } if (Handle != IntPtr.Zero) { XDestroyWindow(LinuxPlatform.Platform.Display, Handle); LinuxPlatform.Platform.windows.Remove(Handle); Handle = IntPtr.Zero; } disposedValue = true; } } // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。 ~XWindow() { // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 Dispose(false); } // 添加此代码以正确实现可处置模式。 public void Dispose() { // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。 Dispose(true); // TODO: 如果在以上内容中替代了终结器,则取消注释以下行。 // GC.SuppressFinalize(this); } #endregion } public delegate void OnEventHandler(ref XEvent xEvent); public unsafe class X11Window : XWindow, IWindowImpl { internal static IntPtr mouseDownWindow; public static X11Window main; protected override void OnCreateWindw() { if (main == null) { //Thread.Sleep(10000); main = this; } x11info = LinuxPlatform.Platform.Info; XSetWindowAttributes attr = new XSetWindowAttributes(); 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 = this is PopWindow; valueMask |= SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel | SetWindowValuemask.BackingStore | SetWindowValuemask.OverrideRedirect //| SetWindowValuemask.BackPixmap | SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity; var depth = 32;// (int)x11info.TransparentVisualInfo.depth; attr.colormap = XCreateColormap(x11info.Display, x11info.RootWindow, x11info.TransparentVisualInfo.visual, 0); valueMask |= SetWindowValuemask.ColorMap; size = new PixelSize(300, 200); position = new PixelPoint(10, 10); Handle = XCreateWindow(x11info.Display, x11info.RootWindow, 10, 10, size.Width, size.Height, 0, depth, (int)CreateWindowArgs.InputOutput, x11info.TransparentVisualInfo.visual, new UIntPtr((uint)valueMask), ref attr); // var attributes = new XSetWindowAttributes(); // attributes.background_pixel = XWhitePixel(x11info.Display, x11info.DefaultScreen); // Handle = XCreateWindow(x11info.Display, x11info.RootWindow, //0, 0, 100, 100, 0, depth, //(int)CreateWindowArgs.InputOutput, XDefaultVisual(x11info.Display, 0) //, ((ulong)SetWindowValuemask.BackPixel), ref attributes); //Handle = XCreateSimpleWindow(x11info.Display, x11info.RootWindow, 10, 10, 100, 100, 0, XWhitePixel(x11info.Display, x11info.DefaultScreen), XWhitePixel(x11info.Display, x11info.DefaultScreen)); // var attributes = new XSetWindowAttributes(); // attributes.background_pixel = XWhitePixel(x11info.Display, x11info.DefaultScreen); // Handle = XCreateWindow(x11info.Display, x11info.RootWindow, //0, 0, 100, 100, 0, depth, //(int)CreateWindowArgs.InputOutput, XDefaultVisual(x11info.Display, 0) //, ((ulong)SetWindowValuemask.BackPixel), ref attributes); var protocols = new[] { x11info.Atoms.WM_DELETE_WINDOW }; XSetWMProtocols(x11info.Display, Handle, protocols, protocols.Length); XChangeProperty(x11info.Display, Handle, x11info.Atoms._NET_WM_WINDOW_TYPE, x11info.Atoms.XA_ATOM, 32, PropertyMode.Replace, new[] { x11info.Atoms._NET_WM_WINDOW_TYPE_NORMAL }, 1); SetWmClass("CPFApplication"); UpdateMotifHints(); var def = IntPtr.Zero; var fontset = XCreateFontSet(x11info.Display, "-*-*-*-*-*-*-14-*-*-*-*-*-*-*", out var missing_charset_list_return, out var missing_charset_count_return, ref def); //Console.WriteLine("Display:" + x11info.Display + " fontset:" + fontset + " missing_charset_count_return:" + missing_charset_count_return); //var list = XVaCreateNestedList(0, XNames.XNFontSet, fontset, IntPtr.Zero); XPoint spot = new XPoint(); var pSL = Marshal.StringToHGlobalAnsi(XNames.XNSpotLocation); var pFS = Marshal.StringToHGlobalAnsi(XNames.XNFontSet); var list = XVaCreateNestedList(0, pSL, spot, pFS, fontset, IntPtr.Zero); //Console.WriteLine("list:" + list); //Console.WriteLine(string.Join(" - ", GetSupportedInputStyles(x11info.Xim))); //UpdateSizeHints(null); xic = XCreateIC(x11info.Xim, XNames.XNInputStyle, styleOverTheSpot, XNames.XNClientWindow, Handle, XNames.XNPreeditAttributes, list, IntPtr.Zero); //xic = LinuxPlatform.CreateIC(x11info.Xim, Handle); //Console.WriteLine("xic:" + xic); if (xic == IntPtr.Zero) { //ximStyle = styleRoot; xic = XCreateIC(x11info.Xim, XNames.XNInputStyle, styleRoot, XNames.XNClientWindow, Handle, XNames.XNFocusWindow, Handle, IntPtr.Zero); Console.WriteLine("xic:" + xic); } if (pSL != IntPtr.Zero) Marshal.FreeHGlobal(pSL); if (pFS != IntPtr.Zero) Marshal.FreeHGlobal(pFS); XFree(list); //long im_event_mask = 0; ////XIMStyles xIMStyles = new XIMStyles(); //var icv = XGetICValues(_xic, XNames.XNFilterEvents, ref im_event_mask, IntPtr.Zero); //Console.WriteLine(icv); XEventMask ignoredMask = XEventMask.SubstructureRedirectMask | XEventMask.ResizeRedirectMask | XEventMask.PointerMotionHintMask; if (LinuxPlatform.Platform.XI2 != null) LinuxPlatform.Platform.XI2.AddWindow(Handle); var mask = new IntPtr(0xffffff ^ (int)ignoredMask); //lock (XlibLock) //{ XSelectInput(x11info.Display, Handle, mask); //} //启用拖拽 IntPtr[] XdndVersion = new IntPtr[] { new IntPtr(4) }; int[] atoms; atoms = new int[XdndVersion.Length]; for (int i = 0; i < XdndVersion.Length; i++) { atoms[i] = XdndVersion[i].ToInt32(); } XChangeProperty(x11info.Display, Handle, x11info.Atoms.XdndAware, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, atoms, 1); ScreenImpl.GetDpi(); XFlush(x11info.Display); //Thread.Sleep(10000); //var att = GetXWindowAttributes(); //Console.WriteLine(att); if (Application.GetDrawingFactory().UseGPU) { //Thread.Sleep(5000); context = new OpenGL.GlxContext(this); if (context.Context == IntPtr.Zero) { Application.GetDrawingFactory().UseGPU = false; context = null; } } } CPF.Linux.OpenGL.GlxContext context; //private IEnumerable GetMatchingStylesInPreferredOrder(IntPtr xim) //{ // XIMProperties[] supportedStyles = GetSupportedInputStyles(xim); // foreach (XIMProperties p in GetPreferredStyles()) // if (Array.IndexOf(supportedStyles, p) >= 0) // yield return p; //} public static XIMProperties[] GetSupportedInputStyles(IntPtr xim) { //Thread.Sleep(10000); IntPtr stylesPtr; string ret = XGetIMValues(xim, XNames.XNQueryInputStyle, out stylesPtr, IntPtr.Zero); if (ret != null || stylesPtr == IntPtr.Zero) return new XIMProperties[0]; XIMStyles styles = (XIMStyles)Marshal.PtrToStructure(stylesPtr, typeof(XIMStyles)); XIMProperties[] supportedStyles = new XIMProperties[styles.count_styles]; var p = (long*)styles.supported_styles; for (int i = 0; i < styles.count_styles; i++) { //supportedStyles[i] = (XIMProperties)Marshal.PtrToStructure(new IntPtr((long)styles.supported_styles + i * Marshal.SizeOf (typeof (IntPtr)))), typeof(XIMProperties)); supportedStyles[i] = (XIMProperties)p[i]; } XFree(stylesPtr); return supportedStyles; } const XIMProperties styleRoot = XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing; const XIMProperties styleOverTheSpot = XIMProperties.XIMPreeditPosition | XIMProperties.XIMStatusNothing; const XIMProperties styleOnTheSpot = XIMProperties.XIMPreeditCallbacks | XIMProperties.XIMStatusNothing; //private XIMProperties[] GetPreferredStyles() //{ // var env = "over-the-spot"; // string[] list = env.Split(' '); // XIMProperties[] ret = new XIMProperties[list.Length]; // for (int i = 0; i < list.Length; i++) // { // string s = list[i]; // switch (s) // { // case "over-the-spot": // ret[i] = styleOverTheSpot; // break; // case "on-the-spot": // ret[i] = styleOnTheSpot; // break; // case "root": // ret[i] = styleRoot; // break; // } // } // return ret; //} private void SetWmClass(string wmClass) { var data = Encoding.ASCII.GetBytes(wmClass); fixed (void* pdata = data) { XChangeProperty(x11info.Display, Handle, x11info.Atoms.XA_WM_CLASS, x11info.Atoms.XA_STRING, 8, PropertyMode.Replace, pdata, data.Length); } } X11Info x11info; IntPtr xic; //X11Window parent; internal View root; PixelSize size; PixelPoint position; //private bool have_Xutf8ResetIC = true; public PixelSize Size { get { return size; } } void UpdateMotifHints() { var functions = MotifFunctions.Move | MotifFunctions.Close | MotifFunctions.Resize | MotifFunctions.Minimize | MotifFunctions.Maximize; ;// //var decorations = MotifDecorations.Menu | MotifDecorations.Title | MotifDecorations.Border | // MotifDecorations.Maximize | MotifDecorations.Minimize | MotifDecorations.ResizeH; //if (_popup || _systemDecorations == SystemDecorations.None) //{ // decorations = 0; //} //else if (_systemDecorations == SystemDecorations.BorderOnly) //{ // decorations = MotifDecorations.Border; //} //if (!_canResize || _systemDecorations == SystemDecorations.BorderOnly) //{ // functions &= ~(MotifFunctions.Resize | MotifFunctions.Maximize); // decorations &= ~(MotifDecorations.Maximize | MotifDecorations.ResizeH); //} var hints = new MotifWmHints { flags = new IntPtr((int)(MotifFlags.Decorations | MotifFlags.Functions)),// //decorations = new IntPtr((int)decorations), functions = new IntPtr((int)functions) }; XChangeProperty(x11info.Display, Handle, x11info.Atoms._MOTIF_WM_HINTS, x11info.Atoms._MOTIF_WM_HINTS, 32, PropertyMode.Replace, ref hints, 5); } void UpdateSizeHints(PixelSize? preResize) { var size = Screen.WorkingArea.Size; var min = new PixelSize(1, 1); //_minMaxSize.minSize; var max = new PixelSize((int)size.Width, (int)size.Height);//_minMaxSize.maxSize; //if (!_canResize || _systemDecorations == SystemDecorations.BorderOnly) // max = min = _realSize; if (preResize.HasValue) { var desired = preResize.Value; max = new PixelSize(Math.Max(desired.Width, max.Width), Math.Max(desired.Height, max.Height)); min = new PixelSize(Math.Min(desired.Width, min.Width), Math.Min(desired.Height, min.Height)); } var hints = new XSizeHints { min_width = min.Width, min_height = min.Height }; hints.height_inc = hints.width_inc = 1; var flags = XSizeHintsFlags.PMinSize | XSizeHintsFlags.PResizeInc; // People might be passing double.MaxValue if (max.Width < 100000 && max.Height < 100000) { hints.max_width = max.Width; hints.max_height = max.Height; flags |= XSizeHintsFlags.PMaxSize; } hints.flags = (IntPtr)flags; XSetWMNormalHints(x11info.Display, Handle, ref hints); } XWindowAttributes GetXWindowAttributes() { XWindowAttributes attributes = new XWindowAttributes(); XGetWindowAttributes(x11info.Display, Handle, ref attributes); return attributes; } //bool _mapped; Bitmap bitmap; public override void OnEvent(ref XEvent e) { //Console.WriteLine(e.type); var ev = e; //if (ev.AnyEvent.type == XEventName.KeyPress || // ev.AnyEvent.type == XEventName.KeyRelease) //{ // // PreFilter() handles "shift key state updates. // //Keyboard.PreFilter(xevent); // if (XFilterEvent(ref ev, Handle)) // { // // probably here we could raise WM_IME_KEYDOWN and // // WM_IME_KEYUP, but I'm not sure it is worthy. // return; // } //} //else if (XFilterEvent(ref ev, IntPtr.Zero)) //{ return; } if (ev.type == XEventName.MapNotify) { //_mapped = true; //if (_useRenderWindow) // XMapWindow(_x11.Display, _renderHandle); } else if (ev.type == XEventName.UnmapNotify) { //_mapped = false; } else if (ev.type == XEventName.Expose) { var rect = new Rect(ev.ExposeEvent.x, ev.ExposeEvent.y, ev.ExposeEvent.width <= 0 ? size.Width : ev.ExposeEvent.width, ev.ExposeEvent.height <= 0 ? size.Height : ev.ExposeEvent.height); if (!firstLayout) { Paint(rect, size); } //if (!_triggeredExpose) //{ // _triggeredExpose = true; // Dispatcher.UIThread.Post(() => // { // _triggeredExpose = false; // DoPaint(); // }, DispatcherPriority.Render); //} } else if (ev.type == XEventName.FocusIn) { activate = true; //if (ActivateTransientChildIfNeeded()) // return; Activated?.Invoke(); //Console.WriteLine("Activated"); //XSetICFocus(xic); } else if (ev.type == XEventName.FocusOut) { activate = false; //Console.WriteLine("Deactivated"); Deactivated?.Invoke(); XUnsetICFocus(xic); if (xic != IntPtr.Zero) { //if (have_Xutf8ResetIC) //{ // try // { // Xutf8ResetIC(xic); // } // catch (EntryPointNotFoundException) // { // have_Xutf8ResetIC = false; // } //} //XUnsetICFocus(xic); } } else if (ev.type == XEventName.MotionNotify) { modifiers = TranslateModifiers(ev.MotionEvent.state); MouseEvent(EventType.MouseMove, new Point(ev.MotionEvent.x, ev.MotionEvent.y), modifiers, new Vector(), MouseButton.None); } else if (ev.type == XEventName.LeaveNotify) { modifiers = TranslateModifiers(ev.CrossingEvent.state); MouseEvent(EventType.MouseLeave, new Point(ev.MotionEvent.x, ev.MotionEvent.y), modifiers, new Vector(), MouseButton.None); } else if (ev.type == XEventName.PropertyNotify) { OnPropertyChange(ev.PropertyEvent.atom, ev.PropertyEvent.state == 0); } else if (ev.type == XEventName.ButtonPress) { //Console.WriteLine("ButtonPress"); if (ActivateTransientChildIfNeeded()) return; 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; } modifiers = TranslateModifiers(ev.ButtonEvent.state); MouseEvent(EventType.MouseDown, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), modifiers, new Vector(), mouseButton); } else { modifiers = TranslateModifiers(ev.ButtonEvent.state); //var delta = ev.ButtonEvent.button == 4 // ? new Vector(0, 1) // : ev.ButtonEvent.button == 5 // ? new Vector(0, -1) // : ev.ButtonEvent.button == 6 // ? new Vector(1, 0) // : new Vector(-1, 0); var delta = ev.ButtonEvent.button == 4 ? new Vector(0, 120) : ev.ButtonEvent.button == 5 ? new Vector(0, -120) : ev.ButtonEvent.button == 6 ? new Vector(120, 0) : new Vector(-120, 0); MouseEvent(EventType.MouseWheel, new Point(ev.MotionEvent.x, ev.MotionEvent.y), modifiers, delta, MouseButton.None); } } else if (ev.type == XEventName.ButtonRelease) { if (ActivateTransientChildIfNeeded()) return; //Console.WriteLine("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; } modifiers = TranslateModifiers(ev.ButtonEvent.state); MouseEvent(EventType.MouseUp, new Point(ev.ButtonEvent.x, ev.ButtonEvent.y), modifiers, new Vector(), mouseButton); } } else if (ev.type == XEventName.ConfigureNotify) { if (ev.ConfigureEvent.window != Handle) return; ev.ConfigureEvent.override_redirect = true; //var needEnqueue = (_configure == null); //_configure = ev.ConfigureEvent; PixelPoint _configurePoint; if (ev.ConfigureEvent.override_redirect || ev.ConfigureEvent.send_event) _configurePoint = new PixelPoint(ev.ConfigureEvent.x, ev.ConfigureEvent.y); else { XTranslateCoordinates(x11info.Display, Handle, x11info.RootWindow, 0, 0, out var tx, out var ty, out _); _configurePoint = new PixelPoint(tx, ty); } //if (needEnqueue) // Dispatcher.UIThread.Post(() => // { //if (_configure == null) // return; var cev = ev.ConfigureEvent; var npos = _configurePoint; var nsize = new PixelSize(cev.width, cev.height); var changedSize = size != nsize; var changedPos = npos != position; size = nsize; position = npos; bool updatedSizeViaScaling = false; //if (changedPos) { PositionChanged?.Invoke(npos); //updatedSizeViaScaling = UpdateScaling(); } if (changedSize && !updatedSizeViaScaling) { Resized?.Invoke(ClientSize); //Console.WriteLine(position + " , " + size); } if (changedSize && !firstLayout) { Paint(new Rect(0, 0, size.Width, size.Height), nsize); } //Console.WriteLine("firstLayout"); if (firstLayout) { root.Invalidate(); } firstLayout = false; // Dispatcher.UIThread.RunJobs(DispatcherPriority.Layout); //}, DispatcherPriority.Layout); //if (_useRenderWindow) // XConfigureResizeWindow(x11info.Info.Display, _renderHandle, ev.ConfigureEvent.width, // ev.ConfigureEvent.height); } else if (ev.type == XEventName.ConfigureRequest) { var v = ev.ConfigureRequestEvent; } else if (ev.type == XEventName.DestroyNotify && ev.AnyEvent.window == Handle) { //Cleanup(); Close(); } else if (ev.type == XEventName.ClientMessage) { //Console.WriteLine(ev.ClientMessageEvent.message_type + " " + GetAtomName(x11info.Display, ev.ClientMessageEvent.ptr1) + ev.ClientMessageEvent.ptr1 + " " + ev.ClientMessageEvent.ptr2); if (ev.ClientMessageEvent.message_type == x11info.Atoms.WM_PROTOCOLS) { if (ev.ClientMessageEvent.ptr1 == x11info.Atoms.WM_DELETE_WINDOW) { Close(); } } //else if (ev.ClientMessageEvent.message_type == x11info.Atoms.XdndSelection) //{ // Console.WriteLine("XdndSelection"); //} //else if (ev.ClientMessageEvent.message_type == x11info.Atoms.XdndPosition) //{ // var pos_x = (int)ev.ClientMessageEvent.ptr3 >> 16; // var pos_y = (int)ev.ClientMessageEvent.ptr3 & 0xFFFF; // Console.WriteLine(pos_x + "," + pos_y); //} else if (ev.ClientMessageEvent.message_type == x11info.Atoms.XdndEnter) { //Console.WriteLine("dragenter"); var mp = MouseDevice.Location; dataObject.AllowedEffects = dataObject.EffectFromAction(ev.ClientMessageEvent.ptr5); dataObject.SetDragDropEffects(root.InputManager.DragDropDevice.DragEnter(new DragEventArgs(dataObject, new Point(mp.X - position.X, mp.Y - position.Y), root) { DragEffects = dataObject.AllowedEffects }, root.LayoutManager.VisibleUIElements)); //查询格式,判断是否可以接受数据 dataObject.DragEnter(ref ev, (s, ee) => { var mmp = MouseDevice.Location; dataObject.SetDragDropEffects(root.InputManager.DragDropDevice.DragOver(new DragEventArgs(dataObject, new Point(mmp.X - position.X, mmp.Y - position.Y), root) { DragEffects = dataObject.AllowedEffects }, root.LayoutManager.VisibleUIElements)); }); } else if (ev.ClientMessageEvent.message_type == x11info.Atoms.XdndDrop) { var mp = MouseDevice.Location; root.InputManager.DragDropDevice.Drop(new DragEventArgs(dataObject, new Point(mp.X - position.X, mp.Y - position.Y), root) { DragEffects = dataObject.DropEffects }, root.LayoutManager.VisibleUIElements); dataObject.StopTimer(); dataObject.SendFinished(); } else if (ev.ClientMessageEvent.message_type == x11info.Atoms.XdndLeave) { root.InputManager.DragDropDevice.DragLeave(root.LayoutManager.VisibleUIElements); dataObject.StopTimer(); } //else if (ev.ClientMessageEvent.message_type == x11info.Atoms.XdndStatus) //{ // Console.WriteLine("XdndStatus"); //} //else if (ev.ClientMessageEvent.message_type == x11info.Atoms.XdndFinished) //{ // Console.WriteLine("XdndFinished"); //} //else if (ev.ClientMessageEvent.message_type == x11info.Atoms.XdndAware) //{ // Console.WriteLine("XdndAware"); //} } //else if (ev.type == XEventName.SelectionNotify) //{ // Console.WriteLine("SelectionNotify:" + dataObject.GetText(ref ev)); //} else if (ev.type == XEventName.KeyPress || ev.type == XEventName.KeyRelease) { if (ActivateTransientChildIfNeeded()) return; var index = ev.KeyEvent.state.HasFlag(XModifierMask.ShiftMask); // We need the latin key, since it's mainly used for hotkeys, we use a different API for text anyway var key = (X11Key)XKeycodeToKeysym(x11info.Display, ev.KeyEvent.keycode, index ? 1 : 0).ToInt32(); // Manually switch the Shift index for the keypad, // there should be a proper way to do this if (ev.KeyEvent.state.HasFlag(XModifierMask.Mod2Mask) && key > X11Key.Num_Lock && key <= X11Key.KP_9) { key = (X11Key)XKeycodeToKeysym(x11info.Display, ev.KeyEvent.keycode, index ? 0 : 1).ToInt32(); } modifiers = TranslateModifiers(ev.KeyEvent.state); root.InputManager.KeyboardDevice.Modifiers = modifiers; if (ev.type == XEventName.KeyPress) { root.InputManager.KeyboardDevice.ProcessEvent(new KeyEventArgs(root, X11KeyConvert.ConvertKey(key), ev.KeyEvent.keycode, modifiers, root.InputManager.KeyboardDevice), KeyEventType.KeyDown); var len = LookupString(ref ev, 24, out var xKeySym, out var status); //Console.WriteLine(lookup_buffer.ToString()); //var buffer = stackalloc byte[1024]; //var len = Xutf8LookupString(_xic, ref ev, buffer, 1024, out var x11Key, out var status); //Console.WriteLine(len); //var len = XwcLookupString(_xic, ref ev, buffer, 1024, out _, out var status); //var str = new StringBuilder(40); //X11Key x11Key= X11Key.a; //LookupStatus status = LookupStatus.XBufferOverflow; //var len = XmbLookupString(_xic, ref ev.KeyEvent, buffer, 1024, out var x11Key, out var status); //Console.WriteLine(status); if (status == XLookupStatus.XLookupChars || status == XLookupStatus.XLookupBoth) { var text = lookup_buffer.ToString(); //var text = Encoding.UTF8.GetString(buffer, len); if ((text.Length == 1 && !(text[0] < ' ' || text[0] == 0x7f)) || text.Length > 1) { root.InputManager.KeyboardDevice.ProcessEvent(new TextInputEventArgs(root, root.InputManager.KeyboardDevice, text), KeyEventType.TextInput); } } } else { root.InputManager.KeyboardDevice.ProcessEvent(new KeyEventArgs(root, X11KeyConvert.ConvertKey(key), ev.KeyEvent.keycode, modifiers, root.InputManager.KeyboardDevice), KeyEventType.KeyUp); } //x11info.LastActivityTimestamp = ev.ButtonEvent.time; } base.OnEvent(ref e); } DataObject dataObject = new DataObject(); private bool have_Xutf8LookupString = true; private byte[] lookup_byte_buffer = new byte[100]; private StringBuilder lookup_buffer = new StringBuilder(24); private int LookupString(ref XEvent xevent, int len, out XKeySym keysym, out XLookupStatus status) { IntPtr keysym_res; int res; status = XLookupStatus.XLookupNone; if (xic != IntPtr.Zero && have_Xutf8LookupString && xevent.type == XEventName.KeyPress) { do { try { res = Xutf8LookupString(xic, ref xevent, lookup_byte_buffer, lookup_byte_buffer.Length, out keysym_res, out status); } catch (EntryPointNotFoundException) { have_Xutf8LookupString = false; // call again, this time we'll go through the non-xic clause return LookupString(ref xevent, len, out keysym, out status); } if (status != XLookupStatus.XBufferOverflow) break; lookup_byte_buffer = new byte[lookup_byte_buffer.Length << 1]; } while (true); lookup_buffer.Length = 0; string s = Encoding.UTF8.GetString(lookup_byte_buffer, 0, res); lookup_buffer.Append(s); keysym = (XKeySym)keysym_res.ToInt32(); return s.Length; } else { IntPtr statusPtr = IntPtr.Zero; res = XLookupString(ref xevent, lookup_byte_buffer, len, out keysym_res, out statusPtr); lookup_buffer.Length = 0; string s = Encoding.ASCII.GetString(lookup_byte_buffer, 0, res); lookup_buffer.Append(s); keysym = (XKeySym)keysym_res.ToInt32(); return res; } } private void Paint(Rect rect, PixelSize size) { root.LayoutManager.ExecuteLayoutPass(); if (context != null) { context.MakeCurrent(); if (root.LayoutManager.VisibleUIElements != null) { context.GetFramebufferInfo(out var fb, out var sam, out var sten); var rt = new OpenGlRenderTarget(context, size.Width, (int)size.Height, fb, sam, sten); using (DrawingContext dc = DrawingContext.FromRenderTarget(rt)) { root.RenderView(dc, rect); } } context.SwapBuffers(); } else { if (bitmap == null || bitmap.Width != size.Width || bitmap.Height != size.Height) { if (bitmap != null) { bitmap.Dispose(); } bitmap = new Bitmap(size.Width, size.Height); } if (root != null && root.LayoutManager.VisibleUIElements != null) { using (DrawingContext dc = DrawingContext.FromBitmap(bitmap)) { root.RenderView(dc, rect); } } 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)size.Width, (uint)size.Height); //XSync(x11info.Display, false); //XUnlockDisplay(x11info.Display); XFreeGC(x11info.Display, gc); //XFlush(x11info.Display); //Console.WriteLine("Expose"); } } } WindowState _lastWindowState = WindowState.Normal; /// <summary> /// /// </summary> /// <param name="atom"></param> /// <param name="hasValue"></param> /// <param name="windowState">部分Linux里会丢失状态消息</param> private void OnPropertyChange(IntPtr atom, bool hasValue, WindowState? windowState = null) { //Console.WriteLine(GetAtomName(x11info.Display, atom)); if (atom == x11info.Atoms._NET_WM_STATE) { WindowState state = WindowState.Normal; if (hasValue) { if (windowState.HasValue) { state = windowState.Value; } else { XGetWindowProperty(x11info.Display, Handle, x11info.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr(256), false, (IntPtr)Atom.XA_ATOM, out _, out _, out var nitems, out _, out var prop); int maximized = 0; var pitems = (IntPtr*)prop.ToPointer(); if (nitems.ToInt32() == 0) { return; } for (var c = 0; c < nitems.ToInt32(); c++) { if (pitems[c] == x11info.Atoms._NET_WM_STATE_HIDDEN) { state = WindowState.Minimized; break; } if (pitems[c] == x11info.Atoms._NET_WM_STATE_FULLSCREEN) { state = WindowState.FullScreen; break; } if (pitems[c] == x11info.Atoms._NET_WM_STATE_MAXIMIZED_HORZ || pitems[c] == x11info.Atoms._NET_WM_STATE_MAXIMIZED_VERT) { maximized++; if (maximized == 2) { state = WindowState.Maximized; break; } } } XFree(prop); } } if (_lastWindowState != state) { var old = _lastWindowState; _lastWindowState = state; WindowStateChanged?.Invoke(); if (state == WindowState.Maximized) { var sc = Screen; lastRect = new Rect(position.X, position.Y, size.Width, size.Height); //Console.WriteLine(sc.WorkingArea); if (sc.Primary) { Position = new PixelPoint((int)sc.WorkingArea.Left, (int)sc.WorkingArea.Top); //root.MarginLeft = sc.WorkingArea.Left / LayoutScaling; //root.MarginTop = sc.WorkingArea.Top / LayoutScaling; PositionChanged(position); Resize(sc.WorkingArea.Size / RenderScaling, true); } else { //Console.WriteLine(sc.Bounds); //Position = new PixelPoint((int)sc.Bounds.Left, (int)sc.Bounds.Top); //PositionChanged(position); //Resize(sc.Bounds.Size, true); root.BeginInvoke(() => { (root as Window).WindowState = WindowState.FullScreen; }); } root.Delay(TimeSpan.FromMilliseconds(100), () => { root.Invalidate();//Centos里开启GPU后最大化有时候会丢失图像,这里强制刷新 }); } else if (state == WindowState.Normal && old == WindowState.Maximized) { Resize(lastRect.Size / RenderScaling, true); root.BeginInvoke(() => { if (!(root is Window window) || !window.IsDragMove) { Position = new PixelPoint((int)lastRect.X, (int)lastRect.Y); } root.Delay(TimeSpan.FromMilliseconds(100), () => { root.Invalidate();//Centos里开启GPU后最大化还原有时候会丢失图像,这里强制刷新 }); }); } //Console.WriteLine(lastRect + " " + state + " " + old); } //else if (fullscreen && _lastWindowState != state && state != WindowState.Normal) //{ // _lastWindowState = state; // WindowStateChanged?.Invoke(); //} } } Rect lastRect; InputModifiers modifiers; public void MouseEvent(EventType type, Point pos, InputModifiers modifiers, Vector delta, MouseButton mouseButton, bool isTouch = false) { this.modifiers = modifiers; if (type == EventType.MouseMove && root is Window window && window.IsDragMove) { if (WindowState == WindowState.Maximized || WindowState == WindowState.FullScreen) { var me = pos / RenderScaling; var left = me.X; var t = me.Y; var w = window.ActualSize.Width; var percent = left / w; var ml = MouseDevice.Location; WindowState = WindowState.Normal; root.BeginInvoke(() => { var atr = GetXWindowAttributes(); var scr = window.Screen; var tt = (ml.Y - scr.WorkingArea.Y) / window.RenderScaling - t; window.MarginTop = tt; window.MarginLeft = left - atr.width / window.RenderScaling * percent; //Console.WriteLine(left + "-" + window.ActualSize.Width + "-" + percent + "--" + tt + "," + window.MarginLeft); DragMove(); }); } else { DragMove(); } return; } if (type == EventType.MouseDown) { mouseDownWindow = Handle; } //modifiers = TranslateModifiers(mods); root.InputManager.KeyboardDevice.Modifiers = modifiers; if (type == EventType.MouseLeave || type == EventType.MouseMove) { root.InputManager.MouseDevice.ProcessEvent(new MouseEventArgs(root, modifiers.HasFlag(InputModifiers.LeftMouseButton), modifiers.HasFlag(InputModifiers.RightMouseButton), modifiers.HasFlag(InputModifiers.MiddleMouseButton), pos / RenderScaling, root.InputManager.MouseDevice, isTouch), root.LayoutManager.VisibleUIElements, type); //Console.WriteLine("mousemove:" + ev.MotionEvent.x + "," + ev.MotionEvent.y + "===" + new Point(ev.MotionEvent.x, ev.MotionEvent.y) / LayoutScaling); } else if (type == EventType.MouseWheel) { root.InputManager.MouseDevice.ProcessEvent(new MouseWheelEventArgs(root, modifiers.HasFlag(InputModifiers.LeftMouseButton), modifiers.HasFlag(InputModifiers.RightMouseButton), modifiers.HasFlag(InputModifiers.MiddleMouseButton), pos / RenderScaling, root.InputManager.MouseDevice, delta), root.LayoutManager.VisibleUIElements, type); } else if (type == EventType.MouseDown || type == EventType.MouseUp) { if (type == EventType.MouseDown) { switch (mouseButton) { case MouseButton.Left: modifiers |= InputModifiers.LeftMouseButton; break; case MouseButton.Middle: modifiers |= InputModifiers.MiddleMouseButton; break; case MouseButton.Right: modifiers |= InputModifiers.RightMouseButton; break; } } else { switch (mouseButton) { case MouseButton.Left: modifiers ^= InputModifiers.LeftMouseButton; break; case MouseButton.Middle: modifiers ^= InputModifiers.MiddleMouseButton; break; case MouseButton.Right: modifiers ^= InputModifiers.RightMouseButton; break; } } root.InputManager.KeyboardDevice.Modifiers = modifiers; root.InputManager.MouseDevice.ProcessEvent(new MouseButtonEventArgs(root, modifiers.HasFlag(InputModifiers.LeftMouseButton), modifiers.HasFlag(InputModifiers.RightMouseButton), modifiers.HasFlag(InputModifiers.MiddleMouseButton), pos / RenderScaling, root.InputManager.MouseDevice, mouseButton, isTouch), root.LayoutManager.VisibleUIElements, type); } if (type == EventType.MouseUp) { if (xic != IntPtr.Zero && !(root.InputManager.KeyboardDevice.FocusedElement is IEditor)) { XUnsetICFocus(xic); } else if (xic != IntPtr.Zero && root.InputManager.KeyboardDevice.FocusedElement is IEditor editor && editor.IsInputMethodEnabled && CanActivate) { XSetICFocus(xic); } mouseDownWindow = IntPtr.Zero; } //x11info.LastActivityTimestamp = ev.ButtonEvent.time; } private void DragMove() { root.BeginInvoke(() => { lock (XlibLock) { XEvent xEvent = new XEvent(); var p = MouseDevice.Location; var display = x11info.Display; xEvent.ClientMessageEvent.type = XEventName.ClientMessage; xEvent.ClientMessageEvent.message_type = x11info.Atoms._NET_WM_MOVERESIZE; xEvent.ClientMessageEvent.display = display; xEvent.ClientMessageEvent.window = Handle; xEvent.ClientMessageEvent.format = 32; xEvent.ClientMessageEvent.ptr1 = (IntPtr)p.X; xEvent.ClientMessageEvent.ptr2 = (IntPtr)p.Y; xEvent.ClientMessageEvent.ptr3 = (IntPtr)8; xEvent.ClientMessageEvent.ptr4 = (IntPtr)1; XUngrabPointer(display, IntPtr.Zero); XSendEvent(display, x11info.RootWindow, false, (IntPtr)(EventMask.SubstructureNotifyMask | EventMask.SubstructureRedirectMask), ref xEvent); XFlush(display); //p = MouseDevice.Location; XEvent xevent = new XEvent(); xevent.type = XEventName.ButtonRelease; xevent.ButtonEvent.button = 1; xevent.ButtonEvent.window = Handle; xevent.ButtonEvent.x = p.X - position.X; xevent.ButtonEvent.y = p.Y - position.Y; xevent.ButtonEvent.x_root = p.X; xevent.ButtonEvent.y_root = p.X; xevent.ButtonEvent.display = display; xevent.ButtonEvent.state = XModifierMask.Button1Mask; XSendEvent(display, Handle, false, (IntPtr)EventMask.ButtonReleaseMask, ref xevent); XFlush(display); } }); } InputModifiers TranslateModifiers(XModifierMask state) { var rv = default(InputModifiers); if (state.HasFlag(XModifierMask.Button1Mask)) rv |= InputModifiers.LeftMouseButton; if (state.HasFlag(XModifierMask.Button2Mask)) rv |= InputModifiers.MiddleMouseButton; if (state.HasFlag(XModifierMask.Button3Mask)) rv |= InputModifiers.RightMouseButton; //if (state.HasFlag(XModifierMask.Button4Mask)) // rv |= InputModifiers.XButton1MouseButton; //if (state.HasFlag(XModifierMask.Button5Mask)) // rv |= InputModifiers.XButton2MouseButton; if (state.HasFlag(XModifierMask.ShiftMask)) rv |= InputModifiers.Shift; if (state.HasFlag(XModifierMask.ControlMask)) rv |= InputModifiers.Control; if (state.HasFlag(XModifierMask.Mod1Mask)) rv |= InputModifiers.Alt; //if (state.HasFlag(XModifierMask.Mod4Mask)) // rv |= InputModifiers.Meta; return rv; } void Resize(Size clientSize, bool force) { if (!force && clientSize == ClientSize) return; var needImmediatePopupResize = clientSize != ClientSize; var pixelSize = ToPixelSize(clientSize); //Paint(new Rect(new Point(), new Size(pixelSize.Width, pixelSize.Height)), pixelSize); //UpdateSizeHints(pixelSize); XConfigureResizeWindow(x11info.Display, Handle, pixelSize); XFlush(x11info.Display); if (force)//|| (_popup && needImmediatePopupResize)) { size = pixelSize; Resized?.Invoke(ClientSize); } } PixelSize ToPixelSize(Size size) => new PixelSize((int)Math.Ceiling(size.Width * RenderScaling), (int)Math.Ceiling(size.Height * RenderScaling)); public void UpdateScaling() { //lock (XlibLock) { //float newScaling; //var pos = new Point(position.X, position.Y); //var monitor = ScreenImpl.Screens.OrderBy(x => x.PixelDensity) // .FirstOrDefault(m => m.Bounds.Contains(pos)); //if (RenderScaling != newScaling * Application.BaseScale) { //var oldScaledSize = ClientSize; //RenderScaling = newScaling * Application.BaseScale; ScalingChanged?.Invoke(); //SetMinMaxSize(_scaledMinMaxSize.minSize, _scaledMinMaxSize.maxSize); Resized(ClientSize); //return true; } //return false; } } public Func<bool> Closing { get; set; } public Action Closed { get; set; } public WindowState WindowState { get { return _lastWindowState; } set { if (_lastWindowState == value) return; if (value == WindowState.Minimized) { XIconifyWindow(x11info.Display, Handle, x11info.DefaultScreen); } else if (value == WindowState.Maximized) { ChangeWMAtoms(false, x11info.Atoms._NET_WM_STATE_HIDDEN); ChangeWMAtoms(false, x11info.Atoms._NET_WM_STATE_FULLSCREEN); ChangeWMAtoms(true, x11info.Atoms._NET_WM_STATE_MAXIMIZED_VERT, x11info.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); lastRect = new Rect(position.X, position.Y, size.Width, size.Height); } else if (value == WindowState.FullScreen) { //lastRect = new Rect(position.X, position.Y, size.Width, size.Height); ChangeWMAtoms(false, x11info.Atoms._NET_WM_STATE_HIDDEN); ChangeWMAtoms(false, x11info.Atoms._NET_WM_STATE_MAXIMIZED_VERT, x11info.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); ChangeWMAtoms(true, x11info.Atoms._NET_WM_STATE_FULLSCREEN); } else { ChangeWMAtoms(false, x11info.Atoms._NET_WM_STATE_HIDDEN); ChangeWMAtoms(false, x11info.Atoms._NET_WM_STATE_FULLSCREEN); ChangeWMAtoms(false, x11info.Atoms._NET_WM_STATE_MAXIMIZED_VERT, x11info.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); SendNetWMMessage(x11info.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1); } //if (fullscreen && value == WindowState.Normal) //{ // SetFullscreen(false); //} //else if (fullscreen && value == WindowState.Maximized) //{ //} XFlush(x11info.Display); OnPropertyChange(x11info.Atoms._NET_WM_STATE, true, value); //_lastWindowState = value; } } public Action WindowStateChanged { get; set; } public Screen Screen { get { //XWindowAttributes attributes = new XWindowAttributes(); //XGetWindowAttributes(x11info.Display, Handle, ref attributes); //var sc = Marshal.PtrToStructure<XScreen>(attributes.screen); var pos = position; var rect = new Rect(pos.X, pos.Y, size.Width, size.Height); var screen = Screen.AllScreens.Select(a => { if (rect.IntersectsWith(a.WorkingArea) || rect == a.WorkingArea || a.WorkingArea.Contains(rect) || rect.Contains(a.WorkingArea)) { var r = rect; r.Intersect(a.WorkingArea); return new { sc = a, v = r.Width * r.Height }; } else { return new { sc = a, v = 0f }; } }).OrderByDescending(a => a.v).Select(a => a.sc).FirstOrDefault(); if (screen == null) { screen = Screen.AllScreens[0]; } return screen; } } bool ismain; public bool IsMain { get { return ismain; } set { ismain = value; if (value) { main = this; } } } public Size ClientSize => new Size(size.Width / RenderScaling, size.Height / RenderScaling); public float RenderScaling { get { return Application.BaseScale * ScreenImpl.DpiScale; } } public float LayoutScaling { get { return RenderScaling; } } public Action ScalingChanged { get; set; } public Action<Size> Resized { get; set; } public Action<PixelPoint> PositionChanged { get; set; } public Action Activated { get; set; } public Action Deactivated { get; set; } public bool CanActivate { get; set; } = true; public PixelPoint Position { get { return position; } set { //if (!(this is PopWindow)) //{ // Console.WriteLine(value); //} //if (this is PopWindow) //{ position = value; XMoveWindow(x11info.Display, Handle, value.X, value.Y); return; //} // //XMoveResizeWindow(x11info.Display, Handle, value.X, value.Y,size.Width,size.Height); } } bool activate; public void Activate() { if (activate) { return; } //if (x11info.Atoms._NET_ACTIVE_WINDOW != IntPtr.Zero) //{//这个鬼东西会有个系统消息提示 // SendNetWMMessage(x11info.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1, x11info.LastActivityTimestamp, // IntPtr.Zero); //} //else { XRaiseWindow(x11info.Display, Handle); XSetInputFocus(x11info.Display, Handle, 0, IntPtr.Zero); } } void SendNetWMMessage(IntPtr message_type, IntPtr l1, IntPtr? l2 = null, IntPtr? l3 = null, IntPtr? l4 = null, IntPtr? l5 = null) { var xev = new XEvent { ClientMessageEvent = { type = XEventName.ClientMessage, send_event = true, window = Handle, message_type = message_type, format = 32, //ptr1 = l1, //ptr2 = l2 ?? IntPtr.Zero, //ptr3 = l3 ?? IntPtr.Zero, //ptr4 = l4 ?? IntPtr.Zero, //ptr5 = l5 ?? IntPtr.Zero } }; xev.ClientMessageEvent.ptr1 = l1; xev.ClientMessageEvent.ptr2 = l2 ?? IntPtr.Zero; xev.ClientMessageEvent.ptr3 = l3 ?? IntPtr.Zero; xev.ClientMessageEvent.ptr4 = l4 ?? IntPtr.Zero; xev.ClientMessageEvent.ptr5 = l5 ?? IntPtr.Zero; lock (XlibLock) { XSendEvent(x11info.Display, x11info.RootWindow, false, new IntPtr((int)(EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev); } XFlush(x11info.Display); } public void Capture() { //throw new NotImplementedException(); } public void Close() { if (Closing.Invoke() != true) { Closed.Invoke(); Dispose(); } } bool paint = false; Rect invalidateRect; public void Invalidate(in Rect rect) { Rect all = new Rect(new Point(), root.ActualSize);//控件区域 if ((all.Contains(rect) || rect.IntersectsWith(all) || all == rect || rect.Contains(all)) && !rect.IsEmpty && !all.IsEmpty) { //Debug.WriteLine(invalidateRect); if (invalidateRect.IsEmpty || rect.Contains(invalidateRect) || rect == invalidateRect)//如果更新区域大于原有失效区域,则当前失效区域设为更新区域 { invalidateRect = rect; } else if (invalidateRect.Contains(rect))//如果更新区域小于原来的失效区域,则失效区域不变 { } else if (invalidateRect.IsEmpty)//如果原来的失效区域为空 { invalidateRect = rect; } else {//如果两个区域没有关联或者相交 var minX = invalidateRect.X < rect.X ? invalidateRect.X : rect.X;//确定包含这两个矩形的最小矩形 var minY = invalidateRect.Y < rect.Y ? invalidateRect.Y : rect.Y; var maxW = (invalidateRect.Width + invalidateRect.X - minX) > (rect.Width + rect.X - minX) ? (invalidateRect.Width + invalidateRect.X - minX) : (rect.Width + rect.X - minX); var maxH = (invalidateRect.Height + invalidateRect.Y - minY) > (rect.Height + rect.Y - minY) ? (invalidateRect.Height + invalidateRect.Y - minY) : (rect.Height + rect.Y - minY); Rect min = new Rect(minX, minY, maxW, maxH); invalidateRect = min; } invalidateRect.Intersect(all);//最后失效区域为在控件区域里面的相交区域 } if (!paint) { paint = true; Threading.Dispatcher.MainThread.BeginInvoke(() => { root.LayoutManager.ExecuteLayoutPass(); //OnPaint(IntPtr.Zero, new Rect(((invalidateRect.X - 1) * scaling), ((invalidateRect.Y - 1) * scaling), ((invalidateRect.Width + 2) * scaling), ((invalidateRect.Height + 2) * scaling))); var r = new Rect(((invalidateRect.X - 1) * RenderScaling), ((invalidateRect.Y - 1) * RenderScaling), ((invalidateRect.Width + 2) * RenderScaling), ((invalidateRect.Height + 2) * RenderScaling)); r.X = Math.Max(0, r.X); r.Y = Math.Max(0, r.Y); var xev = new XEvent { ExposeEvent = { type = XEventName.Expose, send_event = true, window = Handle, count=1, display=x11info.Display, height=(int)r.Height+1, width= (int)r.Width+1, x=(int)r.X, y= (int)r.Y } }; //Console.WriteLine("Invalidate:" + r); lock (XlibLock) { XSendEvent(x11info.Display, Handle, false, new IntPtr((int)(EventMask.ExposureMask)), ref xev); } invalidateRect = new Rect(); paint = false; XFlush(x11info.Display); //Console.WriteLine("Invalidate"); }); } //RECT r = new RECT((int)((rect.X - 1) * scaling), (int)((rect.Y - 1) * scaling), (int)((rect.Right + 2) * scaling), (int)((rect.Bottom + 2) * scaling)); //InvalidateRect(handle, ref r, false); } public Point PointToClient(Point point) { return new Point((point.X - Position.X) / RenderScaling, (point.Y - Position.Y) / RenderScaling); } public Point PointToScreen(Point point) { return new Point( (int)(point.X * RenderScaling + Position.X), (int)(point.Y * RenderScaling + Position.Y)); } public void ReleaseCapture() { //throw new NotImplementedException(); } public void SetCursor(Cursor cursor) { XDefineCursor(x11info.Display, Handle, (IntPtr)cursor.PlatformCursor); } //bool fullscreen; //public void SetFullscreen(bool fullscreen) //{ // this.fullscreen = fullscreen; // //XSetWindowAttributes attr = new XSetWindowAttributes(); // //var valueMask = SetWindowValuemask.OverrideRedirect; // //attr.override_redirect = fullscreen; // //XChangeWindowAttributes(x11info.Display, Handle, (ulong)valueMask, ref attr); // //XMapWindow(x11info.Display, Handle); // //XFlush(x11info.Display); // ChangeWMAtoms(fullscreen, x11info.Atoms._NET_WM_STATE_FULLSCREEN); // if (fullscreen) // { // lastRect = new Rect(position.X, position.Y, size.Width, size.Height); // //var sc = Screen; // //Resize(sc.Bounds.Size, true); // //Position = new PixelPoint(); // _lastWindowState = WindowState.Maximized; // WindowStateChanged?.Invoke(); // } // else // { // if (_lastWindowState == WindowState.Maximized) // { // _lastWindowState = WindowState.Normal; // WindowStateChanged?.Invoke(); // } // } //} public void SetIcon(Image image) { if (image == null) { XDeleteProperty(x11info.Display, Handle, x11info.Atoms._NET_WM_ICON); return; } var _width = Math.Min(image.Width, 128); var _height = Math.Min(image.Height, 128); var _bdata = new uint[_width * _height]; fixed (void* ptr = _bdata) { var iptr = (int*)ptr; iptr[0] = _width; iptr[1] = _height; } var h = GCHandle.Alloc(_bdata, GCHandleType.Pinned); using (Bitmap bmp = new Bitmap(_width, _height, _width * 4, PixelFormat.Bgra, h.AddrOfPinnedObject())) { using (var dc = DrawingContext.FromBitmap(bmp)) { dc.DrawImage(image, new Rect(0, 0, _width, _height), new Rect(0, 0, image.Width, image.Height)); } } h.Free(); var data = new UIntPtr[_width * _height + 2]; data[0] = new UIntPtr((uint)_width); data[1] = new UIntPtr((uint)_height); var offset = 2; for (var y = 0; y < _height; y++) { var r = y * _width; for (var x = 0; x < _width; x++) data[r + x + offset] = new UIntPtr(_bdata[r + x]); } fixed (void* pdata = data) XChangeProperty(x11info.Display, Handle, x11info.Atoms._NET_WM_ICON, new IntPtr((int)Atom.XA_CARDINAL), 32, PropertyMode.Replace, pdata, data.Length); } public void SetIMEEnable(bool enable) { //throw new NotImplementedException(); if (xic != IntPtr.Zero) { //if (!enable) //{ // XUnsetICFocus(xic); //} //else //{ XSetICFocus(xic); //} } } public void SetIMEPosition(Point point) { XPoint spot = new XPoint(); spot.X = (short)point.X; spot.Y = (short)(point.Y + 20); IntPtr pSL = IntPtr.Zero; try { pSL = Marshal.StringToHGlobalAnsi(XNames.XNSpotLocation); IntPtr preedit = XVaCreateNestedList(0, pSL, spot, IntPtr.Zero); XSetICValues(xic, XNames.XNPreeditAttributes, preedit, IntPtr.Zero); } finally { if (pSL != IntPtr.Zero) Marshal.FreeHGlobal(pSL); } //throw new NotImplementedException(); } public void SetRoot(View view) { root = view; root.LayoutUpdated += Root_LayoutUpdated; //root.PropertyChanged += Root_PropertyChanged; } //private void Root_PropertyChanged(object sender, CPFPropertyChangedEventArgs e) //{ // if (e.PropertyName == nameof(Window.IsDragMove)) // { // lock (XlibLock) // { // XEvent xEvent = new XEvent(); // var p = MouseDevice.Location; // var display = x11info.Display; // xEvent.ClientMessageEvent.type = XEventName.ClientMessage; // xEvent.ClientMessageEvent.message_type = x11info.Atoms._NET_WM_MOVERESIZE; // xEvent.ClientMessageEvent.display = display; // xEvent.ClientMessageEvent.window = Handle; // xEvent.ClientMessageEvent.format = 32; // xEvent.ClientMessageEvent.ptr1 = (IntPtr)p.X; // xEvent.ClientMessageEvent.ptr2 = (IntPtr)p.Y; // xEvent.ClientMessageEvent.ptr3 = (IntPtr)8; // xEvent.ClientMessageEvent.ptr4 = (IntPtr)1; // XUngrabPointer(display, IntPtr.Zero); // XSendEvent(display, x11info.RootWindow, false, (IntPtr)(EventMask.SubstructureNotifyMask | EventMask.SubstructureRedirectMask), ref xEvent); // //p = MouseDevice.Location; // XEvent xevent = new XEvent(); // xevent.type = XEventName.ButtonRelease; // xevent.ButtonEvent.button = 1; // xevent.ButtonEvent.window = Handle; // xevent.ButtonEvent.x = p.X - position.X; // xevent.ButtonEvent.y = p.Y - position.Y; // xevent.ButtonEvent.x_root = p.X; // xevent.ButtonEvent.y_root = p.X; // xevent.ButtonEvent.display = display; // xevent.ButtonEvent.state = XModifierMask.Button1Mask; // XSendEvent(display, Handle, false, (IntPtr)EventMask.ButtonReleaseMask, ref xevent); // XFlush(display); // } // } //} bool firstLayout = true; private void Root_LayoutUpdated(object sender, RoutedEventArgs e) { var b = position; var s = root.ActualSize; var l = root.ActualOffset; var src = root.Screen; if (size.Width != (int)Math.Ceiling(s.Width * RenderScaling) || size.Height != (int)Math.Ceiling(s.Height * RenderScaling) || b.X != (int)(l.X * RenderScaling + src.WorkingArea.X) || b.Y != (int)(l.Y * RenderScaling + src.WorkingArea.Y)) { //Bounds = new Rect(l.X * RenderScaling, l.Y * RenderScaling, s.Width * RenderScaling, s.Height * RenderScaling); Position = new PixelPoint((int)(l.X * RenderScaling + src.WorkingArea.X), (int)(l.Y * RenderScaling + src.WorkingArea.Y)); Resize(s, false); } //Console.WriteLine("Root_LayoutUpdated" + l.ToString() + "," + s.ToString() + "----" + position); } public void SetTitle(string text) { var data = Encoding.UTF8.GetBytes(text); fixed (void* pdata = data) { XChangeProperty(x11info.Display, Handle, x11info.Atoms._NET_WM_NAME, x11info.Atoms.UTF8_STRING, 8, PropertyMode.Replace, pdata, data.Length); XStoreName(x11info.Display, Handle, text); //XSetIconName(x11info.Display, Handle, text); //XTextProperty xTextProperty = new XTextProperty { encoding = x11info.Atoms.UTF8_STRING, format = 8, value = (IntPtr)pdata, nitems = (IntPtr)data.Length }; //XSetWMIconName(x11info.Display, Handle, ref xTextProperty); //XSetWMName(x11info.Display, Handle, ref xTextProperty); //Console.WriteLine(r); } } public void SetVisible(bool visible) { if (visible) { XMapWindow(x11info.Display, Handle); root.LayoutManager.ExecuteLayoutPass(); root.Visibility = Visibility.Visible; if (CanActivate) { Activate(); } else { XRaiseWindow(x11info.Display, Handle); } Position = position; //XFlush(x11info.Display); //Console.WriteLine("显示后:" + position); } else { activate = false; //position = new PixelPoint(); XUnmapWindow(x11info.Display, Handle); } } //private HashSet<X11Window> _transientChildren = new HashSet<X11Window>(); public bool ActivateTransientChildIfNeeded() { return _disabled; //if (_disabled) //{ // return true; //} //if (_transientChildren.Count == 0) // return false; //var child = _transientChildren.First(); //if (!child.ActivateTransientChildIfNeeded()) // child.Activate(); //return true; } void SetTransientParent(X11Window window, bool informServer = true) { //parent?._transientChildren.Remove(this); //parent = window; //parent?._transientChildren.Add(this); if (informServer) XSetTransientForHint(x11info.Display, Handle, window?.Handle ?? IntPtr.Zero); } public void ShowDialog(Window window) { SetTransientParent((X11Window)window.ViewImpl); XMapWindow(x11info.Display, Handle); XFlush(x11info.Display); root.LayoutManager.ExecuteLayoutPass(); root.Visibility = Visibility.Visible; } public void ShowInTaskbar(bool value) { ChangeWMAtoms(!value, x11info.Atoms._NET_WM_STATE_SKIP_TASKBAR); } public void TopMost(bool value) { ChangeWMAtoms(value, x11info.Atoms._NET_WM_STATE_ABOVE); } void ChangeWMAtoms(bool enable, params IntPtr[] atoms) { if (atoms.Length < 1 || atoms.Length > 4) throw new ArgumentException(); //if (!_mapped) { XGetWindowProperty(x11info.Display, Handle, x11info.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr(256), false, (IntPtr)Atom.XA_ATOM, out _, out _, out var nitems, out _, out var prop); var ptr = (IntPtr*)prop.ToPointer(); var newAtoms = new HashSet<IntPtr>(); for (var c = 0; c < nitems.ToInt64(); c++) newAtoms.Add(*ptr); XFree(prop); foreach (var atom in atoms) if (enable) newAtoms.Add(atom); else newAtoms.Remove(atom); XChangeProperty(x11info.Display, Handle, x11info.Atoms._NET_WM_STATE, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, newAtoms.ToArray(), newAtoms.Count); } SendNetWMMessage(x11info.Atoms._NET_WM_STATE, (IntPtr)(enable ? 1 : 0), atoms[0], atoms.Length > 1 ? atoms[1] : IntPtr.Zero, atoms.Length > 2 ? atoms[2] : IntPtr.Zero, atoms.Length > 3 ? atoms[3] : IntPtr.Zero ); } protected override void Dispose(bool disposing) { context?.Dispose(); context = null; SetTransientParent(null, false); if (xic != IntPtr.Zero) { XDestroyIC(xic); xic = IntPtr.Zero; } if (bitmap != null) { bitmap.Dispose(); bitmap = null; } base.Dispose(disposing); if (ismain) { LinuxPlatform.Platform.run = false; } } bool _disabled; public void SetEnable(bool enable) { _disabled = !enable; } } public class PopWindow : X11Window, IPopupImpl { } }