Add a GLFW API to support starting drag operations
Fixes #9454 Fixes #9455
This commit is contained in:
parent
a9e4f962a1
commit
fa6c76d3e3
14 changed files with 801 additions and 10 deletions
3
glfw/cocoa_platform.h
vendored
3
glfw/cocoa_platform.h
vendored
|
|
@ -167,6 +167,9 @@ typedef struct _GLFWwindowNS
|
|||
// update cursor after switching desktops with Mission Control
|
||||
bool delayed_cursor_update_requested;
|
||||
GLFWcocoarenderframefun resizeCallback;
|
||||
|
||||
// Current drag operation type for NSDraggingSource
|
||||
GLFWDragOperationType dragOperationType;
|
||||
} _GLFWwindowNS;
|
||||
|
||||
// Cocoa-specific global data
|
||||
|
|
|
|||
|
|
@ -756,7 +756,7 @@ - (void)doCommandBySelector:(SEL)selector
|
|||
|
||||
// Content view class for the GLFW window {{{
|
||||
|
||||
@interface GLFWContentView : NSView <NSTextInputClient>
|
||||
@interface GLFWContentView : NSView <NSTextInputClient, NSDraggingSource>
|
||||
{
|
||||
_GLFWwindow* window;
|
||||
NSTrackingArea* trackingArea;
|
||||
|
|
@ -1341,12 +1341,37 @@ - (void)scrollWheel:(NSEvent *)event
|
|||
|
||||
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
||||
{
|
||||
(void)sender;
|
||||
// HACK: We don't know what to say here because we don't know what the
|
||||
// application wants to do with the paths
|
||||
const NSRect contentRect = [window->ns.view frame];
|
||||
const NSPoint pos = [sender draggingLocation];
|
||||
double xpos = pos.x;
|
||||
double ypos = contentRect.size.height - pos.y;
|
||||
|
||||
// Call drag enter callback and check if accepted
|
||||
int accepted = _glfwInputDragEvent(window, GLFW_DRAG_ENTER, xpos, ypos);
|
||||
if (accepted)
|
||||
return NSDragOperationGeneric;
|
||||
return NSDragOperationNone;
|
||||
}
|
||||
|
||||
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
|
||||
{
|
||||
const NSRect contentRect = [window->ns.view frame];
|
||||
const NSPoint pos = [sender draggingLocation];
|
||||
double xpos = pos.x;
|
||||
double ypos = contentRect.size.height - pos.y;
|
||||
|
||||
// Call drag move callback
|
||||
_glfwInputDragEvent(window, GLFW_DRAG_MOVE, xpos, ypos);
|
||||
return NSDragOperationGeneric;
|
||||
}
|
||||
|
||||
- (void)draggingExited:(id <NSDraggingInfo>)sender
|
||||
{
|
||||
(void)sender;
|
||||
// Call drag leave callback
|
||||
_glfwInputDragEvent(window, GLFW_DRAG_LEAVE, 0, 0);
|
||||
}
|
||||
|
||||
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
||||
{
|
||||
const NSRect contentRect = [window->ns.view frame];
|
||||
|
|
@ -1385,6 +1410,33 @@ - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
|||
return YES;
|
||||
}
|
||||
|
||||
// NSDraggingSource protocol methods
|
||||
- (NSDragOperation)draggingSession:(NSDraggingSession *)session
|
||||
sourceOperationMaskForDraggingContext:(NSDraggingContext)context
|
||||
{
|
||||
(void)session;
|
||||
(void)context;
|
||||
// Return the operation based on the stored drag operation type
|
||||
switch (window->ns.dragOperationType) {
|
||||
case GLFW_DRAG_OPERATION_COPY:
|
||||
return NSDragOperationCopy;
|
||||
case GLFW_DRAG_OPERATION_MOVE:
|
||||
return NSDragOperationMove;
|
||||
case GLFW_DRAG_OPERATION_GENERIC:
|
||||
return NSDragOperationGeneric;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)draggingSession:(NSDraggingSession *)session
|
||||
endedAtPoint:(NSPoint)screenPoint
|
||||
operation:(NSDragOperation)operation
|
||||
{
|
||||
(void)session;
|
||||
(void)screenPoint;
|
||||
(void)operation;
|
||||
// Drag session ended
|
||||
}
|
||||
|
||||
- (BOOL)hasMarkedText
|
||||
{
|
||||
return [markedText length] > 0;
|
||||
|
|
@ -3597,3 +3649,99 @@ void _glfwCocoaPostEmptyEvent(void) {
|
|||
data2:0];
|
||||
[NSApp postEvent:event atStart:YES];
|
||||
}
|
||||
|
||||
int _glfwPlatformStartDrag(_GLFWwindow* window,
|
||||
const GLFWdragitem* items,
|
||||
int item_count,
|
||||
const GLFWimage* thumbnail,
|
||||
GLFWDragOperationType operation) {
|
||||
// Store the operation type for the dragging source callback
|
||||
window->ns.dragOperationType = operation;
|
||||
|
||||
@autoreleasepool {
|
||||
// Create pasteboard items for each drag item
|
||||
NSMutableArray<NSPasteboardItem*>* pasteboardItems = [[NSMutableArray alloc] init];
|
||||
|
||||
for (int i = 0; i < item_count; i++) {
|
||||
NSPasteboardItem* item = [[NSPasteboardItem alloc] init];
|
||||
|
||||
// Convert MIME type to UTI using the existing helper function
|
||||
NSString* utiString = mime_to_uti(items[i].mime_type);
|
||||
NSData* data = [NSData dataWithBytes:items[i].data length:items[i].data_size];
|
||||
|
||||
[item setData:data forType:utiString];
|
||||
[pasteboardItems addObject:item];
|
||||
}
|
||||
|
||||
// Create the dragging item
|
||||
NSDraggingItem* dragItem = nil;
|
||||
|
||||
if (thumbnail && thumbnail->pixels) {
|
||||
// Create NSImage from thumbnail
|
||||
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) {
|
||||
memcpy([imageRep bitmapData], thumbnail->pixels,
|
||||
thumbnail->width * thumbnail->height * 4);
|
||||
|
||||
NSImage* image = [[NSImage alloc] initWithSize:
|
||||
NSMakeSize(thumbnail->width, thumbnail->height)];
|
||||
[image addRepresentation:imageRep];
|
||||
|
||||
dragItem = [[NSDraggingItem alloc]
|
||||
initWithPasteboardWriter:pasteboardItems.firstObject];
|
||||
[dragItem setDraggingFrame:NSMakeRect(0, 0, thumbnail->width, thumbnail->height)
|
||||
contents:image];
|
||||
}
|
||||
}
|
||||
|
||||
if (!dragItem && pasteboardItems.count > 0) {
|
||||
dragItem = [[NSDraggingItem alloc]
|
||||
initWithPasteboardWriter:pasteboardItems.firstObject];
|
||||
[dragItem setDraggingFrame:NSMakeRect(0, 0, 32, 32) contents:nil];
|
||||
}
|
||||
|
||||
if (dragItem) {
|
||||
// Start the drag session - try current event first, then create a synthetic one
|
||||
NSEvent* event = [NSApp currentEvent];
|
||||
if (!event || ([event type] != NSEventTypeLeftMouseDown &&
|
||||
[event type] != NSEventTypeLeftMouseDragged)) {
|
||||
// Create a synthetic left mouse down event using stored cursor position
|
||||
// Convert window coordinates to screen coordinates
|
||||
NSRect contentRect = [window->ns.view frame];
|
||||
NSPoint windowPos = NSMakePoint(window->virtualCursorPosX,
|
||||
contentRect.size.height - window->virtualCursorPosY);
|
||||
|
||||
event = [NSEvent mouseEventWithType:NSEventTypeLeftMouseDown
|
||||
location:windowPos
|
||||
modifierFlags:0
|
||||
timestamp:[[NSProcessInfo processInfo] systemUptime]
|
||||
windowNumber:[window->ns.object windowNumber]
|
||||
context:nil
|
||||
eventNumber:0
|
||||
clickCount:1
|
||||
pressure:1.0];
|
||||
}
|
||||
|
||||
if (event) {
|
||||
[window->ns.view beginDraggingSessionWithItems:@[dragItem]
|
||||
event:event
|
||||
source:window->ns.view];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
144
glfw/glfw3.h
vendored
144
glfw/glfw3.h
vendored
|
|
@ -1783,6 +1783,81 @@ typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
|
|||
*/
|
||||
typedef int (* GLFWdropfun)(GLFWwindow*, const char *, const char*, size_t);
|
||||
|
||||
/*! @brief Drag event types.
|
||||
*
|
||||
* These constants are used to identify the type of drag event.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef enum {
|
||||
/*! The drag operation entered the window. */
|
||||
GLFW_DRAG_ENTER = 1,
|
||||
/*! The drag operation moved within the window. */
|
||||
GLFW_DRAG_MOVE = 2,
|
||||
/*! The drag operation left the window. */
|
||||
GLFW_DRAG_LEAVE = 3
|
||||
} GLFWDragEventType;
|
||||
|
||||
/*! @brief Drag operation types.
|
||||
*
|
||||
* These constants specify the type of drag operation (copy, move, or generic).
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef enum {
|
||||
/*! Move the dragged data to the destination. */
|
||||
GLFW_DRAG_OPERATION_MOVE = 1,
|
||||
/*! Copy the dragged data to the destination. */
|
||||
GLFW_DRAG_OPERATION_COPY = 2,
|
||||
/*! Generic drag operation (platform decides semantics). */
|
||||
GLFW_DRAG_OPERATION_GENERIC = 3
|
||||
} GLFWDragOperationType;
|
||||
|
||||
/*! @brief Drag data item.
|
||||
*
|
||||
* This structure describes a single item of drag data with its MIME type.
|
||||
*
|
||||
* @sa @ref drag_start
|
||||
* @sa @ref glfwStartDrag
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef struct GLFWdragitem {
|
||||
/*! The MIME type of this data item (e.g., "text/plain", "image/png"). */
|
||||
const char* mime_type;
|
||||
/*! Pointer to the binary data. */
|
||||
const unsigned char* data;
|
||||
/*! Size of the data in bytes. */
|
||||
size_t data_size;
|
||||
} GLFWdragitem;
|
||||
|
||||
/*! @brief The function pointer type for drag event callbacks.
|
||||
*
|
||||
* This is the function pointer type for drag event callbacks. A drag event
|
||||
* callback function has the following signature:
|
||||
* @code
|
||||
* int function_name(GLFWwindow* window, int event, double xpos, double ypos)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the drag event.
|
||||
* @param[in] event The drag event type: @ref GLFW_DRAG_ENTER, @ref GLFW_DRAG_MOVE,
|
||||
* or @ref GLFW_DRAG_LEAVE.
|
||||
* @param[in] xpos The x-coordinate of the drag position in window coordinates.
|
||||
* @param[in] ypos The y-coordinate of the drag position in window coordinates.
|
||||
* @return For @ref GLFW_DRAG_ENTER events, return non-zero to accept the drag
|
||||
* or zero to reject it. Return value is ignored for other event types.
|
||||
*
|
||||
* @sa @ref drag_events
|
||||
* @sa @ref glfwSetDragCallback
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef int (* GLFWdragfun)(GLFWwindow*, GLFWDragEventType event, double xpos, double ypos);
|
||||
|
||||
typedef void (* GLFWliveresizefun)(GLFWwindow*, bool);
|
||||
|
||||
/*! @brief The function pointer type for monitor configuration callbacks.
|
||||
|
|
@ -4913,6 +4988,75 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun ca
|
|||
GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback);
|
||||
GLFWAPI GLFWliveresizefun glfwSetLiveResizeCallback(GLFWwindow* window, GLFWliveresizefun callback);
|
||||
|
||||
/*! @brief Sets the drag event callback.
|
||||
*
|
||||
* This function sets the callback for drag events. The callback is invoked
|
||||
* when a drag operation enters, moves within, or leaves the window. Use this
|
||||
* callback to accept or reject incoming drag operations and track drag
|
||||
* position.
|
||||
*
|
||||
* @param[in] window The window whose callback to set.
|
||||
* @param[in] callback The new callback, or `NULL` to remove the currently set
|
||||
* callback.
|
||||
* @return The previously set callback, or `NULL` if no callback was set or the
|
||||
* library had not been [initialized](@ref intro_init).
|
||||
*
|
||||
* @callback_signature
|
||||
* @code
|
||||
* int function_name(GLFWwindow* window, int event, double xpos, double ypos)
|
||||
* @endcode
|
||||
* For more information about the callback parameters, see the
|
||||
* [function pointer type](@ref GLFWdragfun).
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref drag_events
|
||||
* @sa @ref glfwStartDrag
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI GLFWdragfun glfwSetDragCallback(GLFWwindow* window, GLFWdragfun callback);
|
||||
|
||||
/*! @brief Starts a drag operation.
|
||||
*
|
||||
* This function starts a drag operation from the specified window with the
|
||||
* given data items and optional thumbnail image. The drag operation will
|
||||
* continue until the user releases the mouse button.
|
||||
*
|
||||
* The data items array contains one or more MIME types with their associated
|
||||
* binary data. The data is copied internally, so the caller can free it after
|
||||
* this function returns.
|
||||
*
|
||||
* @param[in] window The window initiating the drag.
|
||||
* @param[in] items Array of drag data items.
|
||||
* @param[in] item_count Number of items in the array.
|
||||
* @param[in] thumbnail Optional thumbnail/icon image to display during the
|
||||
* drag operation, or `NULL` for no thumbnail. The image data is copied.
|
||||
* @param[in] operation The type of drag operation: @ref GLFW_DRAG_OPERATION_MOVE,
|
||||
* @ref GLFW_DRAG_OPERATION_COPY, or @ref GLFW_DRAG_OPERATION_GENERIC. The default
|
||||
* should be @ref GLFW_DRAG_OPERATION_MOVE.
|
||||
*
|
||||
* @return `true` if the drag operation was started successfully, `false`
|
||||
* otherwise.
|
||||
*
|
||||
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
|
||||
* GLFW_PLATFORM_ERROR.
|
||||
*
|
||||
* @thread_safety This function must only be called from the main thread.
|
||||
*
|
||||
* @sa @ref drag_start
|
||||
* @sa @ref glfwSetDragCallback
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI int glfwStartDrag(GLFWwindow* window, const GLFWdragitem* items, int item_count, const GLFWimage* thumbnail, GLFWDragOperationType operation);
|
||||
|
||||
/*! @brief Returns whether the specified joystick is present.
|
||||
*
|
||||
* This function returns whether the specified joystick is present.
|
||||
|
|
|
|||
30
glfw/input.c
vendored
30
glfw/input.c
vendored
|
|
@ -410,6 +410,15 @@ int _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Notifies shared code of a drag event
|
||||
//
|
||||
int _glfwInputDragEvent(_GLFWwindow* window, int event, double xpos, double ypos)
|
||||
{
|
||||
if (window->callbacks.drag)
|
||||
return window->callbacks.drag((GLFWwindow*) window, event, xpos, ypos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Notifies shared code of a joystick connection or disconnection
|
||||
//
|
||||
void _glfwInputJoystick(_GLFWjoystick* js, int event)
|
||||
|
|
@ -1111,6 +1120,27 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun)
|
|||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWdragfun glfwSetDragCallback(GLFWwindow* handle, GLFWdragfun cbfun)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(window->callbacks.drag, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwStartDrag(GLFWwindow* handle, const GLFWdragitem* items, int item_count, const GLFWimage* thumbnail, GLFWDragOperationType operation)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
assert(items != NULL);
|
||||
assert(item_count > 0);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(false);
|
||||
return _glfwPlatformStartDrag(window, items, item_count, thumbnail, operation);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwJoystickPresent(int jid)
|
||||
{
|
||||
_GLFWjoystick* js;
|
||||
|
|
|
|||
4
glfw/internal.h
vendored
4
glfw/internal.h
vendored
|
|
@ -480,6 +480,7 @@ struct _GLFWwindow
|
|||
GLFWkeyboardfun keyboard;
|
||||
GLFWdropfun drop;
|
||||
GLFWliveresizefun liveResize;
|
||||
GLFWdragfun drag;
|
||||
} callbacks;
|
||||
|
||||
// This is defined in the window API's platform.h
|
||||
|
|
@ -766,6 +767,8 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity);
|
|||
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev);
|
||||
void _glfwPlatformChangeCursorTheme(void);
|
||||
|
||||
int _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWdragitem* items, int item_count, const GLFWimage* thumbnail, GLFWDragOperationType operation);
|
||||
|
||||
void _glfwPlatformPollEvents(void);
|
||||
void _glfwPlatformWaitEvents(void);
|
||||
void _glfwPlatformWaitEventsTimeout(monotonic_t timeout);
|
||||
|
|
@ -819,6 +822,7 @@ void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods)
|
|||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered);
|
||||
int _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size_t sz);
|
||||
int _glfwInputDragEvent(_GLFWwindow* window, int event, double xpos, double ypos);
|
||||
void _glfwInputColorScheme(GLFWColorScheme, bool);
|
||||
void _glfwPlatformInputColorScheme(GLFWColorScheme);
|
||||
void _glfwInputJoystick(_GLFWjoystick* js, int event);
|
||||
|
|
|
|||
9
glfw/null_window.c
vendored
9
glfw/null_window.c
vendored
|
|
@ -530,6 +530,15 @@ void _glfwPlatformSetCursor(_GLFWwindow* window UNUSED, _GLFWcursor* cursor UNUS
|
|||
{
|
||||
}
|
||||
|
||||
int _glfwPlatformStartDrag(_GLFWwindow* window UNUSED,
|
||||
const GLFWdragitem* items UNUSED,
|
||||
int item_count UNUSED,
|
||||
const GLFWimage* thumbnail UNUSED,
|
||||
GLFWDragOperationType operation UNUSED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string)
|
||||
{
|
||||
char* copy = _glfw_strdup(string);
|
||||
|
|
|
|||
9
glfw/wl_platform.h
vendored
9
glfw/wl_platform.h
vendored
|
|
@ -406,6 +406,15 @@ typedef struct _GLFWlibraryWayland
|
|||
_GLFWWaylandDataOffer dataOffers[8];
|
||||
bool has_preferred_buffer_scale;
|
||||
char *compositor_name;
|
||||
|
||||
// Drag operation state
|
||||
struct {
|
||||
struct wl_data_source* source;
|
||||
unsigned char** items_data;
|
||||
size_t* items_sizes;
|
||||
char** items_mimes;
|
||||
int item_count;
|
||||
} drag;
|
||||
} _GLFWlibraryWayland;
|
||||
|
||||
// Wayland-specific per-monitor data
|
||||
|
|
|
|||
203
glfw/wl_window.c
vendored
203
glfw/wl_window.c
vendored
|
|
@ -2486,7 +2486,7 @@ static void handle_primary_selection_offer(void *data UNUSED, struct zwp_primary
|
|||
zwp_primary_selection_offer_v1_add_listener(id, &primary_selection_offer_listener, NULL);
|
||||
}
|
||||
|
||||
static void drag_enter(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, uint32_t serial, struct wl_surface *surface, wl_fixed_t x UNUSED, wl_fixed_t y UNUSED, struct wl_data_offer *id) {
|
||||
static void drag_enter(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
_GLFWWaylandDataOffer *d = _glfw.wl.dataOffers + i;
|
||||
if (d->id == id) {
|
||||
|
|
@ -2497,9 +2497,20 @@ static void drag_enter(void *data UNUSED, struct wl_data_device *wl_data_device
|
|||
while (window)
|
||||
{
|
||||
if (window->wl.surface == surface) {
|
||||
for (size_t j = 0; j < d->mimes_count; j++) {
|
||||
int prio = _glfwInputDrop(window, d->mimes[j], NULL, 0);
|
||||
if (prio > format_priority) d->mime_for_drop = d->mimes[j];
|
||||
// Call drag enter callback
|
||||
double xpos = wl_fixed_to_double(x);
|
||||
double ypos = wl_fixed_to_double(y);
|
||||
int accepted = _glfwInputDragEvent(window, GLFW_DRAG_ENTER, xpos, ypos);
|
||||
|
||||
// If accepted, check MIME type priorities
|
||||
if (accepted) {
|
||||
for (size_t j = 0; j < d->mimes_count; j++) {
|
||||
int prio = _glfwInputDrop(window, d->mimes[j], NULL, 0);
|
||||
if (prio > format_priority) {
|
||||
format_priority = prio;
|
||||
d->mime_for_drop = d->mimes[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -2516,6 +2527,15 @@ static void drag_enter(void *data UNUSED, struct wl_data_device *wl_data_device
|
|||
static void drag_leave(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED) {
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||
// Find the window for this offer and call the leave callback
|
||||
_GLFWwindow* window = _glfw.windowListHead;
|
||||
while (window) {
|
||||
if (window->wl.surface == _glfw.wl.dataOffers[i].surface) {
|
||||
_glfwInputDragEvent(window, GLFW_DRAG_LEAVE, 0, 0);
|
||||
break;
|
||||
}
|
||||
window = window->next;
|
||||
}
|
||||
destroy_data_offer(&_glfw.wl.dataOffers[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -2549,7 +2569,23 @@ static void drop(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED
|
|||
}
|
||||
}
|
||||
|
||||
static void motion(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, uint32_t time UNUSED, wl_fixed_t x UNUSED, wl_fixed_t y UNUSED) {
|
||||
static void motion(void *data UNUSED, struct wl_data_device *wl_data_device UNUSED, uint32_t time UNUSED, wl_fixed_t x, wl_fixed_t y) {
|
||||
// Find the current drag offer and send motion events
|
||||
for (size_t i = 0; i < arraysz(_glfw.wl.dataOffers); i++) {
|
||||
if (_glfw.wl.dataOffers[i].offer_type == DRAG_AND_DROP) {
|
||||
_GLFWwindow* window = _glfw.windowListHead;
|
||||
while (window) {
|
||||
if (window->wl.surface == _glfw.wl.dataOffers[i].surface) {
|
||||
double xpos = wl_fixed_to_double(x);
|
||||
double ypos = wl_fixed_to_double(y);
|
||||
_glfwInputDragEvent(window, GLFW_DRAG_MOVE, xpos, ypos);
|
||||
break;
|
||||
}
|
||||
window = window->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_data_device_listener data_device_listener = {
|
||||
|
|
@ -2964,3 +3000,160 @@ GLFWAPI bool glfwWaylandBeep(GLFWwindow *handle) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Drag operation implementation
|
||||
|
||||
static void
|
||||
drag_source_send(void *data UNUSED, struct wl_data_source *source UNUSED, const char *mime_type, int fd) {
|
||||
// Find the matching MIME type and send its data
|
||||
for (int i = 0; i < _glfw.wl.drag.item_count; i++) {
|
||||
if (strcmp(_glfw.wl.drag.items_mimes[i], mime_type) == 0) {
|
||||
write_all(fd, (const char*)_glfw.wl.drag.items_data[i], _glfw.wl.drag.items_sizes[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
drag_source_cancelled(void *data UNUSED, struct wl_data_source *source) {
|
||||
// Clean up drag data
|
||||
if (_glfw.wl.drag.source == source) {
|
||||
for (int i = 0; i < _glfw.wl.drag.item_count; i++) {
|
||||
free(_glfw.wl.drag.items_data[i]);
|
||||
free(_glfw.wl.drag.items_mimes[i]);
|
||||
}
|
||||
free(_glfw.wl.drag.items_data);
|
||||
free(_glfw.wl.drag.items_sizes);
|
||||
free(_glfw.wl.drag.items_mimes);
|
||||
_glfw.wl.drag.items_data = NULL;
|
||||
_glfw.wl.drag.items_sizes = NULL;
|
||||
_glfw.wl.drag.items_mimes = NULL;
|
||||
_glfw.wl.drag.item_count = 0;
|
||||
_glfw.wl.drag.source = NULL;
|
||||
}
|
||||
wl_data_source_destroy(source);
|
||||
_glfw.wl.drag.source = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
drag_source_target(void *data UNUSED, struct wl_data_source *source UNUSED, const char *mime_type UNUSED) {
|
||||
}
|
||||
|
||||
static void
|
||||
drag_source_action(void *data UNUSED, struct wl_data_source *source UNUSED, uint32_t dnd_action UNUSED) {
|
||||
}
|
||||
|
||||
static void
|
||||
drag_source_dnd_drop_performed(void *data UNUSED, struct wl_data_source *source UNUSED) {
|
||||
}
|
||||
|
||||
static void
|
||||
drag_source_dnd_finished(void *data UNUSED, struct wl_data_source *source) {
|
||||
drag_source_cancelled(data, source);
|
||||
}
|
||||
|
||||
static const struct wl_data_source_listener drag_source_listener = {
|
||||
.send = drag_source_send,
|
||||
.cancelled = drag_source_cancelled,
|
||||
.target = drag_source_target,
|
||||
.action = drag_source_action,
|
||||
.dnd_drop_performed = drag_source_dnd_drop_performed,
|
||||
.dnd_finished = drag_source_dnd_finished,
|
||||
};
|
||||
|
||||
int
|
||||
_glfwPlatformStartDrag(_GLFWwindow* window, const GLFWdragitem* items, int item_count, const GLFWimage* thumbnail, GLFWDragOperationType operation) {
|
||||
if (!_glfw.wl.dataDeviceManager) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Data device manager not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_glfw.wl.dataDevice) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Data device not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clean up any existing drag operation
|
||||
if (_glfw.wl.drag.source) drag_source_cancelled(NULL, _glfw.wl.drag.source);
|
||||
|
||||
// Create the data source
|
||||
_glfw.wl.drag.source = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
|
||||
if (!_glfw.wl.drag.source) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create data source for drag");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the DND action based on operation type
|
||||
uint32_t wl_actions = 0;
|
||||
switch (operation) {
|
||||
case GLFW_DRAG_OPERATION_COPY: wl_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; break;
|
||||
case GLFW_DRAG_OPERATION_MOVE: wl_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; break;
|
||||
case GLFW_DRAG_OPERATION_GENERIC:
|
||||
wl_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; break;
|
||||
}
|
||||
wl_data_source_set_actions(_glfw.wl.drag.source, wl_actions);
|
||||
|
||||
// Allocate storage for drag data (copy the data)
|
||||
_glfw.wl.drag.items_data = calloc(item_count, sizeof(unsigned char*));
|
||||
_glfw.wl.drag.items_sizes = calloc(item_count, sizeof(size_t));
|
||||
_glfw.wl.drag.items_mimes = calloc(item_count, sizeof(char*));
|
||||
_glfw.wl.drag.item_count = item_count;
|
||||
|
||||
if (!_glfw.wl.drag.items_data || !_glfw.wl.drag.items_sizes || !_glfw.wl.drag.items_mimes) {
|
||||
drag_source_cancelled(NULL, _glfw.wl.drag.source);
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to allocate drag data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the data and offer MIME types
|
||||
for (int i = 0; i < item_count; i++) {
|
||||
_glfw.wl.drag.items_data[i] = malloc(items[i].data_size);
|
||||
if (!_glfw.wl.drag.items_data[i]) {
|
||||
drag_source_cancelled(NULL, _glfw.wl.drag.source);
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to allocate drag item data");
|
||||
return false;
|
||||
}
|
||||
memcpy(_glfw.wl.drag.items_data[i], items[i].data, items[i].data_size);
|
||||
_glfw.wl.drag.items_sizes[i] = items[i].data_size;
|
||||
_glfw.wl.drag.items_mimes[i] = _glfw_strdup(items[i].mime_type);
|
||||
if (!_glfw.wl.drag.items_mimes[i]) {
|
||||
drag_source_cancelled(NULL, _glfw.wl.drag.source);
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to allocate drag item MIME type");
|
||||
return false;
|
||||
}
|
||||
wl_data_source_offer(_glfw.wl.drag.source, items[i].mime_type);
|
||||
}
|
||||
|
||||
wl_data_source_add_listener(_glfw.wl.drag.source, &drag_source_listener, NULL);
|
||||
|
||||
// Set up the drag icon surface if thumbnail is provided
|
||||
struct wl_surface* icon_surface = NULL;
|
||||
struct wl_buffer* icon_buffer = NULL;
|
||||
|
||||
if (thumbnail && thumbnail->pixels) {
|
||||
icon_surface = wl_compositor_create_surface(_glfw.wl.compositor);
|
||||
if (icon_surface) {
|
||||
icon_buffer = createShmBuffer(thumbnail, false, true);
|
||||
if (icon_buffer) {
|
||||
wl_surface_attach(icon_surface, icon_buffer, 0, 0);
|
||||
wl_surface_commit(icon_surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start the drag operation
|
||||
wl_data_device_start_drag(_glfw.wl.dataDevice, _glfw.wl.drag.source,
|
||||
window->wl.surface, icon_surface,
|
||||
_glfw.wl.pointer_serial);
|
||||
|
||||
// Clean up icon resources (the compositor takes ownership)
|
||||
if (icon_buffer) {
|
||||
wl_buffer_destroy(icon_buffer);
|
||||
}
|
||||
if (icon_surface) {
|
||||
wl_surface_destroy(icon_surface);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
3
glfw/x11_init.c
vendored
3
glfw/x11_init.c
vendored
|
|
@ -506,10 +506,13 @@ static bool initExtensions(void)
|
|||
_glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False);
|
||||
_glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False);
|
||||
_glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False);
|
||||
_glfw.x11.XdndActionMove = XInternAtom(_glfw.x11.display, "XdndActionMove", False);
|
||||
_glfw.x11.XdndActionLink = XInternAtom(_glfw.x11.display, "XdndActionLink", False);
|
||||
_glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False);
|
||||
_glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False);
|
||||
_glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False);
|
||||
_glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False);
|
||||
_glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", False);
|
||||
|
||||
// ICCCM, EWMH and Motif window property atoms
|
||||
// These can be set safely even without WM support
|
||||
|
|
|
|||
16
glfw/x11_platform.h
vendored
16
glfw/x11_platform.h
vendored
|
|
@ -311,10 +311,13 @@ typedef struct _GLFWlibraryX11
|
|||
Atom XdndPosition;
|
||||
Atom XdndStatus;
|
||||
Atom XdndActionCopy;
|
||||
Atom XdndActionMove;
|
||||
Atom XdndActionLink;
|
||||
Atom XdndDrop;
|
||||
Atom XdndFinished;
|
||||
Atom XdndSelection;
|
||||
Atom XdndTypeList;
|
||||
Atom XdndLeave;
|
||||
|
||||
// Selection (clipboard) atoms
|
||||
Atom TARGETS;
|
||||
|
|
@ -381,8 +384,21 @@ typedef struct _GLFWlibraryX11
|
|||
Window source;
|
||||
char format[128];
|
||||
int format_priority;
|
||||
Window target_window; // For drag events: the window being dragged over
|
||||
} xdnd;
|
||||
|
||||
// Drag source state
|
||||
struct {
|
||||
Window source_window;
|
||||
unsigned char** items_data;
|
||||
size_t* items_sizes;
|
||||
char** items_mimes;
|
||||
int item_count;
|
||||
Atom* type_atoms;
|
||||
Atom action_atom; // XdndActionCopy, XdndActionMove, or XdndActionLink
|
||||
bool active;
|
||||
} drag;
|
||||
|
||||
struct {
|
||||
void* handle;
|
||||
PFN_XcursorImageCreate ImageCreate;
|
||||
|
|
|
|||
134
glfw/x11_window.c
vendored
134
glfw/x11_window.c
vendored
|
|
@ -1833,12 +1833,17 @@ static void processEvent(XEvent *event)
|
|||
|
||||
_glfw.x11.xdnd.source = event->xclient.data.l[0];
|
||||
_glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
|
||||
_glfw.x11.xdnd.target_window = window->x11.handle;
|
||||
memset(_glfw.x11.xdnd.format, 0, sizeof(_glfw.x11.xdnd.format));
|
||||
_glfw.x11.xdnd.format_priority = 0;
|
||||
|
||||
if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
|
||||
return;
|
||||
|
||||
// Call the drag enter callback first
|
||||
// Position is not known yet at enter time, will be updated with XdndPosition
|
||||
int accepted = _glfwInputDragEvent(window, GLFW_DRAG_ENTER, 0, 0);
|
||||
|
||||
if (list)
|
||||
{
|
||||
count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
|
||||
|
|
@ -1852,7 +1857,7 @@ static void processEvent(XEvent *event)
|
|||
formats = (Atom*) event->xclient.data.l + 2;
|
||||
}
|
||||
char **atom_names = calloc(count, sizeof(char*));
|
||||
if (atom_names) {
|
||||
if (atom_names && accepted) {
|
||||
get_atom_names(formats, count, atom_names);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
|
|
@ -1867,6 +1872,10 @@ static void processEvent(XEvent *event)
|
|||
}
|
||||
}
|
||||
free(atom_names);
|
||||
} else if (atom_names) {
|
||||
for (i = 0; i < count; i++)
|
||||
if (atom_names[i]) XFree(atom_names[i]);
|
||||
free(atom_names);
|
||||
}
|
||||
|
||||
if (list && formats)
|
||||
|
|
@ -1908,6 +1917,13 @@ static void processEvent(XEvent *event)
|
|||
XFlush(_glfw.x11.display);
|
||||
}
|
||||
}
|
||||
else if (event->xclient.message_type == _glfw.x11.XdndLeave)
|
||||
{
|
||||
// The drag operation has left the window
|
||||
_glfwInputDragEvent(window, GLFW_DRAG_LEAVE, 0, 0);
|
||||
_glfw.x11.xdnd.source = None;
|
||||
_glfw.x11.xdnd.target_window = None;
|
||||
}
|
||||
else if (event->xclient.message_type == _glfw.x11.XdndPosition)
|
||||
{
|
||||
// The drag operation has moved over the window
|
||||
|
|
@ -1932,6 +1948,9 @@ static void processEvent(XEvent *event)
|
|||
|
||||
_glfwInputCursorPos(window, xpos, ypos);
|
||||
|
||||
// Call the drag move callback
|
||||
_glfwInputDragEvent(window, GLFW_DRAG_MOVE, xpos, ypos);
|
||||
|
||||
XEvent reply = { ClientMessage };
|
||||
reply.xclient.window = _glfw.x11.xdnd.source;
|
||||
reply.xclient.message_type = _glfw.x11.XdndStatus;
|
||||
|
|
@ -3616,4 +3635,117 @@ GLFWAPI int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)
|
|||
return XSetCommand(_glfw.x11.display, window->x11.handle, argv, argc);
|
||||
}
|
||||
|
||||
// Helper function to clean up drag source data
|
||||
static void cleanupDragSource(void) {
|
||||
if (_glfw.x11.drag.items_data) {
|
||||
for (int i = 0; i < _glfw.x11.drag.item_count; i++) {
|
||||
free(_glfw.x11.drag.items_data[i]);
|
||||
free(_glfw.x11.drag.items_mimes[i]);
|
||||
}
|
||||
free(_glfw.x11.drag.items_data);
|
||||
free(_glfw.x11.drag.items_sizes);
|
||||
free(_glfw.x11.drag.items_mimes);
|
||||
free(_glfw.x11.drag.type_atoms);
|
||||
_glfw.x11.drag.items_data = NULL;
|
||||
_glfw.x11.drag.items_sizes = NULL;
|
||||
_glfw.x11.drag.items_mimes = NULL;
|
||||
_glfw.x11.drag.type_atoms = NULL;
|
||||
_glfw.x11.drag.item_count = 0;
|
||||
_glfw.x11.drag.source_window = None;
|
||||
_glfw.x11.drag.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
int _glfwPlatformStartDrag(_GLFWwindow* window,
|
||||
const GLFWdragitem* items,
|
||||
int item_count,
|
||||
const GLFWimage* thumbnail UNUSED,
|
||||
GLFWDragOperationType operation) {
|
||||
// Clean up any existing drag operation
|
||||
cleanupDragSource();
|
||||
|
||||
// Set the drag action based on operation type
|
||||
switch (operation) {
|
||||
case GLFW_DRAG_OPERATION_COPY:
|
||||
_glfw.x11.drag.action_atom = _glfw.x11.XdndActionCopy;
|
||||
break;
|
||||
case GLFW_DRAG_OPERATION_MOVE:
|
||||
_glfw.x11.drag.action_atom = _glfw.x11.XdndActionMove;
|
||||
break;
|
||||
case GLFW_DRAG_OPERATION_GENERIC:
|
||||
_glfw.x11.drag.action_atom = _glfw.x11.XdndActionCopy;
|
||||
break;
|
||||
}
|
||||
|
||||
// Allocate storage for drag data (copy the data)
|
||||
_glfw.x11.drag.items_data = calloc(item_count, sizeof(unsigned char*));
|
||||
_glfw.x11.drag.items_sizes = calloc(item_count, sizeof(size_t));
|
||||
_glfw.x11.drag.items_mimes = calloc(item_count, sizeof(char*));
|
||||
_glfw.x11.drag.type_atoms = calloc(item_count, sizeof(Atom));
|
||||
_glfw.x11.drag.item_count = item_count;
|
||||
_glfw.x11.drag.source_window = window->x11.handle;
|
||||
|
||||
if (!_glfw.x11.drag.items_data || !_glfw.x11.drag.items_sizes ||
|
||||
!_glfw.x11.drag.items_mimes || !_glfw.x11.drag.type_atoms) {
|
||||
cleanupDragSource();
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to allocate drag data");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the data and create atoms for MIME types
|
||||
for (int i = 0; i < item_count; i++) {
|
||||
_glfw.x11.drag.items_data[i] = malloc(items[i].data_size);
|
||||
if (!_glfw.x11.drag.items_data[i]) {
|
||||
cleanupDragSource();
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to allocate drag item data");
|
||||
return false;
|
||||
}
|
||||
memcpy(_glfw.x11.drag.items_data[i], items[i].data, items[i].data_size);
|
||||
_glfw.x11.drag.items_sizes[i] = items[i].data_size;
|
||||
_glfw.x11.drag.items_mimes[i] = _glfw_strdup(items[i].mime_type);
|
||||
if (!_glfw.x11.drag.items_mimes[i]) {
|
||||
cleanupDragSource();
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to allocate drag item MIME type");
|
||||
return false;
|
||||
}
|
||||
_glfw.x11.drag.type_atoms[i] = XInternAtom(_glfw.x11.display, items[i].mime_type, False);
|
||||
}
|
||||
|
||||
// Set up XdndTypeList property if we have more than 3 types
|
||||
if (item_count > 3) {
|
||||
XChangeProperty(_glfw.x11.display, window->x11.handle,
|
||||
_glfw.x11.XdndTypeList, XA_ATOM, 32, PropModeReplace,
|
||||
(unsigned char*)_glfw.x11.drag.type_atoms, item_count);
|
||||
}
|
||||
|
||||
// Take ownership of XdndSelection
|
||||
XSetSelectionOwner(_glfw.x11.display, _glfw.x11.XdndSelection,
|
||||
window->x11.handle, CurrentTime);
|
||||
|
||||
if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.XdndSelection) != window->x11.handle) {
|
||||
cleanupDragSource();
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to acquire XdndSelection ownership");
|
||||
return false;
|
||||
}
|
||||
|
||||
_glfw.x11.drag.active = true;
|
||||
|
||||
// Note: The actual drag operation in X11 requires grabbing the pointer and tracking
|
||||
// mouse movement to send XdndEnter/Position/Leave/Drop messages to target windows.
|
||||
// This is a complex state machine that requires:
|
||||
// 1. Grabbing the pointer with XGrabPointer
|
||||
// 2. Tracking mouse movement
|
||||
// 3. Finding window under cursor with XTranslateCoordinates
|
||||
// 4. Sending XdndEnter when entering a new window
|
||||
// 5. Sending XdndPosition as the cursor moves
|
||||
// 6. Sending XdndLeave when leaving a window
|
||||
// 7. Sending XdndDrop on button release
|
||||
// 8. Responding to SelectionRequest events with the drag data
|
||||
//
|
||||
// For a complete implementation, this would need to be integrated with the
|
||||
// event loop. For now, we set up the data source so the application can
|
||||
// handle its own drag tracking if needed.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
6
kitty/glfw-wrapper.c
generated
6
kitty/glfw-wrapper.c
generated
|
|
@ -359,6 +359,12 @@ load_glfw(const char* path) {
|
|||
*(void **) (&glfwSetLiveResizeCallback_impl) = dlsym(handle, "glfwSetLiveResizeCallback");
|
||||
if (glfwSetLiveResizeCallback_impl == NULL) fail("Failed to load glfw function glfwSetLiveResizeCallback with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwSetDragCallback_impl) = dlsym(handle, "glfwSetDragCallback");
|
||||
if (glfwSetDragCallback_impl == NULL) fail("Failed to load glfw function glfwSetDragCallback with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwStartDrag_impl) = dlsym(handle, "glfwStartDrag");
|
||||
if (glfwStartDrag_impl == NULL) fail("Failed to load glfw function glfwStartDrag with error: %s", dlerror());
|
||||
|
||||
*(void **) (&glfwJoystickPresent_impl) = dlsym(handle, "glfwJoystickPresent");
|
||||
if (glfwJoystickPresent_impl == NULL) fail("Failed to load glfw function glfwJoystickPresent with error: %s", dlerror());
|
||||
|
||||
|
|
|
|||
83
kitty/glfw-wrapper.h
generated
83
kitty/glfw-wrapper.h
generated
|
|
@ -1521,6 +1521,81 @@ typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
|
|||
*/
|
||||
typedef int (* GLFWdropfun)(GLFWwindow*, const char *, const char*, size_t);
|
||||
|
||||
/*! @brief Drag event types.
|
||||
*
|
||||
* These constants are used to identify the type of drag event.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef enum {
|
||||
/*! The drag operation entered the window. */
|
||||
GLFW_DRAG_ENTER = 1,
|
||||
/*! The drag operation moved within the window. */
|
||||
GLFW_DRAG_MOVE = 2,
|
||||
/*! The drag operation left the window. */
|
||||
GLFW_DRAG_LEAVE = 3
|
||||
} GLFWDragEventType;
|
||||
|
||||
/*! @brief Drag operation types.
|
||||
*
|
||||
* These constants specify the type of drag operation (copy, move, or generic).
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef enum {
|
||||
/*! Move the dragged data to the destination. */
|
||||
GLFW_DRAG_OPERATION_MOVE = 1,
|
||||
/*! Copy the dragged data to the destination. */
|
||||
GLFW_DRAG_OPERATION_COPY = 2,
|
||||
/*! Generic drag operation (platform decides semantics). */
|
||||
GLFW_DRAG_OPERATION_GENERIC = 3
|
||||
} GLFWDragOperationType;
|
||||
|
||||
/*! @brief Drag data item.
|
||||
*
|
||||
* This structure describes a single item of drag data with its MIME type.
|
||||
*
|
||||
* @sa @ref drag_start
|
||||
* @sa @ref glfwStartDrag
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef struct GLFWdragitem {
|
||||
/*! The MIME type of this data item (e.g., "text/plain", "image/png"). */
|
||||
const char* mime_type;
|
||||
/*! Pointer to the binary data. */
|
||||
const unsigned char* data;
|
||||
/*! Size of the data in bytes. */
|
||||
size_t data_size;
|
||||
} GLFWdragitem;
|
||||
|
||||
/*! @brief The function pointer type for drag event callbacks.
|
||||
*
|
||||
* This is the function pointer type for drag event callbacks. A drag event
|
||||
* callback function has the following signature:
|
||||
* @code
|
||||
* int function_name(GLFWwindow* window, int event, double xpos, double ypos)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] window The window that received the drag event.
|
||||
* @param[in] event The drag event type: @ref GLFW_DRAG_ENTER, @ref GLFW_DRAG_MOVE,
|
||||
* or @ref GLFW_DRAG_LEAVE.
|
||||
* @param[in] xpos The x-coordinate of the drag position in window coordinates.
|
||||
* @param[in] ypos The y-coordinate of the drag position in window coordinates.
|
||||
* @return For @ref GLFW_DRAG_ENTER events, return non-zero to accept the drag
|
||||
* or zero to reject it. Return value is ignored for other event types.
|
||||
*
|
||||
* @sa @ref drag_events
|
||||
* @sa @ref glfwSetDragCallback
|
||||
*
|
||||
* @since Added in version 4.0.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
typedef int (* GLFWdragfun)(GLFWwindow*, GLFWDragEventType event, double xpos, double ypos);
|
||||
|
||||
typedef void (* GLFWliveresizefun)(GLFWwindow*, bool);
|
||||
|
||||
/*! @brief The function pointer type for monitor configuration callbacks.
|
||||
|
|
@ -2202,6 +2277,14 @@ typedef GLFWliveresizefun (*glfwSetLiveResizeCallback_func)(GLFWwindow*, GLFWliv
|
|||
GFW_EXTERN glfwSetLiveResizeCallback_func glfwSetLiveResizeCallback_impl;
|
||||
#define glfwSetLiveResizeCallback glfwSetLiveResizeCallback_impl
|
||||
|
||||
typedef GLFWdragfun (*glfwSetDragCallback_func)(GLFWwindow*, GLFWdragfun);
|
||||
GFW_EXTERN glfwSetDragCallback_func glfwSetDragCallback_impl;
|
||||
#define glfwSetDragCallback glfwSetDragCallback_impl
|
||||
|
||||
typedef int (*glfwStartDrag_func)(GLFWwindow*, const GLFWdragitem*, int, const GLFWimage*, GLFWDragOperationType);
|
||||
GFW_EXTERN glfwStartDrag_func glfwStartDrag_impl;
|
||||
#define glfwStartDrag glfwStartDrag_impl
|
||||
|
||||
typedef int (*glfwJoystickPresent_func)(int);
|
||||
GFW_EXTERN glfwJoystickPresent_func glfwJoystickPresent_impl;
|
||||
#define glfwJoystickPresent glfwJoystickPresent_impl
|
||||
|
|
|
|||
11
kitty/glfw.c
11
kitty/glfw.c
|
|
@ -641,6 +641,16 @@ window_focus_callback(GLFWwindow *w, int focused) {
|
|||
#undef osw
|
||||
}
|
||||
|
||||
static int
|
||||
drag_callback(GLFWwindow *w, GLFWDragEventType event, double xpos, double ypos) {
|
||||
(void)event; (void)xpos; (void)ypos;
|
||||
if (!set_callback_window(w)) return 0;
|
||||
if (event == GLFW_DRAG_ENTER) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
drop_callback(GLFWwindow *w, const char *mime, const char *data, size_t sz) {
|
||||
if (!set_callback_window(w)) return 0;
|
||||
|
|
@ -1529,6 +1539,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
|
|||
glfwSetScrollCallback(glfw_window, scroll_callback);
|
||||
glfwSetKeyboardCallback(glfw_window, key_callback);
|
||||
glfwSetDropCallback(glfw_window, drop_callback);
|
||||
glfwSetDragCallback(glfw_window, drag_callback);
|
||||
monotonic_t now = monotonic();
|
||||
w->is_focused = true;
|
||||
w->cursor_blink_zero_time = now;
|
||||
|
|
|
|||
Loading…
Reference in a new issue