mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-03 21:23:43 +02:00
committed by
Kovid Goyal
parent
6506ce1e91
commit
0c0c5a62f7
4
glfw/cocoa_platform.h
vendored
4
glfw/cocoa_platform.h
vendored
@@ -215,6 +215,10 @@ typedef struct _GLFWlibraryNS
|
||||
|
||||
// the callback to handle url open events
|
||||
GLFWhandleurlopen url_open_callback;
|
||||
|
||||
// Active drag session (NSDraggingSession*) and view (NSView*)
|
||||
id drag_session;
|
||||
id drag_view;
|
||||
} _GLFWlibraryNS;
|
||||
|
||||
// Cocoa-specific per-monitor data
|
||||
|
||||
@@ -4069,7 +4069,9 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {@autore
|
||||
[dragItems addObject:dragItem];
|
||||
}
|
||||
|
||||
[v beginDraggingSessionWithItems:dragItems event:event source:[v draggingSource]];
|
||||
[_glfw.ns.drag_session release];
|
||||
_glfw.ns.drag_session = [[v beginDraggingSessionWithItems:dragItems event:event source:[v draggingSource]] retain];
|
||||
_glfw.ns.drag_view = v;
|
||||
return 0;
|
||||
}}
|
||||
|
||||
@@ -4192,10 +4194,59 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {@autore
|
||||
@end
|
||||
|
||||
void
|
||||
_glfwPlatformFreeDragSourceData(void) { }
|
||||
_glfwPlatformFreeDragSourceData(void) {
|
||||
[_glfw.ns.drag_session release];
|
||||
_glfw.ns.drag_session = nil;
|
||||
_glfw.ns.drag_view = nil;
|
||||
}
|
||||
|
||||
int
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) { (void)thumbnail; return 0; /* TODO: Implement me */ }
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) {@autoreleasepool{
|
||||
if (!_glfw.ns.drag_session || !thumbnail || !thumbnail->pixels) return 0;
|
||||
_GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);
|
||||
CGFloat scaleFactor = 1.0;
|
||||
if (window) {
|
||||
NSWindow *nsw = window->ns.object;
|
||||
scaleFactor = [nsw backingScaleFactor];
|
||||
if (scaleFactor == 0) scaleFactor = [NSScreen mainScreen].backingScaleFactor;
|
||||
}
|
||||
NSBitmapImageRep* imageRep = [[NSBitmapImageRep alloc]
|
||||
initWithBitmapDataPlanes:NULL
|
||||
pixelsWide:thumbnail->width
|
||||
pixelsHigh:thumbnail->height
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSDeviceRGBColorSpace
|
||||
bytesPerRow:thumbnail->width * 4
|
||||
bitsPerPixel:32];
|
||||
if (!imageRep) return ENOMEM;
|
||||
memcpy([imageRep bitmapData], thumbnail->pixels, thumbnail->width * thumbnail->height * 4);
|
||||
NSSize pointSize = NSMakeSize(thumbnail->width / scaleFactor, thumbnail->height / scaleFactor);
|
||||
[imageRep setSize:pointSize];
|
||||
NSImage* image = [[[NSImage alloc] initWithSize:pointSize] autorelease];
|
||||
if (!image) { [imageRep release]; return ENOMEM; }
|
||||
[image addRepresentation:imageRep];
|
||||
[imageRep release];
|
||||
|
||||
NSArray *classes = @[[NSPasteboardItem class], [NSFilePromiseProvider class]];
|
||||
[((NSDraggingSession*)_glfw.ns.drag_session)
|
||||
enumerateDraggingItemsWithOptions:0
|
||||
forView:(NSView*)_glfw.ns.drag_view
|
||||
classes:classes
|
||||
searchOptions:@{}
|
||||
usingBlock:^(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop) {
|
||||
if (idx == 0) {
|
||||
NSRect frame = [draggingItem draggingFrame];
|
||||
[draggingItem setDraggingFrame:NSMakeRect(frame.origin.x, frame.origin.y,
|
||||
pointSize.width, pointSize.height)
|
||||
contents:image];
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
return 0;
|
||||
}}
|
||||
|
||||
int
|
||||
_glfwPlatformDragDataReady(const char *mime_type) {
|
||||
|
||||
24
glfw/wl_window.c
vendored
24
glfw/wl_window.c
vendored
@@ -3190,7 +3190,29 @@ add_drag_watch(int fd) {
|
||||
}
|
||||
|
||||
int
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) { (void)thumbnail; return 0; /* TODO: Implement me */ }
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) {
|
||||
if (!_glfw.wl.drag.drag_icon || !thumbnail || !thumbnail->pixels) return 0;
|
||||
struct wl_buffer* icon_buffer = createShmBuffer(thumbnail, false, true);
|
||||
if (!icon_buffer) return ENOMEM;
|
||||
if (_glfw.wl.drag.drag_viewport) {
|
||||
_GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);
|
||||
double f_scale = window ? _glfwWaylandWindowScale(window) : 1.0;
|
||||
int logical_width = (int)(thumbnail->width / f_scale);
|
||||
int logical_height = (int)(thumbnail->height / f_scale);
|
||||
wp_viewport_set_destination(_glfw.wl.drag.drag_viewport, logical_width, logical_height);
|
||||
} else {
|
||||
_GLFWwindow *window = _glfwWindowForId(_glfw.drag.window_id);
|
||||
if (window) {
|
||||
int scale = _glfwWaylandIntegerWindowScale(window);
|
||||
wl_surface_set_buffer_scale(_glfw.wl.drag.drag_icon, scale);
|
||||
}
|
||||
}
|
||||
wl_surface_attach(_glfw.wl.drag.drag_icon, icon_buffer, 0, 0);
|
||||
wl_surface_damage(_glfw.wl.drag.drag_icon, 0, 0, INT32_MAX, INT32_MAX);
|
||||
wl_surface_commit(_glfw.wl.drag.drag_icon);
|
||||
wl_buffer_destroy(icon_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_glfwPlatformDragDataReady(const char *mime_type) {
|
||||
|
||||
217
glfw/x11_window.c
vendored
217
glfw/x11_window.c
vendored
@@ -103,6 +103,7 @@ static void handle_drag_button_release(Time timestamp);
|
||||
static void handle_xdnd_status(const XClientMessageEvent *event);
|
||||
static void handle_xdnd_finished(const XClientMessageEvent *event);
|
||||
static bool create_drag_thumbnail(const GLFWimage* thumbnail, int x, int y);
|
||||
static bool render_drag_thumbnail_to_pixmap(const GLFWimage* thumbnail);
|
||||
|
||||
static void
|
||||
handleEvents(monotonic_t timeout) {
|
||||
@@ -4062,6 +4063,97 @@ send_xdnd_drop(Window target, Time timestamp) {
|
||||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
|
||||
// Render thumbnail pixels into _glfw.x11.drag.thumbnail_pixmap / thumbnail_gc.
|
||||
// thumbnail_window must already exist. On success returns true.
|
||||
static bool
|
||||
render_drag_thumbnail_to_pixmap(const GLFWimage* thumbnail) {
|
||||
// Free any previous pixmap / GC
|
||||
if (_glfw.x11.drag.thumbnail_gc != None) {
|
||||
XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);
|
||||
_glfw.x11.drag.thumbnail_gc = None;
|
||||
}
|
||||
if (_glfw.x11.drag.thumbnail_pixmap != None) {
|
||||
XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);
|
||||
_glfw.x11.drag.thumbnail_pixmap = None;
|
||||
}
|
||||
|
||||
_glfw.x11.drag.thumbnail_pixmap = XCreatePixmap(
|
||||
_glfw.x11.display,
|
||||
_glfw.x11.drag.thumbnail_window,
|
||||
thumbnail->width,
|
||||
thumbnail->height,
|
||||
DefaultDepth(_glfw.x11.display, _glfw.x11.screen)
|
||||
);
|
||||
if (!_glfw.x11.drag.thumbnail_pixmap) return false;
|
||||
|
||||
_glfw.x11.drag.thumbnail_gc = XCreateGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap, 0, NULL);
|
||||
if (!_glfw.x11.drag.thumbnail_gc) {
|
||||
XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);
|
||||
_glfw.x11.drag.thumbnail_pixmap = None;
|
||||
return false;
|
||||
}
|
||||
|
||||
Visual* visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
|
||||
XImage* ximage = XCreateImage(
|
||||
_glfw.x11.display,
|
||||
visual,
|
||||
DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
|
||||
ZPixmap,
|
||||
0,
|
||||
NULL,
|
||||
thumbnail->width,
|
||||
thumbnail->height,
|
||||
32,
|
||||
0
|
||||
);
|
||||
if (!ximage) {
|
||||
XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);
|
||||
XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);
|
||||
_glfw.x11.drag.thumbnail_gc = None;
|
||||
_glfw.x11.drag.thumbnail_pixmap = None;
|
||||
return false;
|
||||
}
|
||||
|
||||
int pixels_size = thumbnail->width * thumbnail->height * 4;
|
||||
unsigned char* ximage_data = malloc(pixels_size);
|
||||
if (!ximage_data) {
|
||||
XDestroyImage(ximage);
|
||||
XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);
|
||||
XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);
|
||||
_glfw.x11.drag.thumbnail_gc = None;
|
||||
_glfw.x11.drag.thumbnail_pixmap = None;
|
||||
return false;
|
||||
}
|
||||
ximage->data = (char*)ximage_data;
|
||||
|
||||
const unsigned char* src = thumbnail->pixels;
|
||||
for (int i = 0; i < thumbnail->width * thumbnail->height; i++) {
|
||||
unsigned char r = src[i * 4 + 0];
|
||||
unsigned char g = src[i * 4 + 1];
|
||||
unsigned char b = src[i * 4 + 2];
|
||||
unsigned char a = src[i * 4 + 3];
|
||||
if (a < 255) {
|
||||
r = (r * a) / 255;
|
||||
g = (g * a) / 255;
|
||||
b = (b * a) / 255;
|
||||
}
|
||||
unsigned long pixel;
|
||||
if (ximage->byte_order == LSBFirst)
|
||||
pixel = ((unsigned long)a << 24) | ((unsigned long)r << 16) | ((unsigned long)g << 8) | b;
|
||||
else
|
||||
pixel = ((unsigned long)b << 24) | ((unsigned long)g << 16) | ((unsigned long)r << 8) | a;
|
||||
XPutPixel(ximage, i % thumbnail->width, i / thumbnail->width, pixel);
|
||||
}
|
||||
|
||||
XPutImage(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap, _glfw.x11.drag.thumbnail_gc,
|
||||
ximage, 0, 0, 0, 0, thumbnail->width, thumbnail->height);
|
||||
XDestroyImage(ximage);
|
||||
|
||||
XSetWindowBackgroundPixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_window,
|
||||
_glfw.x11.drag.thumbnail_pixmap);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create thumbnail window for drag operation
|
||||
static bool
|
||||
create_drag_thumbnail(const GLFWimage* thumbnail, int x, int y) {
|
||||
@@ -4091,108 +4183,12 @@ create_drag_thumbnail(const GLFWimage* thumbnail, int x, int y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create pixmap for the thumbnail image
|
||||
_glfw.x11.drag.thumbnail_pixmap = XCreatePixmap(
|
||||
_glfw.x11.display,
|
||||
_glfw.x11.drag.thumbnail_window,
|
||||
thumbnail->width,
|
||||
thumbnail->height,
|
||||
DefaultDepth(_glfw.x11.display, _glfw.x11.screen)
|
||||
);
|
||||
|
||||
if (!_glfw.x11.drag.thumbnail_pixmap) {
|
||||
if (!render_drag_thumbnail_to_pixmap(thumbnail)) {
|
||||
XDestroyWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);
|
||||
_glfw.x11.drag.thumbnail_window = None;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create GC for drawing
|
||||
_glfw.x11.drag.thumbnail_gc = XCreateGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap, 0, NULL);
|
||||
if (!_glfw.x11.drag.thumbnail_gc) {
|
||||
XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);
|
||||
XDestroyWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);
|
||||
_glfw.x11.drag.thumbnail_window = None;
|
||||
_glfw.x11.drag.thumbnail_pixmap = None;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert RGBA image data to XImage and draw to pixmap
|
||||
Visual* visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
|
||||
XImage* ximage = XCreateImage(
|
||||
_glfw.x11.display,
|
||||
visual,
|
||||
DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
|
||||
ZPixmap,
|
||||
0, // offset
|
||||
NULL, // data (will be set below)
|
||||
thumbnail->width,
|
||||
thumbnail->height,
|
||||
32, // bitmap_pad
|
||||
0 // bytes_per_line (auto-calculate)
|
||||
);
|
||||
|
||||
if (!ximage) {
|
||||
XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);
|
||||
XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);
|
||||
XDestroyWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);
|
||||
_glfw.x11.drag.thumbnail_window = None;
|
||||
_glfw.x11.drag.thumbnail_pixmap = None;
|
||||
_glfw.x11.drag.thumbnail_gc = None;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate buffer for XImage
|
||||
int pixels_size = thumbnail->width * thumbnail->height * 4;
|
||||
unsigned char* ximage_data = malloc(pixels_size);
|
||||
if (!ximage_data) {
|
||||
XDestroyImage(ximage);
|
||||
XFreeGC(_glfw.x11.display, _glfw.x11.drag.thumbnail_gc);
|
||||
XFreePixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap);
|
||||
XDestroyWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);
|
||||
_glfw.x11.drag.thumbnail_window = None;
|
||||
_glfw.x11.drag.thumbnail_pixmap = None;
|
||||
_glfw.x11.drag.thumbnail_gc = None;
|
||||
return false;
|
||||
}
|
||||
|
||||
ximage->data = (char*)ximage_data;
|
||||
|
||||
// Convert RGBA to the format expected by XImage
|
||||
const unsigned char* src = thumbnail->pixels;
|
||||
for (int i = 0; i < thumbnail->width * thumbnail->height; i++) {
|
||||
unsigned long pixel;
|
||||
unsigned char r = src[i * 4 + 0];
|
||||
unsigned char g = src[i * 4 + 1];
|
||||
unsigned char b = src[i * 4 + 2];
|
||||
unsigned char a = src[i * 4 + 3];
|
||||
|
||||
// Premultiply alpha
|
||||
if (a < 255) {
|
||||
r = (r * a) / 255;
|
||||
g = (g * a) / 255;
|
||||
b = (b * a) / 255;
|
||||
}
|
||||
|
||||
// Convert to X11 pixel format (typically BGRA on little-endian)
|
||||
if (ximage->byte_order == LSBFirst) {
|
||||
pixel = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
} else {
|
||||
pixel = (b << 24) | (g << 16) | (r << 8) | a;
|
||||
}
|
||||
|
||||
XPutPixel(ximage, i % thumbnail->width, i / thumbnail->width, pixel);
|
||||
}
|
||||
|
||||
// Draw the image to the pixmap
|
||||
XPutImage(_glfw.x11.display, _glfw.x11.drag.thumbnail_pixmap, _glfw.x11.drag.thumbnail_gc,
|
||||
ximage, 0, 0, 0, 0, thumbnail->width, thumbnail->height);
|
||||
|
||||
// Clean up XImage (including its data)
|
||||
XDestroyImage(ximage); // This also frees ximage_data
|
||||
|
||||
// Set the pixmap as the window's background
|
||||
XSetWindowBackgroundPixmap(_glfw.x11.display, _glfw.x11.drag.thumbnail_window, _glfw.x11.drag.thumbnail_pixmap);
|
||||
|
||||
// Map the window to make it visible
|
||||
XMapRaised(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);
|
||||
XFlush(_glfw.x11.display);
|
||||
@@ -4475,7 +4471,32 @@ _glfwPlatformFreeDragSourceData(void) {
|
||||
}
|
||||
|
||||
int
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) { (void)thumbnail; return 0; /* TODO: Implement me */ }
|
||||
_glfwPlatformChangeDragImage(const GLFWimage *thumbnail) {
|
||||
if (!_glfw.x11.drag.active) return 0;
|
||||
if (!thumbnail || !thumbnail->pixels || thumbnail->width <= 0 || thumbnail->height <= 0) return 0;
|
||||
|
||||
if (_glfw.x11.drag.thumbnail_window == None) {
|
||||
// No thumbnail window yet; query cursor position and create one
|
||||
Window root_return, child_return;
|
||||
int root_x, root_y, win_x, win_y;
|
||||
unsigned int mask_return;
|
||||
XQueryPointer(_glfw.x11.display, _glfw.x11.root,
|
||||
&root_return, &child_return,
|
||||
&root_x, &root_y, &win_x, &win_y, &mask_return);
|
||||
if (!create_drag_thumbnail(thumbnail, root_x + 10, root_y + 10)) return EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Resize the existing window to match the new thumbnail dimensions
|
||||
XResizeWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window,
|
||||
thumbnail->width, thumbnail->height);
|
||||
|
||||
if (!render_drag_thumbnail_to_pixmap(thumbnail)) return EIO;
|
||||
|
||||
XClearWindow(_glfw.x11.display, _glfw.x11.drag.thumbnail_window);
|
||||
XFlush(_glfw.x11.display);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_glfwPlatformDragDataReady(const char *mime_type) {
|
||||
|
||||
Reference in New Issue
Block a user