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 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 DrawImage(INativeImage image, int x, int y) { x -= origin.X; y -= origin.Y; // todo: allow null? X11Image x11Image = (X11Image)image; XRenderPictureAttributes attr = new XRenderPictureAttributes(); var tempPictureId = LibXRender.XRenderCreatePicture(display, x11Image.PixmapId, pictFormatPtr, 0, ref attr); try { LibXRender.XRenderComposite ( display, PictOp.PictOpOver, tempPictureId, 0, pictureId, 0, 0, 0, 0, x, y, (uint)x11Image.Width, (uint)x11Image.Height ); } finally { LibXRender.XRenderFreePicture(display, tempPictureId); } }
public INativeImage CreateImage(int width, int height) { // todo: what is the best place for this validation if (width < 0 || height < 0) { throw new ArgumentException($"Image dimensions cannot be negative ({width} x {height})."); } if (width * 4L > int.MaxValue || height * 4L > int.MaxValue || 4L * width * height > int.MaxValue) { throw new ArgumentException($"Image dimensions are too large ({width} x {height})."); } return(X11Image.Create(display, visual, rootWindow, width, height)); }
public INativeImage LoadImageFromStream(Stream stream) { using (GdkPixBufBitmapSource source = LoadSourceFromStream(stream)) { X11Image disposableImage = X11Image.Create(display, visual, rootWindow, source.Width, source.Height); try { source.CopyToImage(disposableImage); X11Image image = disposableImage; disposableImage = null; return(image); } finally { disposableImage?.Dispose(); } } }
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); } } }
internal X11Image CreateImage(int width, int height) { return(X11Image.Create(display, visual, rootWindow, width, height)); }