CPF/CPF.Linux/X11Window.cs
2025-01-10 17:44:19 +08:00

1802 lines
76 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
}
}