public void CopyToImage(X11Image image) { if (image.Height != height || image.Width != width) { throw new InvalidOperationException($"Source size ({width} x {height}) does not match image size ({image.Width} x {image.Height})."); } using (X11Bitmap xBitmap = X11Bitmap.Create(image.Display, image.Visual, width, height)) { if (pixelFormat == PixelFormat.RGBA_32) { PixelConverter.Convert_RGBA_32BE_To_PARGB_32(dataPtr, stride, xBitmap.ImageData, 4 * width, width, height); } else if (pixelFormat == PixelFormat.RGB_24) { PixelConverter.Convert_RGB_24BE_To_ARGB_32(dataPtr, stride, xBitmap.ImageData, 4 * width, width, height); } else { throw new InvalidOperationException($"Unexpected pixel format: {pixelFormat}."); } var gcValues = new XGCValues(); var gc = LibX11.XCreateGC(image.Display, image.PixmapId, 0, ref gcValues); try { LibX11.XPutImage(image.Display, image.PixmapId, gc, xBitmap.XImage, 0, 0, 0, 0, (uint)width, (uint)height); } finally { LibX11.XFreeGC(image.Display, gc); } } }
private void FillEllipseXLib(Color color, int x, int y, int width, int height) { var gcValues = new XGCValues(); gcValues.foreground = ToPArgb(color); var gcValueMask = XGCValueMask.GCForeground; var gc = LibX11.XCreateGC(display, drawableId, gcValueMask, ref gcValues); try { if (clipRectangles != null) { LibX11.XSetClipRectangles(display, gc, 0, 0, clipRectangles, clipRectangles.Length, 0); } LibX11.XDrawArc(display, drawableId, gc, x, y, (uint)width - 1, (uint)height - 1, 0, 360 * 64); LibX11.XFillArc(display, drawableId, gc, x, y, (uint)width - 1, (uint)height - 1, 0, 360 * 64); } finally { LibX11.XFreeGC(display, gc); } }
public static X11Graphics Create(IntPtr display) { int defaultScreen = LibX11.XDefaultScreen(display); ulong defaultRootWindow = LibX11.XDefaultRootWindow(display); //todo: is is safe to keep pictFormatPtr after reading it into XRenderPictFormat IntPtr pictFormatPtr = LibXRender.XRenderFindStandardFormat(display, StandardPictFormat.PictStandardARGB32); if (pictFormatPtr == IntPtr.Zero) { throw new InvalidOperationException("Display does not support 32-bit ARGB."); } XRenderPictFormat pictFormat = Marshal.PtrToStructure <XRenderPictFormat>(pictFormatPtr); int status = LibX11.XMatchVisualInfo ( display, defaultScreen, pictFormat.depth, VisualClass.TrueColor, out XVisualInfo visualInfo ); if (status == 0) { throw new InvalidOperationException($"TrueColor visual with depth {pictFormat.depth} does not exist."); } ulong colorMap = LibX11.XCreateColormap(display, defaultRootWindow, visualInfo.visual, CreateColormapOption.AllocNone); return(new X11Graphics(display, defaultScreen, defaultRootWindow, visualInfo.visual, colorMap, pictFormat, pictFormatPtr)); }
private void Run() { bool stopped = false; while (!stopped) { LibX11.XNextEvent(display, out XEvent evt); // todo: log and handle exceptions if (evt.type == XEventType.SelectionClear) { HandleSelectionClear(evt.SelectionClearEvent); } else if (evt.type == XEventType.SelectionRequest) { HandleSelectionRequest(evt.SelectionRequestEvent); } else if (evt.type == XEventType.SelectionNotify) { HandleSelectionNotify(evt.SelectionEvent); } else if (evt.type == XEventType.ClientMessage) { if (evt.ClientMessageEvent.message_type == XA_NWINDOWS_CLIPBOARD_STOP) { stopped = true; } } } LibX11.XCloseDisplay(display); }
private void WithExtraCanvas(Rectangle rect, Action <X11Canvas> action) { using (X11Image tempImage = X11Image.Create(display, visual, drawableId, rect.Width, rect.Height)) { var gcValues = new XGCValues(); gcValues.foreground = ToPArgb(Color.Transparent); var gcValueMask = XGCValueMask.GCForeground; var gc = LibX11.XCreateGC(display, drawableId, gcValueMask, ref gcValues); try { // todo: check int -> uint conversion LibX11.XFillRectangle(display, tempImage.PixmapId, gc, 0, 0, (uint)rect.Width, (uint)rect.Height); } finally { LibX11.XFreeGC(display, gc); } using (var innerCanvas = X11Canvas.CreateForDrawable(display, screenNum, objectCache, visual, colormap, pictFormatPtr, tempImage.PixmapId)) { action(innerCanvas); DrawImage(tempImage, rect.X + origin.X, rect.Y + origin.Y); } } }
public void SetTitle(string title) { byte[] windowTitleASCII = Encoding.ASCII.GetBytes(title); LibX11.XChangeProperty( display, windowId, LibX11.XInternAtom(display, "WM_NAME", 0), LibX11.XInternAtom(display, "STRING", 0), XChangePropertyFormat.Byte, XChangePropertyMode.PropModeReplace, windowTitleASCII, windowTitleASCII.Length ); byte[] windowTitleUTF8 = Encoding.UTF8.GetBytes(title); LibX11.XChangeProperty( display, windowId, LibX11.XInternAtom(display, "_NET_WM_NAME", 0), LibX11.XInternAtom(display, "UTF8_STRING", 0), XChangePropertyFormat.Byte, XChangePropertyMode.PropModeReplace, windowTitleUTF8, windowTitleUTF8.Length ); }
private void DrawPathWithXLibDirectly(Color color, int width, Point[] points) { var gcValues = new XGCValues(); gcValues.cap_style = X11CapStyle.CapRound; gcValues.join_style = X11JoinStyle.JoinRound; gcValues.foreground = ToPArgb(color); gcValues.line_width = width; var gcValueMask = XGCValueMask.GCCapStyle | XGCValueMask.GCForeground | XGCValueMask.GCJoinStyle | XGCValueMask.GCLineWidth; var gc = LibX11.XCreateGC(display, drawableId, gcValueMask, ref gcValues); try { if (clipRectangles != null) { LibX11.XSetClipRectangles(display, gc, 0, 0, clipRectangles, clipRectangles.Length, 0); } XPoint[] xPoints = new XPoint[points.Length]; for (int i = 0; i < points.Length; i++) { var point = points[i]; xPoints[i] = new XPoint(point.X, point.Y); } LibX11.XDrawLines(display, drawableId, gc, xPoints, xPoints.Length, X11CoordinateMode.CoordModeOrigin); } finally { LibX11.XFreeGC(display, gc); } }
private bool ConvertOwnSelection(ulong requestor, ulong selection, ulong targetType, ulong targetProperty) { if (selection != XA_CLIPBOARD) { return(false); } string textToConvert; lock (selectionConversionLock) { textToConvert = bufferText; if (textToConvert == null) { return(false); } } if (targetType == XA_TARGETS) { ulong[] targets = { XA_TARGETS, XA_UTF8_STRING }; LibX11.XChangeProperty ( display, requestor, targetProperty, XA_ATOM, XChangePropertyFormat.Atom, XChangePropertyMode.PropModeReplace, targets, targets.Length ); return(true); } if (targetType == XA_UTF8_STRING) { byte[] text = Encoding.UTF8.GetBytes(textToConvert); LibX11.XChangeProperty ( display, requestor, targetProperty, XA_UTF8_STRING, XChangePropertyFormat.Byte, XChangePropertyMode.PropModeReplace, text, text.Length ); return(true); } return(false); }
public void PutText(string text) { lock (selectionConversionLock) { bufferText = text; LibX11.XSetSelectionOwner(display, XA_CLIPBOARD, windowId, 0); LibX11.XFlush(display); } }
public static X11Clipboard Create() { IntPtr display = IntPtr.Zero; ulong windowId = 0; try { display = LibX11.XOpenDisplay(null); if (display == IntPtr.Zero) { throw new InvalidOperationException("Cannot open display."); } ulong rootWindow = LibX11.XDefaultRootWindow(display); int defaultScreen = LibX11.XDefaultScreen(display); IntPtr defaultVisual = LibX11.XDefaultVisual(display, defaultScreen); XSetWindowAttributes attr = new XSetWindowAttributes(); windowId = LibX11.XCreateWindow ( display, rootWindow, 0, 0, 10, 10, 0, 0, WindowClass.InputOnly, defaultVisual, 0, ref attr ); LibX11.XFlush(display); X11Clipboard clipboard = new X11Clipboard(display, windowId); clipboard.Start(); windowId = 0; display = IntPtr.Zero; return(clipboard); } finally { if (windowId != 0) { LibX11.XDestroyWindow(display, windowId); LibX11.XFlush(display); } if (display != IntPtr.Zero) { LibX11.XCloseDisplay(display); } } }
public void Stop() { lock (selectionConversionLock) { var message = XEvent.CreateClientMessage(windowId, XA_NWINDOWS_CLIPBOARD_STOP); LibX11.XSendEvent(display, windowId, 0, XEventMask.NoEventMask, ref message); LibX11.XFlush(display); } thread.Join(TimeSpan.FromSeconds(30)); }
private X11Clipboard(IntPtr display, ulong windowId) { this.display = display; this.windowId = windowId; thread = new Thread(Run); thread.Name = nameof(X11Clipboard); thread.IsBackground = true; XA_ATOM = LibX11.XInternAtom(display, "ATOM", 0); XA_CLIPBOARD = LibX11.XInternAtom(display, "CLIPBOARD", 0); XA_TARGETS = LibX11.XInternAtom(display, "TARGETS", 0); XA_UTF8_STRING = LibX11.XInternAtom(display, "UTF8_STRING", 0); XA_NWINDOWS_CONVERTED_CLIPBOARD = LibX11.XInternAtom(display, "NWINDOWS_CONVERTED_CLIPBOARD", 0); XA_NWINDOWS_CLIPBOARD_STOP = LibX11.XInternAtom(display, "NWINDOWS_CLIPBOARD_STOP", 0); }
public void Init() { LibX11.XInitThreads(); display = LibX11.XOpenDisplay(null); if (display == IntPtr.Zero) { throw new InvalidOperationException("Cannot open display."); } WM_PROTOCOLS = LibX11.XInternAtom(display, "WM_PROTOCOLS", 0); WM_DELETE_WINDOW = LibX11.XInternAtom(display, "WM_DELETE_WINDOW", 0); XA_NWINDOWS_PAINT_COMPLETE = LibX11.XInternAtom(display, "NWINDOWS_PAINT_COMPLETE", 0); graphics = X11Graphics.Create(display); imageCodec = new GdkPixBufImageCodec(display, graphics.Visual, graphics.RootWindow); clipboard = X11Clipboard.Create(); }
public static X11Bitmap Create(IntPtr display, IntPtr visual, int width, int height) { if (width < 0 || height < 0) { throw new ArgumentException($"Image dimensions cannot be negative ({width} x {height})."); } IntPtr imageData = Marshal.AllocHGlobal(4 * width * height); IntPtr xImage = IntPtr.Zero; try { xImage = LibX11.XCreateImage ( display, visual, X11Application.RequiredColorDepth, XImageFormat.ZPixmap, 0, imageData, (uint)width, (uint)height, X11Application.RequiredColorDepth, width * 4 ); X11Bitmap bitmap = new X11Bitmap(xImage, imageData); xImage = IntPtr.Zero; imageData = IntPtr.Zero; return(bitmap); } finally { if (xImage != IntPtr.Zero) { LibX11.XDestroyImage(xImage); } else if (imageData != IntPtr.Zero) { Marshal.FreeHGlobal(imageData); } } }
public static X11Image Create(IntPtr display, IntPtr visual, ulong drawable, int width, int height) { if (width < 0 || height < 0) { throw new ArgumentException($"Image dimensions cannot be negative ({width} x {height})."); } // todo: handle zero width / height ulong pixmapId = LibX11.XCreatePixmap( display, drawable, (uint)width, (uint)height, X11Application.RequiredColorDepth ); return(new X11Image(display, visual, pixmapId, width, height)); }
private void HandleSelectionRequest(XSelectionRequestEvent evt) { if (evt.owner != windowId) { return; } var targetProperty = evt.property; if (targetProperty == 0) { // Owners receiving ConvertSelection requests with a property argument of None are talking to an obsolete client. // They should choose the target atom as the property name to be used for the reply. targetProperty = evt.target; } bool dataSent = ConvertOwnSelection(evt.requestor, evt.selection, evt.target, targetProperty); var response = XEvent.CreateSelectionNotify(evt.requestor, evt.selection, evt.target, dataSent ? targetProperty : 0, evt.time); LibX11.XSendEvent(display, evt.requestor, 0, XEventMask.NoEventMask, ref response); }
public void CopyFromBitmap(Rectangle imageArea, IntPtr bitmap, int bitmapStride) { // todo: create separate validation ? NativeBitmapSourceParameterValidation.CopyToBitmap(this, imageArea, bitmap, bitmapStride, out _); using (X11Bitmap xBitmap = X11Bitmap.Create(Display, Visual, imageArea.Width, imageArea.Height)) { PixelConverter.Convert_ARGB_32_To_PARGB_32(bitmap, bitmapStride, xBitmap.ImageData, imageArea.Width * 4, imageArea.Width, imageArea.Height); var gcValues = new XGCValues(); var gc = LibX11.XCreateGC(Display, PixmapId, 0, ref gcValues); try { LibX11.XPutImage(Display, PixmapId, gc, xBitmap.XImage, 0, 0, imageArea.X, imageArea.Y, (uint)imageArea.Width, (uint)imageArea.Height); } finally { LibX11.XFreeGC(Display, gc); } } }
public bool TryGetText(out string text) { lock (selectionConversionLock) { selectionConvertedEvent.Reset(); // todo: review usage of XLib under lock LibX11.XConvertSelection ( display, XA_CLIPBOARD, XA_UTF8_STRING, XA_NWINDOWS_CONVERTED_CLIPBOARD, windowId, 0 /* CurrentTime */ ); LibX11.XFlush(display); } if (!selectionConvertedEvent.WaitOne(OperationTimeout)) { text = null; return(false); } lock (selectionConversionLock) { if (convertedText == null) { text = null; return(false); } text = convertedText; convertedText = null; return(true); } }
public static bool IsAvailable() { try { LibX11.XInitThreads(); IntPtr display = LibX11.XOpenDisplay(null); if (display == IntPtr.Zero) { return(false); } LibX11.XCloseDisplay(display); return(true); } catch (DllNotFoundException) { return(false); } catch (EntryPointNotFoundException) { return(false); } }
public void CopyToBitmap(Rectangle imageArea, IntPtr bitmap, int bitmapStride) { NativeBitmapSourceParameterValidation.CopyToBitmap(this, imageArea, bitmap, bitmapStride, out _); using (X11Bitmap xBitmap = X11Bitmap.Create(Display, Visual, imageArea.Width, imageArea.Height)) { LibX11.XGetSubImage( Display, PixmapId, imageArea.X, imageArea.Y, (uint)imageArea.Width, (uint)imageArea.Height, ulong.MaxValue, XImageFormat.ZPixmap, xBitmap.XImage, 0, 0 ); PixelConverter.Convert_PARGB_32_To_ARGB_32(xBitmap.ImageData, imageArea.Width * 4, bitmap, bitmapStride, imageArea.Width, imageArea.Height); } }
private static byte[] ReadWindowProperty(IntPtr display, ulong windowId, ulong property) { const long smallPropertyLength = 1024; LibX11.XGetWindowProperty ( display, windowId, property, 0, smallPropertyLength, 1, 0 /* AnyPropertyType */, out ulong type, out int format, out ulong nItems, out ulong bytes_after_return, out IntPtr bufferPtr ); if (bytes_after_return != 0) { if (bufferPtr != IntPtr.Zero) { LibX11.XFree(bufferPtr); } ulong actualLength = bytes_after_return + 4 * smallPropertyLength; if (bytes_after_return > int.MaxValue || actualLength > int.MaxValue) { throw new InvalidOperationException ( $"Property {property} is too long: {nameof(bytes_after_return)} = {bytes_after_return}." ); } LibX11.XGetWindowProperty ( display, windowId, property, 0, (long)(actualLength + 3) / 4, 1, 0 /* AnyPropertyType */, out type, out format, out nItems, out bytes_after_return, out bufferPtr ); } try { if ((nItems != 0) && (format <= 0 || format % 8 != 0)) { throw new InvalidOperationException($"{nameof(LibX11.XGetWindowProperty)} returned an invalid format: {format}."); } int chunkSize = (int)nItems * format / 8; byte[] res = new byte [chunkSize]; if (chunkSize != 0) { if (bufferPtr == IntPtr.Zero) { throw new InvalidOperationException($"{nameof(LibX11.XGetWindowProperty)} returned an empty buffer for a non-empty value."); } Marshal.Copy(bufferPtr, res, 0, chunkSize); } return(res); } finally { if (bufferPtr != IntPtr.Zero) { LibX11.XFree(bufferPtr); } } }
public void Dispose() { // todo: use finalizer? LibX11.XFreePixmap(Display, PixmapId); }
public void Run(INativeWindowStartupInfo window) { XSetWindowAttributes attr = new XSetWindowAttributes(); attr.border_pixel = 0; attr.bit_gravity = 1 /*NorthWestGravity */; attr.event_mask = XEventMask.ExposureMask | XEventMask.ButtonPressMask | XEventMask.ButtonReleaseMask | XEventMask.FocusChangeMask | XEventMask.KeyPressMask | XEventMask.KeyReleaseMask | XEventMask.PointerMotionMask | XEventMask.StructureNotifyMask; attr.colormap = graphics.ColorMap; ulong windowId = LibX11.XCreateWindow ( display, graphics.RootWindow, 0, 0, (uint)window.Width, (uint)window.Height, 1, graphics.PictFormat.depth, WindowClass.InputOutput, graphics.Visual, XSetWindowAttributeMask.CWBorderPixel | XSetWindowAttributeMask.CWBitGravity | XSetWindowAttributeMask.CWEventMask | XSetWindowAttributeMask.CWColormap, ref attr ); X11Window nativeWindow = new X11Window(display, windowId, Invalidate); window.OnCreate(nativeWindow); nativeWindow.SetTitle(window.Title); var protocols = new[] { WM_DELETE_WINDOW }; LibX11.XSetWMProtocols(display, windowId, protocols, protocols.Length); LibX11.XMapWindow(display, windowId); LibX11.XFlush(display); byte[] textBuffer = new byte[32]; bool autoRepeat = false; bool windowClosed = false; while (!windowClosed) { LibX11.XNextEvent(display, out XEvent evt); if (evt.type == XEventType.Expose) { var rect = new Rectangle(evt.ExposeEvent.x, evt.ExposeEvent.y, evt.ExposeEvent.width, evt.ExposeEvent.height); Invalidate(rect); } else if (evt.type == XEventType.MotionNotify) { window.OnMouseMove(new Point(evt.MotionEvent.x, evt.MotionEvent.y)); } else if (evt.type == XEventType.ConfigureNotify) { Size clientArea = new Size(evt.ConfigureEvent.width, evt.ConfigureEvent.height); window.OnResize(clientArea); } else if (evt.type == XEventType.ButtonPress) { window.OnMouseButtonDown( GetMouseButton(evt.ButtonEvent.button), new Point(evt.ButtonEvent.x, evt.ButtonEvent.y), GetModifierKey(evt.ButtonEvent.state) ); } else if (evt.type == XEventType.ButtonRelease) { window.OnMouseButtonUp( GetMouseButton(evt.ButtonEvent.button), new Point(evt.ButtonEvent.x, evt.ButtonEvent.y) ); } else if (evt.type == XEventType.KeyPress) { // XLookupString returns no more than the requested number of characters, but it also writes a zero-byte after them int charCount = LibX11.XLookupString(ref evt.KeyEvent, textBuffer, textBuffer.Length - 1, out var keySym, IntPtr.Zero); var keyCode = X11KeyMap.GetKeyCode(keySym); if (keyCode == NKeyCode.Unknown) { keySym = LibX11.XLookupKeysym(ref evt.KeyEvent, 0); keyCode = X11KeyMap.GetKeyCode(keySym); } window.OnKeyDown(keyCode, GetModifierKey(evt.KeyEvent.state), autoRepeat); autoRepeat = false; if (charCount > 0) { string text = Encoding.UTF8.GetString(textBuffer, 0, charCount); StringBuilder sb = new StringBuilder(text.Length); foreach (char c in text) { if (!char.IsControl(c)) { sb.Append(c); } } if (sb.Length > 0) { window.OnTextInput(sb.ToString()); } } } else if (evt.type == XEventType.KeyRelease) { if (LibX11.XPending(display) > 0) { LibX11.XPeekEvent(display, out XEvent nextEvent); autoRepeat = nextEvent.type == XEventType.KeyPress && nextEvent.KeyEvent.time == evt.KeyEvent.time && nextEvent.KeyEvent.keycode == evt.KeyEvent.keycode; } if (!autoRepeat) { // XLookupString returns no more than the requested number of characters, but it also writes a zero-byte after them LibX11.XLookupString(ref evt.KeyEvent, textBuffer, textBuffer.Length - 1, out var keySym, IntPtr.Zero); var keyCode = X11KeyMap.GetKeyCode(keySym); if (keyCode == NKeyCode.Unknown) { keySym = LibX11.XLookupKeysym(ref evt.KeyEvent, 0); keyCode = X11KeyMap.GetKeyCode(keySym); } window.OnKeyUp(keyCode); } } else if (evt.type == XEventType.FocusIn) { if (IsWindowActivationEvent(evt.FocusChangeEvent.mode, evt.FocusChangeEvent.detail)) { window.OnActivated(); } } else if (evt.type == XEventType.FocusOut) { if (IsWindowActivationEvent(evt.FocusChangeEvent.mode, evt.FocusChangeEvent.detail)) { window.OnDeactivated(); } } else if (evt.type == XEventType.ClientMessage) { var cevt = evt.ClientMessageEvent; if (cevt.message_type == WM_PROTOCOLS && cevt.data0 == WM_DELETE_WINDOW) { windowClosed = true; } else if (cevt.message_type == XA_NWINDOWS_PAINT_COMPLETE) { paintQueued = false; } } if (invalidatedArea != null && !paintQueued) { Rectangle area = invalidatedArea.Value; invalidatedArea = null; using (X11Image image = graphics.CreateImage(area.Width, area.Height)) { using (X11Canvas canvas = graphics.CreateCanvas(image.PixmapId)) { canvas.SetOrigin(area.X, area.Y); window.OnPaint(canvas, area); } using (X11Canvas canvas = graphics.CreateCanvas(windowId)) { canvas.DrawImage(image, area.X, area.Y); } } paintQueued = true; var message = XEvent.CreateClientMessage(windowId, XA_NWINDOWS_PAINT_COMPLETE); LibX11.XSendEvent(display, windowId, 0, XEventMask.NoEventMask, ref message); } } }
public void Dispose() { // todo: use finalizer? LibX11.XDestroyImage(XImage); }
public void Dispose() { clipboard.Stop(); graphics.Dispose(); LibX11.XCloseDisplay(display); }